diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/packets/NetworkInterceptCodeGen.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/packets/NetworkInterceptCodeGen.java new file mode 100644 index 0000000000..6565f5936f --- /dev/null +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/packets/NetworkInterceptCodeGen.java @@ -0,0 +1,123 @@ +package com.denizenscript.denizen.utilities.packets; + +import com.denizenscript.denizen.nms.NMSHandler; +import com.denizenscript.denizen.utilities.debugging.Debug; +import com.denizenscript.denizencore.utilities.codegen.CodeGenUtil; +import org.objectweb.asm.*; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class NetworkInterceptCodeGen { + + public static MethodHandle generateInstance = null; + + public static void generateClass(Class denClass, Class abstractClass, Class nmsClass) { + try { + Constructor origConstructor = denClass.getConstructors()[0]; + // ====== Build class ====== + String className = "com/denizenscript/denizen/network_intercept_codegen/GeneratedInterceptor"; + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, Type.getInternalName(denClass), new String[0]); + cw.visitSource("GENERATED_INTERCEPTOR", null); + // ====== Build constructor ====== + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", Type.getConstructorDescriptor(origConstructor), null, null); + mv.visitCode(); + Label startLabel = new Label(); + mv.visitLabel(startLabel); + mv.visitLineNumber(0, startLabel); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(denClass), "", Type.getConstructorDescriptor(origConstructor), false); + mv.visitInsn(Opcodes.RETURN); + mv.visitLocalVariable("this", "L" + className + ";", null, startLabel, startLabel, 0); + mv.visitMaxs(0, 0); + mv.visitEnd(); + // ====== Find public methods ====== + for (Method method : nmsClass.getDeclaredMethods()) { + int modifier = method.getModifiers(); + if (Modifier.isPublic(modifier) && !Modifier.isFinal(modifier) && !Modifier.isStatic(modifier)) { + boolean hasMethod = false; + try { + abstractClass.getDeclaredMethod(method.getName(), method.getParameterTypes()); + hasMethod = true; + } + catch (NoSuchMethodException ignore) {} + if (!hasMethod) { + if (NMSHandler.debugPackets) { + Debug.log("Must override " + method + " --- " + method.getName() + ", returns " + method.getReturnType() + " is " + modifier + + ", Public=" + Modifier.isPublic(modifier) + ", final=" + Modifier.isFinal(modifier)); + } + mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null); + mv.visitCode(); + startLabel = new Label(); + mv.visitLabel(startLabel); + mv.visitLineNumber(0, startLabel); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(abstractClass), "oldListener", Type.getDescriptor(nmsClass)); + int id = 1; + for (Class type : method.getParameterTypes()) { + if (NMSHandler.debugPackets) { + Debug.log("Var " + id + " is type " + type.getName()); + } + int index = id++; + if (type == int.class || type == boolean.class || type == short.class || type == char.class) { mv.visitVarInsn(Opcodes.ILOAD, index); } // Everything sub-integer-width is secretly integers + else if (type == long.class) { mv.visitVarInsn(Opcodes.LLOAD, index); id++; } + else if (type == float.class) { mv.visitVarInsn(Opcodes.FLOAD, index); } + else if (type == double.class) { mv.visitVarInsn(Opcodes.DLOAD, index); id++; } // Doubles and longs have two vars secretly for some reason + else { mv.visitVarInsn(Opcodes.ALOAD, index); } + } + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(nmsClass), method.getName(), Type.getMethodDescriptor(method), false); + int returnCode = Opcodes.ARETURN; + Class returnType = method.getReturnType(); + if (returnType == int.class || returnType == boolean.class || returnType == short.class || returnType == char.class) { returnCode = Opcodes.IRETURN; } + else if (returnType == long.class) { returnCode = Opcodes.LRETURN; } + else if (returnType == float.class) { returnCode = Opcodes.FRETURN; } + else if (returnType == double.class) { returnCode = Opcodes.DRETURN; } + else if (returnType == void.class) { returnCode = Opcodes.RETURN; } + mv.visitInsn(returnCode); + mv.visitLocalVariable("this", "L" + className + ";", null, startLabel, startLabel, 0); + id = 1; + for (Class type : method.getParameterTypes()) { + mv.visitLocalVariable("var" + id, Type.getDescriptor(type), null, startLabel, startLabel, id); + if (type == double.class || type == long.class) { + id++; + } + id++; + } + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + } + } + // ====== Compile and return ====== + cw.visitEnd(); + byte[] compiled = cw.toByteArray(); + Class generatedClass = CodeGenUtil.loader.define(className.replace('/', '.'), compiled); + Constructor ctor = generatedClass.getDeclaredConstructor(origConstructor.getParameterTypes()); + ctor.setAccessible(true); + generateInstance = MethodHandles.lookup().unreflectConstructor(ctor); + } + catch (Throwable ex) { + Debug.echoError(ex); + } + } + + public static Object generateAppropriateInterceptor(Object netMan, Object player, Class denClass, Class abstractClass, Class nmsClass) { + if (generateInstance == null) { + generateClass(denClass, abstractClass, nmsClass); + } + try { + return generateInstance.invoke(netMan, player); + } + catch (Throwable ex) { + Debug.echoError(ex); + return null; + } + } +} diff --git a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/ReflectionMappingsInfo.java b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/ReflectionMappingsInfo.java index 7bcf14d186..e3253276fa 100644 --- a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/ReflectionMappingsInfo.java +++ b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/ReflectionMappingsInfo.java @@ -74,6 +74,8 @@ public class ReflectionMappingsInfo { public static String ServerGamePacketListenerImpl_aboveGroundTickCount = "G"; public static String ServerGamePacketListenerImpl_aboveGroundVehicleTickCount = "I"; public static String ServerGamePacketListenerImpl_connection = "b"; + public static String ServerGamePacketListenerImpl_awaitingPositionFromClient = "C"; + public static String ServerGamePacketListenerImpl_awaitingTeleport = "D"; // net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket public static String ClientboundPlayerAbilitiesPacket_walkingSpeed = "j"; diff --git a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/AbstractListenerPlayInImpl.java b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/AbstractListenerPlayInImpl.java index 51e60e4bc6..a359881674 100644 --- a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/AbstractListenerPlayInImpl.java +++ b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/AbstractListenerPlayInImpl.java @@ -2,6 +2,9 @@ import com.denizenscript.denizen.events.player.PlayerSendPacketScriptEvent; import com.denizenscript.denizen.nms.NMSHandler; +import com.denizenscript.denizen.nms.v1_19.ReflectionMappingsInfo; +import com.denizenscript.denizencore.utilities.ReflectionHelper; +import com.denizenscript.denizencore.utilities.debugging.Debug; import net.minecraft.network.Connection; import net.minecraft.network.PacketSendListener; import net.minecraft.network.chat.Component; @@ -12,10 +15,12 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.phys.Vec3; import org.bukkit.Location; import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; import org.bukkit.event.player.PlayerTeleportEvent; +import java.lang.reflect.Field; import java.util.Set; public class AbstractListenerPlayInImpl extends ServerGamePacketListenerImpl { @@ -119,10 +124,37 @@ public void send(Packet packet, PacketSendListener listener) { oldListener.send(packet, listener); } + public static Field AWAITING_POS_FIELD = ReflectionHelper.getFields(ServerGamePacketListenerImpl.class).get(ReflectionMappingsInfo.ServerGamePacketListenerImpl_awaitingPositionFromClient, Vec3.class); + public static Field AWAITING_TELEPORT_FIELD = ReflectionHelper.getFields(ServerGamePacketListenerImpl.class).get(ReflectionMappingsInfo.ServerGamePacketListenerImpl_awaitingTeleport, int.class); + + public void debugPacketOutput(Packet packet) { + try { + if (packet instanceof ServerboundMovePlayerPacket) { + ServerboundMovePlayerPacket movePacket = (ServerboundMovePlayerPacket) packet; + DenizenNetworkManagerImpl.doPacketOutput("Packet ServerboundMovePlayerPacket sent from " + player.getScoreboardName() + " with XYZ=" + + movePacket.x + ", " + movePacket.y + ", " + movePacket.z + ", yRot=" + movePacket.yRot + ", xRot=" + movePacket.xRot + + ", onGround=" + movePacket.isOnGround() + ", hasPos=" + movePacket.hasPos + ", hasRot=" + movePacket.hasRot); + } + else if (packet instanceof ServerboundAcceptTeleportationPacket) { + Vec3 awaitPos = (Vec3) AWAITING_POS_FIELD.get(oldListener); + int awaitTeleportId = AWAITING_TELEPORT_FIELD.getInt(oldListener); + ServerboundAcceptTeleportationPacket acceptPacket = (ServerboundAcceptTeleportationPacket) packet; + DenizenNetworkManagerImpl.doPacketOutput("Packet ServerboundAcceptTeleportationPacket sent from " + player.getScoreboardName() + + " with ID=" + acceptPacket.getId() + ", awaitingTeleport=" + awaitTeleportId + ", awaitPos=" + awaitPos); + } + else { + DenizenNetworkManagerImpl.doPacketOutput("Packet: " + packet.getClass().getCanonicalName() + " sent from " + player.getScoreboardName()); + } + } + catch (Throwable ex) { + Debug.echoError(ex); + } + } + public boolean handlePacketIn(Packet packet) { denizenNetworkManager.packetsReceived++; if (NMSHandler.debugPackets) { - DenizenNetworkManagerImpl.doPacketOutput("Packet: " + packet.getClass().getCanonicalName() + " sent from " + player.getScoreboardName()); + debugPacketOutput(packet); } if (PlayerSendPacketScriptEvent.enabled) { if (PlayerSendPacketScriptEvent.fireFor(player.getBukkitEntity(), packet)) { diff --git a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/DenizenNetworkManagerImpl.java b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/DenizenNetworkManagerImpl.java index bbfc16f347..3620d546df 100644 --- a/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/DenizenNetworkManagerImpl.java +++ b/v1_19/src/main/java/com/denizenscript/denizen/nms/v1_19/impl/network/handlers/DenizenNetworkManagerImpl.java @@ -23,6 +23,7 @@ import com.denizenscript.denizen.utilities.entity.HideEntitiesHelper; import com.denizenscript.denizen.utilities.packets.DenizenPacketHandler; import com.denizenscript.denizen.utilities.packets.HideParticles; +import com.denizenscript.denizen.utilities.packets.NetworkInterceptCodeGen; import com.denizenscript.denizencore.objects.core.ElementTag; import com.denizenscript.denizencore.utilities.CoreConfiguration; import com.denizenscript.denizencore.utilities.CoreUtilities; @@ -40,7 +41,6 @@ import net.minecraft.core.NonNullList; import net.minecraft.core.SectionPos; import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.data.BuiltinRegistries; import net.minecraft.network.*; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.Packet; @@ -100,7 +100,7 @@ public DenizenNetworkManagerImpl(ServerPlayer entityPlayer, Connection oldManage super(getProtocolDirection(oldManager)); this.oldManager = oldManager; this.channel = oldManager.channel; - this.packetListener = new DenizenPacketListenerImpl(this, entityPlayer); + this.packetListener = (DenizenPacketListenerImpl) NetworkInterceptCodeGen.generateAppropriateInterceptor(this, entityPlayer, DenizenPacketListenerImpl.class, AbstractListenerPlayInImpl.class, ServerGamePacketListenerImpl.class); oldManager.setListener(packetListener); this.player = this.packetListener.player; }