From 0b289b017bdc42247fc51ef9f6c98b598cd35f00 Mon Sep 17 00:00:00 2001 From: rayanbzd Date: Thu, 14 Dec 2023 19:42:02 +0100 Subject: [PATCH] 1.20.3 & 1.20.4 support --- core/pom.xml | 5 + .../holographicdisplays/core/NMSVersion.java | 3 +- nms/pom.xml | 1 + nms/v1_20_r3/pom.xml | 36 +++++ .../nms/v1_20_R3/DataWatcherKey.java | 56 ++++++++ .../v1_20_R3/DataWatcherPacketBuilder.java | 70 ++++++++++ .../nms/v1_20_R3/EntityDestroyNMSPacket.java | 37 ++++++ .../nms/v1_20_R3/EntityMetadataNMSPacket.java | 45 +++++++ .../nms/v1_20_R3/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_20_R3/EntitySpawnNMSPacket.java | 53 ++++++++ .../nms/v1_20_R3/EntityTeleportNMSPacket.java | 42 ++++++ .../nms/v1_20_R3/EntityTypeID.java | 14 ++ .../nms/v1_20_R3/InboundPacketHandler.java | 46 +++++++ .../nms/v1_20_R3/PacketByteBuffer.java | 85 ++++++++++++ .../VersionClickableNMSPacketEntity.java | 47 +++++++ .../v1_20_R3/VersionItemNMSPacketEntity.java | 56 ++++++++ .../nms/v1_20_R3/VersionNMSManager.java | 123 ++++++++++++++++++ .../nms/v1_20_R3/VersionNMSPacket.java | 22 ++++ .../v1_20_R3/VersionTextNMSPacketEntity.java | 70 ++++++++++ pom.xml | 6 + 20 files changed, 846 insertions(+), 1 deletion(-) create mode 100644 nms/v1_20_r3/pom.xml create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherKey.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherPacketBuilder.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityDestroyNMSPacket.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMetadataNMSPacket.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMountNMSPacket.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntitySpawnNMSPacket.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTeleportNMSPacket.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTypeID.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/InboundPacketHandler.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/PacketByteBuffer.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionClickableNMSPacketEntity.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionItemNMSPacketEntity.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSManager.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSPacket.java create mode 100644 nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionTextNMSPacketEntity.java diff --git a/core/pom.xml b/core/pom.xml index 87fb8354..8b9f8816 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -119,6 +119,11 @@ holographicdisplays-nms-v1_20_r2 + + ${project.groupId} + holographicdisplays-nms-v1_20_r3 + + org.spigotmc spigot-api diff --git a/core/src/main/java/me/filoghost/holographicdisplays/core/NMSVersion.java b/core/src/main/java/me/filoghost/holographicdisplays/core/NMSVersion.java index 173774c0..2d45e0e0 100644 --- a/core/src/main/java/me/filoghost/holographicdisplays/core/NMSVersion.java +++ b/core/src/main/java/me/filoghost/holographicdisplays/core/NMSVersion.java @@ -42,7 +42,8 @@ public enum NMSVersion { /* 1.19.3 */ v1_19_R2(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_19_R2.VersionNMSManager(errorCollector)), /* 1.19.4 */ v1_19_R3(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_19_R3.VersionNMSManager(errorCollector)), /* 1.20 - 1.20.1 */ v1_20_R1(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_20_R1.VersionNMSManager(errorCollector)), - /* 1.20.2 - ? */ v1_20_R2(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_20_R2.VersionNMSManager(errorCollector)), + /* 1.20.2 */ v1_20_R2(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_20_R2.VersionNMSManager(errorCollector)), + /* 1.20.3 - 1.20.4 */ v1_20_R3(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_20_R3.VersionNMSManager(errorCollector)), /* Other versions */ UNKNOWN(NMSManagerFactory.unknownVersion()); private static final NMSVersion CURRENT_VERSION = detectCurrentVersion(); diff --git a/nms/pom.xml b/nms/pom.xml index 65784a03..d6097583 100644 --- a/nms/pom.xml +++ b/nms/pom.xml @@ -32,6 +32,7 @@ v1_19_r3 v1_20_r1 v1_20_r2 + v1_20_r3 diff --git a/nms/v1_20_r3/pom.xml b/nms/v1_20_r3/pom.xml new file mode 100644 index 00000000..37db447d --- /dev/null +++ b/nms/v1_20_r3/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + + me.filoghost.holographicdisplays + holographicdisplays-nms + 3.0.4-SNAPSHOT + + + holographicdisplays-nms-v1_20_r3 + HolographicDisplays NMS v1_20_R3 + + + + ${project.groupId} + holographicdisplays-nms-common + + + + org.spigotmc + spigot + 1.20.4-R0.1-SNAPSHOT + provided + + + + org.joml + joml + 1.10.5 + provided + + + + diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherKey.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherKey.java new file mode 100644 index 00000000..666ecabc --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherKey.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.network.syncher.DataWatcherRegistry; +import net.minecraft.network.syncher.DataWatcherSerializer; +import net.minecraft.world.item.ItemStack; + +import java.util.Optional; + +class DataWatcherKey { + + private static final DataWatcherSerializer BYTE_SERIALIZER = DataWatcherRegistry.a; + private static final DataWatcherSerializer INT_SERIALIZER = DataWatcherRegistry.b; + private static final DataWatcherSerializer BOOLEAN_SERIALIZER = DataWatcherRegistry.k; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.h; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.g; + + static final DataWatcherKey ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER); + static final DataWatcherKey> CUSTOM_NAME = new DataWatcherKey<>(2, OPTIONAL_CHAT_COMPONENT_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER); + static final DataWatcherKey ITEM_STACK = new DataWatcherKey<>(8, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(15, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(16, INT_SERIALIZER); + + private final int index; + private final DataWatcherSerializer serializer; + private final int serializerTypeID; + + private DataWatcherKey(int index, DataWatcherSerializer serializer) { + this.index = index; + this.serializer = serializer; + this.serializerTypeID = DataWatcherRegistry.b(serializer); + if (serializerTypeID < 0) { + throw new EncoderException("Could not find serializer ID of " + serializer); + } + } + + int getIndex() { + return index; + } + + DataWatcherSerializer getSerializer() { + return serializer; + } + + int getSerializerTypeID() { + return serializerTypeID; + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherPacketBuilder.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..5a4ca510 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/DataWatcherPacketBuilder.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.fcommons.Strings; +import net.minecraft.network.chat.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R3.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + private static final int MAX_CUSTOM_NAME_LENGTH = 5000; + + private final PacketByteBuffer packetByteBuffer; + + DataWatcherPacketBuilder(PacketByteBuffer packetByteBuffer) { + this.packetByteBuffer = packetByteBuffer; + } + + DataWatcherPacketBuilder setInvisible() { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ENTITY_STATUS, (byte) 0x20); // Invisible + return this; + } + + DataWatcherPacketBuilder setArmorStandMarker() { + setInvisible(); + packetByteBuffer.writeDataWatcherEntry( + DataWatcherKey.ARMOR_STAND_STATUS, (byte) (0x01 | 0x02 | 0x08 | 0x10)); // Small, no gravity, no base plate, marker + return this; + } + + DataWatcherPacketBuilder setCustomName(String customName) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, getCustomNameDataWatcherValue(customName)); + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName)); + return this; + } + + private Optional getCustomNameDataWatcherValue(String customName) { + customName = Strings.truncate(customName, MAX_CUSTOM_NAME_LENGTH); + if (!Strings.isEmpty(customName)) { + return Optional.of(CraftChatMessage.fromString(customName, false, true)[0]); + } else { + return Optional.empty(); + } + } + + DataWatcherPacketBuilder setItemStack(ItemStack itemStack) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, CraftItemStack.asNMSCopy(itemStack)); + return this; + } + + DataWatcherPacketBuilder setSlimeSmall() { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.SLIME_SIZE, 1); + return this; + } + + T build() { + packetByteBuffer.writeDataWatcherEntriesEnd(); + return createPacket(packetByteBuffer); + } + + abstract T createPacket(PacketByteBuffer packetByteBuffer); + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityDestroyNMSPacket.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..c2ae95b2 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityDestroyNMSPacket.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.nms.common.EntityID; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID entityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarIntArray(entityID.getNumericID()); + + this.rawPacket = new PacketPlayOutEntityDestroy(packetByteBuffer.getInternalSerializer()); + } + + EntityDestroyNMSPacket(EntityID entityID1, EntityID entityID2) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarIntArray(entityID1.getNumericID(), entityID2.getNumericID()); + + this.rawPacket = new PacketPlayOutEntityDestroy(packetByteBuffer.getInternalSerializer()); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMetadataNMSPacket.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..6137d95e --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMetadataNMSPacket.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.nms.common.EntityID; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = new PacketPlayOutEntityMetadata(packetByteBuffer.getInternalSerializer()); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder(EntityID entityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + packetByteBuffer.writeVarInt(entityID.getNumericID()); + return new Builder(packetByteBuffer); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private Builder(PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + } + + @Override + EntityMetadataNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityMetadataNMSPacket(packetByteBuffer); + } + + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMountNMSPacket.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMountNMSPacket.java new file mode 100644 index 00000000..1deffdfd --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityMountNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.nms.common.EntityID; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeVarIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = new PacketPlayOutMount(packetByteBuffer.getInternalSerializer()); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntitySpawnNMSPacket.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..871f59f2 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntitySpawnNMSPacket.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.common.PositionCoordinates; +import me.filoghost.holographicdisplays.nms.common.EntityID; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, PositionCoordinates position, double positionOffsetY) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(position.getX()); + packetByteBuffer.writeDouble(position.getY() + positionOffsetY); + packetByteBuffer.writeDouble(position.getZ()); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Object data + if (entityTypeID == EntityTypeID.ITEM) { + packetByteBuffer.writeInt(1); // Velocity is present and zero (otherwise by default a random velocity is applied) + } else { + packetByteBuffer.writeInt(0); + } + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + this.rawPacket = new PacketPlayOutSpawnEntity(packetByteBuffer.getInternalSerializer()); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTeleportNMSPacket.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..cf3cb512 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTeleportNMSPacket.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.common.PositionCoordinates; +import me.filoghost.holographicdisplays.nms.common.EntityID; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, PositionCoordinates position, double positionOffsetY) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(position.getX()); + packetByteBuffer.writeDouble(position.getY() + positionOffsetY); + packetByteBuffer.writeDouble(position.getZ()); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = new PacketPlayOutEntityTeleport(packetByteBuffer.getInternalSerializer()); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTypeID.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTypeID.java new file mode 100644 index 00000000..758d48b9 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/EntityTypeID.java @@ -0,0 +1,14 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +class EntityTypeID { + + static final int ARMOR_STAND = 2; + static final int ITEM = 54; + static final int SLIME = 88; + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/InboundPacketHandler.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/InboundPacketHandler.java new file mode 100644 index 00000000..6a2a28b4 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/InboundPacketHandler.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import me.filoghost.fcommons.logging.Log; +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.nms.common.NMSErrors; +import me.filoghost.holographicdisplays.nms.common.PacketListener; +import net.minecraft.network.protocol.game.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +class InboundPacketHandler extends ChannelInboundHandlerAdapter { + + public static final String HANDLER_NAME = "holographic_displays_listener"; + private static final ReflectField ENTITY_ID_FIELD = ReflectField.lookup(int.class, PacketPlayInUseEntity.class, "a"); + + private final Player player; + private final PacketListener packetListener; + + InboundPacketHandler(Player player, PacketListener packetListener) { + this.player = player; + this.packetListener = packetListener; + } + + @Override + public void channelRead(ChannelHandlerContext context, Object packet) throws Exception { + try { + if (packet instanceof PacketPlayInUseEntity) { + int entityID = ENTITY_ID_FIELD.get(packet); + boolean cancel = packetListener.onAsyncEntityInteract(player, entityID); + if (cancel) { + return; + } + } + } catch (Throwable t) { + Log.warning(NMSErrors.EXCEPTION_ON_PACKET_READ, t); + } + super.channelRead(context, packet); + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/PacketByteBuffer.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/PacketByteBuffer.java new file mode 100644 index 00000000..68284077 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/PacketByteBuffer.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import io.netty.buffer.Unpooled; +import net.minecraft.network.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + private final PacketDataSerializer serializer; + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + this.serializer = new PacketDataSerializer(Unpooled.buffer()); + } + + void writeBoolean(boolean flag) { + serializer.writeBoolean(flag); + } + + void writeByte(int i) { + serializer.writeByte(i); + } + + void writeShort(int i) { + serializer.writeShort(i); + } + + void writeInt(int i) { + serializer.writeInt(i); + } + + void writeDouble(double d) { + serializer.writeDouble(d); + } + + void writeVarInt(int i) { + serializer.c(i); + } + + void writeVarIntArray(int i1) { + writeVarInt(1); + writeVarInt(i1); + } + + void writeVarIntArray(int i1, int i2) { + writeVarInt(2); + writeVarInt(i1); + writeVarInt(i2); + } + + void writeUUID(UUID uuid) { + serializer.a(uuid); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + serializer.writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(serializer, value); + } + + void writeDataWatcherEntriesEnd() { + serializer.writeByte(0xFF); + } + + public PacketDataSerializer getInternalSerializer() { + return serializer; + } + + void clear() { + serializer.clear(); + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionClickableNMSPacketEntity.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionClickableNMSPacketEntity.java new file mode 100644 index 00000000..0954e545 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionClickableNMSPacketEntity.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.common.PositionCoordinates; +import me.filoghost.holographicdisplays.nms.common.EntityID; +import me.filoghost.holographicdisplays.nms.common.PacketGroup; +import me.filoghost.holographicdisplays.nms.common.entity.ClickableNMSPacketEntity; + +class VersionClickableNMSPacketEntity implements ClickableNMSPacketEntity { + + private final EntityID slimeID; + + VersionClickableNMSPacketEntity(EntityID slimeID) { + this.slimeID = slimeID; + } + + @Override + public EntityID getID() { + return slimeID; + } + + @Override + public PacketGroup newSpawnPackets(PositionCoordinates position) { + return PacketGroup.of( + new EntitySpawnNMSPacket(slimeID, EntityTypeID.SLIME, position, SLIME_Y_OFFSET), + EntityMetadataNMSPacket.builder(slimeID) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public PacketGroup newTeleportPackets(PositionCoordinates position) { + return new EntityTeleportNMSPacket(slimeID, position, SLIME_Y_OFFSET); + } + + @Override + public PacketGroup newDestroyPackets() { + return new EntityDestroyNMSPacket(slimeID); + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionItemNMSPacketEntity.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionItemNMSPacketEntity.java new file mode 100644 index 00000000..59aa8c1d --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionItemNMSPacketEntity.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.common.PositionCoordinates; +import me.filoghost.holographicdisplays.nms.common.EntityID; +import me.filoghost.holographicdisplays.nms.common.PacketGroup; +import me.filoghost.holographicdisplays.nms.common.entity.ItemNMSPacketEntity; +import org.bukkit.inventory.ItemStack; + +class VersionItemNMSPacketEntity implements ItemNMSPacketEntity { + + private final EntityID itemID; + private final EntityID vehicleID; + + VersionItemNMSPacketEntity(EntityID itemID, EntityID vehicleID) { + this.itemID = itemID; + this.vehicleID = vehicleID; + } + + @Override + public PacketGroup newSpawnPackets(PositionCoordinates position, ItemStack itemStack) { + return PacketGroup.of( + new EntitySpawnNMSPacket(vehicleID, EntityTypeID.ARMOR_STAND, position, ITEM_Y_OFFSET), + EntityMetadataNMSPacket.builder(vehicleID) + .setArmorStandMarker() + .build(), + new EntitySpawnNMSPacket(itemID, EntityTypeID.ITEM, position, ITEM_Y_OFFSET), + EntityMetadataNMSPacket.builder(itemID) + .setItemStack(itemStack) + .build(), + new EntityMountNMSPacket(vehicleID, itemID) + ); + } + + @Override + public PacketGroup newChangePackets(ItemStack itemStack) { + return EntityMetadataNMSPacket.builder(itemID) + .setItemStack(itemStack) + .build(); + } + + @Override + public PacketGroup newTeleportPackets(PositionCoordinates position) { + return new EntityTeleportNMSPacket(vehicleID, position, ITEM_Y_OFFSET); + } + + @Override + public PacketGroup newDestroyPackets() { + return new EntityDestroyNMSPacket(itemID, vehicleID); + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSManager.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSManager.java new file mode 100644 index 00000000..7a5a9027 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSManager.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; +import me.filoghost.fcommons.logging.ErrorCollector; +import me.filoghost.fcommons.logging.Log; +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.nms.common.EntityID; +import me.filoghost.holographicdisplays.nms.common.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.nms.common.NMSErrors; +import me.filoghost.holographicdisplays.nms.common.NMSManager; +import me.filoghost.holographicdisplays.nms.common.PacketListener; +import me.filoghost.holographicdisplays.nms.common.entity.ClickableNMSPacketEntity; +import me.filoghost.holographicdisplays.nms.common.entity.ItemNMSPacketEntity; +import me.filoghost.holographicdisplays.nms.common.entity.TextNMSPacketEntity; +import net.minecraft.network.NetworkManager; +import net.minecraft.server.network.PlayerConnection; +import net.minecraft.server.network.ServerCommonPacketListenerImpl; +import net.minecraft.world.entity.Entity; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class VersionNMSManager implements NMSManager { + + private static final ReflectField ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(AtomicInteger.class, Entity.class, "d"); + private static final ReflectField NETWORK_MANAGER_FIELD = ReflectField.lookup(NetworkManager.class, ServerCommonPacketListenerImpl.class, "c"); + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + + // Force initialization of class to eventually throw exceptions early + DataWatcherKey.ENTITY_STATUS.getIndex(); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + AtomicInteger nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + return nmsEntityIDCounter::incrementAndGet; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return new FallbackEntityIDGenerator(); + } + } + + private EntityID newEntityID() { + return new EntityID(entityIDGenerator); + } + + @Override + public TextNMSPacketEntity newTextPacketEntity() { + return new VersionTextNMSPacketEntity(newEntityID()); + } + + @Override + public ItemNMSPacketEntity newItemPacketEntity() { + return new VersionItemNMSPacketEntity(newEntityID(), newEntityID()); + } + + @Override + public ClickableNMSPacketEntity newClickablePacketEntity() { + return new VersionClickableNMSPacketEntity(newEntityID()); + } + + @Override + public void injectPacketListener(Player player, PacketListener packetListener) { + modifyPipeline(player, (ChannelPipeline pipeline) -> { + ChannelHandler currentListener = pipeline.get(InboundPacketHandler.HANDLER_NAME); + if (currentListener != null) { + pipeline.remove(InboundPacketHandler.HANDLER_NAME); + } + pipeline.addBefore("packet_handler", InboundPacketHandler.HANDLER_NAME, new InboundPacketHandler(player, packetListener)); + }); + } + + @Override + public void uninjectPacketListener(Player player) { + modifyPipeline(player, (ChannelPipeline pipeline) -> { + ChannelHandler currentListener = pipeline.get(InboundPacketHandler.HANDLER_NAME); + if (currentListener != null) { + pipeline.remove(InboundPacketHandler.HANDLER_NAME); + } + }); + } + + /* + * Modifying the pipeline in the main thread can cause deadlocks, delays and other concurrency issues, + * which can be avoided by using the event loop. Thanks to ProtocolLib for this insight. + */ + private void modifyPipeline(Player player, Consumer pipelineModifierTask) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().c; + NetworkManager networkManager; + try { + networkManager = NETWORK_MANAGER_FIELD.get(playerConnection); + } catch (ReflectiveOperationException e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + return; + } + Channel channel = networkManager.n; + + channel.eventLoop().execute(() -> { + if (!player.isOnline()) { + return; + } + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); + } + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSPacket.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSPacket.java new file mode 100644 index 00000000..68aab20a --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionNMSPacket.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.nms.common.PacketGroup; +import net.minecraft.network.protocol.Packet; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; + +abstract class VersionNMSPacket implements PacketGroup { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().c.b(getRawPacket()); + } + + abstract Packet getRawPacket(); + +} diff --git a/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionTextNMSPacketEntity.java b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionTextNMSPacketEntity.java new file mode 100644 index 00000000..57546ec4 --- /dev/null +++ b/nms/v1_20_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_20_R3/VersionTextNMSPacketEntity.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_20_R3; + +import me.filoghost.holographicdisplays.common.PositionCoordinates; +import me.filoghost.holographicdisplays.nms.common.EntityID; +import me.filoghost.holographicdisplays.nms.common.IndividualTextPacketGroup; +import me.filoghost.holographicdisplays.nms.common.PacketGroup; +import me.filoghost.holographicdisplays.nms.common.entity.TextNMSPacketEntity; + +class VersionTextNMSPacketEntity implements TextNMSPacketEntity { + + private final EntityID armorStandID; + + VersionTextNMSPacketEntity(EntityID armorStandID) { + this.armorStandID = armorStandID; + } + + @Override + public PacketGroup newSpawnPackets(PositionCoordinates position, String text) { + return PacketGroup.of( + new EntitySpawnNMSPacket(armorStandID, EntityTypeID.ARMOR_STAND, position, ARMOR_STAND_Y_OFFSET), + EntityMetadataNMSPacket.builder(armorStandID) + .setArmorStandMarker() + .setCustomName(text) + .build() + ); + } + + @Override + public IndividualTextPacketGroup newSpawnPackets(PositionCoordinates position) { + return IndividualTextPacketGroup.of( + new EntitySpawnNMSPacket(armorStandID, EntityTypeID.ARMOR_STAND, position, ARMOR_STAND_Y_OFFSET), + (String text) -> EntityMetadataNMSPacket.builder(armorStandID) + .setArmorStandMarker() + .setCustomName(text) + .build() + ); + } + + @Override + public PacketGroup newChangePackets(String text) { + return EntityMetadataNMSPacket.builder(armorStandID) + .setCustomName(text) + .build(); + } + + @Override + public IndividualTextPacketGroup newChangePackets() { + return IndividualTextPacketGroup.of( + (String text) -> EntityMetadataNMSPacket.builder(armorStandID) + .setCustomName(text) + .build() + ); + } + + @Override + public PacketGroup newTeleportPackets(PositionCoordinates position) { + return new EntityTeleportNMSPacket(armorStandID, position, ARMOR_STAND_Y_OFFSET); + } + + @Override + public PacketGroup newDestroyPackets() { + return new EntityDestroyNMSPacket(armorStandID); + } + +} diff --git a/pom.xml b/pom.xml index c3733bfa..48d9e103 100644 --- a/pom.xml +++ b/pom.xml @@ -213,6 +213,12 @@ ${project.version} + + ${project.groupId} + holographicdisplays-nms-v1_20_r3 + ${project.version} + + org.spigotmc spigot-api