From 6019ab177c5acc49582c4d1fd543122dd756b25e Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 4 Mar 2013 00:44:09 +0100 Subject: [PATCH] Create a dummy injector if we haven't yet injected the player. --- .../protocol/concurrency/BlockingHashMap.java | 21 +++-- .../player/NetworkServerInjector.java | 82 ++----------------- .../player/ProxyPlayerInjectionHandler.java | 46 ++++++++++- .../protocol/reflect/FuzzyReflection.java | 18 ++++ .../protocol/utility/MinecraftMethods.java | 64 +++++++++++++++ .../protocol/utility/MinecraftReflection.java | 4 +- 6 files changed, 150 insertions(+), 85 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/BlockingHashMap.java b/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/BlockingHashMap.java index 19d35b51..b5bad572 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/BlockingHashMap.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/concurrency/BlockingHashMap.java @@ -47,6 +47,19 @@ public class BlockingHashMap { // Map of locked objects private final ConcurrentMap locks; + /** + * Retrieve a cache loader that will always throw an exception. + * @return An invalid cache loader. + */ + public static CacheLoader newInvalidCacheLoader() { + return new CacheLoader() { + @Override + public TValue load(TKey key) throws Exception { + throw new IllegalStateException("Illegal use. Access the map directly instead."); + } + }; + } + /** * Initialize a new map. */ @@ -59,12 +72,8 @@ public class BlockingHashMap { locks.remove(entry.getKey()); } }).build( - new CacheLoader() { - @Override - public TValue load(TKey key) throws Exception { - throw new IllegalStateException("Illegal use. Access the map directly instead."); - } - }); + BlockingHashMap.newInvalidCacheLoader() + ); backingMap = backingCache.asMap(); // Normal concurrent hash map 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 d9a7f447..16218066 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 @@ -20,15 +20,7 @@ package com.comphenix.protocol.injector.player; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import net.sf.cglib.proxy.Callback; -import net.sf.cglib.proxy.CallbackFilter; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.Factory; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; -import net.sf.cglib.proxy.NoOp; +import net.sf.cglib.proxy.*; import org.bukkit.entity.Player; @@ -44,8 +36,8 @@ import com.comphenix.protocol.reflect.ObjectWriter; import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.ExistingGenerator; +import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftReflection; -import com.google.common.collect.Maps; /** * Represents a player hook into the NetServerHandler class. @@ -57,7 +49,6 @@ class NetworkServerInjector extends PlayerInjector { private volatile static CallbackFilter callbackFilter; private volatile static Field disconnectField; - private volatile static Method sendPacketMethod; private InjectedServerConnection serverInjection; // Determine if we're listening @@ -88,67 +79,6 @@ class NetworkServerInjector extends PlayerInjector { return sendingFilters.contains(packetID); } - @Override - public void initialize(Object injectionSource) throws IllegalAccessException { - super.initialize(injectionSource); - - // Get the send packet method! - if (hasInitialized) { - if (sendPacketMethod == null) { - try { - sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*"); - } catch (IllegalArgumentException e) { - Map netServer = getMethodList( - MinecraftReflection.getNetServerHandlerClass(), MinecraftReflection.getPacketClass()); - Map netHandler = getMethodList( - MinecraftReflection.getNetHandlerClass(), MinecraftReflection.getPacketClass()); - - // Remove every method in net handler from net server - for (String methodName : netHandler.keySet()) { - netServer.remove(methodName); - } - - // The remainder is the send packet method - if (netServer.size() == 1) { - Method[] methods = netServer.values().toArray(new Method[0]); - sendPacketMethod = methods[0]; - } else { - throw new IllegalArgumentException("Unable to find the sendPacket method in NetServerHandler/PlayerConnection."); - } - } - } - } - } - - /** - * Retrieve a method mapped list of every method with the given signature. - * @param source - class source. - * @param params - parameters. - * @return Method mapped list. - */ - private Map getMethodList(Class source, Class... params) { - return getMappedMethods( - FuzzyReflection.fromClass(source, true). - getMethodListByParameters(Void.TYPE, params) - ); - } - - /** - * Retrieve every method as a map over names. - *

- * Note that overloaded methods will only occur once in the resulting map. - * @param methods - every method. - * @return A map over every given method. - */ - private Map getMappedMethods(List methods) { - Map map = Maps.newHashMap(); - - for (Method method : methods) { - map.put(method.getName(), method); - } - return map; - } - @Override public void sendServerPacket(Object packet, boolean filtered) throws InvocationTargetException { Object serverDelegate = filtered ? serverHandlerRef.getValue() : serverHandlerRef.getOldValue(); @@ -156,7 +86,7 @@ class NetworkServerInjector extends PlayerInjector { if (serverDelegate != null) { try { // Note that invocation target exception is a wrapper for a checked exception - sendPacketMethod.invoke(serverDelegate, packet); + MinecraftMethods.getSendPacketMethod().invoke(serverDelegate, packet); } catch (IllegalArgumentException e) { throw e; @@ -229,14 +159,16 @@ class NetworkServerInjector extends PlayerInjector { }; }; Callback noOpCallback = NoOp.INSTANCE; - + // Share callback filter - that way, we avoid generating a new class for // every logged in player. if (callbackFilter == null) { + final Method sendPacket = MinecraftMethods.getSendPacketMethod(); + callbackFilter = new CallbackFilter() { @Override public int accept(Method method) { - if (method.equals(sendPacketMethod)) + if (method.equals(sendPacket)) return 0; else return 1; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/ProxyPlayerInjectionHandler.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/ProxyPlayerInjectionHandler.java index 14091c80..1d95439b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/ProxyPlayerInjectionHandler.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/ProxyPlayerInjectionHandler.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import net.sf.cglib.proxy.Factory; @@ -30,6 +31,7 @@ import org.bukkit.Server; import org.bukkit.entity.Player; import com.comphenix.protocol.Packets; +import com.comphenix.protocol.concurrency.BlockingHashMap; import com.comphenix.protocol.concurrency.IntegerSet; import com.comphenix.protocol.error.ErrorReporter; import com.comphenix.protocol.events.PacketAdapter; @@ -42,8 +44,11 @@ import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.injector.server.AbstractInputStreamLookup; import com.comphenix.protocol.injector.server.InputStreamLookupBuilder; import com.comphenix.protocol.injector.server.SocketInjector; +import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.base.Predicate; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.collect.Maps; /** @@ -64,6 +69,12 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler { // The last successful player hook private PlayerInjector lastSuccessfulHook; + // Dummy injection + private Cache dummyInjectors = + CacheBuilder.newBuilder(). + expireAfterWrite(30, TimeUnit.SECONDS). + build(BlockingHashMap.newInvalidCacheLoader()); + // Player injection private Map playerInjection = Maps.newConcurrentMap(); @@ -370,7 +381,7 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler { return injector; } - + private void cleanupHook(PlayerInjector injector) { // Clean up as much as possible try { @@ -522,12 +533,43 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler { if (result instanceof PlayerInjector) return (PlayerInjector) result; else - return null; + // Make a dummy injector them + return createDummyInjector(player); + } else { return injector; } } + /** + * Construct a simple dummy injector incase none has been constructed. + * @param player - the CraftPlayer to construct for. + * @return A dummy injector, or NULL if the given player is not a CraftPlayer. + */ + private PlayerInjector createDummyInjector(Player player) { + if (!MinecraftReflection.getCraftPlayerClass().isAssignableFrom(player.getClass())) { + // No - this is not safe + return null; + } + + try { + PlayerInjector dummyInjector = getHookInstance(player, PlayerInjectHooks.NETWORK_SERVER_OBJECT); + dummyInjector.initializePlayer(player); + + // This probably means the player has disconnected + if (dummyInjector.getSocket() == null) { + return null; + } + + inputStreamLookup.setSocketInjector(dummyInjector.getAddress(), dummyInjector); + dummyInjectors.asMap().put(player, dummyInjector); + return dummyInjector; + + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access fields.", e); + } + } + /** * Retrieve a player injector by looking for its NetworkManager. * @param networkManager - current network manager. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index 9da145aa..cbf8da58 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -24,11 +24,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; /** * Retrieves fields and methods by signature, not just name. @@ -422,6 +424,22 @@ public class FuzzyReflection { throw new IllegalArgumentException("Unable to find a method that matches " + matcher); } + /** + * Retrieve every method as a map over names. + *

+ * Note that overloaded methods will only occur once in the resulting map. + * @param methods - every method. + * @return A map over every given method. + */ + public Map getMappedMethods(List methods) { + Map map = Maps.newHashMap(); + + for (Method method : methods) { + map.put(method.getName(), method); + } + return map; + } + /** * Retrieve a list of every constructor that matches the given matcher. *

diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java new file mode 100644 index 00000000..f5706fa4 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java @@ -0,0 +1,64 @@ +package com.comphenix.protocol.utility; + +import java.lang.reflect.Method; +import java.util.Map; + +import com.comphenix.protocol.reflect.FuzzyReflection; + +/** + * Static methods for accessing Minecraft methods. + * + * @author Kristian + */ +public class MinecraftMethods { + // For player connection + private volatile static Method sendPacketMethod; + + /** + * Retrieve the send packet method in PlayerConnection/NetServerHandler. + * @return The send packet method. + */ + public static Method getSendPacketMethod() { + if (sendPacketMethod == null) { + Class serverHandlerClass = MinecraftReflection.getNetServerHandlerClass(); + + try { + sendPacketMethod = FuzzyReflection.fromObject(serverHandlerClass).getMethodByName("sendPacket.*"); + } catch (IllegalArgumentException e) { + Map netServer = getMethodList( + serverHandlerClass, MinecraftReflection.getPacketClass()); + Map netHandler = getMethodList( + MinecraftReflection.getNetHandlerClass(), MinecraftReflection.getPacketClass()); + + // Remove every method in net handler from net server + for (String methodName : netHandler.keySet()) { + netServer.remove(methodName); + } + + // The remainder is the send packet method + if (netServer.size() == 1) { + Method[] methods = netServer.values().toArray(new Method[0]); + sendPacketMethod = methods[0]; + } else { + throw new IllegalArgumentException("Unable to find the sendPacket method in NetServerHandler/PlayerConnection."); + } + } + } + return sendPacketMethod; + } + + /** + * Retrieve a method mapped list of every method with the given signature. + * @param source - class source. + * @param params - parameters. + * @return Method mapped list. + */ + private static Map getMethodList(Class source, Class... params) { + FuzzyReflection reflect = FuzzyReflection.fromClass(source, true); + + return reflect.getMappedMethods( + reflect.getMethodListByParameters(Void.TYPE, params) + ); + } + +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 6f12d76b..399082d1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -85,7 +85,7 @@ public class MinecraftReflection { private static Method craftNMSMethod; private static Method craftBukkitMethod; private static boolean craftItemStackFailed; - + // net.minecraft.server private static Class itemStackArrayClass; @@ -924,7 +924,7 @@ public class MinecraftReflection { public static Class getCraftEntityClass() { return getCraftBukkitClass("entity.CraftEntity"); } - + /** * Retrieve a CraftItemStack from a given ItemStack. * @param bukkitItemStack - the Bukkit ItemStack to convert.