mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 03:25:29 +01:00
Dynamically add or remove injected hooks depending on the listeners.
This occurs whenever a listener is added or removed. A listener can now specify whether or not it's listening for packets sent BEFORE a player has logged in (every packet upto Packet1Login and a few more), or AFTER. By default, listeners only receive notifcation of packets sent and received after. ProtocolLib will now only hook NetLoginHandler if there's a login listener, and vice versa. Thus, the new login feature will only tax the server if another plugin is using it. In addition, ProtocolLib will not consume any resources when it's not serving any listeners.
This commit is contained in:
parent
ecdc9b4b6c
commit
476a918794
@ -58,6 +58,9 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
private int tickCounter = 0;
|
||||
private static final int ASYNC_PACKET_DELAY = 1;
|
||||
|
||||
// Used for debugging
|
||||
private boolean debugListener;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
logger = getLoggerSafely();
|
||||
@ -80,14 +83,10 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
|
||||
// Player login and logout events
|
||||
protocolManager.registerEvents(manager, this);
|
||||
|
||||
// Inject our hook into already existing players
|
||||
protocolManager.initializePlayers(server.getOnlinePlayers());
|
||||
|
||||
|
||||
// Worker that ensures that async packets are eventually sent
|
||||
createAsyncTask(server);
|
||||
|
||||
addDebugListener();
|
||||
//toggleDebugListener();
|
||||
|
||||
// Try to enable statistics
|
||||
try {
|
||||
@ -98,19 +97,38 @@ public class ProtocolLibrary extends JavaPlugin {
|
||||
logger.log(Level.SEVERE, "Metrics cannot be enabled. Incompatible Bukkit version.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDebugListener() {
|
||||
// DEBUG DEBUG
|
||||
protocolManager.addPacketListener(new MonitorAdapter(this, ConnectionSide.BOTH, logger) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
System.out.println("RECEIVING " + event.getPacketID() + " from " + event.getPlayer().getName());
|
||||
};
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
System.out.println("SENDING " + event.getPacketID() + " to " + event.getPlayer().getName());
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggle a listener that prints every sent and received packet.
|
||||
*/
|
||||
void toggleDebugListener() {
|
||||
|
||||
if (debugListener) {
|
||||
protocolManager.removePacketListeners(this);
|
||||
} else {
|
||||
// DEBUG DEBUG
|
||||
protocolManager.addPacketListener(new MonitorAdapter(this, ConnectionSide.BOTH, logger) {
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
Object handle = event.getPacket().getHandle();
|
||||
|
||||
System.out.println(String.format(
|
||||
"RECEIVING %s@%s from %s.",
|
||||
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
||||
));
|
||||
};
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
Object handle = event.getPacket().getHandle();
|
||||
|
||||
System.out.println(String.format(
|
||||
"SENDING %s@%s from %s.",
|
||||
handle.getClass().getSimpleName(), handle.hashCode(), event.getPlayer().getName()
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
debugListener = !debugListener;
|
||||
}
|
||||
|
||||
private void createAsyncTask(Server server) {
|
||||
|
@ -67,7 +67,7 @@ class NullPacketListener implements PacketListener {
|
||||
|
||||
private ListeningWhitelist cloneWhitelist(ListenerPriority priority, ListeningWhitelist whitelist) {
|
||||
if (whitelist != null)
|
||||
return new ListeningWhitelist(priority, whitelist.getWhitelist());
|
||||
return new ListeningWhitelist(priority, whitelist.getWhitelist(), whitelist.getGamePhase());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
@ -36,6 +37,7 @@ public class ListeningWhitelist {
|
||||
|
||||
private ListenerPriority priority;
|
||||
private Set<Integer> whitelist;
|
||||
private GamePhase gamePhase;
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
@ -43,8 +45,19 @@ public class ListeningWhitelist {
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
*/
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist) {
|
||||
this(priority, whitelist, GamePhase.PLAYING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - set of IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
*/
|
||||
public ListeningWhitelist(ListenerPriority priority, Set<Integer> whitelist, GamePhase gamePhase) {
|
||||
this.priority = priority;
|
||||
this.whitelist = whitelist;
|
||||
this.gamePhase = gamePhase;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,6 +68,19 @@ public class ListeningWhitelist {
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer... whitelist) {
|
||||
this.priority = priority;
|
||||
this.whitelist = Sets.newHashSet(whitelist);
|
||||
this.gamePhase = GamePhase.PLAYING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a packet whitelist for a given priority with a set of packet IDs.
|
||||
* @param priority - the listener priority.
|
||||
* @param whitelist - list of packet IDs to observe/enable.
|
||||
* @param gamePhase - which game phase to receieve notifications on.
|
||||
*/
|
||||
public ListeningWhitelist(ListenerPriority priority, Integer[] whitelist, GamePhase gamePhase) {
|
||||
this.priority = priority;
|
||||
this.whitelist = Sets.newHashSet(whitelist);
|
||||
this.gamePhase = gamePhase;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,10 +106,18 @@ public class ListeningWhitelist {
|
||||
public Set<Integer> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve which game phase this listener is active under.
|
||||
* @return The active game phase.
|
||||
*/
|
||||
public GamePhase getGamePhase() {
|
||||
return gamePhase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return Objects.hashCode(priority, whitelist);
|
||||
return Objects.hashCode(priority, whitelist, gamePhase);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,8 +9,8 @@ import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
|
||||
/**
|
||||
@ -38,14 +38,14 @@ public abstract class MonitorAdapter implements PacketListener {
|
||||
// Recover in case something goes wrong
|
||||
try {
|
||||
if (side.isForServer())
|
||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getSupported());
|
||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getSupported(), GamePhase.BOTH);
|
||||
if (side.isForClient())
|
||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getSupported());
|
||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getSupported(), GamePhase.BOTH);
|
||||
} catch (FieldAccessException e) {
|
||||
if (side.isForServer())
|
||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values());
|
||||
this.sending = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Server.getRegistry().values(), GamePhase.BOTH);
|
||||
if (side.isForClient())
|
||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values());
|
||||
this.receiving = new ListeningWhitelist(ListenerPriority.MONITOR, Packets.Client.getRegistry().values(), GamePhase.BOTH);
|
||||
logger.log(Level.WARNING, "Defaulting to 1.3 packets.", e);
|
||||
}
|
||||
}
|
||||
@ -63,16 +63,6 @@ public abstract class MonitorAdapter implements PacketListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
// Empty for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
// Empty for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListeningWhitelist getSendingWhitelist() {
|
||||
return sending;
|
||||
|
@ -21,9 +21,12 @@ import java.util.Set;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
|
||||
/**
|
||||
* Represents a packet listener with useful constructors.
|
||||
*
|
||||
* <p>
|
||||
* Remember to override onPacketReceiving() and onPacketSending(), depending on the ConnectionSide.
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class PacketAdapter implements PacketListener {
|
||||
@ -51,7 +54,20 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, listenerPriority, packets.toArray(new Integer[0]));
|
||||
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optmize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Set<Integer> packets) {
|
||||
this(plugin, connectionSide, listenerPriority, gamePhase, packets.toArray(new Integer[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,20 +78,36 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, Integer... packets) {
|
||||
this(plugin, connectionSide, listenerPriority, GamePhase.PLAYING, packets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a packet listener for a single connection side.
|
||||
* <p>
|
||||
* The game phase is used to optmize performance. A listener should only choose BOTH or LOGIN if it's absolutely necessary.
|
||||
* @param plugin - the plugin that spawned this listener.
|
||||
* @param connectionSide - the packet type the listener is looking for.
|
||||
* @param listenerPriority - the event priority.
|
||||
* @param gamePhase - which game phase this listener is active under.
|
||||
* @param packets - the packet IDs the listener is looking for.
|
||||
*/
|
||||
public PacketAdapter(Plugin plugin, ConnectionSide connectionSide, ListenerPriority listenerPriority, GamePhase gamePhase, Integer... packets) {
|
||||
if (plugin == null)
|
||||
throw new IllegalArgumentException("plugin cannot be null");
|
||||
if (connectionSide == null)
|
||||
throw new IllegalArgumentException("connectionSide cannot be null");
|
||||
if (listenerPriority == null)
|
||||
throw new IllegalArgumentException("listenerPriority cannot be null");
|
||||
if (gamePhase == null)
|
||||
throw new IllegalArgumentException("gamePhase cannot be NULL");
|
||||
if (packets == null)
|
||||
throw new IllegalArgumentException("packets cannot be null");
|
||||
|
||||
// Add whitelists
|
||||
if (connectionSide.isForServer())
|
||||
sendingWhitelist = new ListeningWhitelist(listenerPriority, packets);
|
||||
sendingWhitelist = new ListeningWhitelist(listenerPriority, packets, gamePhase);
|
||||
if (connectionSide.isForClient())
|
||||
receivingWhitelist = new ListeningWhitelist(listenerPriority, packets);
|
||||
receivingWhitelist = new ListeningWhitelist(listenerPriority, packets, gamePhase);
|
||||
|
||||
this.plugin = plugin;
|
||||
this.connectionSide = connectionSide;
|
||||
@ -83,12 +115,14 @@ public abstract class PacketAdapter implements PacketListener {
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event) {
|
||||
// Default is to do nothing
|
||||
// Lets prevent some bugs
|
||||
throw new IllegalStateException("Override onPacketReceiving to get notifcations of received packets!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event) {
|
||||
// And here too
|
||||
// Lets prevent some bugs
|
||||
throw new IllegalStateException("Override onPacketSending to get notifcations of sent packets!");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,39 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
/**
|
||||
* The current player phase. This is used to limit the number of different injections.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public enum GamePhase {
|
||||
/**
|
||||
* Only listen for packets sent or received before a player has logged in.
|
||||
*/
|
||||
LOGIN,
|
||||
|
||||
/**
|
||||
* Only listen for packets sent or received after a player has logged in.
|
||||
*/
|
||||
PLAYING,
|
||||
|
||||
/**
|
||||
* Listen for every sent and received packet.
|
||||
*/
|
||||
BOTH;
|
||||
|
||||
/**
|
||||
* Determine if the current value represents the login phase.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasLogin() {
|
||||
return this == LOGIN || this == BOTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current value represents the playing phase.
|
||||
* @return TRUE if it does, FALSE otherwise.
|
||||
*/
|
||||
public boolean hasPlaying() {
|
||||
return this == PLAYING || this == BOTH;
|
||||
}
|
||||
}
|
@ -23,9 +23,12 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
@ -52,6 +55,7 @@ import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public final class PacketFilterManager implements ProtocolManager, ListenerInvoker {
|
||||
@ -109,6 +113,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
// Error logger
|
||||
private Logger logger;
|
||||
|
||||
// The current server
|
||||
private Server server;
|
||||
|
||||
// The async packet handler
|
||||
private AsyncFilterManager asyncFilterManager;
|
||||
|
||||
@ -116,6 +123,9 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
private Set<Integer> serverPackets;
|
||||
private Set<Integer> clientPackets;
|
||||
|
||||
// Ensure that we're not performing too may injections
|
||||
private AtomicInteger phaseLoginCount = new AtomicInteger(0);
|
||||
private AtomicInteger phasePlayingCount = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* Only create instances of this class if protocol lib is disabled.
|
||||
@ -126,11 +136,26 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
if (classLoader == null)
|
||||
throw new IllegalArgumentException("classLoader cannot be NULL.");
|
||||
|
||||
// Used to determine if injection is needed
|
||||
Predicate<GamePhase> isInjectionNecessary = new Predicate<GamePhase>() {
|
||||
@Override
|
||||
public boolean apply(@Nullable GamePhase phase) {
|
||||
boolean result = true;
|
||||
|
||||
if (phase.hasLogin())
|
||||
result &= getPhaseLoginCount() > 0;
|
||||
if (phase.hasPlaying())
|
||||
result &= getPhasePlayingCount() > 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// Initialize values
|
||||
this.server = server;
|
||||
this.classLoader = classLoader;
|
||||
this.logger = logger;
|
||||
this.playerInjection = new PlayerInjectionHandler(classLoader, logger, this, server);
|
||||
this.playerInjection = new PlayerInjectionHandler(classLoader, logger, isInjectionNecessary, this, server);
|
||||
this.packetInjector = new PacketInjector(classLoader, this, playerInjection);
|
||||
this.asyncFilterManager = new AsyncFilterManager(logger, server.getScheduler(), this);
|
||||
|
||||
@ -210,11 +235,51 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
enablePacketFilters(listener, ConnectionSide.CLIENT_SIDE, receiving.getWhitelist());
|
||||
}
|
||||
|
||||
// Increment phases too
|
||||
if (hasSending)
|
||||
incrementPhases(sending.getGamePhase());
|
||||
if (hasReceiving)
|
||||
incrementPhases(receiving.getGamePhase());
|
||||
|
||||
// Inform our injected hooks
|
||||
packetListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to handle the different game phases of a added listener.
|
||||
* @param phase - listener's game game phase.
|
||||
*/
|
||||
private void incrementPhases(GamePhase phase) {
|
||||
if (phase.hasLogin())
|
||||
phaseLoginCount.incrementAndGet();
|
||||
|
||||
// We may have to inject into every current player
|
||||
if (phase.hasPlaying()) {
|
||||
if (phasePlayingCount.incrementAndGet() == 1) {
|
||||
// Inject our hook into already existing players
|
||||
initializePlayers(server.getOnlinePlayers());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to handle the different game phases of a removed listener.
|
||||
* @param phase - listener's game game phase.
|
||||
*/
|
||||
private void decrementPhases(GamePhase phase) {
|
||||
if (phase.hasLogin())
|
||||
phaseLoginCount.decrementAndGet();
|
||||
|
||||
// We may have to inject into every current player
|
||||
if (phase.hasPlaying()) {
|
||||
if (phasePlayingCount.decrementAndGet() == 0) {
|
||||
// Inject our hook into already existing players
|
||||
uninitializePlayers(server.getOnlinePlayers());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the packet IDs in a whitelist is valid.
|
||||
* @param listener - the listener that will be mentioned in the error.
|
||||
@ -246,11 +311,15 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
if (!packetListeners.remove(listener))
|
||||
return;
|
||||
|
||||
// Add listeners
|
||||
if (sending != null && sending.isEnabled())
|
||||
// Remove listeners and phases
|
||||
if (sending != null && sending.isEnabled()) {
|
||||
sendingRemoved = sendingListeners.removeListener(listener, sending);
|
||||
if (receiving != null && receiving.isEnabled())
|
||||
decrementPhases(sending.getGamePhase());
|
||||
}
|
||||
if (receiving != null && receiving.isEnabled()) {
|
||||
receivingRemoved = recievedListeners.removeListener(listener, receiving);
|
||||
decrementPhases(receiving.getGamePhase());
|
||||
}
|
||||
|
||||
// Remove hooks, if needed
|
||||
if (sendingRemoved != null && sendingRemoved.size() > 0)
|
||||
@ -470,7 +539,16 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
for (Player player : players)
|
||||
playerInjection.injectPlayer(player);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uninitialize the packet injection of every player.
|
||||
* @param players - list of players to uninject.
|
||||
*/
|
||||
public void uninitializePlayers(Player[] players) {
|
||||
for (Player player : players)
|
||||
playerInjection.uninjectPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this protocol manager on Bukkit.
|
||||
* @param manager - Bukkit plugin manager that provides player join/leave events.
|
||||
@ -488,6 +566,7 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
// This call will be ignored if no listeners are registered
|
||||
playerInjection.injectPlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@ -512,6 +591,22 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of listeners that expect packets during playing.
|
||||
* @return Number of listeners.
|
||||
*/
|
||||
private int getPhasePlayingCount() {
|
||||
return phasePlayingCount.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of listeners that expect packets during login.
|
||||
* @return Number of listeners
|
||||
*/
|
||||
private int getPhaseLoginCount() {
|
||||
return phaseLoginCount.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPacketID(Packet packet) {
|
||||
if (packet == null)
|
||||
|
@ -198,9 +198,13 @@ class PacketInjector {
|
||||
|
||||
// Called from the ReadPacketModified monitor
|
||||
PacketEvent packetRecieved(PacketContainer packet, DataInputStream input) {
|
||||
|
||||
Player client = playerInjection.getPlayerByConnection(input);
|
||||
return packetRecieved(packet, client);
|
||||
|
||||
// Never invoke a event if we don't know where it's from
|
||||
if (client != null)
|
||||
return packetRecieved(packet, client);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,10 +102,11 @@ class ReadPacketModifier implements MethodInterceptor {
|
||||
// Let the people know
|
||||
PacketContainer container = new PacketContainer(packetID, (Packet) thisObj);
|
||||
PacketEvent event = packetInjector.packetRecieved(container, input);
|
||||
Packet result = event.getPacket().getHandle();
|
||||
|
||||
// Handle override
|
||||
if (event != null) {
|
||||
Packet result = event.getPacket().getHandle();
|
||||
|
||||
if (event.isCancelled()) {
|
||||
override.put(thisObj, null);
|
||||
} else if (!objectEquals(thisObj, result)) {
|
||||
|
@ -4,11 +4,13 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.player.TemporaryPlayerFactory.InjectContainer;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@ -25,12 +27,16 @@ class NetLoginInjector {
|
||||
private PlayerInjectionHandler injectionHandler;
|
||||
private Server server;
|
||||
|
||||
// The current logger
|
||||
private Logger logger;
|
||||
|
||||
private ReadWriteLock injectionLock = new ReentrantReadWriteLock();
|
||||
|
||||
// Used to create fake players
|
||||
private TemporaryPlayerFactory tempPlayerFactory = new TemporaryPlayerFactory();
|
||||
|
||||
public NetLoginInjector(PlayerInjectionHandler injectionHandler, Server server) {
|
||||
public NetLoginInjector(Logger logger, PlayerInjectionHandler injectionHandler, Server server) {
|
||||
this.logger = logger;
|
||||
this.injectionHandler = injectionHandler;
|
||||
this.server = server;
|
||||
}
|
||||
@ -45,6 +51,10 @@ class NetLoginInjector {
|
||||
injectionLock.writeLock().lock();
|
||||
|
||||
try {
|
||||
// Make sure we actually need to inject during this phase
|
||||
if (!injectionHandler.isInjectionNecessary(GamePhase.LOGIN))
|
||||
return inserting;
|
||||
|
||||
Player fakePlayer = tempPlayerFactory.createTemporaryPlayer(server);
|
||||
PlayerInjector injector = injectionHandler.injectPlayer(fakePlayer, inserting, GamePhase.LOGIN);
|
||||
injector.updateOnLogin = true;
|
||||
@ -56,6 +66,11 @@ class NetLoginInjector {
|
||||
// NetServerInjector can never work (currently), so we don't need to replace the NetLoginHandler
|
||||
return inserting;
|
||||
|
||||
} catch (Throwable e) {
|
||||
// Minecraft can't handle this, so we'll deal with it here
|
||||
logger.log(Level.WARNING, "Unable to hook NetLoginHandler.", e);
|
||||
return inserting;
|
||||
|
||||
} finally {
|
||||
injectionLock.writeLock().unlock();
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ import org.bukkit.entity.Player;
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
|
@ -36,9 +36,9 @@ import org.bukkit.entity.Player;
|
||||
import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||
|
||||
/**
|
||||
* Injection method that overrides the NetworkHandler itself, and it's sendPacket-method.
|
||||
|
@ -34,9 +34,9 @@ import net.sf.cglib.proxy.NoOp;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.ObjectCloner;
|
||||
@ -264,7 +264,7 @@ public class NetworkServerInjector extends PlayerInjector {
|
||||
@Override
|
||||
public boolean canInject(GamePhase phase) {
|
||||
// Doesn't work when logging in
|
||||
return phase == GamePhase.PLAYING || phase == GamePhase.CLOSING;
|
||||
return phase == GamePhase.PLAYING;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,9 +38,11 @@ import org.bukkit.entity.Player;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@ -51,16 +53,6 @@ import com.google.common.collect.Maps;
|
||||
*/
|
||||
public class PlayerInjectionHandler {
|
||||
|
||||
/**
|
||||
* The current player phase.
|
||||
* @author Kristian
|
||||
*/
|
||||
enum GamePhase {
|
||||
LOGIN,
|
||||
PLAYING,
|
||||
CLOSING,
|
||||
}
|
||||
|
||||
// Server connection injection
|
||||
private InjectedServerConnection serverInjection;
|
||||
|
||||
@ -93,11 +85,17 @@ public class PlayerInjectionHandler {
|
||||
// The class loader we're using
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, ListenerInvoker invoker, Server server) {
|
||||
// Used to filter injection attempts
|
||||
private Predicate<GamePhase> injectionFilter;
|
||||
|
||||
public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, Predicate<GamePhase> injectionFilter,
|
||||
ListenerInvoker invoker, Server server) {
|
||||
|
||||
this.classLoader = classLoader;
|
||||
this.logger = logger;
|
||||
this.invoker = invoker;
|
||||
this.netLoginInjector = new NetLoginInjector(this, server);
|
||||
this.injectionFilter = injectionFilter;
|
||||
this.netLoginInjector = new NetLoginInjector(logger, this, server);
|
||||
this.serverInjection = new InjectedServerConnection(logger, server, netLoginInjector);
|
||||
serverInjection.injectList();
|
||||
}
|
||||
@ -185,26 +183,44 @@ public class PlayerInjectionHandler {
|
||||
|
||||
/**
|
||||
* Initialize a player hook, allowing us to read server packets.
|
||||
* <p>
|
||||
* This call will be ignored if there's no listener that can receive the given events.
|
||||
* @param player - player to hook.
|
||||
*/
|
||||
public void injectPlayer(Player player) {
|
||||
// Inject using the player instance itself
|
||||
injectPlayer(player, player, GamePhase.PLAYING);
|
||||
if (isInjectionNecessary(GamePhase.PLAYING)) {
|
||||
injectPlayer(player, player, GamePhase.PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if it's truly necessary to perform the given player injection.
|
||||
* @param phase - current game phase.
|
||||
* @return TRUE if we should perform the injection, FALSE otherwise.
|
||||
*/
|
||||
public boolean isInjectionNecessary(GamePhase phase) {
|
||||
return injectionFilter.apply(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a player hook, allowing us to read server packets.
|
||||
* <p>
|
||||
* This method will always perform the instructed injection.
|
||||
*
|
||||
* @param player - player to hook.
|
||||
* @param injectionPoint - the object to use during the injection process.
|
||||
* @param phase - the current game phase.
|
||||
* @return The resulting player injector, or NULL if the injection failed.
|
||||
*/
|
||||
PlayerInjector injectPlayer(Player player, Object injectionPoint, GamePhase phase) {
|
||||
|
||||
|
||||
PlayerInjector injector = playerInjection.get(player);
|
||||
PlayerInjectHooks tempHook = playerHook;
|
||||
PlayerInjectHooks permanentHook = tempHook;
|
||||
|
||||
// The given player object may be fake, so be careful!
|
||||
|
||||
// See if we need to inject something else
|
||||
boolean invalidInjector = injector != null ? !injector.canInject(phase) : true;
|
||||
|
||||
|
@ -38,9 +38,9 @@ import com.comphenix.protocol.Packets;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.GamePhase;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
|
Loading…
Reference in New Issue
Block a user