Skip to content

Commit

Permalink
Add support for 1.20.6 and Paper's relocation changes
Browse files Browse the repository at this point in the history
  • Loading branch information
d0by1 committed May 18, 2024
1 parent f1290a1 commit 88a713c
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 24 deletions.
10 changes: 6 additions & 4 deletions src/main/java/eu/decentsoftware/holograms/api/nms/NMS.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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"),
Expand All @@ -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);
Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand Down Expand Up @@ -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<Object> 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)) {
Expand All @@ -348,8 +418,13 @@ private void sendEntityMetadata(Player player, int entityId, List<Object> 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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class ReflectionUtil {
* Class -> (Field Name -> Field)
*/
private static final Map<Class<?>, Map<String, Field>> FIELD_CACHE = new ConcurrentHashMap<>();
private static final String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
private static String version;

/**
Expand All @@ -39,12 +40,36 @@ public static <T> 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.
* <p>
* 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 <T> The type of the field.
* @return The value of the field, or null if the field was not found.
*/
@Nullable
public static <T> 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.
* <p>
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 88a713c

Please sign in to comment.