diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java index 8479227d..189679b4 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java @@ -36,8 +36,9 @@ import com.comphenix.protocol.injector.server.SocketInjector; import com.comphenix.protocol.injector.server.TemporaryPlayerFactory; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.VolatileField; -import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; -import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.FieldAccessor; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.utility.MinecraftFields; import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftReflection; @@ -213,10 +214,10 @@ class ChannelInjector extends ByteToMessageDecoder { patchEncoder(vanillaEncoder); if (DECODE_BUFFER == null) - DECODE_BUFFER = FuzzyReflection.getMethodAccessor(vanillaDecoder.getClass(), + DECODE_BUFFER = Accessors.getMethodAccessor(vanillaDecoder.getClass(), "decode", ChannelHandlerContext.class, ByteBuf.class, List.class); if (ENCODE_BUFFER == null) - ENCODE_BUFFER = FuzzyReflection.getMethodAccessor(vanillaEncoder.getClass(), + ENCODE_BUFFER = Accessors.getMethodAccessor(vanillaEncoder.getClass(), "encode", ChannelHandlerContext.class, Object.class, ByteBuf.class); // Intercept sent packets @@ -265,7 +266,7 @@ class ChannelInjector extends ByteToMessageDecoder { */ private void patchEncoder(MessageToByteEncoder encoder) { if (ENCODER_TYPE_MATCHER == null) { - ENCODER_TYPE_MATCHER = FuzzyReflection.getFieldAccessor(encoder.getClass(), "matcher", true); + ENCODER_TYPE_MATCHER = Accessors.getFieldAccessor(encoder.getClass(), "matcher", true); } ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass())); } @@ -490,7 +491,7 @@ class ChannelInjector extends ByteToMessageDecoder { */ public Protocol getCurrentProtocol() { if (PROTOCOL_ACCESSOR == null) { - PROTOCOL_ACCESSOR = FuzzyReflection.getFieldAccessor( + PROTOCOL_ACCESSOR = Accessors.getFieldAccessor( networkManager.getClass(), MinecraftReflection.getEnumProtocolClass(), true); } return Protocol.fromVanilla((Enum) PROTOCOL_ACCESSOR.get(networkManager)); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java index 2f689bbd..4efe126d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java @@ -5,8 +5,8 @@ import java.net.SocketAddress; import java.util.Map; import java.util.concurrent.Callable; -import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.google.common.collect.Maps; import net.minecraft.util.io.netty.buffer.ByteBufAllocator; @@ -128,7 +128,7 @@ abstract class ChannelProxy implements Channel { if (accessor == null) { try { - accessor = FuzzyReflection.getFieldAccessor(clazz, messageClass, true); + accessor = Accessors.getFieldAccessor(clazz, messageClass, true); } catch (IllegalArgumentException e) { accessor = MARK_NO_MESSAGE; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java index 5a4b4eff..5d72c54f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java @@ -6,7 +6,6 @@ import java.util.List; import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor; import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor; -import com.comphenix.protocol.utility.MinecraftReflection; import com.google.common.collect.Lists; import net.sf.cglib.asm.ClassReader; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactReflection.java new file mode 100644 index 00000000..dba7f2ac --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactReflection.java @@ -0,0 +1,143 @@ +package com.comphenix.protocol.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; + +import javax.annotation.Nonnull; + +import com.google.common.base.Preconditions; + +public class ExactReflection { + // The class we're actually representing + private Class source; + private boolean forceAccess; + + private ExactReflection(Class source, boolean forceAccess) { + this.source = Preconditions.checkNotNull(source, "source class cannot be NULL"); + this.forceAccess = forceAccess; + } + + /** + * Retrieves an exact reflection instance from a given class. + * @param source - the class we'll use. + * @return A fuzzy reflection instance. + */ + public static ExactReflection fromClass(Class source) { + return fromClass(source, false); + } + + /** + * Retrieves an exact reflection instance from a given class. + * @param source - the class we'll use. + * @param forceAccess - whether or not to override scope restrictions. + * @return A fuzzy reflection instance. + */ + public static ExactReflection fromClass(Class source, boolean forceAccess) { + return new ExactReflection(source, forceAccess); + } + + /** + * Retrieves an exact reflection instance from an object. + * @param reference - the object we'll use. + * @return A fuzzy reflection instance that uses the class of the given object. + */ + public static ExactReflection fromObject(Object reference) { + return new ExactReflection(reference.getClass(), false); + } + + /** + * Retrieves an exact reflection instance from an object. + * @param reference - the object we'll use. + * @param forceAccess - whether or not to override scope restrictions. + * @return A fuzzy reflection instance that uses the class of the given object. + */ + public static ExactReflection fromObject(Object reference, boolean forceAccess) { + return new ExactReflection(reference.getClass(), forceAccess); + } + + /** + * Retrieve the first method in the class hierachy with the given name and parameters. + *

+ * If {@link #isForceAccess()} is TRUE, we will also search for protected and private methods. + * @param methodName - the method name to find, or NULL to look for everything. + * @param parameters - the parameters. + * @return The first matched method. + * @throws IllegalArgumentException If we cannot find a method by this name. + */ + public Method getMethod(String methodName, Class... parameters) { + return getMethod(source, methodName, parameters); + } + + // For recursion + private Method getMethod(Class instanceClass, String methodName, Class... parameters) { + for (Method method : instanceClass.getDeclaredMethods()) { + if ((forceAccess || Modifier.isPublic(method.getModifiers())) && + (methodName == null || method.getName().equals(methodName)) && + Arrays.equals(method.getParameterTypes(), parameters)) { + + method.setAccessible(true); + return method; + } + } + // Search in every superclass + if (instanceClass.getSuperclass() != null) + return getMethod(instanceClass.getSuperclass(), methodName, parameters); + throw new IllegalArgumentException(String.format( + "Unable to find method %s (%s) in %s.", methodName, Arrays.asList(parameters), source)); + } + + /** + * Retrieve a field in the class hierachy by the given name. + *

+ * If {@link #isForceAccess()} is TRUE, we will also search for protected and private fields. + * @param fieldName - the field name. Cannot be NULL. + * @return The first matched field. + */ + public Field getField(String fieldName) { + return getField(source, fieldName); + } + + // For recursion + private Field getField(Class instanceClass, @Nonnull String fieldName) { + // Ignore access rules + for (Field field : instanceClass.getDeclaredFields()) { + if (field.getName().equals(fieldName)) { + field.setAccessible(true); + return field; + } + } + + // Recursively fild the correct field + if (instanceClass.getSuperclass() != null) + return getField(instanceClass.getSuperclass(), fieldName); + throw new IllegalArgumentException(String.format( + "Unable to find field %s in %s.", fieldName, source)); + } + + /** + * Retrieve an {@link ExactReflection} object where scope restrictions are ignored. + * @return A copy of the current object. + */ + public ExactReflection forceAccess() { + return new ExactReflection(source, true); + } + + /** + * Determine if we are overriding scope restrictions and will also find + * private, protected or package members. + * @return TRUE if we are, FALSE otherwise. + */ + public boolean isForceAccess() { + return forceAccess; + } + + /** + * Retrieve the source class we are searching. + * @return The source. + */ + public Class getSource() { + return source; + } + } 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 0c887a05..02c77e09 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -29,9 +29,9 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; -import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -42,85 +42,6 @@ import com.google.common.collect.Sets; * @author Kristian */ public class FuzzyReflection { - /** - * Represents an interface for accessing a field. - * @author Kristian - */ - public interface FieldAccessor { - /** - * Retrieve the value of a field for a particular instance. - * @param instance - the instance, or NULL for a static field. - * @return The value of the field. - * @throws IllegalStateException If the current security context prohibits reflection. - */ - public Object get(Object instance); - - /** - * Set the value of a field for a particular instance. - * @param instance - the instance, or NULL for a static field. - * @param value - the new value of the field. - */ - public void set(Object instance, Object value); - - /** - * Retrieve the underlying field. - * @return The field. - */ - public Field getField(); - } - - /** - * Represents an interface for invoking a method. - * @author Kristian - */ - public interface MethodAccessor { - /** - * Invoke the underlying method. - * @param target - the target instance, or NULL for a static method. - * @param args - the arguments to pass to the method. - * @return The return value, or NULL for void methods. - */ - public Object invoke(Object target, Object... args); - - /** - * Retrieve the underlying method. - * @return The method. - */ - public Method getMethod(); - } - - /** - * Represents a field accessor that synchronizes access to the underlying field. - * @author Kristian - */ - private static final class SynchronizedFieldAccessor implements FieldAccessor { - private final FieldAccessor accessor; - private SynchronizedFieldAccessor(FieldAccessor accessor) { - this.accessor = accessor; - } - - @Override - public void set(Object instance, Object value) { - Object lock = accessor.get(instance); - - if (lock != null) { - synchronized (lock) { - accessor.set(instance, value); - } - } else { - accessor.set(instance, value); - } - } - @Override - public Object get(Object instance) { - return accessor.get(instance); - } - @Override - public Field getField() { - return accessor.getField(); - } - } - // The class we're actually representing private Class source; @@ -170,107 +91,6 @@ public class FuzzyReflection { return new FuzzyReflection(reference.getClass(), forceAccess); } - /** - * Retrieve an accessor for the first field of the given type. - * @param instanceClass - the type of the instance to retrieve. - * @param fieldClass - type of the field to retrieve. - * @param forceAccess - whether or not to look for private and protected fields. - * @return The value of that field. - * @throws IllegalArgumentException If the field cannot be found. - */ - public static FieldAccessor getFieldAccessor(Class instanceClass, Class fieldClass, boolean forceAccess) { - // Get a field accessor - Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass); - return getFieldAccessor(field); - } - - /** - * Retrieve an accessor for the first field of the given type. - * @param instanceClass - the type of the instance to retrieve. - * @param fieldClass - type of the field to retrieve. - * @param forceAccess - whether or not to look for private and protected fields. - * @return The value of that field. - * @throws IllegalArgumentException If the field cannot be found. - */ - public static FieldAccessor getFieldAccessor(Class instanceClass, String fieldName, boolean forceAccess) { - return getFieldAccessor(FieldUtils.getField(instanceClass, fieldName, forceAccess)); - } - - /** - * Retrieve a field accessor from a given field that uses unchecked exceptions. - * @param field - the field. - * @return The field accessor. - */ - public static FieldAccessor getFieldAccessor(final Field field) { - return getFieldAccessor(field, true); - } - - /** - * Retrieve a field accessor from a given field that uses unchecked exceptions. - * @param field - the field. - * @param forceAccess - whether or not to skip Java access checking. - * @return The field accessor. - */ - public static FieldAccessor getFieldAccessor(final Field field, boolean forceAccess) { - field.setAccessible(true); - return new DefaultFieldAccessor(field); - } - - /** - * Retrieve a field accessor where the write operation is synchronized on the current field value. - * @param accessor - the accessor. - * @return The field accessor. - */ - public static FieldAccessor getSynchronized(final FieldAccessor accessor) { - // Only wrap once - if (accessor instanceof SynchronizedFieldAccessor) - return accessor; - return new SynchronizedFieldAccessor(accessor); - } - - /** - * Retrieve a method accessor for a method with the given name and signature. - * @param instanceClass - the parent class. - * @param name - the method name. - * @param parameters - the parameters. - * @return The method accessor. - */ - public static MethodAccessor getMethodAccessor(Class instanceClass, String name, Class... parameters) { - return getMethodAccessor(instanceClass, instanceClass, name, parameters); - } - - // Helper method - private static MethodAccessor getMethodAccessor( - Class initialClass, Class instanceClass, String name, Class... parameters) { - - try { - Method method = instanceClass.getDeclaredMethod(name, parameters); - method.setAccessible(true); - return getMethodAccessor(method); - - } catch (NoSuchMethodException e) { - // Search for a private method in the superclass - if (initialClass.getSuperclass() != null) - return getMethodAccessor(initialClass, instanceClass.getSuperclass(), name, parameters); - - // Unable to find it - throw new IllegalArgumentException("Unable to find method " + name + - "(" + Joiner.on(", ").join(parameters) +") in " + initialClass); - - } catch (Exception e) { - throw new RuntimeException("Unable to retrieve methods.", e); - } - } - - /** - * Retrieve a method accessor for a particular method, avoding checked exceptions. - * @param method - the method to access. - * @return The method accessor. - */ - public static MethodAccessor getMethodAccessor(final Method method) { - return new DefaultMethodAccessor(method); - } - /** * Retrieve the value of the first field of the given type. * @param instance - the instance to retrieve from. @@ -281,7 +101,7 @@ public class FuzzyReflection { */ public static T getFieldValue(Object instance, Class fieldClass, boolean forceAccess) { @SuppressWarnings("unchecked") - T result = (T) getFieldAccessor(instance.getClass(), fieldClass, forceAccess).get(instance); + T result = (T) Accessors.getFieldAccessor(instance.getClass(), fieldClass, forceAccess).get(instance); return result; } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java index bcd7d564..a3f8ea84 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/VolatileField.java @@ -19,7 +19,8 @@ package com.comphenix.protocol.reflect; import java.lang.reflect.Field; -import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.google.common.base.Objects; /** @@ -48,7 +49,7 @@ public class VolatileField { * @param container - the object this field belongs to. */ public VolatileField(Field field, Object container) { - this.accessor = FuzzyReflection.getFieldAccessor(field); + this.accessor = Accessors.getFieldAccessor(field); this.container = container; } @@ -59,7 +60,7 @@ public class VolatileField { * @param forceAccess - whether or not to override any scope restrictions. */ public VolatileField(Field field, Object container, boolean forceAccess) { - this.accessor = FuzzyReflection.getFieldAccessor(field, true); + this.accessor = Accessors.getFieldAccessor(field, true); this.container = container; this.forceAccess = forceAccess; } @@ -193,7 +194,7 @@ public class VolatileField { * @return A synchronized volatile field. */ public VolatileField toSynchronized() { - return new VolatileField(FuzzyReflection.getSynchronized(accessor), container); + return new VolatileField(Accessors.getSynchronized(accessor), container); } /** diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/Accessors.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/Accessors.java new file mode 100644 index 00000000..937f060c --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/Accessors.java @@ -0,0 +1,125 @@ +package com.comphenix.protocol.reflect.accessors; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import com.comphenix.protocol.reflect.ExactReflection; +import com.comphenix.protocol.reflect.FuzzyReflection; + +public final class Accessors { + /** + * Represents a field accessor that synchronizes access to the underlying field. + * @author Kristian + */ + public static final class SynchronizedFieldAccessor implements FieldAccessor { + private final FieldAccessor accessor; + private SynchronizedFieldAccessor(FieldAccessor accessor) { + this.accessor = accessor; + } + + @Override + public void set(Object instance, Object value) { + Object lock = accessor.get(instance); + + if (lock != null) { + synchronized (lock) { + accessor.set(instance, value); + } + } else { + accessor.set(instance, value); + } + } + + @Override + public Object get(Object instance) { + return accessor.get(instance); + } + + @Override + public Field getField() { + return accessor.getField(); + } + } + + /** + * Retrieve an accessor for the first field of the given type. + * @param instanceClass - the type of the instance to retrieve. + * @param fieldClass - type of the field to retrieve. + * @param forceAccess - whether or not to look for private and protected fields. + * @return The value of that field. + * @throws IllegalArgumentException If the field cannot be found. + */ + public static FieldAccessor getFieldAccessor(Class instanceClass, Class fieldClass, boolean forceAccess) { + // Get a field accessor + Field field = FuzzyReflection.fromClass(instanceClass, forceAccess).getFieldByType(null, fieldClass); + return Accessors.getFieldAccessor(field); + } + + /** + * Retrieve an accessor for the first field of the given type. + * @param instanceClass - the type of the instance to retrieve. + * @param fieldClass - type of the field to retrieve. + * @param forceAccess - whether or not to look for private and protected fields. + * @return The value of that field. + * @throws IllegalArgumentException If the field cannot be found. + */ + public static FieldAccessor getFieldAccessor(Class instanceClass, String fieldName, boolean forceAccess) { + return Accessors.getFieldAccessor(ExactReflection.fromClass(instanceClass, true).getField(fieldName)); + } + + /** + * Retrieve a field accessor from a given field that uses unchecked exceptions. + * @param field - the field. + * @return The field accessor. + */ + public static FieldAccessor getFieldAccessor(final Field field) { + return Accessors.getFieldAccessor(field, true); + } + + /** + * Retrieve a field accessor from a given field that uses unchecked exceptions. + * @param field - the field. + * @param forceAccess - whether or not to skip Java access checking. + * @return The field accessor. + */ + public static FieldAccessor getFieldAccessor(final Field field, boolean forceAccess) { + field.setAccessible(true); + return new DefaultFieldAccessor(field); + } + + /** + * Retrieve a field accessor where the write operation is synchronized on the current field value. + * @param accessor - the accessor. + * @return The field accessor. + */ + public static FieldAccessor getSynchronized(final FieldAccessor accessor) { + // Only wrap once + if (accessor instanceof SynchronizedFieldAccessor) + return accessor; + return new SynchronizedFieldAccessor(accessor); + } + + /** + * Retrieve a method accessor for a method with the given name and signature. + * @param instanceClass - the parent class. + * @param methodName - the method name. + * @param parameters - the parameters. + * @return The method accessor. + */ + public static MethodAccessor getMethodAccessor(Class instanceClass, String methodName, Class... parameters) { + return new DefaultMethodAccessor(ExactReflection.fromClass(instanceClass, true).getMethod(methodName, parameters)); + } + + /** + * Retrieve a method accessor for a particular method, avoding checked exceptions. + * @param method - the method to access. + * @return The method accessor. + */ + public static MethodAccessor getMethodAccessor(final Method method) { + return new DefaultMethodAccessor(method); + } + + // Seal this class + private Accessors() { + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/DefaultFieldAccessor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/DefaultFieldAccessor.java similarity index 92% rename from ProtocolLib/src/main/java/com/comphenix/protocol/reflect/DefaultFieldAccessor.java rename to ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/DefaultFieldAccessor.java index d3400d18..3a654cd0 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/DefaultFieldAccessor.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/DefaultFieldAccessor.java @@ -1,9 +1,7 @@ -package com.comphenix.protocol.reflect; +package com.comphenix.protocol.reflect.accessors; import java.lang.reflect.Field; -import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; - final class DefaultFieldAccessor implements FieldAccessor { private final Field field; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/DefaultMethodAccessor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/DefaultMethodAccessor.java similarity index 91% rename from ProtocolLib/src/main/java/com/comphenix/protocol/reflect/DefaultMethodAccessor.java rename to ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/DefaultMethodAccessor.java index 0129d411..c43d1777 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/DefaultMethodAccessor.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/DefaultMethodAccessor.java @@ -1,10 +1,8 @@ -package com.comphenix.protocol.reflect; +package com.comphenix.protocol.reflect.accessors; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; - final class DefaultMethodAccessor implements MethodAccessor { private final Method method; diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/FieldAccessor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/FieldAccessor.java new file mode 100644 index 00000000..b7148c2c --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/FieldAccessor.java @@ -0,0 +1,30 @@ +package com.comphenix.protocol.reflect.accessors; + +import java.lang.reflect.Field; + +/** + * Represents an interface for accessing a field. + * @author Kristian + */ +public interface FieldAccessor { + /** + * Retrieve the value of a field for a particular instance. + * @param instance - the instance, or NULL for a static field. + * @return The value of the field. + * @throws IllegalStateException If the current security context prohibits reflection. + */ + public Object get(Object instance); + + /** + * Set the value of a field for a particular instance. + * @param instance - the instance, or NULL for a static field. + * @param value - the new value of the field. + */ + public void set(Object instance, Object value); + + /** + * Retrieve the underlying field. + * @return The field. + */ + public Field getField(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/MethodAccessor.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/MethodAccessor.java new file mode 100644 index 00000000..83b4400a --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/accessors/MethodAccessor.java @@ -0,0 +1,23 @@ +package com.comphenix.protocol.reflect.accessors; + +import java.lang.reflect.Method; + +/** + * Represents an interface for invoking a method. + * @author Kristian + */ +public interface MethodAccessor { + /** + * Invoke the underlying method. + * @param target - the target instance, or NULL for a static method. + * @param args - the arguments to pass to the method. + * @return The return value, or NULL for void methods. + */ + public Object invoke(Object target, Object... args); + + /** + * Retrieve the underlying method. + * @return The method. + */ + public Method getMethod(); +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java index 858d391e..74df7758 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ByteBufAdapter.java @@ -12,8 +12,8 @@ import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; import java.nio.channels.WritableByteChannel; -import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.google.common.io.ByteStreams; import com.google.common.io.LimitInputStream; @@ -47,10 +47,10 @@ class ByteBufAdapter extends AbstractByteBuf { // Prepare accessors try { if (READER_INDEX == null) { - READER_INDEX = FuzzyReflection.getFieldAccessor(AbstractByteBuf.class.getDeclaredField("readerIndex")); + READER_INDEX = Accessors.getFieldAccessor(AbstractByteBuf.class.getDeclaredField("readerIndex")); } if (WRITER_INDEX == null) { - WRITER_INDEX = FuzzyReflection.getFieldAccessor(AbstractByteBuf.class.getDeclaredField("writerIndex")); + WRITER_INDEX = Accessors.getFieldAccessor(AbstractByteBuf.class.getDeclaredField("writerIndex")); } } catch (Exception e) { throw new RuntimeException("Cannot initialize ByteBufAdapter.", e); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java index dff9895f..434b1999 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/ChatExtensions.java @@ -33,7 +33,8 @@ import com.comphenix.protocol.injector.PacketConstructor; import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.google.common.base.Strings; @@ -108,10 +109,10 @@ public class ChatExtensions { // Try one of the string constructors if (MinecraftReflection.isUsingNetty()) { - messageFactory = FuzzyReflection.getMethodAccessor( + messageFactory = Accessors.getMethodAccessor( MinecraftReflection.getCraftMessageClass(), "fromString", String.class); } else { - messageFactory = FuzzyReflection.getMethodAccessor( + messageFactory = Accessors.getMethodAccessor( FuzzyReflection.fromClass(messageClass).getMethod( FuzzyMethodContract.newBuilder(). requireModifier(Modifier.STATIC). diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java index a24ada26..14e074c7 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java @@ -3,8 +3,8 @@ package com.comphenix.protocol.utility; import org.bukkit.entity.Player; import com.comphenix.protocol.injector.BukkitUnwrapper; -import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.FuzzyReflection.FieldAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.FieldAccessor; /** * Retrieve the content of well-known fields in Minecraft. @@ -30,7 +30,7 @@ public class MinecraftFields { if (NETWORK_ACCESSOR == null) { Class networkClass = MinecraftReflection.getNetworkManagerClass(); Class connectionClass = MinecraftReflection.getNetServerHandlerClass(); - NETWORK_ACCESSOR = FuzzyReflection.getFieldAccessor(connectionClass, networkClass, true); + NETWORK_ACCESSOR = Accessors.getFieldAccessor(connectionClass, networkClass, true); } // Retrieve the network manager return NETWORK_ACCESSOR.get(getPlayerConnection(nmsPlayer)); @@ -49,7 +49,7 @@ public class MinecraftFields { private static Object getPlayerConnection(Object nmsPlayer) { if (CONNECTION_ACCESSOR == null) { Class connectionClass = MinecraftReflection.getNetServerHandlerClass(); - CONNECTION_ACCESSOR = FuzzyReflection.getFieldAccessor(nmsPlayer.getClass(), connectionClass, true); + CONNECTION_ACCESSOR = Accessors.getFieldAccessor(nmsPlayer.getClass(), connectionClass, true); } return CONNECTION_ACCESSOR.get(nmsPlayer); } 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 7cde4435..f8715b50 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -55,6 +55,7 @@ import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.reflect.ClassAnalyser; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod; +import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor; import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor; import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher; @@ -654,7 +655,7 @@ public class MinecraftReflection { return getMinecraftClass("IChatBaseComponent"); } catch (RuntimeException e) { return setMinecraftClass("IChatBaseComponent", - FuzzyReflection.getMethodAccessor(getCraftChatMessage(), "fromString", String.class). + Accessors.getMethodAccessor(getCraftChatMessage(), "fromString", String.class). getMethod().getReturnType().getComponentType() ); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java index 3e630648..17a386da 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/StreamSerializer.java @@ -13,7 +13,8 @@ import org.bukkit.inventory.ItemStack; import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; @@ -100,7 +101,7 @@ public class StreamSerializer { if (MinecraftReflection.isUsingNetty()) { if (READ_ITEM_METHOD == null) { - READ_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + READ_ITEM_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). getMethodByParameters("readItemStack", MinecraftReflection.getItemStackClass(), new Class[0]) @@ -110,7 +111,7 @@ public class StreamSerializer { } else { if (READ_ITEM_METHOD == null) { - READ_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + READ_ITEM_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(1). @@ -143,7 +144,7 @@ public class StreamSerializer { // Invoke the correct method if (MinecraftReflection.isUsingNetty()) { if (READ_NBT_METHOD == null) { - READ_NBT_METHOD = FuzzyReflection.getMethodAccessor( + READ_NBT_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). getMethodByParameters("readNbtCompound", MinecraftReflection.getNBTCompoundClass(), new Class[0]) @@ -153,7 +154,7 @@ public class StreamSerializer { } else { if (READ_NBT_METHOD == null) { - READ_NBT_METHOD = FuzzyReflection.getMethodAccessor( + READ_NBT_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(1). @@ -196,7 +197,7 @@ public class StreamSerializer { if (MinecraftReflection.isUsingNetty()) { if (READ_STRING_METHOD == null) { - READ_STRING_METHOD = FuzzyReflection.getMethodAccessor( + READ_STRING_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). getMethodByParameters("readString", String.class, new Class[] { int.class }) ); @@ -205,7 +206,7 @@ public class StreamSerializer { } else { if (READ_STRING_METHOD == null) { - READ_STRING_METHOD = FuzzyReflection.getMethodAccessor( + READ_STRING_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(2). @@ -254,7 +255,7 @@ public class StreamSerializer { if (MinecraftReflection.isUsingNetty()) { if (WRITE_ITEM_METHOD == null) { - WRITE_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + WRITE_ITEM_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). getMethodByParameters("writeStack", MinecraftReflection.getItemStackClass()) ); @@ -263,7 +264,7 @@ public class StreamSerializer { } else { if (WRITE_ITEM_METHOD == null) - WRITE_ITEM_METHOD = FuzzyReflection.getMethodAccessor( + WRITE_ITEM_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(2). @@ -293,7 +294,7 @@ public class StreamSerializer { if (MinecraftReflection.isUsingNetty()) { if (WRITE_NBT_METHOD == null) { - WRITE_NBT_METHOD = FuzzyReflection.getMethodAccessor( + WRITE_NBT_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). getMethodByParameters("writeNbtCompound", MinecraftReflection.getNBTCompoundClass()) ); @@ -302,7 +303,7 @@ public class StreamSerializer { } else { if (WRITE_NBT_METHOD == null) { - WRITE_NBT_METHOD = FuzzyReflection.getMethodAccessor( + WRITE_NBT_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(2). @@ -332,7 +333,7 @@ public class StreamSerializer { if (MinecraftReflection.isUsingNetty()) { if (WRITE_STRING_METHOD == null) { - WRITE_STRING_METHOD = FuzzyReflection.getMethodAccessor( + WRITE_STRING_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true). getMethodByParameters("writeString", String.class) ); @@ -341,7 +342,7 @@ public class StreamSerializer { } else { if (WRITE_STRING_METHOD == null) { - WRITE_STRING_METHOD = FuzzyReflection.getMethodAccessor( + WRITE_STRING_METHOD = Accessors.getMethodAccessor( FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()).getMethod( FuzzyMethodContract.newBuilder(). parameterCount(2). diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java index f14dbbd5..057a5f3f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java @@ -3,7 +3,8 @@ package com.comphenix.protocol.wrappers; import org.bukkit.ChatColor; import com.comphenix.protocol.reflect.FuzzyReflection; -import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.utility.MinecraftReflection; /** @@ -21,13 +22,13 @@ public class WrappedChatComponent { FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER); // Retrieve the correct methods - SERIALIZE_COMPONENT = FuzzyReflection.getMethodAccessor( + SERIALIZE_COMPONENT = Accessors.getMethodAccessor( fuzzy.getMethodByParameters("serialize", String.class, new Class[] { COMPONENT })); - DESERIALIZE_COMPONENT = FuzzyReflection.getMethodAccessor( + DESERIALIZE_COMPONENT = Accessors.getMethodAccessor( fuzzy.getMethodByParameters("serialize", COMPONENT, new Class[] { String.class })); // Get a component from a standard Minecraft message - CONSTRUCT_COMPONENT = FuzzyReflection.getMethodAccessor( + CONSTRUCT_COMPONENT = Accessors.getMethodAccessor( MinecraftReflection.getCraftChatMessage(), "fromString", String.class); } diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/accessors/AccessorsTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/accessors/AccessorsTest.java new file mode 100644 index 00000000..20be86b1 --- /dev/null +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/accessors/AccessorsTest.java @@ -0,0 +1,57 @@ +package com.comphenix.protocol.reflect.accessors; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class AccessorsTest { + // --- Some classes we can use for testing --- + private static class Entity { + private int id; + + public Entity(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + @SuppressWarnings("unused") + private void setId(int value) { + this.id = value; + } + } + + private static class Player extends Entity { + private String name; + + public Player(int id, String name) { + super(id); + this.name = name; + } + + public String getName() { + return name; + } + } + // --- Test classes --- + + @Test + public void testField() { + Player player = new Player(123, "ABC"); + + Accessors.getFieldAccessor(player.getClass(), "id", true).set(player, 0); + Accessors.getFieldAccessor(player.getClass(), "name", true).set(player, "MODIFIED"); + assertEquals(0, player.getId()); + assertEquals("MODIFIED", player.getName()); + } + + @Test + public void testMethod() { + Player player = new Player(123, "ABC"); + + Accessors.getMethodAccessor(player.getClass(), "setId", int.class).invoke(player, 0); + assertEquals(0, player.getId()); + } +}