From 8d48e74f490de1de1276a7e037c6f143ab615e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Eisenbl=C3=A4tter?= Date: Wed, 24 Apr 2024 18:30:19 +0200 Subject: [PATCH] feat: add support for 1.20.5 --- .../netty/channel/ChannelProtocolUtil.java | 48 +- .../netty/channel/NettyChannelInjector.java | 14 +- .../injector/packet/PacketRegistry.java | 938 +++++++++--------- .../protocol/utility/MinecraftReflection.java | 7 +- .../utility/MinecraftRegistryAccess.java | 11 + 5 files changed, 523 insertions(+), 495 deletions(-) diff --git a/src/main/java/com/comphenix/protocol/injector/netty/channel/ChannelProtocolUtil.java b/src/main/java/com/comphenix/protocol/injector/netty/channel/ChannelProtocolUtil.java index 08aa24d3..69c0145f 100644 --- a/src/main/java/com/comphenix/protocol/injector/netty/channel/ChannelProtocolUtil.java +++ b/src/main/java/com/comphenix/protocol/injector/netty/channel/ChannelProtocolUtil.java @@ -33,7 +33,8 @@ final class ChannelProtocolUtil { BiFunction baseResolver = null; if (attributeKeys.isEmpty()) { - baseResolver = new Post1_20_4WrappedResolver(); + // since 1.20.5 the protocol is stored as final field in de-/encoder + baseResolver = new Post1_20_5WrappedResolver(); } else if (attributeKeys.size() == 1) { // if there is only one attribute key we can assume it's the correct one (1.8 - 1.20.1) Object protocolKey = Accessors.getFieldAccessor(attributeKeys.get(0)).get(null); @@ -137,7 +138,10 @@ final class ChannelProtocolUtil { } } - private static final class Post1_20_4WrappedResolver implements BiFunction { + /** + * Since 1.20.5 the protocol is stored as final field in de-/encoder + */ + private static final class Post1_20_5WrappedResolver implements BiFunction { // lazy initialized when needed private Function serverProtocolAccessor; @@ -151,23 +155,11 @@ final class ChannelProtocolUtil { return null; } - Function protocolAccessor = this.getProtocolAccessor(sender, codecHandler.getClass()); - + Function protocolAccessor = this.getProtocolAccessor(codecHandler.getClass(), sender); return protocolAccessor.apply(codecHandler); } - private String getKeyForSender(PacketType.Sender sender) { - switch (sender) { - case SERVER: - return "encoder"; - case CLIENT: - return "decoder"; - default: - throw new IllegalArgumentException("Illegal packet sender " + sender.name()); - } - } - - private Function getProtocolAccessor(PacketType.Sender sender, Class codecHandler) { + private Function getProtocolAccessor(Class codecHandler, PacketType.Sender sender) { switch (sender) { case SERVER: if (this.serverProtocolAccessor == null) { @@ -184,17 +176,27 @@ final class ChannelProtocolUtil { } } - private Function getProtocolAccessor(Class codecHandler) { - Class protocolInfoClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo"); + private String getKeyForSender(PacketType.Sender sender) { + switch (sender) { + case SERVER: + return "encoder"; + case CLIENT: + return "decoder"; + default: + throw new IllegalArgumentException("Illegal packet sender " + sender.name()); + } + } - MethodAccessor protocolAccessor = Accessors.getMethodAccessor( - FuzzyReflection.fromClass(protocolInfoClass) - .getMethod(FuzzyMethodContract.newBuilder() - .returnTypeExact(MinecraftReflection.getEnumProtocolClass()) - .build())); + private Function getProtocolAccessor(Class codecHandler) { + Class protocolInfoClass = MinecraftReflection.getProtocolInfoClass(); + + MethodAccessor protocolAccessor = Accessors.getMethodAccessor(FuzzyReflection + .fromClass(protocolInfoClass) + .getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0])); FieldAccessor protocolInfoAccessor = Accessors.getFieldAccessor(codecHandler, protocolInfoClass, true); + // get ProtocolInfo from handler and get EnumProtocol of ProtocolInfo return (handler) -> { Object protocolInfo = protocolInfoAccessor.get(handler); return protocolAccessor.invoke(protocolInfo); diff --git a/src/main/java/com/comphenix/protocol/injector/netty/channel/NettyChannelInjector.java b/src/main/java/com/comphenix/protocol/injector/netty/channel/NettyChannelInjector.java index 7dab3461..b3e08c0a 100644 --- a/src/main/java/com/comphenix/protocol/injector/netty/channel/NettyChannelInjector.java +++ b/src/main/java/com/comphenix/protocol/injector/netty/channel/NettyChannelInjector.java @@ -31,6 +31,7 @@ import com.comphenix.protocol.wrappers.WrappedGameProfile; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoop; import io.netty.util.AttributeKey; import org.bukkit.Server; @@ -202,9 +203,18 @@ public class NettyChannelInjector implements Injector { return false; } + ChannelPipeline pipeline = this.wrappedChannel.pipeline(); + + // since 1.20.5 the encoder is renamed to outbound_config only in the handshake phase + String encoderName = pipeline.get("outbound_config") != null + ? "outbound_config" : "encoder"; + // inject our handlers - this.wrappedChannel.pipeline().addAfter("encoder", WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER); - this.wrappedChannel.pipeline().addAfter( + pipeline.addAfter( + encoderName, + WIRE_PACKET_ENCODER_NAME, + WIRE_PACKET_ENCODER); + pipeline.addAfter( "decoder", INTERCEPTOR_NAME, new InboundPacketInterceptor(this, this.channelListener)); 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 b1e7b4ba..1b205e93 100644 --- a/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java +++ b/src/main/java/com/comphenix/protocol/injector/packet/PacketRegistry.java @@ -18,7 +18,6 @@ package com.comphenix.protocol.injector.packet; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -39,6 +38,9 @@ import com.comphenix.protocol.PacketType.Sender; import com.comphenix.protocol.ProtocolLogger; 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.FieldAccessor; +import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; @@ -47,11 +49,12 @@ import com.comphenix.protocol.utility.MinecraftVersion; /** * Static packet registry in Minecraft. + * * @author Kristian */ public class PacketRegistry { - // Whether or not the registry has been initialized - private static volatile boolean INITIALIZED = false; + // Whether or not the registry has been initialized + private static volatile boolean INITIALIZED = false; static void reset() { synchronized (registryLock) { @@ -59,321 +62,265 @@ public class PacketRegistry { } } - /** - * Represents a register we are currently building. - * @author Kristian - */ - protected static class Register { - // The main lookup table - final Map>> typeToClass = new ConcurrentHashMap<>(); + /** + * Represents a register we are currently building. + * + * @author Kristian + */ + protected static class Register { + // The main lookup table + final Map>> typeToClass = new ConcurrentHashMap<>(); - final Map, PacketType> classToType = new ConcurrentHashMap<>(); - final Map, PacketType>> protocolClassToType = new ConcurrentHashMap<>(); + final Map, PacketType> classToType = new ConcurrentHashMap<>(); + final Map, PacketType>> protocolClassToType = new ConcurrentHashMap<>(); - volatile Set serverPackets = new HashSet<>(); - volatile Set clientPackets = new HashSet<>(); - final List containers = new ArrayList<>(); + volatile Set serverPackets = new HashSet<>(); + volatile Set clientPackets = new HashSet<>(); + final List containers = new ArrayList<>(); - public Register() {} + public Register() { + } - public void registerPacket(PacketType type, Class clazz, Sender sender) { - typeToClass.put(type, Optional.of(clazz)); + public void registerPacket(PacketType type, Class clazz, Sender sender) { + typeToClass.put(type, Optional.of(clazz)); - classToType.put(clazz, type); - protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(clazz, type); + classToType.put(clazz, type); + protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(clazz, type); - if (sender == Sender.CLIENT) { - clientPackets.add(type); - } else { - serverPackets.add(type); - } - } + if (sender == Sender.CLIENT) { + clientPackets.add(type); + } else { + serverPackets.add(type); + } + } - public void addContainer(MapContainer container) { - containers.add(container); - } + public void addContainer(MapContainer container) { + containers.add(container); + } - /** - * Determine if the current register is outdated. - * @return TRUE if it is, FALSE otherwise. - */ - public boolean isOutdated() { - for (MapContainer container : containers) { - if (container.hasChanged()) { - return true; - } - } - return false; - } - } + /** + * Determine if the current register is outdated. + * + * @return TRUE if it is, FALSE otherwise. + */ + public boolean isOutdated() { + for (MapContainer container : containers) { + if (container.hasChanged()) { + return true; + } + } + return false; + } + } - protected static final Class ENUM_PROTOCOL = MinecraftReflection.getEnumProtocolClass(); + protected static final Class ENUM_PROTOCOL = MinecraftReflection.getEnumProtocolClass(); - // Current register - protected static volatile Register REGISTER; + // Current register + protected static volatile Register REGISTER; - /** - * Ensure that our local register is up-to-date with Minecraft. - *

- * This operation may block the calling thread. - */ - public static synchronized void synchronize() { - // Check if the packet registry has changed - if (REGISTER.isOutdated()) { - initialize(); - } - } + /** + * Ensure that our local register is up-to-date with Minecraft. + *

+ * This operation may block the calling thread. + */ + public static synchronized void synchronize() { + // Check if the packet registry has changed + if (REGISTER.isOutdated()) { + initialize(); + } + } - protected static synchronized Register createOldRegister() { - Object[] protocols = ENUM_PROTOCOL.getEnumConstants(); + protected static synchronized Register createOldRegister() { + Object[] protocols = ENUM_PROTOCOL.getEnumConstants(); - // ID to Packet class maps - final Map>> serverMaps = new LinkedHashMap<>(); - final Map>> clientMaps = new LinkedHashMap<>(); + // ID to Packet class maps + final Map>> serverMaps = new LinkedHashMap<>(); + final Map>> clientMaps = new LinkedHashMap<>(); - Register result = new Register(); - StructureModifier modifier = null; + Register result = new Register(); + StructureModifier modifier = null; - // Iterate through the protocols - for (Object protocol : protocols) { - if (modifier == null) { - modifier = new StructureModifier<>(protocol.getClass().getSuperclass()); - } + // Iterate through the protocols + for (Object protocol : protocols) { + if (modifier == null) { + modifier = new StructureModifier<>(protocol.getClass().getSuperclass()); + } - StructureModifier>>> maps = modifier.withTarget(protocol).withType(Map.class); - for (Map.Entry>> entry : maps.read(0).entrySet()) { - String direction = entry.getKey().toString(); - if (direction.contains("CLIENTBOUND")) { // Sent by Server - serverMaps.put(protocol, entry.getValue()); - } else if (direction.contains("SERVERBOUND")) { // Sent by Client - clientMaps.put(protocol, entry.getValue()); - } - } - } + StructureModifier>>> maps = modifier.withTarget(protocol) + .withType(Map.class); + for (Map.Entry>> entry : maps.read(0).entrySet()) { + String direction = entry.getKey().toString(); + if (direction.contains("CLIENTBOUND")) { // Sent by Server + serverMaps.put(protocol, entry.getValue()); + } else if (direction.contains("SERVERBOUND")) { // Sent by Client + clientMaps.put(protocol, entry.getValue()); + } + } + } - // Maps we have to occasionally check have changed - for (Object map : serverMaps.values()) { - result.addContainer(new MapContainer(map)); - } + // Maps we have to occasionally check have changed + for (Object map : serverMaps.values()) { + result.addContainer(new MapContainer(map)); + } - for (Object map : clientMaps.values()) { - result.addContainer(new MapContainer(map)); - } + for (Object map : clientMaps.values()) { + result.addContainer(new MapContainer(map)); + } - for (Object protocol : protocols) { - Enum enumProtocol = (Enum) protocol; - PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); + for (Object protocol : protocols) { + Enum enumProtocol = (Enum) protocol; + PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); - // Associate known types - if (serverMaps.containsKey(protocol)) - associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER); - if (clientMaps.containsKey(protocol)) - associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT); - } + // Associate known types + if (serverMaps.containsKey(protocol)) + associatePackets(result, serverMaps.get(protocol), equivalent, Sender.SERVER); + if (clientMaps.containsKey(protocol)) + associatePackets(result, clientMaps.get(protocol), equivalent, Sender.CLIENT); + } - return result; - } + return result; + } - @SuppressWarnings("unchecked") - private static synchronized Register createNewRegister() { - Object[] protocols = ENUM_PROTOCOL.getEnumConstants(); + @SuppressWarnings("unchecked") + private static synchronized Register createRegisterV1_15_0() { + Object[] protocols = ENUM_PROTOCOL.getEnumConstants(); - // ID to Packet class maps - final Map, Integer>> serverMaps = new LinkedHashMap<>(); - final Map, Integer>> clientMaps = new LinkedHashMap<>(); + // ID to Packet class maps + final Map, Integer>> serverMaps = new LinkedHashMap<>(); + final Map, Integer>> clientMaps = new LinkedHashMap<>(); - Register result = new Register(); + Register result = new Register(); - Field mainMapField = null; - Field packetMapField = null; - Field holderClassField = null; // only 1.20.2+ + Field mainMapField = null; + Field packetMapField = null; + Field holderClassField = null; // only 1.20.2+ - // Iterate through the protocols - for (Object protocol : protocols) { - if (mainMapField == null) { - FuzzyReflection fuzzy = FuzzyReflection.fromClass(protocol.getClass(), true); - mainMapField = fuzzy.getField(FuzzyFieldContract.newBuilder() - .banModifier(Modifier.STATIC) - .requireModifier(Modifier.FINAL) - .typeDerivedOf(Map.class) - .build()); - mainMapField.setAccessible(true); - } + // Iterate through the protocols + for (Object protocol : protocols) { + if (mainMapField == null) { + FuzzyReflection fuzzy = FuzzyReflection.fromClass(protocol.getClass(), true); + mainMapField = fuzzy.getField(FuzzyFieldContract.newBuilder().banModifier(Modifier.STATIC) + .requireModifier(Modifier.FINAL).typeDerivedOf(Map.class).build()); + mainMapField.setAccessible(true); + } - Map directionMap; + Map directionMap; - try { - directionMap = (Map) mainMapField.get(protocol); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException("Failed to access packet map", ex); - } + try { + directionMap = (Map) mainMapField.get(protocol); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException("Failed to access packet map", ex); + } - for (Map.Entry entry : directionMap.entrySet()) { - Object holder = entry.getValue(); - if (packetMapField == null) { - Class packetHolderClass = holder.getClass(); - if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { - FuzzyReflection holderFuzzy = FuzzyReflection.fromClass(packetHolderClass, true); - holderClassField = holderFuzzy.getField(FuzzyFieldContract.newBuilder() - .banModifier(Modifier.STATIC) - .requireModifier(Modifier.FINAL) - .typeMatches(FuzzyClassContract.newBuilder() - .method(FuzzyMethodContract.newBuilder() - .returnTypeExact(MinecraftReflection.getPacketClass()) - .parameterCount(2) - .parameterExactType(int.class, 0) - .parameterExactType(MinecraftReflection.getPacketDataSerializerClass(), 1) - .build()) - .build()) - .build()); - holderClassField.setAccessible(true); - packetHolderClass = holderClassField.getType(); - } + for (Map.Entry entry : directionMap.entrySet()) { + Object holder = entry.getValue(); + if (packetMapField == null) { + Class packetHolderClass = holder.getClass(); + if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { + FuzzyReflection holderFuzzy = FuzzyReflection.fromClass(packetHolderClass, true); + holderClassField = holderFuzzy + .getField(FuzzyFieldContract.newBuilder().banModifier(Modifier.STATIC) + .requireModifier(Modifier.FINAL) + .typeMatches(FuzzyClassContract.newBuilder().method(FuzzyMethodContract + .newBuilder().returnTypeExact(MinecraftReflection.getPacketClass()) + .parameterCount(2).parameterExactType(int.class, 0).parameterExactType( + MinecraftReflection.getPacketDataSerializerClass(), 1) + .build()).build()) + .build()); + holderClassField.setAccessible(true); + packetHolderClass = holderClassField.getType(); + } - FuzzyReflection fuzzy = FuzzyReflection.fromClass(packetHolderClass, true); - packetMapField = fuzzy.getField(FuzzyFieldContract.newBuilder() - .banModifier(Modifier.STATIC) - .requireModifier(Modifier.FINAL) - .typeDerivedOf(Map.class) - .build()); - packetMapField.setAccessible(true); - } + FuzzyReflection fuzzy = FuzzyReflection.fromClass(packetHolderClass, true); + packetMapField = fuzzy.getField(FuzzyFieldContract.newBuilder().banModifier(Modifier.STATIC) + .requireModifier(Modifier.FINAL).typeDerivedOf(Map.class).build()); + packetMapField.setAccessible(true); + } - Object holderInstance = holder; - if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { - try { - holderInstance = holderClassField.get(holder); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException("Failed to access packet map", ex); - } - } + Object holderInstance = holder; + if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) { + try { + holderInstance = holderClassField.get(holder); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException("Failed to access packet map", ex); + } + } - Map, Integer> packetMap; - try { - packetMap = (Map, Integer>) packetMapField.get(holderInstance); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException("Failed to access packet map", ex); - } + Map, Integer> packetMap; + try { + packetMap = (Map, Integer>) packetMapField.get(holderInstance); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException("Failed to access packet map", ex); + } - String direction = entry.getKey().toString(); - if (direction.contains("CLIENTBOUND")) { // Sent by Server - serverMaps.put(protocol, packetMap); - } else if (direction.contains("SERVERBOUND")) { // Sent by Client - clientMaps.put(protocol, packetMap); - } - } - } + String direction = entry.getKey().toString(); + if (direction.contains("CLIENTBOUND")) { // Sent by Server + serverMaps.put(protocol, packetMap); + } else if (direction.contains("SERVERBOUND")) { // Sent by Client + clientMaps.put(protocol, packetMap); + } + } + } - // Maps we have to occasionally check have changed - // TODO: Find equivalent in Object2IntMap + // Maps we have to occasionally check have changed + // TODO: Find equivalent in Object2IntMap - /* for (Object map : serverMaps.values()) { - result.containers.add(new MapContainer(map)); - } + /* + * for (Object map : serverMaps.values()) { result.containers.add(new + * MapContainer(map)); } + * + * for (Object map : clientMaps.values()) { result.containers.add(new + * MapContainer(map)); } + */ - for (Object map : clientMaps.values()) { - result.containers.add(new MapContainer(map)); - } */ + for (Object protocol : protocols) { + Enum enumProtocol = (Enum) protocol; + PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); - for (Object protocol : protocols) { - Enum enumProtocol = (Enum) protocol; - PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); + // Associate known types + if (serverMaps.containsKey(protocol)) { + associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER); + } + if (clientMaps.containsKey(protocol)) { + associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT); + } + } - // Associate known types - if (serverMaps.containsKey(protocol)) { - associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER); - } - if (clientMaps.containsKey(protocol)) { - associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT); - } - } + return result; + } - return result; - } + @SuppressWarnings("unchecked") + private static synchronized Register createRegisterV1_20_5() { + Object[] protocols = ENUM_PROTOCOL.getEnumConstants(); - @SuppressWarnings("unchecked") - private static synchronized Register createNewNewRegister() { - Object[] protocols = ENUM_PROTOCOL.getEnumConstants(); + // PacketType to class map + final Map> packetTypeMap = new HashMap<>(); - // ID to Packet class maps - final Map> packetTypeMap = new HashMap<>(); - final Map, Integer>> serverMaps = new LinkedHashMap<>(); - final Map, Integer>> clientMaps = new LinkedHashMap<>(); + // 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[] protocolClassNames = new String[] { - "configuration.ConfigurationProtocols", - "game.GameProtocols", - "handshake.HandshakeProtocols", - "login.LoginProtocols", - "status.StatusProtocols" - }; - - String[] packetTypesClassNames = new String[] { - "common.CommonPacketTypes", - "configuration.ConfigurationPacketTypes", - "cookie.CookiePacketTypes", - "game.GamePacketTypes", - "handshake.HandshakePacketTypes", - "login.LoginPacketTypes", - "ping.PingPacketTypes", - "status.StatusPacketTypes", - }; - - Class protocolInfoClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo"); - Class protocolInfoUnboundClass = MinecraftReflection.getMinecraftClass("network.ProtocolInfo$a"); - Class streamCodecClass = MinecraftReflection.getMinecraftClass("network.codec.StreamCodec"); - Class idDispatchCodecClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec"); Class packetTypeClass = MinecraftReflection.getMinecraftClass("network.protocol.PacketType"); - Class protocolDirectionClass = MinecraftReflection.getMinecraftClass("network.protocol.EnumProtocolDirection"); - - Function emptyFunction = input -> null; - - Method protocolMethod = FuzzyReflection.fromClass(protocolInfoClass) - .getMethod(FuzzyMethodContract.newBuilder() - .returnTypeExact(MinecraftReflection.getEnumProtocolClass()) - .build()); - protocolMethod.setAccessible(true); - - Method directionMethod = FuzzyReflection.fromClass(protocolInfoClass) - .getMethod(FuzzyMethodContract.newBuilder() - .returnTypeExact(protocolDirectionClass) - .build()); - directionMethod.setAccessible(true); - - Method codecMethod = FuzzyReflection.fromClass(protocolInfoClass) - .getMethod(FuzzyMethodContract.newBuilder() - .returnTypeExact(streamCodecClass) - .build()); - codecMethod.setAccessible(true); - - Method bindMethod = FuzzyReflection.fromClass(protocolInfoUnboundClass) - .getMethod(FuzzyMethodContract.newBuilder() - .returnTypeExact(protocolInfoClass) - .build()); - bindMethod.setAccessible(true); - - Field toIdField = FuzzyReflection.fromClass(idDispatchCodecClass, true) - .getField(FuzzyFieldContract.newBuilder() - .typeDerivedOf(Map.class) - .build()); - toIdField.setAccessible(true); for (String packetTypesClassName : packetTypesClassNames) { - Class packetTypesClass = MinecraftReflection.getMinecraftClass("network.protocol." + packetTypesClassName); + Class packetTypesClass = MinecraftReflection + .getMinecraftClass("network.protocol." + packetTypesClassName); + // check every field for "static final PacketType" for (Field field : packetTypesClass.getDeclaredFields()) { try { if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) { - System.out.println("failed modifier " + field); continue; } Object packetType = field.get(null); if (!packetTypeClass.isInstance(packetType)) { - System.out.println("failed packetTypeClass " + field); continue; } + // retrieve the generic type T of the PacketType field Type packet = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; if (packet instanceof Class) { packetTypeMap.put(packetType, (Class) packet); @@ -385,53 +332,91 @@ public class PacketRegistry { } } + // ID to Packet class maps + final Map, Integer>> serverMaps = new LinkedHashMap<>(); + final Map, Integer>> clientMaps = new LinkedHashMap<>(); + + // List of all class containing ProtocolInfos + 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"); + + Function emptyFunction = input -> null; + + FuzzyReflection protocolInfoReflection = FuzzyReflection.fromClass(protocolInfoClass); + + MethodAccessor protocolAccessor = Accessors.getMethodAccessor(protocolInfoReflection + .getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0])); + + MethodAccessor directionAccessor = Accessors.getMethodAccessor(protocolInfoReflection + .getMethodByReturnTypeAndParameters("flow", protocolDirectionClass, new Class[0])); + + MethodAccessor codecAccessor = Accessors.getMethodAccessor( + protocolInfoReflection.getMethodByReturnTypeAndParameters("codec", streamCodecClass, new Class[0])); + + MethodAccessor bindAccessor = Accessors.getMethodAccessor(FuzzyReflection.fromClass(protocolInfoUnboundClass) + .getMethodByReturnTypeAndParameters("bind", protocolInfoClass, new Class[] { Function.class })); + + FieldAccessor toIdAccessor = Accessors.getFieldAccessor(FuzzyReflection.fromClass(idDispatchCodecClass, true) + .getField(FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).build())); + for (String protocolClassName : protocolClassNames) { Class protocolClass = MinecraftReflection.getMinecraftClass("network.protocol." + protocolClassName); for (Field field : protocolClass.getDeclaredFields()) { try { + // ignore none static and final fields if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) { - System.out.println("failed modifier " + field); continue; } Object protocolInfo = field.get(null); + + // bind unbound ProtocolInfo to empty function to get real ProtocolInfo if (protocolInfoUnboundClass.isInstance(protocolInfo)) { - protocolInfo = bindMethod.invoke(protocolInfo, new Object[] { emptyFunction }); + protocolInfo = bindAccessor.invoke(protocolInfo, new Object[] { emptyFunction }); } + // ignore any field that isn't a ProtocolInfo if (!protocolInfoClass.isInstance(protocolInfo)) { - System.out.println("failed protocolInfoClass " + field); continue; } - Object codec = codecMethod.invoke(protocolInfo); + // 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)) { - System.out.println("failed idDispatchCodecClass " + field); continue; } + // retrieve packetTypeMap and convert it to packetIdMap Map, Integer> packetMap = new HashMap<>(); - Map packetTypeIdMap = (Map) toIdField.get(codec); + Map packetTypeIdMap = (Map) toIdAccessor.get(codec); for (Map.Entry entry : packetTypeIdMap.entrySet()) { Class packet = packetTypeMap.get(entry.getKey()); if (packet == null) { throw new RuntimeException("packetType missing packet " + entry.getKey()); } - + packetMap.put(packet, entry.getValue()); } - - Object protocol = protocolMethod.invoke(protocolInfo); - String direction = directionMethod.invoke(protocolInfo).toString(); + // get EnumProtocol and Direction of protocol info + Object protocol = protocolAccessor.invoke(protocolInfo); + String direction = directionAccessor.invoke(protocolInfo).toString(); - if (direction.contains("CLIENTBOUND")) { // Sent by Server - serverMaps.put(protocol, packetMap); - } else if (direction.contains("SERVERBOUND")) { // Sent by Client - clientMaps.put(protocol, packetMap); - } + if (direction.contains("CLIENTBOUND")) { // Sent by Server + serverMaps.put(protocol, packetMap); + } else if (direction.contains("SERVERBOUND")) { // Sent by Client + clientMaps.put(protocol, packetMap); + } } catch (Exception e) { e.printStackTrace(); continue; @@ -439,227 +424,242 @@ public class PacketRegistry { } } - Register result = new Register(); + Register result = new Register(); - for (Object protocol : protocols) { - Enum enumProtocol = (Enum) protocol; - PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); + for (Object protocol : protocols) { + Enum enumProtocol = (Enum) protocol; + PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol); - // Associate known types - if (serverMaps.containsKey(protocol)) { - associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER); - } - if (clientMaps.containsKey(protocol)) { - associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT); - } - } + // Associate known types + if (serverMaps.containsKey(protocol)) { + associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER); + } + if (clientMaps.containsKey(protocol)) { + associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT); + } + } - return result; - } + return result; + } - /** - * Reverses a key->value map to value->key - * Non-deterministic behavior when multiple keys are mapped to the same value - */ - private static Map reverse(Map map) { - Map newMap = new HashMap<>(map.size()); - for (Map.Entry entry : map.entrySet()) { - newMap.put(entry.getValue(), entry.getKey()); - } - return newMap; - } + /** + * Reverses a key->value map to value->key Non-deterministic behavior when + * multiple keys are mapped to the same value + */ + private static Map reverse(Map map) { + Map newMap = new HashMap<>(map.size()); + for (Map.Entry entry : map.entrySet()) { + newMap.put(entry.getValue(), entry.getKey()); + } + return newMap; + } - protected static void associatePackets(Register register, Map> lookup, PacketType.Protocol protocol, Sender sender) { - for (Map.Entry> entry : lookup.entrySet()) { - int packetId = entry.getKey(); - Class packetClass = entry.getValue(); + protected static void associatePackets(Register register, Map> lookup, + PacketType.Protocol protocol, Sender sender) { + for (Map.Entry> entry : lookup.entrySet()) { + int packetId = entry.getKey(); + Class packetClass = entry.getValue(); - PacketType type = PacketType.fromCurrent(protocol, sender, packetId, packetClass); + PacketType type = PacketType.fromCurrent(protocol, sender, packetId, packetClass); - try { - register.registerPacket(type, packetClass, sender); - } catch (Exception ex) { - ProtocolLogger.debug("Encountered an exception associating packet " + type, ex); - } - } - } + try { + register.registerPacket(type, packetClass, sender); + } catch (Exception ex) { + ProtocolLogger.debug("Encountered an exception associating packet " + type, ex); + } + } + } - private static void associate(PacketType type, Class clazz) { - if (clazz != null) { - REGISTER.typeToClass.put(type, Optional.of(clazz)); - REGISTER.classToType.put(clazz, type); - } else { - REGISTER.typeToClass.put(type, Optional.empty()); - } - } + private static void associate(PacketType type, Class clazz) { + if (clazz != null) { + REGISTER.typeToClass.put(type, Optional.of(clazz)); + REGISTER.classToType.put(clazz, type); + } else { + REGISTER.typeToClass.put(type, Optional.empty()); + } + } - private static final Object registryLock = new Object(); + private static final Object registryLock = new Object(); - /** - * Initializes the packet registry. - */ - static void initialize() { - if (INITIALIZED) { - return; - } + /** + * Initializes the packet registry. + */ + static void initialize() { + if (INITIALIZED) { + return; + } - synchronized (registryLock) { - if (INITIALIZED) { - return; - } + synchronized (registryLock) { + if (INITIALIZED) { + return; + } - if (MinecraftVersion.v1_20_5.atOrAbove()) { - REGISTER = createNewNewRegister(); - } else if (MinecraftVersion.BEE_UPDATE.atOrAbove()) { - REGISTER = createNewRegister(); - } else { - REGISTER = createOldRegister(); - } + if (MinecraftVersion.v1_20_5.atOrAbove()) { + REGISTER = createRegisterV1_20_5(); + } else if (MinecraftVersion.BEE_UPDATE.atOrAbove()) { + REGISTER = createRegisterV1_15_0(); + } else { + REGISTER = createOldRegister(); + } - INITIALIZED = true; - } - } + INITIALIZED = true; + } + } - /** - * Determine if the given packet type is supported on the current server. - * @param type - the type to check. - * @return TRUE if it is, FALSE otherwise. - */ - public static boolean isSupported(PacketType type) { - initialize(); - return tryGetPacketClass(type).isPresent(); - } + /** + * Determine if the given packet type is supported on the current server. + * + * @param type - the type to check. + * @return TRUE if it is, FALSE otherwise. + */ + public static boolean isSupported(PacketType type) { + initialize(); + return tryGetPacketClass(type).isPresent(); + } - /** - * Retrieve every known and supported server packet type. - * @return Every server packet type. - */ - public static Set getServerPacketTypes() { - initialize(); - synchronize(); + /** + * Retrieve every known and supported server packet type. + * + * @return Every server packet type. + */ + public static Set getServerPacketTypes() { + initialize(); + synchronize(); - return Collections.unmodifiableSet(REGISTER.serverPackets); - } - - /** - * Retrieve every known and supported server packet type. - * @return Every server packet type. - */ - public static Set getClientPacketTypes() { - initialize(); - synchronize(); + return Collections.unmodifiableSet(REGISTER.serverPackets); + } - return Collections.unmodifiableSet(REGISTER.clientPackets); - } + /** + * Retrieve every known and supported server packet type. + * + * @return Every server packet type. + */ + public static Set getClientPacketTypes() { + initialize(); + synchronize(); - private static Class searchForPacket(List classNames) { - for (String name : classNames) { - try { - Class clazz = MinecraftReflection.getMinecraftClass(name); - if (MinecraftReflection.getPacketClass().isAssignableFrom(clazz) - && !Modifier.isAbstract(clazz.getModifiers())) { - return clazz; - } - } catch (Exception ignored) {} - } + return Collections.unmodifiableSet(REGISTER.clientPackets); + } - return null; - } + private static Class searchForPacket(List classNames) { + for (String name : classNames) { + try { + Class clazz = MinecraftReflection.getMinecraftClass(name); + if (MinecraftReflection.getPacketClass().isAssignableFrom(clazz) + && !Modifier.isAbstract(clazz.getModifiers())) { + return clazz; + } + } catch (Exception ignored) { + } + } - /** - * Retrieves the correct packet class from a given type. - * - * @param type - the packet type. - * @param forceVanilla - whether or not to look for vanilla classes, not injected classes. - * @return The associated class. - * @deprecated forceVanilla no longer has any effect - */ - @Deprecated - public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) { - return getPacketClassFromType(type); - } + return null; + } - public static Optional> tryGetPacketClass(PacketType type) { - initialize(); + /** + * Retrieves the correct packet class from a given type. + * + * @param type - the packet type. + * @param forceVanilla - whether or not to look for vanilla classes, not + * injected classes. + * @return The associated class. + * @deprecated forceVanilla no longer has any effect + */ + @Deprecated + public static Class getPacketClassFromType(PacketType type, boolean forceVanilla) { + return getPacketClassFromType(type); + } - // Try the lookup first (may be null, so check contains) - Optional> res = REGISTER.typeToClass.get(type); - if (res != null) { - if(res.isPresent() && MinecraftReflection.isBundleDelimiter(res.get())) { - return MinecraftReflection.getPackedBundlePacketClass(); - } - return res; - } + public static Optional> tryGetPacketClass(PacketType type) { + initialize(); - // Then try looking up the class names - Class clazz = searchForPacket(type.getClassNames()); - if (clazz != null) { - // we'd like for it to be associated correctly from the get-go; this is OK on older versions though - ProtocolLogger.warnAbove(type.getCurrentVersion(), "Updating associated class for {0} to {1}", type.name(), clazz); - } + // Try the lookup first (may be null, so check contains) + Optional> res = REGISTER.typeToClass.get(type); + if (res != null) { + if (res.isPresent() && MinecraftReflection.isBundleDelimiter(res.get())) { + return MinecraftReflection.getPackedBundlePacketClass(); + } + return res; + } - // cache it for next time - associate(type, clazz); - if(clazz != null && MinecraftReflection.isBundleDelimiter(clazz)) { - clazz = MinecraftReflection.getPackedBundlePacketClass().orElseThrow(() -> new IllegalStateException("Packet bundle class not found.")); - } - return Optional.ofNullable(clazz); - } + // Then try looking up the class names + Class clazz = searchForPacket(type.getClassNames()); + if (clazz != null) { + // we'd like for it to be associated correctly from the get-go; this is OK on + // older versions though + ProtocolLogger.warnAbove(type.getCurrentVersion(), "Updating associated class for {0} to {1}", type.name(), + clazz); + } - /** - * Get the packet class associated with a given type. First attempts to read from the - * type-to-class mapping, and tries - * @param type the packet type - * @return The associated class - */ - public static Class getPacketClassFromType(PacketType type) { - return tryGetPacketClass(type) - .orElseThrow(() -> new IllegalArgumentException("Could not find packet for type " + type.name())); - } + // cache it for next time + associate(type, clazz); + if (clazz != null && MinecraftReflection.isBundleDelimiter(clazz)) { + clazz = MinecraftReflection.getPackedBundlePacketClass() + .orElseThrow(() -> new IllegalStateException("Packet bundle class not found.")); + } + return Optional.ofNullable(clazz); + } - /** - * Retrieve the packet type of a given packet. - * @param packet - the class of the packet. - * @return The packet type, or NULL if not found. - * @deprecated major issues due to packets with shared classes being registered in multiple states. - */ - @Deprecated - public static PacketType getPacketType(Class packet) { - initialize(); + /** + * Get the packet class associated with a given type. First attempts to read + * from the type-to-class mapping, and tries + * + * @param type the packet type + * @return The associated class + */ + public static Class getPacketClassFromType(PacketType type) { + return tryGetPacketClass(type) + .orElseThrow(() -> new IllegalArgumentException("Could not find packet for type " + type.name())); + } - if (MinecraftReflection.isBundlePacket(packet)) { - return PacketType.Play.Server.BUNDLE; - } - - return REGISTER.classToType.get(packet); - } + /** + * Retrieve the packet type of a given packet. + * + * @param packet - the class of the packet. + * @return The packet type, or NULL if not found. + * @deprecated major issues due to packets with shared classes being registered + * in multiple states. + */ + @Deprecated + public static PacketType getPacketType(Class packet) { + initialize(); - /** - * Retrieve the associated packet type for a packet class in the given protocol state. - * - * @param protocol the protocol state to retrieve the packet from. - * @param packet the class identifying the packet type. - * @return the packet type associated with the given class in the given protocol state, or null if not found. - */ - public static PacketType getPacketType(PacketType.Protocol protocol, Class packet) { - initialize(); - if (MinecraftReflection.isBundlePacket(packet)) { - return PacketType.Play.Server.BUNDLE; - } + if (MinecraftReflection.isBundlePacket(packet)) { + return PacketType.Play.Server.BUNDLE; + } - Map, PacketType> classToTypesForProtocol = REGISTER.protocolClassToType.get(protocol); - return classToTypesForProtocol == null ? null : classToTypesForProtocol.get(packet); - } + return REGISTER.classToType.get(packet); + } - /** - * Retrieve the packet type of a given packet. - * @param packet - the class of the packet. - * @param sender - the sender of the packet, or NULL. - * @return The packet type, or NULL if not found. - * @deprecated sender no longer has any effect - */ - @Deprecated - public static PacketType getPacketType(Class packet, Sender sender) { - return getPacketType(packet); - } + /** + * Retrieve the associated packet type for a packet class in the given protocol + * state. + * + * @param protocol the protocol state to retrieve the packet from. + * @param packet the class identifying the packet type. + * @return the packet type associated with the given class in the given protocol + * state, or null if not found. + */ + public static PacketType getPacketType(PacketType.Protocol protocol, Class packet) { + initialize(); + if (MinecraftReflection.isBundlePacket(packet)) { + return PacketType.Play.Server.BUNDLE; + } + + Map, PacketType> classToTypesForProtocol = REGISTER.protocolClassToType.get(protocol); + return classToTypesForProtocol == null ? null : classToTypesForProtocol.get(packet); + } + + /** + * Retrieve the packet type of a given packet. + * + * @param packet - the class of the packet. + * @param sender - the sender of the packet, or NULL. + * @return The packet type, or NULL if not found. + * @deprecated sender no longer has any effect + */ + @Deprecated + public static PacketType getPacketType(Class packet, Sender sender) { + return getPacketType(packet); + } } diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index 287197fc..da64a8d6 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -638,7 +638,8 @@ public final class MinecraftReflection { } public static boolean isBundleDelimiter(Class packetClass) { - return Optionals.Equals(getBundleDelimiterClass(), packetClass); + Class bundleDelimiterClass = getBundleDelimiterClass().orElse(null); + return bundleDelimiterClass != null && (packetClass.equals(bundleDelimiterClass) || bundleDelimiterClass.isAssignableFrom(packetClass)); } public static Optional> getBundleDelimiterClass() { @@ -1724,4 +1725,8 @@ public final class MinecraftReflection { public static Class getHolderLookupProviderClass() { return getMinecraftClass("core.HolderLookup$a" /* Spigot Mappings */, "core.HolderLookup$Provider" /* Mojang Mappings */); } + + public static Class getProtocolInfoClass() { + return getMinecraftClass("network.ProtocolInfo"); + } } diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java b/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java index d0643c24..5d1a0130 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftRegistryAccess.java @@ -9,11 +9,16 @@ import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; +/** + * Static getter for the registry access accessor which is need for most of the methods + * since 1.20.5 that access the registry and in form. + */ public class MinecraftRegistryAccess { private static MethodAccessor GET_SERVER = null; private static MethodAccessor REGISTRY_ACCESS = null; + // lazy initialized private static Object registryAccess = null; static { @@ -34,6 +39,12 @@ public class MinecraftRegistryAccess { } } + /** + * Returns the composite global registry access. Equiv. of + *
((CraftServer) Bukkit.getServer()).getServer().registryAccess()
+ * + * @return composite registy acesss + */ public static Object get() { if (GET_SERVER == null || REGISTRY_ACCESS == null) { return null;