From 22f2f45d1eefdfd9c27e1c5e3589d30422d9c6a1 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 26 Sep 2012 03:31:59 +0200 Subject: [PATCH] Added support for a hook method that can intercept map chunk packets. --- .../injector/NetworkServerInjector.java | 49 +++++++++-- .../injector/PacketFilterManager.java | 2 +- .../protocol/injector/PlayerInjector.java | 2 +- .../reflect/instances/DefaultInstances.java | 84 +++++++++++++------ .../reflect/instances/ExistingGenerator.java | 83 ++++++++++++++++++ 5 files changed, 186 insertions(+), 34 deletions(-) create mode 100644 ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java index 7569f9b8..eaa1d65e 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/NetworkServerInjector.java @@ -12,7 +12,12 @@ import net.sf.cglib.proxy.MethodProxy; import org.bukkit.entity.Player; +import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.instances.CollectionGenerator; +import com.comphenix.protocol.reflect.instances.DefaultInstances; +import com.comphenix.protocol.reflect.instances.ExistingGenerator; +import com.comphenix.protocol.reflect.instances.PrimitiveGenerator; /** * Represents a player hook into the NetServerHandler class. @@ -21,7 +26,7 @@ import com.comphenix.protocol.reflect.FuzzyReflection; */ public class NetworkServerInjector extends PlayerInjector { - private static Method sendPacket; + private static Method sendPacketMethod; public NetworkServerInjector(Player player, PacketFilterManager manager, Set sendingFilters) throws IllegalAccessException { super(player, manager, sendingFilters); @@ -33,8 +38,8 @@ public class NetworkServerInjector extends PlayerInjector { // Get the send packet method! if (hasInitialized) { - if (sendPacket == null) - sendPacket = FuzzyReflection.fromObject(serverHandler).getMethodByParameters("sendPacket", Packet.class); + if (sendPacketMethod == null) + sendPacketMethod = FuzzyReflection.fromObject(serverHandler).getMethodByName("sendPacket.*"); } } @@ -45,7 +50,7 @@ public class NetworkServerInjector extends PlayerInjector { if (serverDeleage != null) { try { // Note that invocation target exception is a wrapper for a checked exception - sendPacket.invoke(serverDeleage, packet); + sendPacketMethod.invoke(serverDeleage, packet); } catch (IllegalArgumentException e) { throw e; @@ -67,14 +72,17 @@ public class NetworkServerInjector extends PlayerInjector { if (serverHandlerRef.getValue() instanceof Factory) return; + Class serverClass = serverHandler.getClass(); + Enhancer ex = new Enhancer(); ex.setClassLoader(manager.getClassLoader()); - ex.setSuperclass(serverHandler.getClass()); + ex.setSuperclass(serverClass); ex.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + // The send packet method! - if (method.equals(sendPacket)) { + if (method.equals(sendPacketMethod)) { Packet packet = (Packet) args[0]; if (packet != null) { @@ -97,14 +105,39 @@ public class NetworkServerInjector extends PlayerInjector { } }); + // Use the existing field values when we create our copy + DefaultInstances serverInstances = DefaultInstances.fromArray( + ExistingGenerator.fromObjectFields(serverHandler), + PrimitiveGenerator.INSTANCE, + CollectionGenerator.INSTANCE); + + Object proxyObject = serverInstances.forEnhancer(ex).getDefault(serverClass); + // Inject it now - serverHandlerRef.setValue(ex.create()); + if (proxyObject != null) { + serverHandlerRef.setValue(proxyObject); + } } @Override public void cleanupAll() { - if (serverHandlerRef != null) + if (serverHandlerRef != null) { serverHandlerRef.revertValue(); + } + + try { + if (getNetHandler() != null) { + // Restore packet listener + try { + FieldUtils.writeField(netHandlerField, networkManager, serverHandlerRef.getOldValue(), true); + } catch (IllegalAccessException e) { + // Oh well + e.printStackTrace(); + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } } @Override diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java index 9ce1a23b..18fc3738 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PacketFilterManager.java @@ -490,7 +490,7 @@ public final class PacketFilterManager implements ProtocolManager { if (event instanceof PlayerJoinEvent) injectPlayer(((PlayerJoinEvent) event).getPlayer()); else if (event instanceof PlayerQuitEvent) - injectPlayer(((PlayerQuitEvent) event).getPlayer()); + uninjectPlayer(((PlayerQuitEvent) event).getPlayer()); } return null; } diff --git a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java index 5dd24cf2..a05b30a7 100644 --- a/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java +++ b/ProtocolLib/src/com/comphenix/protocol/injector/PlayerInjector.java @@ -127,7 +127,7 @@ abstract class PlayerInjector { * @return Current net handler. * @throws IllegalAccessException Unable to find or retrieve net handler. */ - private Object getNetHandler() throws IllegalAccessException { + protected Object getNetHandler() throws IllegalAccessException { // What a mess try { diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java index 6ab5c1dd..7c240ea0 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/DefaultInstances.java @@ -20,6 +20,10 @@ package com.comphenix.protocol.reflect.instances; import java.lang.reflect.Constructor; import java.util.*; +import javax.print.CancelablePrintJob; + +import net.sf.cglib.proxy.Enhancer; + import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; @@ -100,6 +104,38 @@ public class DefaultInstances { return getDefaultInternal(type, registered, 0); } + /** + * Retrieve the constructor with the fewest number of parameters. + * @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) { + + Constructor minimum = null; + int lastCount = Integer.MAX_VALUE; + + // Find the constructor with the fewest parameters + for (Constructor candidate : type.getConstructors()) { + Class[] types = candidate.getParameterTypes(); + + // Note that we don't allow recursive types - that is, types that + // require itself in the constructor. + if (types.length < lastCount) { + if (!contains(types, type)) { + minimum = (Constructor) candidate; + lastCount = types.length; + + // Don't loop again if we've already found the best possible constructor + if (lastCount == 0) + break; + } + } + } + + return minimum; + } + /** * Retrieves a default instance or value that is assignable to this type. *

@@ -136,36 +172,18 @@ public class DefaultInstances { if (value != null) return (T) value; } - - Constructor minimum = null; - int lastCount = Integer.MAX_VALUE; - - // Find the constructor with the fewest parameters - for (Constructor candidate : type.getConstructors()) { - Class[] types = candidate.getParameterTypes(); - - // Note that we don't allow recursive types - that is, types that - // require itself in the constructor. - if (types.length < lastCount) { - if (!contains(types, type)) { - minimum = (Constructor) candidate; - lastCount = types.length; - - // Don't loop again if we've already found the best possible constructor - if (lastCount == 0) - break; - } - } - } - + + Constructor minimum = getMinimumConstructor(type); + int parameterCount = minimum.getParameterTypes().length; + // Create the type with this constructor using default values. This might fail, though. try { if (minimum != null) { - Object[] params = new Object[lastCount]; + Object[] params = new Object[parameterCount]; Class[] types = minimum.getParameterTypes(); // Fill out - for (int i = 0; i < lastCount; i++) { + for (int i = 0; i < parameterCount; i++) { params[i] = getDefaultInternal(types[i], providers, recursionLevel + 1); } @@ -180,6 +198,24 @@ public class DefaultInstances { return null; } + /** + * Construct default instances using the CGLIB enhancer object instead. + * @param enhancer - a CGLIB enhancer to use. + * @return A default instance generator that uses the CGLIB enhancer. + */ + public DefaultInstances forEnhancer(Enhancer enhancer) { + final Enhancer ex = enhancer; + + return new DefaultInstances(registered) { + @SuppressWarnings("unchecked") + @Override + protected T createInstance(Class type, Constructor constructor, Class[] types, Object[] params) { + // Use the enhancer instead + return (T) ex.create(types, params); + } + }; + } + /** * Used by the default instance provider to create a class from a given constructor. * The default method uses reflection. diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java new file mode 100644 index 00000000..882bd614 --- /dev/null +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/instances/ExistingGenerator.java @@ -0,0 +1,83 @@ +package com.comphenix.protocol.reflect.instances; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.comphenix.protocol.reflect.FieldUtils; +import com.comphenix.protocol.reflect.FuzzyReflection; + +/** + * Provides instance constructors using a list of existing values. + *

+ * Only one instance per individual class. + * @author Kristian + */ +public class ExistingGenerator implements InstanceProvider { + + @SuppressWarnings("rawtypes") + private Map existingValues = new HashMap(); + + private ExistingGenerator() { + // Only accessible to the constructors + } + + /** + * Automatically create an instance provider from a objects public and private fields. + *

+ * If two or more fields share the same type, the last declared non-null field will take + * precedent. + * @param object - object to create an instance generator from. + * @return The instance generator. + */ + public static ExistingGenerator fromObjectFields(Object object) { + ExistingGenerator generator = new ExistingGenerator(); + + // Read instances from every field. + for (Field field : FuzzyReflection.fromObject(object, true).getFields()) { + try { + Object value = FieldUtils.readField(field, object, true); + + if (value != null) + generator.addObject(value); + + } catch (Exception e) { + // Yes, swallow it. No, really. + } + } + + return generator; + } + + /** + * Create an instance generator from a pre-defined array of values. + * @param values - values to provide. + * @return An instance provider that uses these values. + */ + public static ExistingGenerator fromObjectArray(Object[] values) { + ExistingGenerator generator = new ExistingGenerator(); + + for (Object value : values) + generator.addObject(value); + + return generator; + } + + private void addObject(Object value) { + if (value == null) + throw new IllegalArgumentException("Value cannot be NULL."); + + existingValues.put(value.getClass(), value); + } + + @Override + public Object create(@Nullable Class type) { + + Object value = existingValues.get(type); + + // NULL values indicate that the generator failed + return value; + } +}