diff --git a/pom.xml b/pom.xml index b291c902..4702c721 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ - net.sf - com.comphenix.net.sf + net.bytebuddy + com.comphenix.net.bytebuddy @@ -100,6 +100,8 @@ maven-surefire-plugin 3.0.0-M5 + false + false projectVersion @@ -291,11 +293,11 @@ ${spigot.version} provided + - cglib - cglib-nodep - 3.2.5 - compile + net.bytebuddy + byte-buddy + 1.10.16 diff --git a/src/main/java/com/comphenix/protocol/CommandPacket.java b/src/main/java/com/comphenix/protocol/CommandPacket.java index a6ac8be1..30208929 100644 --- a/src/main/java/com/comphenix/protocol/CommandPacket.java +++ b/src/main/java/com/comphenix/protocol/CommandPacket.java @@ -29,13 +29,12 @@ import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; -import net.sf.cglib.proxy.Factory; - import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; +import com.comphenix.protocol.utility.ByteBuddyGenerated; import com.comphenix.protocol.PacketType.Sender; import com.comphenix.protocol.concurrency.PacketTypeSet; import com.comphenix.protocol.error.ErrorReporter; @@ -464,7 +463,7 @@ class CommandPacket extends CommandBase { // Get the first Minecraft super class while (clazz != null && clazz != Object.class && (!MinecraftReflection.isMinecraftClass(clazz) || - Factory.class.isAssignableFrom(clazz))) { + ByteBuddyGenerated.class.isAssignableFrom(clazz))) { clazz = clazz.getSuperclass(); } diff --git a/src/main/java/com/comphenix/protocol/ProtocolLib.java b/src/main/java/com/comphenix/protocol/ProtocolLib.java index 42ce511b..84138a10 100644 --- a/src/main/java/com/comphenix/protocol/ProtocolLib.java +++ b/src/main/java/com/comphenix/protocol/ProtocolLib.java @@ -37,7 +37,7 @@ import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; import com.comphenix.protocol.updater.Updater; import com.comphenix.protocol.updater.Updater.UpdateType; import com.comphenix.protocol.utility.ChatExtensions; -import com.comphenix.protocol.utility.EnhancerFactory; +import com.comphenix.protocol.utility.ByteBuddyFactory; import com.comphenix.protocol.utility.MinecraftVersion; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; @@ -144,7 +144,7 @@ public class ProtocolLib extends JavaPlugin { ProtocolLogger.init(this); // Initialize enhancer factory - EnhancerFactory.getInstance().setClassLoader(getClassLoader()); + ByteBuddyFactory.getInstance().setClassLoader(getClassLoader()); // Add global parameters DetailedErrorReporter detailedReporter = new DetailedErrorReporter(this); diff --git a/src/main/java/com/comphenix/protocol/events/SerializedOfflinePlayer.java b/src/main/java/com/comphenix/protocol/events/SerializedOfflinePlayer.java index c130da21..618a7225 100644 --- a/src/main/java/com/comphenix/protocol/events/SerializedOfflinePlayer.java +++ b/src/main/java/com/comphenix/protocol/events/SerializedOfflinePlayer.java @@ -21,20 +21,32 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; +import net.bytebuddy.description.ByteCodeElement; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.InvocationHandlerAdapter; +import net.bytebuddy.implementation.MethodCall; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.FieldValue; +import net.bytebuddy.implementation.bind.annotation.Pipe; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; import org.bukkit.*; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; -import com.comphenix.protocol.utility.EnhancerFactory; +import com.comphenix.protocol.utility.ByteBuddyFactory; /** * Represents a player object that can be serialized by Java. @@ -60,9 +72,8 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable { private boolean playedBefore; private boolean online; private boolean whitelisted; - - // Proxy helper - private static Map lookup = new ConcurrentHashMap(); + + private static final Constructor proxyPlayerConstructor = setupProxyPlayerConstructor(); /** * Constructor used by serialization. @@ -243,42 +254,78 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable { } /** - * Retrieve a player object that implements OfflinePlayer by refering to this object. + * Retrieve a player object that implements OfflinePlayer by referring to this object. *

* All other methods cause an exception. * @return Proxy object. */ public Player getProxyPlayer() { - - // Remember to initialize the method filter - if (lookup.size() == 0) { - // Add all public methods - for (Method method : OfflinePlayer.class.getMethods()) { - lookup.put(method.getName(), method); - } + try { + return (Player) proxyPlayerConstructor.newInstance(this); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } catch (InstantiationException e) { + throw new RuntimeException("Cannot instantiate object.", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Error in invocation.", e); } - - // MORE CGLIB magic! - Enhancer ex = EnhancerFactory.getInstance().createEnhancer(); - ex.setSuperclass(Player.class); - ex.setCallback(new MethodInterceptor() { - @Override - public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - - // There's no overloaded methods, so we don't care - Method offlineMethod = lookup.get(method.getName()); - - // Ignore all other methods - if (offlineMethod == null) { - throw new UnsupportedOperationException( - "The method " + method.getName() + " is not supported for offline players."); - } + } - // Invoke our on method - return offlineMethod.invoke(SerializedOfflinePlayer.this, args); - } - }); - - return (Player) ex.create(); + private static Constructor setupProxyPlayerConstructor() + { + final Method[] offlinePlayerMethods = OfflinePlayer.class.getMethods(); + final String[] methodNames = new String[offlinePlayerMethods.length]; + for (int idx = 0; idx < offlinePlayerMethods.length; ++idx) + methodNames[idx] = offlinePlayerMethods[idx].getName(); + + final ElementMatcher.Junction forwardedMethods = ElementMatchers.namedOneOf(methodNames); + + try { + final MethodDelegation forwarding = MethodDelegation.withDefaultConfiguration() + .withBinders(Pipe.Binder.install(Function.class)) + .to(new Object() { + @RuntimeType + public Object intercept(@Pipe Function pipe, + @FieldValue("offlinePlayer") OfflinePlayer proxy) { + return pipe.apply(proxy); + } + }); + + final InvocationHandlerAdapter throwException = InvocationHandlerAdapter.of((obj, method, args) -> { + throw new UnsupportedOperationException( + "The method " + method.getName() + " is not supported for offline players."); + }); + + return ByteBuddyFactory.getInstance() + .createSubclass(PlayerUnion.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) + .name(SerializedOfflinePlayer.class.getPackage().getName() + ".PlayerInvocationHandler") + + .defineField("offlinePlayer", OfflinePlayer.class, Visibility.PRIVATE) + .defineConstructor(Visibility.PUBLIC) + .withParameters(OfflinePlayer.class) + .intercept(MethodCall.invoke(Object.class.getDeclaredConstructor()) + .andThen(FieldAccessor.ofField("offlinePlayer").setsArgumentAt(0))) + + .method(forwardedMethods) + .intercept(forwarding) + + .method(ElementMatchers.not(forwardedMethods)) + .intercept(throwException) + + .make() + .load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded() + .getDeclaredConstructor(OfflinePlayer.class); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Failed to find Player constructor!", e); + } + } + + /** + * This interface extends both OfflinePlayer and Player (in that order) so that the class generated by ByteBuddy + * looks at OfflinePlayer's methods first while still being a Player. + */ + private interface PlayerUnion extends OfflinePlayer, Player + { } } diff --git a/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java b/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java index 0a51184f..c12b1a7e 100644 --- a/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java +++ b/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java @@ -30,6 +30,8 @@ import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.VolatileField; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.FieldAccessor; +import com.comphenix.protocol.utility.ByteBuddyFactory; +import com.comphenix.protocol.utility.ByteBuddyGenerated; import com.comphenix.protocol.utility.MinecraftFields; import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftProtocolVersion; @@ -37,7 +39,9 @@ import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.ObjectReconstructor; import com.comphenix.protocol.wrappers.Pair; import com.comphenix.protocol.wrappers.WrappedGameProfile; + import com.google.common.base.Preconditions; + import io.netty.buffer.ByteBuf; import io.netty.channel.*; import io.netty.channel.socket.SocketChannel; @@ -45,7 +49,7 @@ import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; import io.netty.util.AttributeKey; import io.netty.util.internal.TypeParameterMatcher; -import net.sf.cglib.proxy.Factory; + import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -224,7 +228,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector { synchronized (networkManager) { if (closed) return false; - if (originalChannel instanceof Factory) + if (originalChannel instanceof ByteBuddyFactory) return false; if (!originalChannel.isActive()) return false; @@ -703,7 +707,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector { */ private void disconnect(String message) { // If we're logging in, we can only close the channel - if (playerConnection == null || player instanceof Factory) { + if (playerConnection == null || player instanceof ByteBuddyGenerated) { originalChannel.disconnect(); } else { // Call the disconnect method @@ -736,7 +740,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector { // Attempt to send the packet with NetworkMarker.handle(), or the PlayerConnection if its active try { - if (player instanceof Factory) { + if (player instanceof ByteBuddyGenerated) { MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet); } else { MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet); diff --git a/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java b/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java index 052797f8..fc95f834 100644 --- a/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java +++ b/src/main/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactory.java @@ -17,15 +17,25 @@ package com.comphenix.protocol.injector.server; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import net.sf.cglib.proxy.Callback; -import net.sf.cglib.proxy.CallbackFilter; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; -import net.sf.cglib.proxy.NoOp; +import com.comphenix.protocol.utility.ByteBuddyFactory; +import net.bytebuddy.description.ByteCodeElement; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; +import net.bytebuddy.implementation.FieldAccessor; +import net.bytebuddy.implementation.MethodCall; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.FieldValue; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; import org.bukkit.Server; import org.bukkit.entity.Player; @@ -38,8 +48,7 @@ import com.comphenix.protocol.utility.ChatExtensions; * Create fake player instances that represents pre-authenticated clients. */ public class TemporaryPlayerFactory { - // Prevent too many class creations - private static CallbackFilter callbackFilter; + private static final Constructor temporaryPlayerConstructor = setupProxyPlayerConstructor(); /** * Retrieve the injector from a given player if it contains one. @@ -82,21 +91,34 @@ public class TemporaryPlayerFactory { * @return A temporary player instance. */ public Player createTemporaryPlayer(final Server server) { - - // Default implementation - Callback implementation = new MethodInterceptor() { - @Override - public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + try { + return temporaryPlayerConstructor.newInstance(server); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } catch (InstantiationException e) { + throw new RuntimeException("Cannot instantiate object.", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Error in invocation.", e); + } + } + + private static Constructor setupProxyPlayerConstructor() + { + final MethodDelegation implementation = MethodDelegation.to(new Object() { + @RuntimeType + public Object delegate(@This Object obj, @Origin Method method, @FieldValue("server") Server server, + @AllArguments Object... args) throws Throwable { + String methodName = method.getName(); SocketInjector injector = ((TemporaryPlayer) obj).getInjector(); - + if (injector == null) throw new IllegalStateException("Unable to find injector."); // Use the socket to get the address else if (methodName.equals("getPlayer")) return injector.getUpdatedPlayer(); - else if (methodName.equals("getAddress")) + else if (methodName.equals("getAddress")) return injector.getAddress(); else if (methodName.equals("getServer")) return server; @@ -104,70 +126,71 @@ public class TemporaryPlayerFactory { // Handle send message methods if (methodName.equals("chat") || methodName.equals("sendMessage")) { try { - Object argument = args[0]; - - // Dynamic overloading - if (argument instanceof String) { - return sendMessage(injector, (String) argument); - } else if (argument instanceof String[]) { - for (String message : (String[]) argument) { - sendMessage(injector, message); + Object argument = args[0]; + + // Dynamic overloading + if (argument instanceof String) { + return sendMessage(injector, (String) argument); + } else if (argument instanceof String[]) { + for (String message : (String[]) argument) { + sendMessage(injector, message); + } + return null; } - return null; - } } catch (InvocationTargetException e) { throw e.getCause(); } } - + // Also, handle kicking if (methodName.equals("kickPlayer")) { injector.disconnect((String) args[0]); return null; } - + // The fallback instance Player updated = injector.getUpdatedPlayer(); - + if (updated != obj && updated != null) { - return proxy.invoke(updated, args); + return method.invoke(updated, args); } - + // Methods that are supported in the fallback instance if (methodName.equals("isOnline")) return injector.getSocket() != null && injector.getSocket().isConnected(); else if (methodName.equals("getName")) return "UNKNOWN[" + injector.getSocket().getRemoteSocketAddress() + "]"; - + // Ignore all other methods throw new UnsupportedOperationException( "The method " + method.getName() + " is not supported for temporary players."); } - }; - - // Shared callback filter - if (callbackFilter == null) { - callbackFilter = new CallbackFilter() { - @Override - public int accept(Method method) { - // Do not override the object method or the superclass methods - if (method.getDeclaringClass().equals(Object.class) || - method.getDeclaringClass().equals(TemporaryPlayer.class)) - return 0; - else - return 1; - } - }; + }); + + final ElementMatcher.Junction callbackFilter = ElementMatchers.not( + ElementMatchers.isDeclaredBy(Object.class).or(ElementMatchers.isDeclaredBy(TemporaryPlayer.class))); + + try { + final Constructor constructor = ByteBuddyFactory.getInstance() + .createSubclass(TemporaryPlayer.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) + .name(TemporaryPlayerFactory.class.getPackage().getName() + ".TemporaryPlayerInvocationHandler") + .implement(Player.class) + + .defineField("server", Server.class, Visibility.PRIVATE) + .defineConstructor(Visibility.PUBLIC) + .withParameters(Server.class) + .intercept(MethodCall.invoke(TemporaryPlayer.class.getDeclaredConstructor()) + .andThen(FieldAccessor.ofField("server").setsArgumentAt(0))) + .method(callbackFilter) + .intercept(implementation) + .make() + .load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded() + .getDeclaredConstructor(Server.class); + return (Constructor) constructor; + } catch (NoSuchMethodException e) { + throw new RuntimeException("Failed to find Temporary Player constructor!", e); } - - // CGLib is amazing - Enhancer ex = new Enhancer(); - ex.setSuperclass(TemporaryPlayer.class); - ex.setInterfaces(new Class[] { Player.class }); - ex.setCallbacks(new Callback[] { NoOp.INSTANCE, implementation }); - ex.setCallbackFilter(callbackFilter); - - return (Player) ex.create(); } /** @@ -191,7 +214,7 @@ public class TemporaryPlayerFactory { * @throws InvocationTargetException If the message couldn't be sent. * @throws FieldAccessException If we were unable to construct the message packet. */ - private Object sendMessage(SocketInjector injector, String message) throws InvocationTargetException, FieldAccessException { + private static Object sendMessage(SocketInjector injector, String message) throws InvocationTargetException, FieldAccessException { for (PacketContainer packet : ChatExtensions.createChatPackets(message)) { injector.sendServerPacket(packet.getHandle(), null, false); } diff --git a/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java b/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java index ab95602e..975f83c0 100644 --- a/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java +++ b/src/main/java/com/comphenix/protocol/reflect/ClassAnalyser.java @@ -6,8 +6,11 @@ import java.util.List; import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes; import com.google.common.collect.Lists; - -import net.sf.cglib.asm.*; +import net.bytebuddy.jar.asm.ClassReader; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.jar.asm.Type; public class ClassAnalyser { /** @@ -26,11 +29,11 @@ public class ClassAnalyser { public static AsmOpcodes fromIntOpcode(int opcode) { switch (opcode) { - case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL; - case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL; - case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC; - case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE; - case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC; + case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL; + case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL; + case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC; + case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE; + case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC; default: throw new IllegalArgumentException("Unknown opcode: " + opcode); } } @@ -105,18 +108,18 @@ public class ClassAnalyser { * @throws IOException Cannot access the parent class. */ private List getMethodCalls(Class clazz, Method method) throws IOException { - final $ClassReader reader = new $ClassReader(clazz.getCanonicalName()); + final ClassReader reader = new ClassReader(clazz.getCanonicalName()); final List output = Lists.newArrayList(); // The method we are looking for final String methodName = method.getName(); - final String methodDescription = $Type.getMethodDescriptor(method); + final String methodDescription = Type.getMethodDescriptor(method); - reader.accept(new $ClassVisitor($Opcodes.ASM5) { + reader.accept(new ClassVisitor(Opcodes.ASM5) { @Override - public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (methodName.equals(name) && methodDescription.equals(desc)) { - return new $MethodVisitor($Opcodes.ASM5) { + return new MethodVisitor(Opcodes.ASM5) { @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) { output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc)); @@ -126,7 +129,7 @@ public class ClassAnalyser { return null; } - }, $ClassReader.EXPAND_FRAMES); + }, ClassReader.EXPAND_FRAMES); return output; } } diff --git a/src/main/java/com/comphenix/protocol/reflect/compiler/BoxingHelper.java b/src/main/java/com/comphenix/protocol/reflect/compiler/BoxingHelper.java index 7d5938f9..5b52cbea 100644 --- a/src/main/java/com/comphenix/protocol/reflect/compiler/BoxingHelper.java +++ b/src/main/java/com/comphenix/protocol/reflect/compiler/BoxingHelper.java @@ -17,25 +17,25 @@ package com.comphenix.protocol.reflect.compiler; -import net.sf.cglib.asm.$MethodVisitor; -import net.sf.cglib.asm.$Opcodes; -import net.sf.cglib.asm.$Type; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.jar.asm.Type; /** * Used by the compiler to automatically box and unbox values. */ class BoxingHelper { - private final static $Type BYTE_$Type = $Type.getObjectType("java/lang/Byte"); - private final static $Type BOOLEAN_$Type = $Type.getObjectType("java/lang/Boolean"); - private final static $Type SHORT_$Type = $Type.getObjectType("java/lang/Short"); - private final static $Type CHARACTER_$Type = $Type.getObjectType("java/lang/Character"); - private final static $Type INTEGER_$Type = $Type.getObjectType("java/lang/Integer"); - private final static $Type FLOAT_$Type = $Type.getObjectType("java/lang/Float"); - private final static $Type LONG_$Type = $Type.getObjectType("java/lang/Long"); - private final static $Type DOUBLE_$Type = $Type.getObjectType("java/lang/Double"); - private final static $Type NUMBER_$Type = $Type.getObjectType("java/lang/Number"); - private final static $Type OBJECT_$Type = $Type.getObjectType("java/lang/Object"); + private final static Type BYTE_Type = Type.getObjectType("java/lang/Byte"); + private final static Type BOOLEAN_Type = Type.getObjectType("java/lang/Boolean"); + private final static Type SHORT_Type = Type.getObjectType("java/lang/Short"); + private final static Type CHARACTER_Type = Type.getObjectType("java/lang/Character"); + private final static Type INTEGER_Type = Type.getObjectType("java/lang/Integer"); + private final static Type FLOAT_Type = Type.getObjectType("java/lang/Float"); + private final static Type LONG_Type = Type.getObjectType("java/lang/Long"); + private final static Type DOUBLE_Type = Type.getObjectType("java/lang/Double"); + private final static Type NUMBER_Type = Type.getObjectType("java/lang/Number"); + private final static Type OBJECT_Type = Type.getObjectType("java/lang/Object"); private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()"); private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()"); @@ -44,9 +44,9 @@ class BoxingHelper { private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()"); private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()"); - private $MethodVisitor mv; + private MethodVisitor mv; - public BoxingHelper($MethodVisitor mv) { + public BoxingHelper(MethodVisitor mv) { this.mv = mv; } @@ -54,42 +54,42 @@ class BoxingHelper { * Generates the instructions to box the top stack value. This value is * replaced by its boxed equivalent on top of the stack. * - * @param type the $Type of the top stack value. + * @param type the Type of the top stack value. */ - public void box(final $Type type){ - if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) { + public void box(final Type type){ + if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { return; } - if(type == $Type.VOID_TYPE) { + if(type == Type.VOID_TYPE) { push((String) null); } else { - $Type boxed = type; + Type boxed = type; switch(type.getSort()) { - case $Type.BYTE: - boxed = BYTE_$Type; + case Type.BYTE: + boxed = BYTE_Type; break; - case $Type.BOOLEAN: - boxed = BOOLEAN_$Type; + case Type.BOOLEAN: + boxed = BOOLEAN_Type; break; - case $Type.SHORT: - boxed = SHORT_$Type; + case Type.SHORT: + boxed = SHORT_Type; break; - case $Type.CHAR: - boxed = CHARACTER_$Type; + case Type.CHAR: + boxed = CHARACTER_Type; break; - case $Type.INT: - boxed = INTEGER_$Type; + case Type.INT: + boxed = INTEGER_Type; break; - case $Type.FLOAT: - boxed = FLOAT_$Type; + case Type.FLOAT: + boxed = FLOAT_Type; break; - case $Type.LONG: - boxed = LONG_$Type; + case Type.LONG: + boxed = LONG_Type; break; - case $Type.DOUBLE: - boxed = DOUBLE_$Type; + case Type.DOUBLE: + boxed = DOUBLE_Type; break; } @@ -105,46 +105,46 @@ class BoxingHelper { swap(); } - invokeConstructor(boxed, new MethodDescriptor("", $Type.VOID_TYPE, new $Type[] {type})); + invokeConstructor(boxed, new MethodDescriptor("", Type.VOID_TYPE, new Type[] {type})); } } /** * Generates the instruction to invoke a constructor. * - * @param $Type the class in which the constructor is defined. + * @param Type the class in which the constructor is defined. * @param method the constructor to be invoked. */ - public void invokeConstructor(final $Type $Type, final MethodDescriptor method){ - invokeInsn($Opcodes.INVOKESPECIAL, $Type, method); + public void invokeConstructor(final Type Type, final MethodDescriptor method){ + invokeInsn(Opcodes.INVOKESPECIAL, Type, method); } /** * Generates a DUP_X1 instruction. */ public void dupX1(){ - mv.visitInsn($Opcodes.DUP_X1); + mv.visitInsn(Opcodes.DUP_X1); } /** * Generates a DUP_X2 instruction. */ public void dupX2(){ - mv.visitInsn($Opcodes.DUP_X2); + mv.visitInsn(Opcodes.DUP_X2); } /** * Generates a POP instruction. */ public void pop(){ - mv.visitInsn($Opcodes.POP); + mv.visitInsn(Opcodes.POP); } /** * Generates a SWAP instruction. */ public void swap(){ - mv.visitInsn($Opcodes.SWAP); + mv.visitInsn(Opcodes.SWAP); } /** @@ -163,11 +163,11 @@ class BoxingHelper { */ public void push(final int value) { if (value >= -1 && value <= 5) { - mv.visitInsn($Opcodes.ICONST_0 + value); + mv.visitInsn(Opcodes.ICONST_0 + value); } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { - mv.visitIntInsn($Opcodes.BIPUSH, value); + mv.visitIntInsn(Opcodes.BIPUSH, value); } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { - mv.visitIntInsn($Opcodes.SIPUSH, value); + mv.visitIntInsn(Opcodes.SIPUSH, value); } else { mv.visitLdcInsn(new Integer(value)); } @@ -176,10 +176,10 @@ class BoxingHelper { /** * Generates the instruction to create a new object. * - * @param $Type the class of the object to be created. + * @param Type the class of the object to be created. */ - public void newInstance(final $Type $Type){ - $TypeInsn($Opcodes.NEW, $Type); + public void newInstance(final Type Type){ + TypeInsn(Opcodes.NEW, Type); } /** @@ -189,7 +189,7 @@ class BoxingHelper { */ public void push(final String value) { if (value == null) { - mv.visitInsn($Opcodes.ACONST_NULL); + mv.visitInsn(Opcodes.ACONST_NULL); } else { mv.visitLdcInsn(value); } @@ -200,35 +200,35 @@ class BoxingHelper { * replaced by its unboxed equivalent on top of the stack. * * @param type - * the $Type of the top stack value. + * the Type of the top stack value. */ - public void unbox(final $Type type){ - $Type t = NUMBER_$Type; + public void unbox(final Type type){ + Type t = NUMBER_Type; MethodDescriptor sig = null; switch(type.getSort()) { - case $Type.VOID: + case Type.VOID: return; - case $Type.CHAR: - t = CHARACTER_$Type; + case Type.CHAR: + t = CHARACTER_Type; sig = CHAR_VALUE; break; - case $Type.BOOLEAN: - t = BOOLEAN_$Type; + case Type.BOOLEAN: + t = BOOLEAN_Type; sig = BOOLEAN_VALUE; break; - case $Type.DOUBLE: + case Type.DOUBLE: sig = DOUBLE_VALUE; break; - case $Type.FLOAT: + case Type.FLOAT: sig = FLOAT_VALUE; break; - case $Type.LONG: + case Type.LONG: sig = LONG_VALUE; break; - case $Type.INT: - case $Type.SHORT: - case $Type.BYTE: + case Type.INT: + case Type.SHORT: + case Type.BYTE: sig = INT_VALUE; } @@ -242,13 +242,13 @@ class BoxingHelper { /** * Generates the instruction to check that the top stack value is of the - * given $Type. + * given Type. * - * @param $Type a class or interface $Type. + * @param Type a class or interface Type. */ - public void checkCast(final $Type $Type){ - if(!$Type.equals(OBJECT_$Type)) { - $TypeInsn($Opcodes.CHECKCAST, $Type); + public void checkCast(final Type Type){ + if(!Type.equals(OBJECT_Type)) { + TypeInsn(Opcodes.CHECKCAST, Type); } } @@ -258,35 +258,35 @@ class BoxingHelper { * @param owner the class in which the method is defined. * @param method the method to be invoked. */ - public void invokeVirtual(final $Type owner, final MethodDescriptor method){ - invokeInsn($Opcodes.INVOKEVIRTUAL, owner, method); + public void invokeVirtual(final Type owner, final MethodDescriptor method){ + invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); } /** * Generates an invoke method instruction. * * @param opcode the instruction's opcode. - * @param $Type the class in which the method is defined. + * @param type the class in which the method is defined. * @param method the method to be invoked. */ - private void invokeInsn(final int opcode, final $Type $Type, final MethodDescriptor method){ - String owner = $Type.getSort() == $Type.ARRAY ? $Type.getDescriptor() : $Type.getInternalName(); + private void invokeInsn(final int opcode, final Type type, final MethodDescriptor method){ + String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName(); mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor()); } /** - * Generates a $Type dependent instruction. + * Generates a Type dependent instruction. * * @param opcode the instruction's opcode. - * @param $Type the instruction's operand. + * @param type the instruction's operand. */ - private void $TypeInsn(final int opcode, final $Type $Type){ + private void TypeInsn(final int opcode, final Type type){ String desc; - if($Type.getSort() == $Type.ARRAY) { - desc = $Type.getDescriptor(); + if(type.getSort() == Type.ARRAY) { + desc = type.getDescriptor(); } else { - desc = $Type.getInternalName(); + desc = type.getInternalName(); } mv.visitTypeInsn(opcode, desc); diff --git a/src/main/java/com/comphenix/protocol/reflect/compiler/MethodDescriptor.java b/src/main/java/com/comphenix/protocol/reflect/compiler/MethodDescriptor.java index 71a6ffd8..7a82b22c 100644 --- a/src/main/java/com/comphenix/protocol/reflect/compiler/MethodDescriptor.java +++ b/src/main/java/com/comphenix/protocol/reflect/compiler/MethodDescriptor.java @@ -20,7 +20,7 @@ package com.comphenix.protocol.reflect.compiler; import java.util.HashMap; import java.util.Map; -import net.sf.cglib.asm.$Type; +import net.bytebuddy.jar.asm.Type; /** * Represents a method. @@ -75,10 +75,10 @@ class MethodDescriptor { */ public MethodDescriptor( final String name, - final $Type returnType, - final $Type[] argumentTypes) + final Type returnType, + final Type[] argumentTypes) { - this(name, $Type.getMethodDescriptor(returnType, argumentTypes)); + this(name, Type.getMethodDescriptor(returnType, argumentTypes)); } /** @@ -206,8 +206,8 @@ class MethodDescriptor { * * @return the return type of the method described by this object. */ - public $Type getReturnType() { - return $Type.getReturnType(desc); + public Type getReturnType() { + return Type.getReturnType(desc); } /** @@ -215,8 +215,8 @@ class MethodDescriptor { * * @return the argument types of the method described by this object. */ - public $Type[] getArgumentTypes() { - return $Type.getArgumentTypes(desc); + public Type[] getArgumentTypes() { + return Type.getArgumentTypes(desc); } public String toString() { diff --git a/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java b/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java index 5f9af5e6..9ead20b2 100644 --- a/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java +++ b/src/main/java/com/comphenix/protocol/reflect/compiler/StructureCompiler.java @@ -25,19 +25,13 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import net.sf.cglib.asm.$ClassWriter; -import net.sf.cglib.asm.$FieldVisitor; -import net.sf.cglib.asm.$Label; -import net.sf.cglib.asm.$MethodVisitor; -import net.sf.cglib.asm.$Opcodes; -import net.sf.cglib.asm.$Type; - import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.error.Report; import com.comphenix.protocol.error.ReportType; import com.comphenix.protocol.reflect.StructureModifier; import com.google.common.base.Objects; import com.google.common.primitives.Primitives; +import net.bytebuddy.jar.asm.*; // public class CompiledStructureModifierPacket20 extends CompiledStructureModifier { // @@ -276,15 +270,15 @@ public final class StructureCompiler { */ private Class generateClass(StructureModifier source) { - $ClassWriter cw = new $ClassWriter(0); + ClassWriter cw = new ClassWriter(0); Class targetType = source.getTargetType(); String className = getCompiledName(source); - String targetSignature = $Type.getDescriptor(targetType); + String targetSignature = Type.getDescriptor(targetType); String targetName = targetType.getName().replace('.', '/'); // Define class - cw.visit($Opcodes.V1_6, $Opcodes.ACC_PUBLIC + $Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className, + cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className, null, COMPILED_CLASS, null); createFields(cw, targetSignature); @@ -352,35 +346,35 @@ public final class StructureCompiler { return !Modifier.isFinal(field.getModifiers()); } - private void createFields($ClassWriter cw, String targetSignature) { - $FieldVisitor typedField = cw.visitField($Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null); + private void createFields(ClassWriter cw, String targetSignature) { + FieldVisitor typedField = cw.visitField(Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null); typedField.visitEnd(); } - private void createWriteMethod($ClassWriter cw, String className, List fields, String targetSignature, String targetName) { + private void createWriteMethod(ClassWriter cw, String className, List fields, String targetSignature, String targetName) { String methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";"; String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";"; - $MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature, + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature, new String[] { FIELD_EXCEPTION_CLASS }); BoxingHelper boxingHelper = new BoxingHelper(mv); String generatedClassName = PACKAGE_NAME + "/" + className; mv.visitCode(); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature); - mv.visitVarInsn($Opcodes.ASTORE, 3); - mv.visitVarInsn($Opcodes.ILOAD, 1); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature); + mv.visitVarInsn(Opcodes.ASTORE, 3); + mv.visitVarInsn(Opcodes.ILOAD, 1); // The last $Label is for the default switch - $Label[] $Labels = new $Label[fields.size()]; - $Label error$Label = new $Label(); - $Label return$Label = new $Label(); + Label[] $Labels = new Label[fields.size()]; + Label error$Label = new Label(); + Label return$Label = new Label(); // Generate $Labels for (int i = 0; i < fields.size(); i++) { - $Labels[i] = new $Label(); + $Labels[i] = new Label(); } mv.visitTableSwitchInsn(0, $Labels.length - 1, error$Label, $Labels); @@ -390,82 +384,82 @@ public final class StructureCompiler { Field field = fields.get(i); Class outputType = field.getType(); Class inputType = Primitives.wrap(outputType); - String typeDescriptor = $Type.getDescriptor(outputType); + String typeDescriptor = Type.getDescriptor(outputType); String inputPath = inputType.getName().replace('.', '/'); mv.visitLabel($Labels[i]); // Push the compare object if (i == 0) - mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null); + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null); else - mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); // Only write to public non-final fields if (isPublic(field) && isNonFinal(field)) { - mv.visitVarInsn($Opcodes.ALOAD, 3); - mv.visitVarInsn($Opcodes.ALOAD, 2); + mv.visitVarInsn(Opcodes.ALOAD, 3); + mv.visitVarInsn(Opcodes.ALOAD, 2); if (!outputType.isPrimitive()) - mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath); + mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath); else - boxingHelper.unbox($Type.getType(outputType)); + boxingHelper.unbox(Type.getType(outputType)); - mv.visitFieldInsn($Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor); + mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor); } else { // Use reflection. We don't have a choice, unfortunately. - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitVarInsn($Opcodes.ILOAD, 1); - mv.visitVarInsn($Opcodes.ALOAD, 2); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V"); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ILOAD, 1); + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V"); } - mv.visitJumpInsn($Opcodes.GOTO, return$Label); + mv.visitJumpInsn(Opcodes.GOTO, return$Label); } mv.visitLabel(error$Label); - mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null); - mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS); - mv.visitInsn($Opcodes.DUP); - mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder"); - mv.visitInsn($Opcodes.DUP); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS); + mv.visitInsn(Opcodes.DUP); + mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); + mv.visitInsn(Opcodes.DUP); mv.visitLdcInsn("Invalid index "); - mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); - mv.visitVarInsn($Opcodes.ILOAD, 1); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); - mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "", "(Ljava/lang/String;)V"); - mv.visitInsn($Opcodes.ATHROW); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); + mv.visitVarInsn(Opcodes.ILOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "", "(Ljava/lang/String;)V"); + mv.visitInsn(Opcodes.ATHROW); mv.visitLabel(return$Label); - mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitInsn($Opcodes.ARETURN); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(5, 4); mv.visitEnd(); } - private void createReadMethod($ClassWriter cw, String className, List fields, String targetSignature, String targetName) { - $MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null, + private void createReadMethod(ClassWriter cw, String className, List fields, String targetSignature, String targetName) { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null, new String[] { "com/comphenix/protocol/reflect/FieldAccessException" }); BoxingHelper boxingHelper = new BoxingHelper(mv); String generatedClassName = PACKAGE_NAME + "/" + className; mv.visitCode(); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature); - mv.visitVarInsn($Opcodes.ASTORE, 2); - mv.visitVarInsn($Opcodes.ILOAD, 1); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature); + mv.visitVarInsn(Opcodes.ASTORE, 2); + mv.visitVarInsn(Opcodes.ILOAD, 1); // The last $Label is for the default switch - $Label[] $Labels = new $Label[fields.size()]; - $Label error$Label = new $Label(); + Label[] $Labels = new Label[fields.size()]; + Label error$Label = new Label(); // Generate $Labels for (int i = 0; i < fields.size(); i++) { - $Labels[i] = new $Label(); + $Labels[i] = new Label(); } mv.visitTableSwitchInsn(0, fields.size() - 1, error$Label, $Labels); @@ -474,74 +468,74 @@ public final class StructureCompiler { Field field = fields.get(i); Class outputType = field.getType(); - String typeDescriptor = $Type.getDescriptor(outputType); + String typeDescriptor = Type.getDescriptor(outputType); mv.visitLabel($Labels[i]); // Push the compare object if (i == 0) - mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null); + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null); else - mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); // Note that byte code cannot access non-public fields if (isPublic(field)) { - mv.visitVarInsn($Opcodes.ALOAD, 2); - mv.visitFieldInsn($Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor); + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitFieldInsn(Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor); - boxingHelper.box($Type.getType(outputType)); + boxingHelper.box(Type.getType(outputType)); } else { // We have to use reflection for private and protected fields. - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitVarInsn($Opcodes.ILOAD, 1); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;"); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ILOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;"); } - mv.visitInsn($Opcodes.ARETURN); + mv.visitInsn(Opcodes.ARETURN); } mv.visitLabel(error$Label); - mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null); - mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS); - mv.visitInsn($Opcodes.DUP); - mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder"); - mv.visitInsn($Opcodes.DUP); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS); + mv.visitInsn(Opcodes.DUP); + mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder"); + mv.visitInsn(Opcodes.DUP); mv.visitLdcInsn("Invalid index "); - mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); - mv.visitVarInsn($Opcodes.ILOAD, 1); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); - mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "", "(Ljava/lang/String;)V"); - mv.visitInsn($Opcodes.ATHROW); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "", "(Ljava/lang/String;)V"); + mv.visitVarInsn(Opcodes.ILOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "", "(Ljava/lang/String;)V"); + mv.visitInsn(Opcodes.ATHROW); mv.visitMaxs(5, 3); mv.visitEnd(); } - private void createConstructor($ClassWriter cw, String className, String targetSignature, String targetName) { - $MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PUBLIC, "", + private void createConstructor(ClassWriter cw, String className, String targetSignature, String targetName) { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V", "(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V", null); String fullClassName = PACKAGE_NAME + "/" + className; mv.visitCode(); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitMethodInsn($Opcodes.INVOKESPECIAL, COMPILED_CLASS, "", "()V"); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitVarInsn($Opcodes.ALOAD, 1); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V"); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitVarInsn($Opcodes.ALOAD, 1); - mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;"); - mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;"); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitFieldInsn($Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;"); - mv.visitTypeInsn($Opcodes.CHECKCAST, targetName); - mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature); - mv.visitVarInsn($Opcodes.ALOAD, 0); - mv.visitVarInsn($Opcodes.ALOAD, 2); - mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;"); - mv.visitInsn($Opcodes.RETURN); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "", "()V"); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V"); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;"); + mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;"); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;"); + mv.visitTypeInsn(Opcodes.CHECKCAST, targetName); + mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;"); + mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(2, 3); mv.visitEnd(); } diff --git a/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java b/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java index 1a560c7e..88a0b72f 100644 --- a/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java +++ b/src/main/java/com/comphenix/protocol/reflect/instances/DefaultInstances.java @@ -25,8 +25,6 @@ import java.util.logging.Level; import javax.annotation.Nullable; -import net.sf.cglib.proxy.Enhancer; - import com.comphenix.protocol.ProtocolLogger; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; @@ -304,24 +302,6 @@ public class DefaultInstances implements InstanceProvider { 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(this) { - @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/src/main/java/com/comphenix/protocol/utility/ByteBuddyFactory.java b/src/main/java/com/comphenix/protocol/utility/ByteBuddyFactory.java new file mode 100644 index 00000000..6d2404eb --- /dev/null +++ b/src/main/java/com/comphenix/protocol/utility/ByteBuddyFactory.java @@ -0,0 +1,65 @@ +package com.comphenix.protocol.utility; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; + +/** + * Represents a shared ByteBuddy factory. + * @author Kristian + */ +public class ByteBuddyFactory { + private static ByteBuddyFactory INSTANCE = new ByteBuddyFactory(); + + // The current class loader + private ClassLoader loader = ByteBuddyFactory.class.getClassLoader(); + + public static ByteBuddyFactory getInstance() { + return INSTANCE; + } + + /** + * Set the current class loader to use when constructing enhancers. + * @param loader - the class loader + */ + public void setClassLoader(ClassLoader loader) { + this.loader = loader; + } + + /** + * Get the current class loader we are using. + * @return The current class loader. + */ + public ClassLoader getClassLoader() { + return loader; + } + + /** + * Creates a type builder for a subclass of a given {@link Class}. + * @param clz The class for which to create a subclass. + * @return A type builder for creating a new class extending the provided clz and implementing + * {@link ByteBuddyGenerated}. + */ + public DynamicType.Builder.MethodDefinition.ImplementationDefinition.Optional createSubclass(Class clz) + { + return new ByteBuddy() + .subclass(clz) + .implement(ByteBuddyGenerated.class); + } + + /** + * Creates a type builder for a subclass of a given {@link Class}. + * @param clz The class for which to create a subclass. + * @param constructorStrategy The constructor strategy to use. + * @return A type builder for creating a new class extending the provided clz and implementing + * {@link ByteBuddyGenerated}. + */ + public DynamicType.Builder.MethodDefinition.ImplementationDefinition.Optional createSubclass(Class clz, + ConstructorStrategy.Default constructorStrategy) + { + return new ByteBuddy() + .subclass(clz, constructorStrategy) + .implement(ByteBuddyGenerated.class); + } +} diff --git a/src/main/java/com/comphenix/protocol/utility/ByteBuddyGenerated.java b/src/main/java/com/comphenix/protocol/utility/ByteBuddyGenerated.java new file mode 100644 index 00000000..b531d306 --- /dev/null +++ b/src/main/java/com/comphenix/protocol/utility/ByteBuddyGenerated.java @@ -0,0 +1,9 @@ +package com.comphenix.protocol.utility; + +/** + * Represents an object that has been generated using ByteBuddy. + * + * @author Pim + */ +public interface ByteBuddyGenerated { +} diff --git a/src/main/java/com/comphenix/protocol/utility/EnhancerFactory.java b/src/main/java/com/comphenix/protocol/utility/EnhancerFactory.java deleted file mode 100644 index c64fb1e8..00000000 --- a/src/main/java/com/comphenix/protocol/utility/EnhancerFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.comphenix.protocol.utility; - -import net.sf.cglib.proxy.Enhancer; - -/** - * Represents a shared enchancer factory. - * @author Kristian - */ -public class EnhancerFactory { - private static EnhancerFactory INSTANCE = new EnhancerFactory(); - - // The current class loader - private ClassLoader loader = EnhancerFactory.class.getClassLoader(); - - public static EnhancerFactory getInstance() { - return INSTANCE; - } - - /** - * Create a new CGLib enhancer. - * @return The new enhancer. - */ - public Enhancer createEnhancer() { - Enhancer enhancer = new Enhancer(); - enhancer.setClassLoader(loader); - return enhancer; - } - - /** - * Set the current class loader to use when constructing enhancers. - * @param loader - the class loader - */ - public void setClassLoader(ClassLoader loader) { - this.loader = loader; - } - - /** - * Get the current class loader we are using. - * @return The current class loader. - */ - public ClassLoader getClassLoader() { - return loader; - } -} diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java b/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java index 677a586b..0c6c3418 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java @@ -1,10 +1,12 @@ package com.comphenix.protocol.utility; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.PacketContainer; @@ -14,8 +16,13 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import net.bytebuddy.matcher.ElementMatchers; /** * Static methods for accessing Minecraft methods. @@ -33,6 +40,8 @@ public class MinecraftMethods { // For packet private volatile static Method packetReadByteBuf; private volatile static Method packetWriteByteBuf; + + private static Constructor proxyConstructor; /** * Retrieve the send packet method in PlayerConnection/NetServerHandler. @@ -166,36 +175,59 @@ public class MinecraftMethods { initializePacket(); return packetWriteByteBuf; } - + + private static Constructor setupProxyConstructor() + { + try { + return ByteBuddyFactory.getInstance() + .createSubclass(MinecraftReflection.getPacketDataSerializerClass()) + .name(MinecraftMethods.class.getPackage().getName() + ".PacketDecorator") + .method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))) + .intercept(MethodDelegation.to(new Object() { + @RuntimeType + public Object delegate(@SuperCall Callable zuper, @Origin Method method) throws Exception { + if (method.getName().contains("read")) { + throw new ReadMethodException(); + } + + if (method.getName().contains("write")) { + throw new WriteMethodException(); + } + return zuper.call(); + } + })) + .make() + .load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded() + .getDeclaredConstructor(MinecraftReflection.getByteBufClass()); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Failed to find constructor!", e); + } + } + /** * Initialize the two read() and write() methods. */ private static void initializePacket() { + // Initialize the methods if (packetReadByteBuf == null || packetWriteByteBuf == null) { - // This object will allow us to detect which methods were called - Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer(); - enhancer.setSuperclass(MinecraftReflection.getPacketDataSerializerClass()); - enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { - if (method.getName().contains("read")) { - throw new ReadMethodException(); - } + if (proxyConstructor == null) + proxyConstructor = setupProxyConstructor(); - if (method.getName().contains("write")) { - throw new WriteMethodException(); - } + final Object javaProxy; + try { + javaProxy = proxyConstructor.newInstance(Unpooled.buffer()); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } catch (InstantiationException e) { + throw new RuntimeException("Cannot instantiate object.", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Error in invocation.", e); + } - return proxy.invokeSuper(obj, args); - }); - - // Create our proxy object - Object javaProxy = enhancer.create( - new Class[] { MinecraftReflection.getByteBufClass() }, - new Object[] { Unpooled.buffer() } - ); - - Object lookPacket = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW).getHandle(); - List candidates = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()) + final Object lookPacket = new PacketContainer(PacketType.Play.Client.CLOSE_WINDOW).getHandle(); + final List candidates = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass()) .getMethodListByParameters(Void.TYPE, new Class[] { MinecraftReflection.getPacketDataSerializerClass() }); // Look through all the methods diff --git a/src/main/java/com/comphenix/protocol/wrappers/nbt/TileEntityAccessor.java b/src/main/java/com/comphenix/protocol/wrappers/nbt/TileEntityAccessor.java index f62e1314..81c20716 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/nbt/TileEntityAccessor.java +++ b/src/main/java/com/comphenix/protocol/wrappers/nbt/TileEntityAccessor.java @@ -1,6 +1,8 @@ package com.comphenix.protocol.wrappers.nbt; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.concurrent.ConcurrentMap; @@ -11,18 +13,20 @@ 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.reflect.fuzzy.FuzzyMethodContract; -import com.comphenix.protocol.utility.EnhancerFactory; +import com.comphenix.protocol.utility.ByteBuddyFactory; +import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; + import com.google.common.collect.Maps; -import net.sf.cglib.asm.$ClassReader; -import net.sf.cglib.asm.$ClassVisitor; -import net.sf.cglib.asm.$MethodVisitor; -import net.sf.cglib.asm.$Opcodes; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.InvocationHandlerAdapter; +import net.bytebuddy.jar.asm.ClassReader; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.matcher.ElementMatchers; import org.bukkit.block.BlockState; @@ -43,14 +47,12 @@ class TileEntityAccessor { */ private static final ConcurrentMap, TileEntityAccessor> cachedAccessors = Maps.newConcurrentMap(); + private static Constructor nbtCompoundParserConstructor; + private FieldAccessor tileEntityField; private MethodAccessor readCompound; private MethodAccessor writeCompound; - // For CGLib detection - private boolean writeDetected; - private boolean readDetected; - TileEntityAccessor() { // Do nothing } @@ -97,7 +99,7 @@ class TileEntityAccessor { } catch (IOException ex1) { try { // Much slower though - findMethodUsingCGLib(state); + findMethodUsingByteBuddy(state); } catch (Exception ex2) { throw new RuntimeException("Cannot find read/write methods in " + type, ex2); } @@ -117,26 +119,26 @@ class TileEntityAccessor { private void findMethodsUsingASM() throws IOException { final Class nbtCompoundClass = MinecraftReflection.getNBTCompoundClass(); final Class tileEntityClass = MinecraftReflection.getTileEntityClass(); - final $ClassReader reader = new $ClassReader(tileEntityClass.getCanonicalName()); + final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName()); final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass()); final String expectedDesc = "(L" + tagCompoundName + ";)"; - reader.accept(new $ClassVisitor($Opcodes.ASM5) { + reader.accept(new ClassVisitor(Opcodes.ASM5) { @Override - public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { final String methodName = name; // Detect read/write calls to NBTTagCompound if (desc.startsWith(expectedDesc)) { - return new $MethodVisitor($Opcodes.ASM5) { + return new MethodVisitor(Opcodes.ASM5) { private int readMethods; private int writeMethods; @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean intf) { // This must be a virtual call on NBTTagCompound that accepts a String - if (opcode == $Opcodes.INVOKEVIRTUAL + if (opcode == Opcodes.INVOKEVIRTUAL && tagCompoundName.equals(owner) && desc.startsWith("(Ljava/lang/String")) { @@ -167,47 +169,60 @@ class TileEntityAccessor { }, 0); } + private static Constructor setupNBTCompoundParserConstructor() + { + final Class nbtCompoundClass = MinecraftReflection.getNBTCompoundClass(); + try { + return ByteBuddyFactory.getInstance() + .createSubclass(nbtCompoundClass) + .name(MinecraftMethods.class.getPackage().getName() + ".NBTInvocationHandler") + .method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))) + .intercept(InvocationHandlerAdapter.of((obj, method, args) -> { + // If true, we've found a write method. Otherwise, a read method. + if (method.getReturnType().equals(Void.TYPE)) + throw new WriteMethodException(); + throw new ReadMethodException(); + })) + .make() + .load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION) + .getLoaded() + .getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Failed to find NBTCompound constructor."); + } + } + /** * Find the read/write methods in TileEntity. * @param blockState - the block state. * @throws IOException If we cannot find these methods. */ - private void findMethodUsingCGLib(T blockState) throws IOException { + private void findMethodUsingByteBuddy(T blockState) throws IllegalAccessException, InvocationTargetException, + InstantiationException { + if (nbtCompoundParserConstructor == null) + nbtCompoundParserConstructor = setupNBTCompoundParserConstructor(); + final Class nbtCompoundClass = MinecraftReflection.getNBTCompoundClass(); - // This is a much slower method, but it is necessary in MCPC - Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer(); - enhancer.setSuperclass(nbtCompoundClass); - enhancer.setCallback(new MethodInterceptor() { - @Override - public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - if (method.getReturnType().equals(Void.TYPE)) { - // Write method - writeDetected = true; - } else { - // Read method - readDetected = true; - } - throw new RuntimeException("Stop execution."); - } - }); - Object compound = enhancer.create(); - Object tileEntity = tileEntityField.get(blockState); + final Object compound = nbtCompoundParserConstructor.newInstance(); + final Object tileEntity = tileEntityField.get(blockState); // Look in every read/write like method for (Method method : FuzzyReflection.fromObject(tileEntity, true). getMethodListByParameters(Void.TYPE, new Class[] { nbtCompoundClass })) { try { - readDetected = false; - writeDetected = false; method.invoke(tileEntity, compound); - } catch (Exception e) { - // Okay - see if we detected a write or read - if (readDetected) + } catch (InvocationTargetException e) { + if (e.getCause() instanceof ReadMethodException) { readCompound = Accessors.getMethodAccessor(method, true); - if (writeDetected) + } else if (e.getCause() instanceof WriteMethodException) { writeCompound = Accessors.getMethodAccessor(method, true); + } else { + // throw new RuntimeException("Inner exception.", e); + } + } catch (Exception e) { + throw new RuntimeException("Generic reflection error.", e); } } } @@ -284,4 +299,28 @@ class TileEntityAccessor { } return (TileEntityAccessor) (accessor != EMPTY_ACCESSOR ? accessor : null); } + + /** + * An internal exception used to detect read methods. + * @author Kristian + */ + private static class ReadMethodException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public ReadMethodException() { + super("A read method was executed."); + } + } + + /** + * An internal exception used to detect write methods. + * @author Kristian + */ + private static class WriteMethodException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public WriteMethodException() { + super("A write method was executed."); + } + } } diff --git a/src/test/java/com/comphenix/protocol/events/SerializedOfflinePlayerTest.java b/src/test/java/com/comphenix/protocol/events/SerializedOfflinePlayerTest.java new file mode 100644 index 00000000..eabf8383 --- /dev/null +++ b/src/test/java/com/comphenix/protocol/events/SerializedOfflinePlayerTest.java @@ -0,0 +1,72 @@ +package com.comphenix.protocol.events; + +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +public class SerializedOfflinePlayerTest { + + @Mock + static OfflinePlayer offlinePlayer; + + private static final String name = "playerName"; + private static UUID uuid; + private static final long firstPlayed = 1000L; + private static final long lastPlayed = firstPlayed + 100L; + private static final boolean isOp = false; + private static final boolean playedBefore = true; + private static final boolean whitelisted = true; + + private static SerializedOfflinePlayer serializedOfflinePlayer; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + + uuid = UUID.randomUUID(); + when(offlinePlayer.getName()).thenReturn(name); + when(offlinePlayer.getUniqueId()).thenReturn(uuid); + when(offlinePlayer.getFirstPlayed()).thenReturn(firstPlayed); + when(offlinePlayer.getLastPlayed()).thenReturn(lastPlayed); + when(offlinePlayer.isOp()).thenReturn(isOp); + when(offlinePlayer.hasPlayedBefore()).thenReturn(playedBefore); + when(offlinePlayer.isWhitelisted()).thenReturn(whitelisted); + + serializedOfflinePlayer = new SerializedOfflinePlayer(offlinePlayer); + } + + @Test + public void getProxyPlayer() { + Player player = serializedOfflinePlayer.getProxyPlayer(); + Assert.assertNotNull(player); + + // getDisplayName only works for online players. + assertThrows(UnsupportedOperationException.class, player::getDisplayName); + + assertEquals(uuid, player.getUniqueId()); + assertEquals(name, player.getName()); + assertEquals(firstPlayed, player.getFirstPlayed()); + assertEquals(lastPlayed, player.getLastPlayed()); + assertEquals(isOp, player.isOp()); + assertEquals(playedBefore, player.hasPlayedBefore()); + assertEquals(whitelisted, player.isWhitelisted()); + } + + @Test + public void getSecondProxyPlayer() { + // Make sure that the proxyPlayer generation doesn't work only once. + Player player = serializedOfflinePlayer.getProxyPlayer(); + Assert.assertNotNull(player); + + assertEquals(uuid, player.getUniqueId()); + } +} diff --git a/src/test/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactoryTest.java b/src/test/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactoryTest.java new file mode 100644 index 00000000..8fe0db80 --- /dev/null +++ b/src/test/java/com/comphenix/protocol/injector/server/TemporaryPlayerFactoryTest.java @@ -0,0 +1,42 @@ +package com.comphenix.protocol.injector.server; + +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.*; + +public class TemporaryPlayerFactoryTest { + + private static final TemporaryPlayerFactory temporaryPlayerFactory = new TemporaryPlayerFactory(); + + @Mock + Server server; + @Mock + SocketInjector socketInjector; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testUnavailableSocketInjector() + { + Player player = temporaryPlayerFactory.createTemporaryPlayer(server); + assertThrows(IllegalStateException.class, player::getPlayer); + } + + @Test + public void createTemporaryPlayer() { + + Player player = temporaryPlayerFactory.createTemporaryPlayer(server, socketInjector); + assertEquals(server, player.getServer()); + + // May seem dumb, but this makes sure that the .equals method is still instact. + assertEquals(player, player); + } +} diff --git a/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java b/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java index fc2fc1c4..eef7603d 100644 --- a/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java +++ b/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java @@ -7,6 +7,8 @@ import org.junit.Test; import com.comphenix.protocol.BukkitInitialization; +import java.lang.reflect.Field; + public class MinecraftMethodsTest { @BeforeClass @@ -19,4 +21,19 @@ public class MinecraftMethodsTest { assertNotNull(MinecraftMethods.getSendPacketMethod()); assertNotNull(MinecraftMethods.getNetworkManagerHandleMethod()); } + + private void setNull(final String fieldName) throws NoSuchFieldException, IllegalAccessException { + Field field = MinecraftMethods.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(null, null); + } + + @Test + public void initializePacket() throws NoSuchFieldException, IllegalAccessException { + setNull("packetReadByteBuf"); + setNull("packetWriteByteBuf"); + + assertNotNull(MinecraftMethods.getPacketWriteByteBufMethod()); + assertNotNull(MinecraftMethods.getPacketReadByteBufMethod()); + } }