();
+
+ 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 extends Player> 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 extends Player> 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 extends Player> 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 extends Player>) 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());
+ }
}