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.concurrent.Callable; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.ConstructorAccessor; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; 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. * * @author Kristian */ public final class MinecraftMethods { // For player connection private volatile static MethodAccessor sendPacketMethod; private volatile static MethodAccessor disconnectMethod; // For network manager private volatile static MethodAccessor networkManagerHandle; private volatile static MethodAccessor networkManagerPacketRead; // For packet private volatile static MethodAccessor packetReadByteBuf; private volatile static MethodAccessor packetWriteByteBuf; // Decorated PacketSerializer to identify methods private volatile static ConstructorAccessor decoratedDataSerializerAccessor; private MinecraftMethods() { // sealed } /** * Retrieve the send packet method in PlayerConnection/NetServerHandler. * * @return The send packet method. */ public static MethodAccessor getSendPacketMethod() { if (sendPacketMethod == null) { FuzzyReflection serverHandlerClass = FuzzyReflection.fromClass(MinecraftReflection.getPlayerConnectionClass()); try { sendPacketMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder() .parameterCount(1) .returnTypeVoid() .parameterExactType(MinecraftReflection.getPacketClass(), 0) .build())); } catch (IllegalArgumentException e) { sendPacketMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder() .nameRegex("sendPacket.*") .returnTypeVoid() .parameterCount(1) .build())); } } return sendPacketMethod; } /** * Retrieve the disconnect method for a given player connection. * * @param playerConnection - the player connection. * @return The */ public static MethodAccessor getDisconnectMethod(Class> playerConnection) { if (disconnectMethod == null) { FuzzyReflection playerConnectionClass = FuzzyReflection.fromClass(playerConnection); try { disconnectMethod = Accessors.getMethodAccessor(playerConnectionClass.getMethod(FuzzyMethodContract.newBuilder() .returnTypeVoid() .nameRegex("disconnect.*") .parameterCount(1) .parameterExactType(String.class, 0) .build())); } catch (IllegalArgumentException e) { // Just assume it's the first String method Method disconnect = playerConnectionClass.getMethodByParameters("disconnect", String.class); disconnectMethod = Accessors.getMethodAccessor(disconnect); } } return disconnectMethod; } /** * Retrieve the handle/send packet method of network manager. * * @return The handle method. */ public static MethodAccessor getNetworkManagerHandleMethod() { if (networkManagerHandle == null) { Method handleMethod = FuzzyReflection .fromClass(MinecraftReflection.getNetworkManagerClass(), true) .getMethod(FuzzyMethodContract.newBuilder() .banModifier(Modifier.STATIC) .returnTypeVoid() .parameterCount(1) .parameterExactType(MinecraftReflection.getPacketClass(), 0) .build()); networkManagerHandle = Accessors.getMethodAccessor(handleMethod); } return networkManagerHandle; } /** * Retrieve the packetRead(ChannelHandlerContext, Packet) method of NetworkManager. * * @return The packetRead method. */ public static MethodAccessor getNetworkManagerReadPacketMethod() { if (networkManagerPacketRead == null) { Method messageReceived = FuzzyReflection .fromClass(MinecraftReflection.getNetworkManagerClass(), true) .getMethodByParameters("packetRead", ChannelHandlerContext.class, MinecraftReflection.getPacketClass()); networkManagerPacketRead = Accessors.getMethodAccessor(messageReceived); } return networkManagerPacketRead; } /** * Retrieve the Packet.read(PacketDataSerializer) method. * * @return The packet read method. */ public static MethodAccessor getPacketReadByteBufMethod() { initializePacket(); return packetReadByteBuf; } /** * Retrieve the Packet.write(PacketDataSerializer) method. *
* This only exists in version 1.7.2 and above.
*
* @return The packet write method.
*/
public static MethodAccessor getPacketWriteByteBufMethod() {
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) {
// setups a decorated PacketDataSerializer which we can use to identity read/write methods in the packet class
if (decoratedDataSerializerAccessor == null) {
decoratedDataSerializerAccessor = Accessors.getConstructorAccessor(setupProxyConstructor());
}
// constructs a new decorated serializer
Object decoratedSerializer = decoratedDataSerializerAccessor.invoke(Unpooled.EMPTY_BUFFER);
// find all methods which might be the read or write methods
List