diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 26a59d80..066b4eb6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: '19' + java-version: '21' cache: 'gradle' - name: Run gradle build lifecycle diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b0f9adc9..12ca758b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: '19' + java-version: '21' cache: 'gradle' - name: Initialize CodeQL diff --git a/build.gradle b/build.gradle index fae984ae..06a3f19e 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ repositories { } dependencies { - implementation 'net.bytebuddy:byte-buddy:1.14.9' + implementation 'net.bytebuddy:byte-buddy:1.14.14' compileOnly 'org.spigotmc:spigot-api:1.20.5-R0.1-SNAPSHOT' compileOnly 'org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT' compileOnly 'io.netty:netty-all:4.0.23.Final' @@ -69,6 +69,9 @@ shadowJar { test { useJUnitPlatform() + testLogging { + exceptionFormat = 'full' + } } processResources { diff --git a/src/main/java/com/comphenix/protocol/PacketType.java b/src/main/java/com/comphenix/protocol/PacketType.java index 0b9cd1eb..e4b39e50 100644 --- a/src/main/java/com/comphenix/protocol/PacketType.java +++ b/src/main/java/com/comphenix/protocol/PacketType.java @@ -5,8 +5,17 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.*; -import java.util.function.Consumer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiConsumer; + +import org.apache.commons.lang.WordUtils; +import org.bukkit.Bukkit; import com.comphenix.protocol.PacketTypeLookup.ClassLookup; import com.comphenix.protocol.events.ConnectionSide; @@ -17,9 +26,6 @@ import com.comphenix.protocol.utility.MinecraftVersion; import com.google.common.base.Preconditions; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Iterables; - -import org.apache.commons.lang.WordUtils; -import org.bukkit.Bukkit; /** * Represents the type of a packet in a specific protocol. *

@@ -101,7 +107,7 @@ public class PacketType implements Serializable, Cloneable, Comparable onDynamicCreate = x -> {}; + static BiConsumer onDynamicCreate = (type, className) -> {}; + static BiConsumer onIdMismatch = (type, newId) -> {}; /** * Retrieve a packet type from a protocol, sender, ID, and class for 1.8+ @@ -1019,7 +1071,7 @@ public class PacketType implements Serializable, Cloneable, Comparable map, String clazz) { - PacketType ret = map.get(clazz); + private static PacketType find(Map map, Class packetClass) { + String className = packetClass.getName(); + PacketType ret = map.get(className); if (ret != null) { return ret; } @@ -1044,7 +1099,7 @@ public class PacketType implements Serializable, Cloneable, Comparable aliases = check.getClassNames(); if (aliases.size() > 1) { for (String alias : aliases) { - if (alias.equals(clazz)) { + if (alias.equals(className) || alias.equals(packetClass.getSimpleName())) { // We have a match! return check; } @@ -1174,6 +1229,9 @@ public class PacketType implements Serializable, Cloneable, Comparable HANDSHAKE_CLIENT = new IntegerMap<>(); @@ -27,6 +28,8 @@ class PacketTypeLookup { public final IntegerMap STATUS_SERVER = new IntegerMap<>(); public final IntegerMap LOGIN_CLIENT = new IntegerMap<>(); public final IntegerMap LOGIN_SERVER = new IntegerMap<>(); + public final IntegerMap CONFIGURATION_CLIENT = new IntegerMap<>(); + public final IntegerMap CONFIGURATION_SERVER = new IntegerMap<>(); /** * Retrieve the correct integer map for a specific protocol and sender. @@ -44,6 +47,8 @@ class PacketTypeLookup { return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER; case LOGIN: return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER; + case CONFIGURATION: + return sender == Sender.CLIENT ? CONFIGURATION_CLIENT : CONFIGURATION_SERVER; default: throw new IllegalArgumentException("Unable to find protocol " + protocol); } @@ -87,11 +92,6 @@ class PacketTypeLookup { } } - // Packet IDs from 1.6.4 and below - private final IntegerMap legacyLookup = new IntegerMap<>(); - private final IntegerMap serverLookup = new IntegerMap<>(); - private final IntegerMap clientLookup = new IntegerMap<>(); - // Packets for 1.7.2 private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup(); @@ -123,9 +123,11 @@ class PacketTypeLookup { * Retrieve a packet type from a legacy (1.6.4 and below) packet ID. * @param packetId - the legacy packet ID. * @return The corresponding packet type, or NULL if not found. + * @deprecated no longer works and will always return null */ + @Deprecated public PacketType getFromLegacy(int packetId) { - return legacyLookup.get(packetId); + return null; } /** @@ -142,20 +144,11 @@ class PacketTypeLookup { * @param packetId - the legacy packet ID. * @param preference - which packet type to look for first. * @return The corresponding packet type, or NULL if not found. + * @deprecated no longer works and will always return null */ - public PacketType getFromLegacy(int packetId, Sender preference) { - if (preference == Sender.CLIENT) - return getFirst(packetId, clientLookup, serverLookup); - else - return getFirst(packetId, serverLookup, clientLookup); - } - - // Helper method for looking up in two sets - private T getFirst(int packetId, IntegerMap first, IntegerMap second) { - if (first.containsKey(packetId)) - return first.get(packetId); - else - return second.get(packetId); + @Deprecated + public PacketType getFromLegacy(int packetId, Sender preference) { + return null; } /** diff --git a/src/main/java/com/comphenix/protocol/events/PacketContainer.java b/src/main/java/com/comphenix/protocol/events/PacketContainer.java index db7d2af3..aebcb8cd 100644 --- a/src/main/java/com/comphenix/protocol/events/PacketContainer.java +++ b/src/main/java/com/comphenix/protocol/events/PacketContainer.java @@ -34,6 +34,7 @@ import java.util.function.Function; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.injector.StructureCache; +import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.ObjectWriter; import com.comphenix.protocol.reflect.StructureModifier; @@ -56,6 +57,7 @@ import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.wrappers.Converters; +import com.comphenix.protocol.wrappers.WrappedStreamCodec; import com.google.common.collect.Sets; import io.netty.buffer.ByteBuf; import io.netty.util.ReferenceCountUtil; @@ -345,6 +347,11 @@ public class PacketContainer extends AbstractStructure implements Serializable { } Function deserializer = PACKET_DESERIALIZER_METHODS.computeIfAbsent(packetType, type -> { + WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass()); + if (streamCodec != null) { + return streamCodec::decode; + } + if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { // best guess - a constructor which takes a buffer as the only argument ConstructorAccessor bufferConstructor = Accessors.getConstructorAccessorOrNull( @@ -392,7 +399,14 @@ public class PacketContainer extends AbstractStructure implements Serializable { } Object targetBuffer = MinecraftReflection.createPacketDataSerializer(0); - MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer); + + WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass()); + if (streamCodec != null) { + streamCodec.encode(targetBuffer, handle); + } else { + MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer); + } + return targetBuffer; } diff --git a/src/main/java/com/comphenix/protocol/injector/StructureCache.java b/src/main/java/com/comphenix/protocol/injector/StructureCache.java index 8817e01c..1568d356 100644 --- a/src/main/java/com/comphenix/protocol/injector/StructureCache.java +++ b/src/main/java/com/comphenix/protocol/injector/StructureCache.java @@ -20,22 +20,28 @@ package com.comphenix.protocol.injector; import java.security.PublicKey; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.injector.packet.PacketRegistry; +import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.ConstructorAccessor; +import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.utility.ByteBuddyFactory; import com.comphenix.protocol.utility.MinecraftMethods; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.utility.MinecraftRegistryAccess; import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.ZeroBuffer; import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.comphenix.protocol.wrappers.WrappedStreamCodec; import com.google.common.base.Preconditions; + import io.netty.buffer.ByteBuf; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default; @@ -57,22 +63,47 @@ public class StructureCache { private static final Object TRICK_INIT_LOCK = new Object(); private static boolean TRICK_TRIED = false; - private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE; - private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON; + private static Supplier TRICKED_DATA_SERIALIZER_BASE; + private static Supplier TRICKED_DATA_SERIALIZER_JSON; public static Object newPacket(Class packetClass) { - Supplier packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> { + Supplier packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, packetClassKey -> { + WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(packetClassKey); + + // use the new stream codec for versions above 1.20.5 if possible + if (streamCodec != null && tryInitTrickDataSerializer()) { + try { + // first try with the base accessor + Object serializer = TRICKED_DATA_SERIALIZER_BASE.get(); + streamCodec.decode(serializer); // throwaway instance, for testing + + // method is working + return () -> streamCodec.decode(serializer); + } catch (Exception exception) { + try { + // try with the json accessor + Object serializer = TRICKED_DATA_SERIALIZER_JSON.get(); + streamCodec.decode(serializer); // throwaway instance, for testing + + // method is working + return () -> streamCodec.decode(serializer); + } catch (Exception ignored) { + // shrug, fall back to default behaviour + } + } + } + // prefer construction via PacketDataSerializer constructor on 1.17 and above - if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { + if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull( - clazz, + packetClassKey, MinecraftReflection.getPacketDataSerializerClass()); if (serializerAccessor != null) { // check if the method is possible if (tryInitTrickDataSerializer()) { try { // first try with the base accessor - Object serializer = TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer()); + Object serializer = TRICKED_DATA_SERIALIZER_BASE.get(); serializerAccessor.invoke(serializer); // throwaway instance, for testing // method is working @@ -80,7 +111,7 @@ public class StructureCache { } catch (Exception exception) { try { // try with the json accessor - Object serializer = TRICKED_DATA_SERIALIZER_JSON.invoke(new ZeroBuffer()); + Object serializer = TRICKED_DATA_SERIALIZER_JSON.get(); serializerAccessor.invoke(serializer); // throwaway instance, for testing // method is working @@ -95,8 +126,8 @@ public class StructureCache { // try via DefaultInstances as fallback return () -> { - Object packetInstance = DefaultInstances.DEFAULT.create(clazz); - Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + clazz); + Object packetInstance = DefaultInstances.DEFAULT.create(packetClassKey); + Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + packetClassKey + " - " + tryInitTrickDataSerializer() + " - " + streamCodec); return packetInstance; }; }); @@ -156,16 +187,19 @@ public class StructureCache { */ public static Object newNullDataSerializer() { tryInitTrickDataSerializer(); - return TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer()); + return TRICKED_DATA_SERIALIZER_BASE.get(); } static void initTrickDataSerializer() { + Optional> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass(); + // create an empty instance of a nbt tag compound / text compound that we can re-use when needed Object textCompound = WrappedChatComponent.fromText("").getHandle(); Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke(); + // base builder which intercepts a few methods DynamicType.Builder baseBuilder = ByteBuddyFactory.getInstance() - .createSubclass(MinecraftReflection.getPacketDataSerializerClass()) + .createSubclass(registryByteBuf.orElse(MinecraftReflection.getPacketDataSerializerClass())) .name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase") .method(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass()) .and(ElementMatchers.returns(ElementMatchers.isSubTypeOf(MinecraftReflection.getNBTBaseClass())))) @@ -177,9 +211,19 @@ public class StructureCache { Class serializerBase = baseBuilder.make() .load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION) .getLoaded(); - TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class); - // extended builder which intercepts the read string method as well + if (registryByteBuf.isPresent()) { + ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(serializerBase, true).getConstructor(FuzzyMethodContract.newBuilder() + .parameterDerivedOf(ByteBuf.class) + .parameterDerivedOf(MinecraftReflection.getRegistryAccessClass()) + .build())); + TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get()); + } else { + ConstructorAccessor accessor = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class); + TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer()); + } + + //xtended builder which intercepts the read string method as well Class withStringIntercept = baseBuilder .name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson") .method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class))) @@ -187,7 +231,17 @@ public class StructureCache { .make() .load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION) .getLoaded(); - TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class); + + if (registryByteBuf.isPresent()) { + ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(withStringIntercept).getConstructor(FuzzyMethodContract.newBuilder() + .parameterDerivedOf(ByteBuf.class) + .parameterDerivedOf(MinecraftReflection.getRegistryAccessClass()) + .build())); + TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get()); + } else { + ConstructorAccessor accessor = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class); + TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer()); + } } /** diff --git a/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java b/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java index 1b205e93..f422efb1 100644 --- a/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java +++ b/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java @@ -33,6 +33,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import javax.annotation.Nullable; + import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType.Sender; import com.comphenix.protocol.ProtocolLogger; @@ -46,6 +48,7 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; +import com.comphenix.protocol.wrappers.WrappedStreamCodec; /** * Static packet registry in Minecraft. @@ -67,11 +70,12 @@ public class PacketRegistry { * * @author Kristian */ - protected static class Register { + private static class Register { // The main lookup table final Map>> typeToClass = new ConcurrentHashMap<>(); final Map, PacketType> classToType = new ConcurrentHashMap<>(); + final Map, WrappedStreamCodec> classToCodec = new ConcurrentHashMap<>(); final Map, PacketType>> protocolClassToType = new ConcurrentHashMap<>(); volatile Set serverPackets = new HashSet<>(); @@ -297,16 +301,27 @@ public class PacketRegistry { final Map> packetTypeMap = new HashMap<>(); // List of all class containing PacketTypes - String[] packetTypesClassNames = new String[] { "common.CommonPacketTypes", - "configuration.ConfigurationPacketTypes", "cookie.CookiePacketTypes", "game.GamePacketTypes", - "handshake.HandshakePacketTypes", "login.LoginPacketTypes", "ping.PingPacketTypes", - "status.StatusPacketTypes", }; + String[] packetTypesClassNames = new String[] { + "common.CommonPacketTypes", + "configuration.ConfigurationPacketTypes", + "cookie.CookiePacketTypes", + "game.GamePacketTypes", + "handshake.HandshakePacketTypes", + "login.LoginPacketTypes", + "ping.PingPacketTypes", + "status.StatusPacketTypes" + }; Class packetTypeClass = MinecraftReflection.getMinecraftClass("network.protocol.PacketType"); for (String packetTypesClassName : packetTypesClassNames) { Class packetTypesClass = MinecraftReflection - .getMinecraftClass("network.protocol." + packetTypesClassName); + .getOptionalNMS("network.protocol." + packetTypesClassName) + .orElse(null); + if (packetTypesClass == null) { + ProtocolLogger.debug("Can't find PacketType class: {0}, will skip it", packetTypesClassName); + continue; + } // check every field for "static final PacketType" for (Field field : packetTypesClass.getDeclaredFields()) { @@ -336,16 +351,24 @@ public class PacketRegistry { final Map, Integer>> serverMaps = new LinkedHashMap<>(); final Map, Integer>> clientMaps = new LinkedHashMap<>(); + // global registry instance + final Register result = new Register(); + // List of all class containing ProtocolInfos - String[] protocolClassNames = new String[] { "configuration.ConfigurationProtocols", "game.GameProtocols", - "handshake.HandshakeProtocols", "login.LoginProtocols", "status.StatusProtocols" }; + String[] protocolClassNames = new String[] { + "configuration.ConfigurationProtocols", + "game.GameProtocols", + "handshake.HandshakeProtocols", + "login.LoginProtocols", + "status.StatusProtocols" + }; Class protocolInfoClass = MinecraftReflection.getProtocolInfoClass(); - Class protocolInfoUnboundClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo$a"); - Class streamCodecClass = MinecraftReflection.getMinecraftClass("network.codec.StreamCodec"); - Class idDispatchCodecClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec"); - Class protocolDirectionClass = MinecraftReflection - .getMinecraftClass("network.protocol.EnumProtocolDirection"); + Class protocolInfoUnboundClass = MinecraftReflection.getProtocolInfoUnboundClass(); + Class streamCodecClass = MinecraftReflection.getStreamCodecClass(); + Class idCodecClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec"); + Class idCodecEntryClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec$Entry", "network.codec.IdDispatchCodec$b"); + Class protocolDirectionClass = MinecraftReflection.getPacketFlowClass(); Function emptyFunction = input -> null; @@ -363,11 +386,30 @@ public class PacketRegistry { MethodAccessor bindAccessor = Accessors.getMethodAccessor(FuzzyReflection.fromClass(protocolInfoUnboundClass) .getMethodByReturnTypeAndParameters("bind", protocolInfoClass, new Class[] { Function.class })); - FieldAccessor toIdAccessor = Accessors.getFieldAccessor(FuzzyReflection.fromClass(idDispatchCodecClass, true) + FuzzyReflection idCodecReflection = FuzzyReflection.fromClass(idCodecClass, true); + + FieldAccessor byIdAccessor = Accessors.getFieldAccessor(idCodecReflection + .getField(FuzzyFieldContract.newBuilder().typeDerivedOf(List.class).build())); + + FieldAccessor toIdAccessor = Accessors.getFieldAccessor(idCodecReflection .getField(FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).build())); + FuzzyReflection idCodecEntryReflection = FuzzyReflection.fromClass(idCodecEntryClass, true); + + MethodAccessor idCodecEntryTypeAccessor = Accessors.getMethodAccessor(idCodecEntryReflection + .getMethodByReturnTypeAndParameters("type", Object.class, new Class[0])); + + MethodAccessor idCodecEntrySerializerAccessor = Accessors.getMethodAccessor(idCodecEntryReflection + .getMethodByReturnTypeAndParameters("serializer", streamCodecClass, new Class[0])); + for (String protocolClassName : protocolClassNames) { - Class protocolClass = MinecraftReflection.getMinecraftClass("network.protocol." + protocolClassName); + Class protocolClass = MinecraftReflection + .getOptionalNMS("network.protocol." + protocolClassName) + .orElse(null); + if (protocolClass == null) { + ProtocolLogger.debug("Can't find protocol class: {0}, will skip it", protocolClassName); + continue; + } for (Field field : protocolClass.getDeclaredFields()) { try { @@ -391,21 +433,35 @@ public class PacketRegistry { // get codec and check if codec is instance of IdDispatchCodec // since that is the only support codec as of now Object codec = codecAccessor.invoke(protocolInfo); - if (!idDispatchCodecClass.isInstance(codec)) { + if (!idCodecClass.isInstance(codec)) { continue; } // retrieve packetTypeMap and convert it to packetIdMap Map, Integer> packetMap = new HashMap<>(); + List serializerList = (List) byIdAccessor.get(codec); Map packetTypeIdMap = (Map) toIdAccessor.get(codec); for (Map.Entry entry : packetTypeIdMap.entrySet()) { - Class packet = packetTypeMap.get(entry.getKey()); - if (packet == null) { + Class packetClass = packetTypeMap.get(entry.getKey()); + if (packetClass == null) { throw new RuntimeException("packetType missing packet " + entry.getKey()); } - packetMap.put(packet, entry.getValue()); + packetMap.put(packetClass, entry.getValue()); + } + + // retrieve packet codecs for packet construction and write methods + for (Object entry : serializerList) { + Object packetType = idCodecEntryTypeAccessor.invoke(entry); + + Class packetClass = packetTypeMap.get(packetType); + if (packetClass == null) { + throw new RuntimeException("packetType missing packet " + packetType); + } + + Object serializer = idCodecEntrySerializerAccessor.invoke(entry); + result.classToCodec.put(packetClass, new WrappedStreamCodec(serializer)); } // get EnumProtocol and Direction of protocol info @@ -424,8 +480,6 @@ public class PacketRegistry { } } - Register result = new Register(); - for (Object protocol : protocols) { Enum enumProtocol = (Enum) protocol; PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); @@ -506,6 +560,18 @@ public class PacketRegistry { } } + /** + * Returns the wrapped stream codec to de-/serialize the given packet class + * + * @param packetClass - the packet class + * @return wrapped stream codec if existing, otherwise null + */ + @Nullable + public static WrappedStreamCodec getStreamCodec(Class packetClass) { + initialize(); + return REGISTER.classToCodec.get(packetClass); + } + /** * Determine if the given packet type is supported on the current server. * diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java b/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java index ff6abd6f..b484447a 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftMethods.java @@ -5,7 +5,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; +import java.util.Optional; import java.util.concurrent.Callable; +import java.util.function.Function; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.PacketContainer; @@ -15,9 +17,9 @@ 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.ByteBuf; 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; @@ -47,9 +49,29 @@ public final class MinecraftMethods { // Decorated PacketSerializer to identify methods private volatile static ConstructorAccessor decoratedDataSerializerAccessor; + private volatile static Function friendlyBufBufConstructor; + private MinecraftMethods() { // sealed } + + public static Function getFriendlyBufBufConstructor() { + if (friendlyBufBufConstructor == null) { + Optional> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass(); + + if (registryByteBuf.isPresent()) { + ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(registryByteBuf.get()).getConstructor(FuzzyMethodContract.newBuilder() + .parameterDerivedOf(ByteBuf.class) + .parameterDerivedOf(MinecraftReflection.getRegistryAccessClass()) + .build())); + friendlyBufBufConstructor = (byteBuf) -> accessor.invoke(byteBuf, MinecraftRegistryAccess.get()); + } else { + ConstructorAccessor accessor = Accessors.getConstructorAccessor(MinecraftReflection.getPacketDataSerializerClass(), ByteBuf.class); + friendlyBufBufConstructor = (byteBuf) -> accessor.invoke(byteBuf); + } + } + return friendlyBufBufConstructor; + } /** * Retrieve the send packet method in PlayerConnection/NetServerHandler. @@ -145,6 +167,7 @@ public final class MinecraftMethods { * Retrieve the Packet.read(PacketDataSerializer) method. * * @return The packet read method. + * @deprecated no longer works since 1.20.5 */ public static MethodAccessor getPacketReadByteBufMethod() { initializePacket(); @@ -157,6 +180,7 @@ public final class MinecraftMethods { * This only exists in version 1.7.2 and above. * * @return The packet write method. + * @deprecated no longer works since 1.20.5 */ public static MethodAccessor getPacketWriteByteBufMethod() { initializePacket(); @@ -195,6 +219,11 @@ public final class MinecraftMethods { * Initialize the two read() and write() methods. */ private static void initializePacket() { + // write and read methods are no longer part of the packet interface since 1.20.5 + if (MinecraftVersion.v1_20_5.atOrAbove()) { + throw new IllegalStateException("can't access packet read/write method after 1.20.5"); + } + // 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 diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index da64a8d6..b18282a7 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -30,6 +30,12 @@ import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLogger; import com.comphenix.protocol.injector.BukkitUnwrapper; @@ -43,12 +49,9 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.wrappers.EnumWrappers; + +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; /** * Methods and constants specifically used in conjuction with reflecting Minecraft object. @@ -160,7 +163,7 @@ public final class MinecraftReflection { Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE); if (packageMatcher.matches()) { packageVersion = packageMatcher.group(1); - } else { + } else if (!MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { // ignore version prefix since it's no longer needed MinecraftVersion version = new MinecraftVersion(craftServer); // Just assume R1 - it's probably fine (warn anyway) @@ -1500,9 +1503,7 @@ public final class MinecraftReflection { */ public static Object getPacketDataSerializer(Object buffer) { try { - // TODO: move this to MinecraftMethods, or at least, cache the constructor accessor - Class packetSerializer = getPacketDataSerializerClass(); - return packetSerializer.getConstructor(getByteBufClass()).newInstance(buffer); + return MinecraftMethods.getFriendlyBufBufConstructor().apply((ByteBuf) buffer); } catch (Exception e) { throw new RuntimeException("Cannot construct packet serializer.", e); } @@ -1721,12 +1722,32 @@ public final class MinecraftReflection { public static Class getCraftServer() { return getCraftBukkitClass("CraftServer"); } - + public static Class getHolderLookupProviderClass() { return getMinecraftClass("core.HolderLookup$a" /* Spigot Mappings */, "core.HolderLookup$Provider" /* Mojang Mappings */); } + public static Class getRegistryAccessClass() { + return getMinecraftClass("core.IRegistryCustom" /* Spigot Mappings */, "core.RegistryAccess" /* Mojang Mappings */); + } + public static Class getProtocolInfoClass() { return getMinecraftClass("network.ProtocolInfo"); } + + public static Class getProtocolInfoUnboundClass() { + return getMinecraftClass("network.ProtocolInfo$a" /* Spigot Mappings */, "network.ProtocolInfo$Unbound" /* Mojang Mappings */); + } + + public static Class getPacketFlowClass() { + return getMinecraftClass("network.protocol.EnumProtocolDirection" /* Spigot Mappings */, "network.protocol.PacketFlow" /* Mojang Mappings */); + } + + public static Class getStreamCodecClass() { + return getMinecraftClass("network.codec.StreamCodec"); + } + + public static Optional> getRegistryFriendlyByteBufClass() { + return getOptionalNMS("network.RegistryFriendlyByteBuf"); + } } diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java b/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java index 5d1a0130..04c2079b 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java @@ -34,7 +34,7 @@ public class MinecraftRegistryAccess { FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), false) .getMethod(FuzzyMethodContract.newBuilder() .banModifier(Modifier.STATIC) - .returnDerivedOf(MinecraftReflection.getHolderLookupProviderClass()) + .returnDerivedOf(MinecraftReflection.getRegistryAccessClass()) .build())); } } diff --git a/src/main/java/com/comphenix/protocol/utility/Util.java b/src/main/java/com/comphenix/protocol/utility/Util.java index 3f496d47..a73bdc63 100644 --- a/src/main/java/com/comphenix/protocol/utility/Util.java +++ b/src/main/java/com/comphenix/protocol/utility/Util.java @@ -23,7 +23,6 @@ public final class Util { private static final boolean SPIGOT = classExists("org.spigotmc.SpigotConfig"); private static final boolean FOLIA = classExists("io.papermc.paper.threadedregions.RegionizedServer"); - private static Class cachedBundleClass; public static boolean classExists(String className) { try { diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java index caf5114e..d5e6a8e2 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedChatComponent.java @@ -9,6 +9,7 @@ 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 com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftRegistryAccess; import com.comphenix.protocol.utility.MinecraftVersion; @@ -35,24 +36,30 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null; static { - FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true); + FuzzyReflection reflection = FuzzyReflection.fromClass(SERIALIZER, true); // Retrieve the correct methods if (MinecraftVersion.v1_20_5.atOrAbove()) { - SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */ - String.class, new Class[] { COMPONENT, MinecraftReflection.getHolderLookupProviderClass() })); + SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder() + .returnTypeExact(String.class) + .parameterDerivedOf(COMPONENT) + .parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass()) + .build())); } else { - SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */ + SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethodByReturnTypeAndParameters("serialize", /* a */ String.class, new Class[] { COMPONENT })); } - GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null); + GSON = Accessors.getFieldAccessor(reflection.getFieldByType("gson", GSON_CLASS)).get(null); if (MinecraftVersion.v1_20_5.atOrAbove()) { - DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false) - .getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class, MinecraftReflection.getHolderLookupProviderClass() })); + DESERIALIZE = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder() + .returnDerivedOf(COMPONENT) + .parameterExactType(String.class) + .parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass()) + .build())); } else if (MinecraftVersion.v1_20_4.atOrAbove()) { - DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SERIALIZER, false) + DESERIALIZE = Accessors.getMethodAccessor(reflection .getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class })); } else { try { diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedLevelChunkData.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedLevelChunkData.java index 756bf2c0..bf81039c 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/WrappedLevelChunkData.java +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedLevelChunkData.java @@ -1,23 +1,24 @@ package com.comphenix.protocol.wrappers; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + import com.comphenix.protocol.injector.StructureCache; 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.FieldAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; +import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; -import com.comphenix.protocol.utility.ZeroBuffer; import com.comphenix.protocol.wrappers.nbt.NbtCompound; import com.comphenix.protocol.wrappers.nbt.NbtFactory; import com.google.common.collect.Lists; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.List; /** * Wrapper classes for ClientboundLevelChunkWithLightPacket @@ -45,8 +46,11 @@ public final class WrappedLevelChunkData { static { FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true); - LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE, - MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class); + LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(reflection.getConstructor(FuzzyMethodContract.newBuilder() + .parameterDerivedOf(MinecraftReflection.getPacketDataSerializerClass()) + .parameterExactType(int.class) + .parameterExactType(int.class) + .build())); BLOCK_ENTITIES_DATA_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder() .typeExact(List.class) .build())); @@ -160,8 +164,11 @@ public final class WrappedLevelChunkData { static { FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true); - LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE, - MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class); + LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(reflection.getConstructor(FuzzyMethodContract.newBuilder() + .parameterDerivedOf(MinecraftReflection.getPacketDataSerializerClass()) + .parameterExactType(int.class) + .parameterExactType(int.class) + .build())); BIT_SET_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, BitSet.class, true); BYTE_ARRAY_LIST_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, List.class, true); @@ -333,7 +340,7 @@ public final class WrappedLevelChunkData { */ public static LightData fromValues(BitSet skyYMask, BitSet blockYMask, BitSet emptySkyYMask, BitSet emptyBlockYMask, List skyUpdates, List blockUpdates) { - LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(MinecraftReflection.getPacketDataSerializer(new ZeroBuffer()), 0, 0)); + LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0)); data.setSkyYMask(skyYMask); data.setBlockYMask(blockYMask); diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java index 4b8b15e1..41efd1c7 100644 --- a/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedParticle.java @@ -217,8 +217,8 @@ public class WrappedParticle { if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) { StructureModifier modifier = new StructureModifier<>(handle.getClass()).withTarget(handle); - org.joml.Vector3f toRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(0); - org.joml.Vector3f fromRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(1); + org.joml.Vector3f toRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(1); + org.joml.Vector3f fromRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(0); size = (float) modifier.withType(float.class).read(0); fromR = (int) (fromRGB.x() * 255); diff --git a/src/main/java/com/comphenix/protocol/wrappers/WrappedStreamCodec.java b/src/main/java/com/comphenix/protocol/wrappers/WrappedStreamCodec.java new file mode 100644 index 00000000..5b498c8e --- /dev/null +++ b/src/main/java/com/comphenix/protocol/wrappers/WrappedStreamCodec.java @@ -0,0 +1,41 @@ +package com.comphenix.protocol.wrappers; + +import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.accessors.Accessors; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; +import com.comphenix.protocol.utility.MinecraftReflection; + +/** + * Wrapper for StreamCodec class which is primarily used to de-/serialize + * packets since 1.20.5 + */ +public class WrappedStreamCodec extends AbstractWrapper { + + // use the de-/encoder interfaces to get the right method to avoid future errors + private static final Class DECODER_TYPE = MinecraftReflection.getMinecraftClass("network.codec.StreamDecoder"); + private static final Class ENCODER_TYPE = MinecraftReflection.getMinecraftClass("network.codec.StreamEncoder"); + + private static final MethodAccessor DECODE_ACCESSOR; + private static final MethodAccessor ENCODE_ACCESSOR; + + static { + DECODE_ACCESSOR = Accessors.getMethodAccessor(FuzzyReflection.fromClass(DECODER_TYPE) + .getMethodByReturnTypeAndParameters("decode", Object.class, new Class[] { Object.class })); + + ENCODE_ACCESSOR = Accessors.getMethodAccessor(FuzzyReflection.fromClass(ENCODER_TYPE) + .getMethodByReturnTypeAndParameters("encode", Void.TYPE, new Class[] { Object.class, Object.class })); + } + + public WrappedStreamCodec(Object handle) { + super(MinecraftReflection.getStreamCodecClass()); + setHandle(handle); + } + + public Object decode(Object buffer) { + return DECODE_ACCESSOR.invoke(handle, buffer); + } + + public void encode(Object buffer, Object value) { + ENCODE_ACCESSOR.invoke(handle, buffer, value); + } +} diff --git a/src/test/java/com/comphenix/protocol/BukkitInitialization.java b/src/test/java/com/comphenix/protocol/BukkitInitialization.java index 646d81c5..3acfc21a 100644 --- a/src/test/java/com/comphenix/protocol/BukkitInitialization.java +++ b/src/test/java/com/comphenix/protocol/BukkitInitialization.java @@ -134,12 +134,16 @@ public class BukkitInitialization { String serverVersion = CraftServer.class.getPackage().getImplementationVersion(); // Mock the server object - Server mockedServer = mock(Server.class); + CraftServer mockedServer = mock(CraftServer.class); + DedicatedServer mockedGameServer = mock(DedicatedServer.class); + + when(mockedGameServer.bc()/*registryAccess*/).thenReturn(registryCustom); when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft")); when(mockedServer.getName()).thenReturn("Mock Server"); when(mockedServer.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")"); when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion()); + when(mockedServer.getServer()).thenReturn(mockedGameServer); when(mockedServer.isPrimaryThread()).thenReturn(true); when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance()); diff --git a/src/test/java/com/comphenix/protocol/PacketTypeTest.java b/src/test/java/com/comphenix/protocol/PacketTypeTest.java index 5daa7156..66a67664 100644 --- a/src/test/java/com/comphenix/protocol/PacketTypeTest.java +++ b/src/test/java/com/comphenix/protocol/PacketTypeTest.java @@ -14,32 +14,35 @@ */ package com.comphenix.protocol; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import com.comphenix.protocol.PacketType.Protocol; import com.comphenix.protocol.PacketType.Sender; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.injector.packet.PacketRegistry; import com.comphenix.protocol.utility.MinecraftReflection; -import com.comphenix.protocol.utility.MinecraftReflectionTestUtil; import com.comphenix.protocol.wrappers.WrappedChatComponent; -import net.minecraft.network.EnumProtocol; -import net.minecraft.network.protocol.EnumProtocolDirection; + import net.minecraft.network.protocol.login.PacketLoginInStart; -import org.apache.commons.lang.WordUtils; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.util.*; -import java.util.Map.Entry; - -import static org.junit.jupiter.api.Assertions.*; /** * @author dmulloy2 */ public class PacketTypeTest { + private static final Pattern PACKET_PATTERN = Pattern.compile("(?Serverbound|Clientbound)(?\\w+)Packet"); + @BeforeAll public static void beforeClass() { BukkitInitialization.initializeAll(); @@ -52,60 +55,41 @@ public class PacketTypeTest { @AfterAll public static void afterClass() { - PacketType.onDynamicCreate = __ -> { + PacketType.onDynamicCreate = (x, y) -> { }; } - @SuppressWarnings("unchecked") - // @Test - // public static void main(String[] args) throws Exception { - public void generateNewPackets() throws Exception { - MinecraftReflectionTestUtil.init(); + public static void main(String[] args) throws Exception { +// public void generateNewPackets() throws Exception { + BukkitInitialization.initializeAll(); - Set> allTypes = new HashSet<>(); - List> newTypes = new ArrayList<>(); + PacketType.onDynamicCreate = (type, className) -> { + String packetTypeClassName = className; - EnumProtocol[] protocols = EnumProtocol.values(); - for (EnumProtocol protocol : protocols) { - System.out.println(WordUtils.capitalize(protocol.name().toLowerCase())); + Matcher matcher = PACKET_PATTERN.matcher(className); + if (matcher.find()) { + if (!matcher.group("sender").equals(type.getSender().getMojangName())) { + throw new RuntimeException(String.format("wrong packet flow, exepected: %s, got: %s", type.getSender().getMojangName(), matcher.group("sender"))); + } + packetTypeClassName = matcher.group("name"); + } - Field field = EnumProtocol.class.getDeclaredField("k"); - field.setAccessible(true); + System.out.printf("%s, %s = new PacketType(PROTOCOL, SENDER, %s, \"%s\") %s\n", type.getProtocol(), type.getSender(), formatHex(type.getCurrentId()), packetTypeClassName, className); + }; - Map map = (Map) field.get(protocol); - for (Entry entry : map.entrySet()) { - Field mapField = entry.getValue().getClass().getDeclaredField("b"); - mapField.setAccessible(true); - - Map, Integer> reverseMap = (Map, Integer>) mapField.get(entry.getValue()); - - Map> treeMap = new TreeMap<>(); - for (Entry, Integer> entry1 : reverseMap.entrySet()) { - treeMap.put(entry1.getValue(), entry1.getKey()); - } - - System.out.println(" " + entry.getKey()); - for (Entry> entry1 : treeMap.entrySet()) { - System.out.println(generateNewType(entry1.getKey(), entry1.getValue())); - allTypes.add(entry1.getValue()); - - try { - PacketType.fromClass(entry1.getValue()); - } catch (Exception ex) { - newTypes.add(entry1.getValue()); - } - } - } - } - - System.out.println("New types: " + newTypes); + PacketType.onIdMismatch = (type, newId) -> { + System.out.printf("%s, %s, %s %s MISMTACH %s\n", type.getProtocol(), type.getSender(), type.name(), formatHex(type.getCurrentId()), formatHex(newId)); + }; + + // initialize packet registry + PacketRegistry.getClientPacketTypes(); for (PacketType type : PacketType.values()) { if (type.isDeprecated()) { continue; } - if (!allTypes.contains(type.getPacketClass())) { + if (type.getPacketClass() == null) { System.out.println(type + " was removed"); } } @@ -286,60 +270,17 @@ public class PacketTypeTest { } @Test - @SuppressWarnings("unchecked") - public void ensureTypesAreCorrect() throws Exception { - PacketType.onDynamicCreate = className -> { - throw new RuntimeException("Dynamically generated packet " + className); - }; - + public void ensureRegistryInitializes() throws Exception { try { - boolean fail = false; + PacketType.onDynamicCreate = (type, className) -> { + throw new RuntimeException("Dynamically generated packet " + className); + }; - EnumProtocol[] protocols = EnumProtocol.values(); - for (EnumProtocol protocol : protocols) { - Field field = EnumProtocol.class.getDeclaredField("h"); - field.setAccessible(true); - - Map map = (Map) field.get(protocol); - for (Entry entry : map.entrySet()) { - Field holderField = entry.getValue().getClass().getDeclaredField("c"); - holderField.setAccessible(true); - - Object holder = holderField.get(entry.getValue()); - Field mapField = holder.getClass().getDeclaredField("b"); - mapField.setAccessible(true); - - Map, Integer> reverseMap = (Map, Integer>) mapField.get(holder); - - Map> treeMap = new TreeMap<>(); - for (Entry, Integer> entry1 : reverseMap.entrySet()) { - treeMap.put(entry1.getValue(), entry1.getKey()); - } - - for (Entry> entry1 : treeMap.entrySet()) { - try { - PacketType type = PacketType.fromClass(entry1.getValue()); - if (type.getCurrentId() != entry1.getKey()) { - throw new IllegalStateException( - "Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got " - + type.getCurrentId()); - } - } catch (Throwable ex) { - if (ex.getMessage().contains("BundleDelimiterPacket")) { - continue; - } - - ex.printStackTrace(); - fail = true; - } - } - } - } - - assertFalse(fail, "Packet type(s) were incorrect!"); - } finally { - PacketType.onDynamicCreate = __ -> { }; - } + // try to initialize packet registry + PacketRegistry.getClientPacketTypes(); + } finally { + PacketType.onDynamicCreate = (x, y) -> { }; + } } @Test diff --git a/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java b/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java index 4d1966e3..6b94098d 100644 --- a/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java +++ b/src/test/java/com/comphenix/protocol/utility/MinecraftMethodsTest.java @@ -31,8 +31,12 @@ public class MinecraftMethodsTest { this.setNull("packetReadByteBuf"); this.setNull("packetWriteByteBuf"); - assertNotNull(MinecraftMethods.getPacketWriteByteBufMethod()); - // TODO it's now a constructor - // assertNotNull(MinecraftMethods.getPacketReadByteBufMethod()); + // the write and read method got replaced by the StreamCodec class and each + // packet now has it's own unique codec instance + if (!MinecraftVersion.v1_20_5.atOrAbove()) { + assertNotNull(MinecraftMethods.getPacketWriteByteBufMethod()); + // TODO it's now a constructor + // assertNotNull(MinecraftMethods.getPacketReadByteBufMethod()); + } } } diff --git a/src/test/java/com/comphenix/protocol/utility/MinecraftVersionTest.java b/src/test/java/com/comphenix/protocol/utility/MinecraftVersionTest.java index 0f68b48e..59d4d20e 100644 --- a/src/test/java/com/comphenix/protocol/utility/MinecraftVersionTest.java +++ b/src/test/java/com/comphenix/protocol/utility/MinecraftVersionTest.java @@ -48,7 +48,7 @@ class MinecraftVersionTest { @Test void testCurrent() { - assertEquals(MinecraftVersion.v1_20_4, MinecraftVersion.getCurrentVersion()); + assertEquals(MinecraftVersion.v1_20_5, MinecraftVersion.getCurrentVersion()); } @Test