From 88a713c2daf0cad04147b38b140f67385201bb48 Mon Sep 17 00:00:00 2001 From: tplansky Date: Sat, 18 May 2024 21:17:09 +0200 Subject: [PATCH] Add support for 1.20.6 and Paper's relocation changes --- .../decentsoftware/holograms/api/nms/NMS.java | 10 +- .../api/nms/PacketHandlerCommon.java | 10 +- .../holograms/api/nms/versions/NMS_1_17.java | 107 +++++++++++++++--- .../api/utils/reflect/ReflectField.java | 8 ++ .../api/utils/reflect/ReflectionUtil.java | 29 ++++- 5 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/main/java/eu/decentsoftware/holograms/api/nms/NMS.java b/src/main/java/eu/decentsoftware/holograms/api/nms/NMS.java index 0bdac9dd..b574b09d 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/nms/NMS.java +++ b/src/main/java/eu/decentsoftware/holograms/api/nms/NMS.java @@ -53,23 +53,25 @@ public abstract class NMS { } } if (Version.afterOrEqual(Version.v1_20_R2)) { - for (Field field : playerConnectionClass.getFields()) { + // Since 1.20, the field is in a parent class. + Class playerConnectionParentClass = ReflectionUtil.getNMClass("server.network.ServerCommonPacketListenerImpl"); + for (Field field : playerConnectionParentClass.getDeclaredFields()) { if (field.getType().isAssignableFrom(networkManagerClass)) { - PLAYER_CONNECTION_NETWORK_MANAGER_FIELD = new ReflectField<>(playerConnectionClass, field.getName()); + PLAYER_CONNECTION_NETWORK_MANAGER_FIELD = new ReflectField<>(field); break; } } } else { for (Field field : playerConnectionClass.getDeclaredFields()) { if (field.getType().isAssignableFrom(networkManagerClass)) { - PLAYER_CONNECTION_NETWORK_MANAGER_FIELD = new ReflectField<>(playerConnectionClass, field.getName()); + PLAYER_CONNECTION_NETWORK_MANAGER_FIELD = new ReflectField<>(field); break; } } } for (Field field : networkManagerClass.getDeclaredFields()) { if (field.getType().isAssignableFrom(Channel.class)) { - NETWORK_MANAGER_CHANNEL_FIELD = new ReflectField<>(networkManagerClass, field.getName()); + NETWORK_MANAGER_CHANNEL_FIELD = new ReflectField<>(field); break; } } diff --git a/src/main/java/eu/decentsoftware/holograms/api/nms/PacketHandlerCommon.java b/src/main/java/eu/decentsoftware/holograms/api/nms/PacketHandlerCommon.java index acbab259..929b13e6 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/nms/PacketHandlerCommon.java +++ b/src/main/java/eu/decentsoftware/holograms/api/nms/PacketHandlerCommon.java @@ -30,9 +30,15 @@ public final class PacketHandlerCommon { ENTITY_USE_PACKET_CLASS = ReflectionUtil.getNMSClass("PacketPlayInUseEntity"); PACKET_DATA_SERIALIZER_CLASS = ReflectionUtil.getNMSClass("PacketDataSerializer"); } - ENTITY_USE_PACKET_ID_FIELD = new ReflectField<>(ENTITY_USE_PACKET_CLASS, "a"); + if (Version.afterOrEqual(Version.v1_20_R4)) { + ENTITY_USE_PACKET_ID_FIELD = new ReflectField<>(ENTITY_USE_PACKET_CLASS, "b"); + } else { + ENTITY_USE_PACKET_ID_FIELD = new ReflectField<>(ENTITY_USE_PACKET_CLASS, "a"); + } PACKET_DATA_SERIALIZER_CONSTRUCTOR = new ReflectConstructor(PACKET_DATA_SERIALIZER_CLASS, ByteBuf.class); - if (Version.afterOrEqual(Version.v1_20_R3)) { + if (Version.afterOrEqual(Version.v1_20_R4)) { + PACKET_DATA_SERIALIZER_READ_INT_METHOD = new ReflectMethod(PACKET_DATA_SERIALIZER_CLASS, "l"); + } else if (Version.afterOrEqual(Version.v1_20_R3)) { PACKET_DATA_SERIALIZER_READ_INT_METHOD = new ReflectMethod(PACKET_DATA_SERIALIZER_CLASS, "n"); } else if (Version.afterOrEqual(Version.v1_19_R3)) { PACKET_DATA_SERIALIZER_READ_INT_METHOD = new ReflectMethod(PACKET_DATA_SERIALIZER_CLASS, "m"); diff --git a/src/main/java/eu/decentsoftware/holograms/api/nms/versions/NMS_1_17.java b/src/main/java/eu/decentsoftware/holograms/api/nms/versions/NMS_1_17.java index 1542d5d8..b2f7eb04 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/nms/versions/NMS_1_17.java +++ b/src/main/java/eu/decentsoftware/holograms/api/nms/versions/NMS_1_17.java @@ -16,6 +16,7 @@ import org.bukkit.inventory.ItemStack; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Random; @@ -54,7 +55,6 @@ public class NMS_1_17 extends NMS { // PACKETS private static final ReflectConstructor PACKET_SPAWN_ENTITY_CONSTRUCTOR; private static final ReflectConstructor PACKET_SPAWN_ENTITY_LIVING_CONSTRUCTOR; - private static final ReflectConstructor PACKET_ENTITY_METADATA_CONSTRUCTOR; private static final ReflectConstructor PACKET_ENTITY_TELEPORT_CONSTRUCTOR; private static final ReflectConstructor PACKET_MOUNT_CONSTRUCTOR; private static final ReflectConstructor PACKET_ENTITY_EQUIPMENT_CONSTRUCTOR; @@ -143,8 +143,6 @@ public class NMS_1_17 extends NMS { PACKET_SPAWN_ENTITY_CONSTRUCTOR = new ReflectConstructor(ReflectionUtil.getNMClass("network.protocol.game.PacketPlayOutSpawnEntity"), int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, ENTITY_TYPES_CLASS, int.class, VEC_3D_CLASS, double.class); } - PACKET_ENTITY_METADATA_CONSTRUCTOR = new ReflectConstructor(ReflectionUtil.getNMClass("network.protocol.game.PacketPlayOutEntityMetadata"), - PACKET_DATA_SERIALIZER_CLASS); PACKET_ENTITY_TELEPORT_CONSTRUCTOR = new ReflectConstructor(ReflectionUtil.getNMClass("network.protocol.game.PacketPlayOutEntityTeleport"), PACKET_DATA_SERIALIZER_CLASS); PACKET_MOUNT_CONSTRUCTOR = new ReflectConstructor(ReflectionUtil.getNMClass("network.protocol.game.PacketPlayOutMount"), @@ -155,7 +153,12 @@ public class NMS_1_17 extends NMS { int[].class); // DATA WATCHER OBJECT if (Version.afterOrEqual(18)) { - if (Version.afterOrEqual(Version.v1_20_R2)) { + if (Version.afterOrEqual(Version.v1_20_R4)) { + DWO_ENTITY_DATA = new ReflectField<>(ENTITY_CLASS, "ap").getValue(null); + DWO_CUSTOM_NAME = new ReflectField<>(ENTITY_CLASS, "aS").getValue(null); + DWO_CUSTOM_NAME_VISIBLE = new ReflectField<>(ENTITY_CLASS, "aT").getValue(null); + DWO_ARMOR_STAND_DATA = new ReflectField<>(ENTITY_ARMOR_STAND_CLASS, "bG").getValue(null); + } else if (Version.afterOrEqual(Version.v1_20_R2)) { DWO_ENTITY_DATA = new ReflectField<>(ENTITY_CLASS, "ao").getValue(null); DWO_CUSTOM_NAME = new ReflectField<>(ENTITY_CLASS, "aU").getValue(null); DWO_CUSTOM_NAME_VISIBLE = new ReflectField<>(ENTITY_CLASS, "aV").getValue(null); @@ -187,7 +190,11 @@ public class NMS_1_17 extends NMS { DWO_CUSTOM_NAME_VISIBLE = new ReflectField<>(ENTITY_CLASS, "aK").getValue(null); DWO_ARMOR_STAND_DATA = new ReflectField<>(ENTITY_ARMOR_STAND_CLASS, "bG").getValue(null); } - DWO_ITEM = new ReflectField<>(ENTITY_ITEM_CLASS, "c").getValue(null); + if (Version.afterOrEqual(Version.v1_20_R4)) { + DWO_ITEM = new ReflectField<>(ENTITY_ITEM_CLASS, "d").getValue(null); + } else { + DWO_ITEM = new ReflectField<>(ENTITY_ITEM_CLASS, "c").getValue(null); + } // ENTITY TYPES ENTITY_TYPES_A_METHOD = new ReflectMethod(ENTITY_TYPES_CLASS, "a", String.class); ENTITY_TYPE_GET_KEY_METHOD = new ReflectMethod(EntityType.class, "getKey"); @@ -200,7 +207,9 @@ public class NMS_1_17 extends NMS { ENTITY_TYPES_GET_SIZE_METHOD = new ReflectMethod(ENTITY_TYPES_CLASS, Version.afterOrEqual(Version.v1_19_R2) ? "n" : "m"); ENTITY_SIZE_HEIGHT_FIELD = new ReflectField<>(ReflectionUtil.getNMClass("world.entity.EntitySize"), "b"); - if (Version.afterOrEqual(Version.v1_19_R3)) { + if (Version.afterOrEqual(Version.v1_20_R4)) { + ENTITY_COUNTER_FIELD = new ReflectField<>(ENTITY_CLASS, "c"); + } else if (Version.afterOrEqual(Version.v1_19_R3)) { ENTITY_COUNTER_FIELD = new ReflectField<>(ENTITY_CLASS, "d"); } else if (Version.CURRENT.equals(Version.v1_18_R2) || Version.afterOrEqual(19)) { ENTITY_COUNTER_FIELD = new ReflectField<>(ENTITY_CLASS, "c"); @@ -320,20 +329,81 @@ private void showFakeEntityLiving(Player player, Location location, int entityTy sendPacket(player, PACKET_SPAWN_ENTITY_LIVING_CONSTRUCTOR.newInstance(packetDataSerializer)); } - private static final Class DWR_CLASS = ReflectionUtil.getNMClass("network.syncher.DataWatcherRegistry"); - private static final ReflectMethod DWI_GET_OBJECT_METHOD = new ReflectMethod(DWI_CLASS, "a"); - private static final ReflectMethod DWI_GET_VALUE_METHOD = new ReflectMethod(DWI_CLASS, "b"); - private static final ReflectMethod DWO_GET_SERIALIZER_METHOD = new ReflectMethod(DWO_CLASS, "b"); - private static final ReflectMethod DWO_GET_INDEX_METHOD = new ReflectMethod(DWO_CLASS, "a"); - private static final ReflectMethod DWS_GET_TYPE_ID_METHOD = new ReflectMethod(DWR_CLASS, "b", DWS_CLASS); - private static final ReflectMethod DWS_SERIALIZE_METHOD = new ReflectMethod(DWS_CLASS, "a", - PACKET_DATA_SERIALIZER_CLASS, Object.class); + private static final Class REGISTRY_FRIENDLY_BYTE_BUF_CLASS; + private static final Class IREGISTRYCUSTOM_CLASS; + private static final Class BUILTINREGISTRIES_CLASS; + private static final Class IREGISTRYCUSTOM_C_CLASS; + private static final ReflectConstructor REGISTRY_FRIENDLY_BYTE_BUF_CONSTRUCTOR; + private static final ReflectConstructor IREGISTRYCUSTOM_C_CONSTRUCTOR; + private static final Class CODEC_CLASS; + private static final ReflectMethod DWS_GET_CODEC_METHOD; + private static final ReflectMethod CODEC_ENCODE_METHOD; + private static final Object ITEM_REGISTRY; + private static final Object DATA_COMPONENT_TYPE_REGISTRY; + + private static final Class DWR_CLASS; + private static final ReflectMethod DWI_GET_OBJECT_METHOD; + private static final ReflectMethod DWI_GET_VALUE_METHOD; + private static final ReflectMethod DWO_GET_SERIALIZER_METHOD; + private static final ReflectMethod DWO_GET_INDEX_METHOD; + private static final ReflectMethod DWS_GET_TYPE_ID_METHOD; + private static final ReflectMethod DWS_SERIALIZE_METHOD; + private static final ReflectConstructor PACKET_ENTITY_METADATA_CONSTRUCTOR; + + static { + Class metadataPacketClass = ReflectionUtil.getNMClass("network.protocol.game.PacketPlayOutEntityMetadata"); + + DWR_CLASS = ReflectionUtil.getNMClass("network.syncher.DataWatcherRegistry"); + DWI_GET_OBJECT_METHOD = new ReflectMethod(DWI_CLASS, "a"); + DWI_GET_VALUE_METHOD = new ReflectMethod(DWI_CLASS, "b"); + DWO_GET_SERIALIZER_METHOD = new ReflectMethod(DWO_CLASS, "b"); + DWO_GET_INDEX_METHOD = new ReflectMethod(DWO_CLASS, "a"); + DWS_GET_TYPE_ID_METHOD = new ReflectMethod(DWR_CLASS, "b", DWS_CLASS); + + if (Version.afterOrEqual(Version.v1_20_R4)) { + REGISTRY_FRIENDLY_BYTE_BUF_CLASS = ReflectionUtil.getNMClass("network.RegistryFriendlyByteBuf"); + IREGISTRYCUSTOM_CLASS = ReflectionUtil.getNMClass("core.IRegistryCustom"); + BUILTINREGISTRIES_CLASS = ReflectionUtil.getNMClass("core.registries.BuiltInRegistries"); + IREGISTRYCUSTOM_C_CLASS = ReflectionUtil.getNMClass("core.IRegistryCustom$c"); + REGISTRY_FRIENDLY_BYTE_BUF_CONSTRUCTOR = new ReflectConstructor(REGISTRY_FRIENDLY_BYTE_BUF_CLASS, ByteBuf.class, IREGISTRYCUSTOM_CLASS); + IREGISTRYCUSTOM_C_CONSTRUCTOR = new ReflectConstructor(IREGISTRYCUSTOM_C_CLASS, List.class); + CODEC_CLASS = ReflectionUtil.getNMClass("network.codec.StreamCodec"); + DWS_GET_CODEC_METHOD = new ReflectMethod(DWS_CLASS, "codec"); + CODEC_ENCODE_METHOD = new ReflectMethod(CODEC_CLASS, "encode", Object.class, Object.class); + ITEM_REGISTRY = ReflectionUtil.getFieldValue(BUILTINREGISTRIES_CLASS, "h"); + DATA_COMPONENT_TYPE_REGISTRY = ReflectionUtil.getFieldValue(BUILTINREGISTRIES_CLASS, "as"); + + DWS_SERIALIZE_METHOD = null; + PACKET_ENTITY_METADATA_CONSTRUCTOR = new ReflectConstructor(metadataPacketClass, REGISTRY_FRIENDLY_BYTE_BUF_CLASS); + } else { + REGISTRY_FRIENDLY_BYTE_BUF_CLASS = null; + IREGISTRYCUSTOM_CLASS = null; + BUILTINREGISTRIES_CLASS = null; + IREGISTRYCUSTOM_C_CLASS = null; + REGISTRY_FRIENDLY_BYTE_BUF_CONSTRUCTOR = null; + IREGISTRYCUSTOM_C_CONSTRUCTOR = null; + CODEC_CLASS = null; + DWS_GET_CODEC_METHOD = null; + CODEC_ENCODE_METHOD = null; + ITEM_REGISTRY = null; + DATA_COMPONENT_TYPE_REGISTRY = null; + + DWS_SERIALIZE_METHOD = new ReflectMethod(DWS_CLASS, "a", PACKET_DATA_SERIALIZER_CLASS, Object.class); + PACKET_ENTITY_METADATA_CONSTRUCTOR = new ReflectConstructor(metadataPacketClass, PACKET_DATA_SERIALIZER_CLASS); + } + } private void sendEntityMetadata(Player player, int entityId, List items) { Validate.notNull(player); Validate.notNull(items); - Object packetDataSerializer = PACKET_DATA_SERIALIZER_CONSTRUCTOR.newInstance(Unpooled.buffer()); + Object packetDataSerializer; + if (Version.afterOrEqual(Version.v1_20_R4)) { + Object c = IREGISTRYCUSTOM_C_CONSTRUCTOR.newInstance(Arrays.asList(ITEM_REGISTRY, DATA_COMPONENT_TYPE_REGISTRY)); + packetDataSerializer = REGISTRY_FRIENDLY_BYTE_BUF_CONSTRUCTOR.newInstance(Unpooled.buffer(), c); + } else { + packetDataSerializer = PACKET_DATA_SERIALIZER_CONSTRUCTOR.newInstance(Unpooled.buffer()); + } PACKET_DATA_SERIALIZER_WRITE_INT_METHOD.invoke(packetDataSerializer, entityId); for (Object item : items) { if (!item.getClass().isAssignableFrom(DWI_CLASS)) { @@ -348,8 +418,13 @@ private void sendEntityMetadata(Player player, int entityId, List items) PACKET_DATA_SERIALIZER_WRITE_BYTE_METHOD.invoke(packetDataSerializer, (byte) serializerIndex); PACKET_DATA_SERIALIZER_WRITE_INT_METHOD.invoke(packetDataSerializer, serializerTypeId); - DWS_SERIALIZE_METHOD.invoke(serializer, packetDataSerializer, value); + if (Version.afterOrEqual(Version.v1_20_R4)) { + Object codec = DWS_GET_CODEC_METHOD.invoke(serializer); + CODEC_ENCODE_METHOD.invoke(codec, packetDataSerializer, value); + } else if (Version.afterOrEqual(Version.v1_20_R3)) { + DWS_SERIALIZE_METHOD.invoke(serializer, packetDataSerializer, value); + } } PACKET_DATA_SERIALIZER_WRITE_BYTE_METHOD.invoke(packetDataSerializer, 0xFF); sendPacket(player, PACKET_ENTITY_METADATA_CONSTRUCTOR.newInstance(packetDataSerializer)); diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectField.java b/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectField.java index e714c68e..09b528e2 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectField.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectField.java @@ -14,6 +14,14 @@ public ReflectField(Class clazz, String name) { this.name = name; } + public ReflectField(Field field) { + this.field = field; + this.clazz = field.getDeclaringClass(); + this.name = field.getName(); + + this.field.setAccessible(true); + } + private void init() throws Exception { if (field == null) { try { diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java b/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java index 94e06743..97ad9060 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java @@ -18,6 +18,7 @@ public class ReflectionUtil { * Class -> (Field Name -> Field) */ private static final Map, Map> FIELD_CACHE = new ConcurrentHashMap<>(); + private static final String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName(); private static String version; /** @@ -39,12 +40,36 @@ public static boolean setFieldValue(final @NotNull Object object, final @Not field.set(object, value); return true; } catch (IllegalAccessException ignored) { - // Field is not accessible + // The field is not accessible } } return false; } + /** + * Get the value of a field with the given name from the given object. + *

+ * If the field is not found, or is not accessible, this method will return null. + * + * @param object The object to get the field from. + * @param fieldName The name of the field to get. + * @param The type of the field. + * @return The value of the field, or null if the field was not found. + */ + @Nullable + public static T getFieldValue(final @NotNull Object object, final @NotNull String fieldName) { + Class clazz = object instanceof Class ? (Class) object : object.getClass(); + Field field = getCachedField(clazz, fieldName); + if (field != null) { + try { + return (T) field.get(object); + } catch (IllegalAccessException ignored) { + // The field is not accessible + } + } + return null; + } + /** * Get a field with the given name from the given class. *

@@ -164,7 +189,7 @@ public static Class getNMSClass(final @NotNull String classPath) { @Nullable public static Class getObcClass(final @NotNull String classPath) { try { - return Class.forName("org.bukkit.craftbukkit." + getVersion() + "." + classPath); + return Class.forName(CRAFTBUKKIT_PACKAGE + "." + classPath); } catch (ClassNotFoundException e) { e.printStackTrace(); return null;