diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index 1a83647b..5f6466b6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -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) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/async/NullPacketListener.java b/ProtocolLib/src/main/java/com/comphenix/protocol/async/NullPacketListener.java index a5077dbf..418d85cc 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/async/NullPacketListener.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/async/NullPacketListener.java @@ -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; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java index 8f8859b0..979c8ec6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/ListeningWhitelist.java @@ -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 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 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 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 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); } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/MonitorAdapter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/MonitorAdapter.java index 9036971a..832c2d8f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/MonitorAdapter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/MonitorAdapter.java @@ -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; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java index c49d74ec..270fa53e 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/events/PacketAdapter.java @@ -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. - * + *

+ * 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 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. + *

+ * 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 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. + *

+ * 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 diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/GamePhase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/GamePhase.java new file mode 100644 index 00000000..07c7e7a5 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/GamePhase.java @@ -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; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java index 0f9de6c1..2ce8ab6a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java @@ -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 serverPackets; private Set 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 isInjectionNecessary = new Predicate() { + @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) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java index 84b0d30a..1361d64c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java @@ -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; } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java index ff3e5d51..54a75b54 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/ReadPacketModifier.java @@ -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)) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java index 8a9c221d..7f753fbb 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetLoginInjector.java @@ -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(); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java index 13f3edcc..c3c4f26d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkFieldInjector.java @@ -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; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java index d8a86078..e5140ab4 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkObjectInjector.java @@ -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. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java index 82513c00..01ea2fc9 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/NetworkServerInjector.java @@ -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 diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java index d08e39b1..707bef86 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjectionHandler.java @@ -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 injectionFilter; + + public PlayerInjectionHandler(ClassLoader classLoader, Logger logger, Predicate 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. + *

+ * 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. + *

+ * 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; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index 51dc949e..3e2457e9 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -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;