diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java index 55ebd698..72de1223 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/player/NetworkServerInjector.java @@ -21,6 +21,7 @@ import com.comphenix.protocol.injector.ListenerInvoker; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.ObjectCloner; +import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.ExistingGenerator; @@ -97,6 +98,29 @@ public class NetworkServerInjector extends PlayerInjector { if (serverHandlerRef.getValue() instanceof Factory) return; + if (!tryInjectManager()) { + + // Try to override the proxied object + if (proxyServerField != null) { + serverHandlerRef = new VolatileField(proxyServerField, serverHandler, true); + serverHandler = serverHandlerRef.getValue(); + + if (serverHandler == null) + throw new RuntimeException("Cannot hook player: Inner proxy object is NULL."); + + // Try again + if (tryInjectManager()) { + // It worked - probably + return; + } + } + + throw new RuntimeException( + "Cannot hook player: Unable to find a valid constructor for the NetServerHandler object."); + } + } + + private boolean tryInjectManager() { Class serverClass = serverHandler.getClass(); Enhancer ex = new Enhancer(); @@ -135,18 +159,22 @@ public class NetworkServerInjector extends PlayerInjector { } }); - // Use the existing field values when we create our copy + // Find the Minecraft NetServerHandler superclass + Class minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass()); + ExistingGenerator generator = ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass); DefaultInstances serverInstances = null; - if (hasProxyServerHandler()) { - Class minecraftSuperClass = getFirstMinecraftSuperClass(serverHandler.getClass()); - serverInstances = DefaultInstances.fromArray( - ExistingGenerator.fromObjectFields(serverHandler, minecraftSuperClass)); + // Maybe the proxy instance can help? + Object proxyInstance = getProxyServerHandler(); + + // Use the existing server proxy when we create one + if (proxyInstance != null && proxyInstance != serverHandler) { + serverInstances = DefaultInstances.fromArray(generator, + ExistingGenerator.fromObjectArray(new Object[] { proxyInstance })); } else { - serverInstances = DefaultInstances.fromArray( - ExistingGenerator.fromObjectFields(serverHandler)); + serverInstances = DefaultInstances.fromArray(generator); } - + serverInstances.setNonNull(true); serverInstances.setMaximumRecursion(1); @@ -158,19 +186,31 @@ public class NetworkServerInjector extends PlayerInjector { //copyTo(serverHandler, proxyObject); serverInjection.replaceServerHandler(serverHandler, proxyObject); serverHandlerRef.setValue(proxyObject); + return true; } else { - throw new RuntimeException( - "Cannot hook player: Unable to find a valid constructor for the NetServerHandler object."); + return false; } } + private Object getProxyServerHandler() { + if (proxyServerField != null && !proxyServerField.equals(serverHandlerRef.getField())) { + try { + return FieldUtils.readField(proxyServerField, serverHandler, true); + } catch (Throwable e) { + // Oh well + } + } + + return null; + } + private Class getFirstMinecraftSuperClass(Class clazz) { - if (clazz.getName().startsWith("net.minecraft")) + if (clazz.getName().startsWith("net.minecraft.server.")) return clazz; else if (clazz.equals(Object.class)) return clazz; else - return clazz.getSuperclass(); + return getFirstMinecraftSuperClass(clazz.getSuperclass()); } @Override diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java index f80b8e49..62e01f33 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -43,8 +43,8 @@ import com.comphenix.protocol.reflect.VolatileField; abstract class PlayerInjector { // Cache previously retrieved fields - private static Field serverHandlerField; - private static Field proxyServerField; + protected static Field serverHandlerField; + protected static Field proxyServerField; protected static Field networkManagerField; protected static Field inputField; @@ -115,12 +115,7 @@ abstract class PlayerInjector { } // Yo dawg - if (proxyServerField != null) { - Object container = FieldUtils.readField(serverHandlerField, notchEntity, true); - serverHandlerRef = new VolatileField(proxyServerField, container); - } else { - serverHandlerRef = new VolatileField(serverHandlerField, notchEntity); - } + serverHandlerRef = new VolatileField(serverHandlerField, notchEntity); serverHandler = serverHandlerRef.getValue(); // Next, get the network manager @@ -166,6 +161,7 @@ abstract class PlayerInjector { return null; hasProxyType = true; + logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!"); // No? Is it a Proxy type? try { @@ -175,7 +171,7 @@ abstract class PlayerInjector { return reflection.getFieldByType(".*NetServerHandler"); } catch (RuntimeException e) { - logger.log(Level.WARNING, "Detected server handler proxy type by another plugin. Conflict may occur!"); + // Damn } } diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java index fad95379..7dd03cf7 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java @@ -156,9 +156,12 @@ public class DefaultInstances { * @param type - type to construct. * @return A constructor with the fewest number of parameters, or NULL if the type has no constructors. */ - @SuppressWarnings("unchecked") public Constructor getMinimumConstructor(Class type) { - + return getMinimumConstructor(type, registered, 0); + } + + @SuppressWarnings("unchecked") + private Constructor getMinimumConstructor(Class type, List providers, int recursionLevel) { Constructor minimum = null; int lastCount = Integer.MAX_VALUE; @@ -170,6 +173,13 @@ public class DefaultInstances { // require itself in the constructor. if (types.length < lastCount) { if (!contains(types, type)) { + if (nonNull) { + // Make sure all of these types are non-null + if (isAnyNull(types, providers, recursionLevel)) { + continue; + } + } + minimum = (Constructor) candidate; lastCount = types.length; @@ -183,6 +193,27 @@ public class DefaultInstances { return minimum; } + /** + * Determine if any of the given types will be NULL once created. + *

+ * Recursion level is the number of times the default method has been called. + * @param types - types to check. + * @param providers - instance providers. + * @param recursionLevel - current recursion level. + * @return + */ + private boolean isAnyNull(Class[] types, List providers, int recursionLevel) { + // Just check if any of them are NULL + for (Class type : types) { + if (getDefaultInternal(type, providers, recursionLevel) == null) { + System.out.println(type.getName() + " is NULL!"); + return true; + } + } + + return false; + } + /** * Retrieves a default instance or value that is assignable to this type. *

@@ -198,7 +229,7 @@ public class DefaultInstances { * * * @param type - the type to construct a default value. - * @param providers - instance providers used during the + * @param providers - instance providers used during the construction. * @return A default value/instance, or NULL if not possible. */ public T getDefault(Class type, List providers) { @@ -221,7 +252,7 @@ public class DefaultInstances { return null; } - Constructor minimum = getMinimumConstructor(type); + Constructor minimum = getMinimumConstructor(type, providers, recursionLevel + 1); // Create the type with this constructor using default values. This might fail, though. try { diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java index 25f78a03..d183ad95 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java @@ -17,8 +17,7 @@ import com.comphenix.protocol.reflect.FuzzyReflection; */ public class ExistingGenerator implements InstanceProvider { - @SuppressWarnings("rawtypes") - private Map existingValues = new HashMap(); + private Map existingValues = new HashMap(); private ExistingGenerator() { // Only accessible to the constructors @@ -72,7 +71,7 @@ public class ExistingGenerator implements InstanceProvider { // Yes, swallow it. No, really. } } - + return generator; } @@ -94,19 +93,18 @@ public class ExistingGenerator implements InstanceProvider { if (value == null) throw new IllegalArgumentException("Value cannot be NULL."); - existingValues.put(value.getClass(), value); + existingValues.put(value.getClass().getName(), value); } private void addObject(Class type, Object value) { - existingValues.put(type, value); + existingValues.put(type.getName(), value); } - @Override public Object create(@Nullable Class type) { - Object value = existingValues.get(type); - + Object value = existingValues.get(type.getName()); + // NULL values indicate that the generator failed return value; }