From 4f86a8e9c2d71f8eca4b6b0b1acbb0079f04cd39 Mon Sep 17 00:00:00 2001 From: filoghost Date: Mon, 9 Aug 2021 09:20:54 +0200 Subject: [PATCH] Add other major NMS implementations --- .../nms/v1_10_R1/DataWatcherKey.java | 54 +++++++++ .../v1_10_R1/DataWatcherPacketBuilder.java | 55 ++++++++++ .../nms/v1_10_R1/EntityDestroyNMSPacket.java | 30 +++++ .../v1_10_R1/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_10_R1/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_10_R1/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_10_R1/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_10_R1/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_10_R1/EntityTypeID.java | 14 +++ .../nms/v1_10_R1/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_10_R1/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_10_R1/VersionNMSManager.java | 87 ++++++++++++++- .../nms/v1_10_R1/VersionNMSPacket.java | 34 ++++++ .../nms/v1_10_R1/VersionNMSPacketList.java | 102 +++++++++++++++++ .../nms/v1_11_R1/DataWatcherKey.java | 53 +++++++++ .../v1_11_R1/DataWatcherPacketBuilder.java | 54 +++++++++ .../nms/v1_11_R1/EntityDestroyNMSPacket.java | 30 +++++ .../v1_11_R1/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_11_R1/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_11_R1/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_11_R1/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_11_R1/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_11_R1/EntityTypeID.java | 14 +++ .../nms/v1_11_R1/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_11_R1/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_11_R1/VersionNMSManager.java | 87 ++++++++++++++- .../nms/v1_11_R1/VersionNMSPacket.java | 34 ++++++ .../nms/v1_11_R1/VersionNMSPacketList.java | 102 +++++++++++++++++ .../nms/v1_12_R1/DataWatcherKey.java | 53 +++++++++ .../v1_12_R1/DataWatcherPacketBuilder.java | 54 +++++++++ .../nms/v1_12_R1/EntityDestroyNMSPacket.java | 30 +++++ .../v1_12_R1/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_12_R1/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_12_R1/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_12_R1/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_12_R1/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_12_R1/EntityTypeID.java | 14 +++ .../nms/v1_12_R1/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_12_R1/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_12_R1/VersionNMSManager.java | 87 ++++++++++++++- .../nms/v1_12_R1/VersionNMSPacket.java | 34 ++++++ .../nms/v1_12_R1/VersionNMSPacketList.java | 102 +++++++++++++++++ .../nms/v1_13_R2/DataWatcherKey.java | 56 ++++++++++ .../v1_13_R2/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_13_R2/EntityDestroyNMSPacket.java | 30 +++++ .../v1_13_R2/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_13_R2/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_13_R2/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_13_R2/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_13_R2/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_13_R2/EntityTypeID.java | 14 +++ .../nms/v1_13_R2/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_13_R2/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_13_R2/VersionNMSManager.java | 87 ++++++++++++++- .../nms/v1_13_R2/VersionNMSPacket.java | 34 ++++++ .../nms/v1_13_R2/VersionNMSPacketList.java | 102 +++++++++++++++++ .../nms/v1_14_R1/DataWatcherKey.java | 56 ++++++++++ .../v1_14_R1/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_14_R1/EntityDestroyNMSPacket.java | 30 +++++ .../v1_14_R1/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_14_R1/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_14_R1/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_14_R1/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_14_R1/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_14_R1/EntityTypeID.java | 14 +++ .../nms/v1_14_R1/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_14_R1/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_14_R1/VersionNMSManager.java | 71 +++++++++++- .../nms/v1_14_R1/VersionNMSPacket.java | 34 ++++++ .../nms/v1_14_R1/VersionNMSPacketList.java | 100 +++++++++++++++++ .../nms/v1_15_R1/DataWatcherKey.java | 56 ++++++++++ .../v1_15_R1/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_15_R1/EntityDestroyNMSPacket.java | 30 +++++ .../v1_15_R1/EntityLivingSpawnNMSPacket.java | 48 ++++++++ .../nms/v1_15_R1/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_15_R1/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_15_R1/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_15_R1/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_15_R1/EntityTypeID.java | 14 +++ .../nms/v1_15_R1/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_15_R1/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_15_R1/VersionNMSManager.java | 71 +++++++++++- .../nms/v1_15_R1/VersionNMSPacket.java | 34 ++++++ .../nms/v1_15_R1/VersionNMSPacketList.java | 103 ++++++++++++++++++ .../nms/v1_16_R1/DataWatcherKey.java | 56 ++++++++++ .../v1_16_R1/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_16_R1/EntityDestroyNMSPacket.java | 30 +++++ .../v1_16_R1/EntityLivingSpawnNMSPacket.java | 48 ++++++++ .../nms/v1_16_R1/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_16_R1/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_16_R1/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_16_R1/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_16_R1/EntityTypeID.java | 14 +++ .../nms/v1_16_R1/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_16_R1/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_16_R1/VersionNMSManager.java | 71 +++++++++++- .../nms/v1_16_R1/VersionNMSPacket.java | 34 ++++++ .../nms/v1_16_R1/VersionNMSPacketList.java | 103 ++++++++++++++++++ .../nms/v1_16_R2/DataWatcherKey.java | 56 ++++++++++ .../v1_16_R2/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_16_R2/EntityDestroyNMSPacket.java | 30 +++++ .../v1_16_R2/EntityLivingSpawnNMSPacket.java | 48 ++++++++ .../nms/v1_16_R2/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_16_R2/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_16_R2/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_16_R2/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_16_R2/EntityTypeID.java | 14 +++ .../nms/v1_16_R2/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_16_R2/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_16_R2/VersionNMSManager.java | 71 +++++++++++- .../nms/v1_16_R2/VersionNMSPacket.java | 34 ++++++ .../nms/v1_16_R2/VersionNMSPacketList.java | 103 ++++++++++++++++++ .../nms/v1_16_R3/DataWatcherKey.java | 56 ++++++++++ .../v1_16_R3/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_16_R3/EntityDestroyNMSPacket.java | 30 +++++ .../v1_16_R3/EntityLivingSpawnNMSPacket.java | 48 ++++++++ .../nms/v1_16_R3/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_16_R3/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_16_R3/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_16_R3/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_16_R3/EntityTypeID.java | 14 +++ .../nms/v1_16_R3/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_16_R3/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_16_R3/VersionNMSManager.java | 71 +++++++++++- .../nms/v1_16_R3/VersionNMSPacket.java | 34 ++++++ .../nms/v1_16_R3/VersionNMSPacketList.java | 103 ++++++++++++++++++ .../v1_17_R1/DataWatcherPacketBuilder.java | 67 ++++++++++++ .../nms/v1_17_R1/EntityMetadataNMSPacket.java | 63 ++--------- .../nms/v1_17_R1/EntitySpawnNMSPacket.java | 6 +- .../nms/v1_8_R3/DataWatcherKey.java | 39 +++++++ .../nms/v1_8_R3/DataWatcherPacketBuilder.java | 54 +++++++++ .../nms/v1_8_R3/DataWatcherSerializer.java | 28 +++++ .../nms/v1_8_R3/EntityDestroyNMSPacket.java | 30 +++++ .../v1_8_R3/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_8_R3/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_8_R3/EntityMountNMSPacket.java | 32 ++++++ .../nms/v1_8_R3/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_8_R3/EntityTeleportNMSPacket.java | 42 +++++++ .../nms/v1_8_R3/EntityTypeID.java | 14 +++ .../nms/v1_8_R3/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_8_R3/PacketByteBuffer.java | 51 +++++++++ .../nms/v1_8_R3/VersionNMSManager.java | 84 +++++++++++++- .../nms/v1_8_R3/VersionNMSPacket.java | 34 ++++++ .../nms/v1_8_R3/VersionNMSPacketList.java | 102 +++++++++++++++++ .../nms/v1_9_R2/DataWatcherKey.java | 54 +++++++++ .../nms/v1_9_R2/DataWatcherPacketBuilder.java | 55 ++++++++++ .../nms/v1_9_R2/EntityDestroyNMSPacket.java | 30 +++++ .../v1_9_R2/EntityLivingSpawnNMSPacket.java | 103 ++++++++++++++++++ .../nms/v1_9_R2/EntityMetadataNMSPacket.java | 45 ++++++++ .../nms/v1_9_R2/EntityMountNMSPacket.java | 30 +++++ .../nms/v1_9_R2/EntitySpawnNMSPacket.java | 52 +++++++++ .../nms/v1_9_R2/EntityTeleportNMSPacket.java | 41 +++++++ .../nms/v1_9_R2/EntityTypeID.java | 14 +++ .../nms/v1_9_R2/InboundPacketHandler.java | 46 ++++++++ .../nms/v1_9_R2/PacketByteBuffer.java | 48 ++++++++ .../nms/v1_9_R2/VersionNMSManager.java | 87 ++++++++++++++- .../nms/v1_9_R2/VersionNMSPacket.java | 34 ++++++ .../nms/v1_9_R2/VersionNMSPacketList.java | 102 +++++++++++++++++ .../plugin/util/NMSVersion.java | 22 ++-- 159 files changed, 8007 insertions(+), 99 deletions(-) create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherKey.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityDestroyNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityMetadataNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityMountNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntitySpawnNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTeleportNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTypeID.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/InboundPacketHandler.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/PacketByteBuffer.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacket.java create mode 100644 nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacketList.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherKey.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityDestroyNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityMetadataNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityMountNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntitySpawnNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTeleportNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTypeID.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/InboundPacketHandler.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/PacketByteBuffer.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacket.java create mode 100644 nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacketList.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherKey.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityDestroyNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityMetadataNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityMountNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntitySpawnNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTeleportNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTypeID.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/InboundPacketHandler.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/PacketByteBuffer.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacket.java create mode 100644 nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacketList.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherKey.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherPacketBuilder.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityDestroyNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityMetadataNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityMountNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntitySpawnNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTeleportNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTypeID.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/InboundPacketHandler.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/PacketByteBuffer.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacket.java create mode 100644 nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacketList.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherKey.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityDestroyNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityMetadataNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityMountNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntitySpawnNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTeleportNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTypeID.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/InboundPacketHandler.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/PacketByteBuffer.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacket.java create mode 100644 nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacketList.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherKey.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityDestroyNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityMetadataNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityMountNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntitySpawnNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTeleportNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTypeID.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/InboundPacketHandler.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/PacketByteBuffer.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacket.java create mode 100644 nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacketList.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherKey.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityDestroyNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityMetadataNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityMountNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntitySpawnNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTeleportNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTypeID.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/InboundPacketHandler.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/PacketByteBuffer.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacket.java create mode 100644 nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacketList.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherKey.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherPacketBuilder.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityDestroyNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityMetadataNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityMountNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntitySpawnNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTeleportNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTypeID.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/InboundPacketHandler.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/PacketByteBuffer.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacket.java create mode 100644 nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacketList.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherKey.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherPacketBuilder.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityDestroyNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityMetadataNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityMountNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntitySpawnNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTeleportNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTypeID.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/InboundPacketHandler.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/PacketByteBuffer.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacket.java create mode 100644 nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacketList.java create mode 100644 nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/DataWatcherPacketBuilder.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherKey.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherPacketBuilder.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherSerializer.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityDestroyNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMetadataNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMountNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntitySpawnNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityTeleportNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityTypeID.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/InboundPacketHandler.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/PacketByteBuffer.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacket.java create mode 100644 nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacketList.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherKey.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherPacketBuilder.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityDestroyNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityLivingSpawnNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityMetadataNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityMountNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntitySpawnNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTeleportNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTypeID.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/InboundPacketHandler.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/PacketByteBuffer.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacket.java create mode 100644 nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacketList.java diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherKey.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherKey.java new file mode 100644 index 00000000..a8176647 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherKey.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import com.google.common.base.Optional; +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_10_R1.DataWatcherRegistry; +import net.minecraft.server.v1_10_R1.DataWatcherSerializer; +import net.minecraft.server.v1_10_R1.ItemStack; + +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.h; + private static final DataWatcherSerializer> ITEM_STACK_SERIALIZER = DataWatcherRegistry.f; + private static final DataWatcherSerializer STRING_SERIALIZER = DataWatcherRegistry.d; + + static final DataWatcherKey ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER); + static final DataWatcherKey> ITEM_STACK = new DataWatcherKey<>(6, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(12, 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_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherPacketBuilder.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..52c2380d --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import com.google.common.base.Optional; +import me.filoghost.fcommons.Strings; +import org.bukkit.craftbukkit.v1_10_R1.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, no base plate, marker + return this; + } + + DataWatcherPacketBuilder setCustomName(String customName) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300)); + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName)); + return this; + } + + DataWatcherPacketBuilder setItemStack(ItemStack itemStack) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, Optional.fromNullable(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_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityDestroyNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..f9d8f78b --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityLivingSpawnNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..5fbc7143 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_10_R1.DataWatcher; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketDataSerializer; +import net.minecraft.server.v1_10_R1.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "m"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityMetadataNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..15a4c0f1 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/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_10_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityMountNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityMountNMSPacket.java new file mode 100644 index 00000000..f7bc5bdc --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/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_10_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntitySpawnNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..4742d3c2 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTeleportNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..2a1d2677 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTypeID.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/EntityTypeID.java new file mode 100644 index 00000000..ea8a33d8 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/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_10_R1; + +class EntityTypeID { + + static final int ARMOR_STAND = 78; + static final int ITEM = 2; + static final int SLIME = 55; + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/InboundPacketHandler.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/InboundPacketHandler.java new file mode 100644 index 00000000..afaa9afa --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/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_10_R1; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_10_R1.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/PacketByteBuffer.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/PacketByteBuffer.java new file mode 100644 index 00000000..55ff26a6 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_10_R1.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSManager.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSManager.java index 98a1f19f..6e32ac5e 100644 --- a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSManager.java +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSManager.java @@ -5,32 +5,113 @@ */ package me.filoghost.holographicdisplays.nms.v1_10_R1; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_10_R1.Entity; +import net.minecraft.server.v1_10_R1.NetworkManager; +import net.minecraft.server.v1_10_R1.PlayerConnection; +import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer; import org.bukkit.entity.Player; +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(int.class, Entity.class, "entityCount"); + private final Supplier fallbackEntityIDGenerator; + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.fallbackEntityIDGenerator = new FallbackEntityIDGenerator(); + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + + // Force initialization of class to eventually throw exceptions early + DataWatcherKey.ENTITY_STATUS.getIndex(); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + testStaticFieldReadWrite(ENTITY_ID_COUNTER_FIELD); + + return () -> { + try { + int nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + ENTITY_ID_COUNTER_FIELD.setStatic(nmsEntityIDCounter + 1); + return nmsEntityIDCounter; + } catch (ReflectiveOperationException e) { + // Should not happen, access is tested beforehand + return fallbackEntityIDGenerator.get(); + } + }; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return fallbackEntityIDGenerator; + } + } + + private void testStaticFieldReadWrite(ReflectField field) throws ReflectiveOperationException { + T value = field.getStatic(); + field.setStatic(value); + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacket.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacket.java new file mode 100644 index 00000000..56295bab --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_10_R1.Packet; +import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacketList.java b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacketList.java new file mode 100644 index 00000000..91039c23 --- /dev/null +++ b/nms/v1_10_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_10_R1/VersionNMSPacketList.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_10_R1; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherKey.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherKey.java new file mode 100644 index 00000000..0b010d7a --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherKey.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_11_R1.DataWatcherRegistry; +import net.minecraft.server.v1_11_R1.DataWatcherSerializer; +import net.minecraft.server.v1_11_R1.ItemStack; + +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.h; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.f; + private static final DataWatcherSerializer STRING_SERIALIZER = DataWatcherRegistry.d; + + static final DataWatcherKey ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER); + static final DataWatcherKey ITEM_STACK = new DataWatcherKey<>(6, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(12, 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_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherPacketBuilder.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..bf6caf7c --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.fcommons.Strings; +import org.bukkit.craftbukkit.v1_11_R1.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, no base plate, marker + return this; + } + + DataWatcherPacketBuilder setCustomName(String customName) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300)); + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName)); + return this; + } + + 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_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityDestroyNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..f9e42601 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityLivingSpawnNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..78f3b893 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_11_R1.DataWatcher; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketDataSerializer; +import net.minecraft.server.v1_11_R1.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "m"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityMetadataNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..3938aaa8 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/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_11_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityMountNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityMountNMSPacket.java new file mode 100644 index 00000000..bf53ec15 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/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_11_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntitySpawnNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..cc3b1395 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTeleportNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..e6bc1e3f --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTypeID.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/EntityTypeID.java new file mode 100644 index 00000000..d17ad6f3 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/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_11_R1; + +class EntityTypeID { + + static final int ARMOR_STAND = 78; + static final int ITEM = 2; + static final int SLIME = 55; + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/InboundPacketHandler.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/InboundPacketHandler.java new file mode 100644 index 00000000..d4aed901 --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/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_11_R1; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_11_R1.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/PacketByteBuffer.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/PacketByteBuffer.java new file mode 100644 index 00000000..d386229b --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_11_R1.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSManager.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSManager.java index 4b2e8e00..4191368d 100644 --- a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSManager.java +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSManager.java @@ -5,32 +5,113 @@ */ package me.filoghost.holographicdisplays.nms.v1_11_R1; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_11_R1.Entity; +import net.minecraft.server.v1_11_R1.NetworkManager; +import net.minecraft.server.v1_11_R1.PlayerConnection; +import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer; import org.bukkit.entity.Player; +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(int.class, Entity.class, "entityCount"); + private final Supplier fallbackEntityIDGenerator; + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.fallbackEntityIDGenerator = new FallbackEntityIDGenerator(); + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + + // Force initialization of class to eventually throw exceptions early + DataWatcherKey.ENTITY_STATUS.getIndex(); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + testStaticFieldReadWrite(ENTITY_ID_COUNTER_FIELD); + + return () -> { + try { + int nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + ENTITY_ID_COUNTER_FIELD.setStatic(nmsEntityIDCounter + 1); + return nmsEntityIDCounter; + } catch (ReflectiveOperationException e) { + // Should not happen, access is tested beforehand + return fallbackEntityIDGenerator.get(); + } + }; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return fallbackEntityIDGenerator; + } + } + + private void testStaticFieldReadWrite(ReflectField field) throws ReflectiveOperationException { + T value = field.getStatic(); + field.setStatic(value); + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacket.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacket.java new file mode 100644 index 00000000..820e717e --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_11_R1.Packet; +import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacketList.java b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacketList.java new file mode 100644 index 00000000..d740fb6a --- /dev/null +++ b/nms/v1_11_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_11_R1/VersionNMSPacketList.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_11_R1; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherKey.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherKey.java new file mode 100644 index 00000000..f9d35658 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherKey.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_12_R1.DataWatcherRegistry; +import net.minecraft.server.v1_12_R1.DataWatcherSerializer; +import net.minecraft.server.v1_12_R1.ItemStack; + +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.h; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.f; + private static final DataWatcherSerializer STRING_SERIALIZER = DataWatcherRegistry.d; + + static final DataWatcherKey ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER); + static final DataWatcherKey ITEM_STACK = new DataWatcherKey<>(6, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(12, 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_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherPacketBuilder.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..e9d4f75b --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.fcommons.Strings; +import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, no base plate, marker + return this; + } + + DataWatcherPacketBuilder setCustomName(String customName) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300)); + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName)); + return this; + } + + 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_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityDestroyNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..47f84e3f --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityLivingSpawnNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..882deb73 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_12_R1.DataWatcher; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketDataSerializer; +import net.minecraft.server.v1_12_R1.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "m"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityMetadataNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..19ee86f0 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/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_12_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityMountNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityMountNMSPacket.java new file mode 100644 index 00000000..bdbeaf7e --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/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_12_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntitySpawnNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..a97ab371 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTeleportNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..916b122a --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTypeID.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/EntityTypeID.java new file mode 100644 index 00000000..aab29e19 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/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_12_R1; + +class EntityTypeID { + + static final int ARMOR_STAND = 78; + static final int ITEM = 2; + static final int SLIME = 55; + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/InboundPacketHandler.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/InboundPacketHandler.java new file mode 100644 index 00000000..1b1cf406 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/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_12_R1; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_12_R1.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/PacketByteBuffer.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/PacketByteBuffer.java new file mode 100644 index 00000000..892c4de8 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_12_R1.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSManager.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSManager.java index a9fd6a08..43073512 100644 --- a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSManager.java +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSManager.java @@ -5,32 +5,113 @@ */ package me.filoghost.holographicdisplays.nms.v1_12_R1; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_12_R1.Entity; +import net.minecraft.server.v1_12_R1.NetworkManager; +import net.minecraft.server.v1_12_R1.PlayerConnection; +import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; import org.bukkit.entity.Player; +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(int.class, Entity.class, "entityCount"); + private final Supplier fallbackEntityIDGenerator; + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.fallbackEntityIDGenerator = new FallbackEntityIDGenerator(); + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + + // Force initialization of class to eventually throw exceptions early + DataWatcherKey.ENTITY_STATUS.getIndex(); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + testStaticFieldReadWrite(ENTITY_ID_COUNTER_FIELD); + + return () -> { + try { + int nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + ENTITY_ID_COUNTER_FIELD.setStatic(nmsEntityIDCounter + 1); + return nmsEntityIDCounter; + } catch (ReflectiveOperationException e) { + // Should not happen, access is tested beforehand + return fallbackEntityIDGenerator.get(); + } + }; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return fallbackEntityIDGenerator; + } + } + + private void testStaticFieldReadWrite(ReflectField field) throws ReflectiveOperationException { + T value = field.getStatic(); + field.setStatic(value); + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacket.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacket.java new file mode 100644 index 00000000..f47fb9c0 --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_12_R1.Packet; +import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacketList.java b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacketList.java new file mode 100644 index 00000000..1b2c2daa --- /dev/null +++ b/nms/v1_12_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_12_R1/VersionNMSPacketList.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_12_R1; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherKey.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherKey.java new file mode 100644 index 00000000..d74b7b58 --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/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_13_R2; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_13_R2.DataWatcherRegistry; +import net.minecraft.server.v1_13_R2.DataWatcherSerializer; +import net.minecraft.server.v1_13_R2.IChatBaseComponent; +import net.minecraft.server.v1_13_R2.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.i; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.g; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f; + + 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<>(6, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(12, 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_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherPacketBuilder.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..6dcc213b --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.fcommons.Strings; +import net.minecraft.server.v1_13_R2.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_13_R2.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + if (!Strings.isEmpty(customName)) { + return Optional.of(CraftChatMessage.fromString(customName, false)[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_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityDestroyNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..59107e7c --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityLivingSpawnNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..8adcdd05 --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_13_R2.DataWatcher; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketDataSerializer; +import net.minecraft.server.v1_13_R2.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "m"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityMetadataNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..ce493e94 --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/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_13_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityMountNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityMountNMSPacket.java new file mode 100644 index 00000000..3e5282e3 --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/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_13_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntitySpawnNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..a287f1fb --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTeleportNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..534d1c2c --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTypeID.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/EntityTypeID.java new file mode 100644 index 00000000..7c9a65a4 --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/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_13_R2; + +class EntityTypeID { + + static final int ARMOR_STAND = 78; + static final int ITEM = 2; + static final int SLIME = 64; + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/InboundPacketHandler.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/InboundPacketHandler.java new file mode 100644 index 00000000..09bd6af3 --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/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_13_R2; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_13_R2.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/PacketByteBuffer.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/PacketByteBuffer.java new file mode 100644 index 00000000..be5ef4ae --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_13_R2.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSManager.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSManager.java index 2db42578..cfb0d9e7 100644 --- a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSManager.java +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSManager.java @@ -5,32 +5,113 @@ */ package me.filoghost.holographicdisplays.nms.v1_13_R2; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_13_R2.Entity; +import net.minecraft.server.v1_13_R2.NetworkManager; +import net.minecraft.server.v1_13_R2.PlayerConnection; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; import org.bukkit.entity.Player; +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(int.class, Entity.class, "entityCount"); + private final Supplier fallbackEntityIDGenerator; + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.fallbackEntityIDGenerator = new FallbackEntityIDGenerator(); + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + + // Force initialization of class to eventually throw exceptions early + DataWatcherKey.ENTITY_STATUS.getIndex(); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + testStaticFieldReadWrite(ENTITY_ID_COUNTER_FIELD); + + return () -> { + try { + int nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + ENTITY_ID_COUNTER_FIELD.setStatic(nmsEntityIDCounter + 1); + return nmsEntityIDCounter; + } catch (ReflectiveOperationException e) { + // Should not happen, access is tested beforehand + return fallbackEntityIDGenerator.get(); + } + }; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return fallbackEntityIDGenerator; + } + } + + private void testStaticFieldReadWrite(ReflectField field) throws ReflectiveOperationException { + T value = field.getStatic(); + field.setStatic(value); + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacket.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacket.java new file mode 100644 index 00000000..7e22face --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_13_R2.Packet; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacketList.java b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacketList.java new file mode 100644 index 00000000..75e6daaf --- /dev/null +++ b/nms/v1_13_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_13_R2/VersionNMSPacketList.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_13_R2; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherKey.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherKey.java new file mode 100644 index 00000000..4be99358 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/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_14_R1; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_14_R1.DataWatcherRegistry; +import net.minecraft.server.v1_14_R1.DataWatcherSerializer; +import net.minecraft.server.v1_14_R1.IChatBaseComponent; +import net.minecraft.server.v1_14_R1.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.i; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.g; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f; + + 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<>(7, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(13, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(14, 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_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherPacketBuilder.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..f77cb382 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.fcommons.Strings; +import net.minecraft.server.v1_14_R1.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_14_R1.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + if (!Strings.isEmpty(customName)) { + return Optional.of(CraftChatMessage.fromString(customName, false)[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_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityDestroyNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..38a6f241 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityLivingSpawnNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..824c887d --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_14_R1.DataWatcher; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketDataSerializer; +import net.minecraft.server.v1_14_R1.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "m"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityMetadataNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..cc4b60ab --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/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_14_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityMountNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityMountNMSPacket.java new file mode 100644 index 00000000..08d09a54 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/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_14_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntitySpawnNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..52c20450 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTeleportNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..acb2d358 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTypeID.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/EntityTypeID.java new file mode 100644 index 00000000..98a9697e --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/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_14_R1; + +class EntityTypeID { + + static final int ARMOR_STAND = 1; + static final int ITEM = 34; + static final int SLIME = 67; + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/InboundPacketHandler.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/InboundPacketHandler.java new file mode 100644 index 00000000..ea47e17d --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/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_14_R1; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_14_R1.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/PacketByteBuffer.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/PacketByteBuffer.java new file mode 100644 index 00000000..fafc025f --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_14_R1.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSManager.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSManager.java index 1ee2c2f6..9459bddf 100644 --- a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSManager.java +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSManager.java @@ -5,32 +5,97 @@ */ package me.filoghost.holographicdisplays.nms.v1_14_R1; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_14_R1.Entity; +import net.minecraft.server.v1_14_R1.NetworkManager; +import net.minecraft.server.v1_14_R1.PlayerConnection; +import org.bukkit.craftbukkit.v1_14_R1.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, "entityCount"); + 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(); + } + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacket.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacket.java new file mode 100644 index 00000000..a70f37ec --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_14_R1.Packet; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacketList.java b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacketList.java new file mode 100644 index 00000000..ebc9d760 --- /dev/null +++ b/nms/v1_14_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_14_R1/VersionNMSPacketList.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_14_R1; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityLivingSpawnNMSPacket.builder( + entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherKey.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherKey.java new file mode 100644 index 00000000..36a63140 --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/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_15_R1; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_15_R1.DataWatcherRegistry; +import net.minecraft.server.v1_15_R1.DataWatcherSerializer; +import net.minecraft.server.v1_15_R1.IChatBaseComponent; +import net.minecraft.server.v1_15_R1.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.i; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.g; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f; + + 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<>(7, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(15, 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_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherPacketBuilder.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..a451d27e --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.fcommons.Strings; +import net.minecraft.server.v1_15_R1.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_15_R1.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + if (!Strings.isEmpty(customName)) { + return Optional.of(CraftChatMessage.fromString(customName, false)[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_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityDestroyNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..ea5e1901 --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityLivingSpawnNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..a79a646a --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityLivingSpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + this.rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityMetadataNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..52e5c5be --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/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_15_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityMountNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityMountNMSPacket.java new file mode 100644 index 00000000..4a443482 --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/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_15_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntitySpawnNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..5725153d --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTeleportNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..6f6816ae --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTypeID.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/EntityTypeID.java new file mode 100644 index 00000000..8104b289 --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/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_15_R1; + +class EntityTypeID { + + static final int ARMOR_STAND = 1; + static final int ITEM = 35; + static final int SLIME = 68; + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/InboundPacketHandler.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/InboundPacketHandler.java new file mode 100644 index 00000000..e9f4d672 --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/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_15_R1; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_15_R1.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/PacketByteBuffer.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/PacketByteBuffer.java new file mode 100644 index 00000000..9ff1b3ae --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_15_R1.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSManager.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSManager.java index 38b64e5d..308b3435 100644 --- a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSManager.java +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSManager.java @@ -5,32 +5,97 @@ */ package me.filoghost.holographicdisplays.nms.v1_15_R1; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_15_R1.Entity; +import net.minecraft.server.v1_15_R1.NetworkManager; +import net.minecraft.server.v1_15_R1.PlayerConnection; +import org.bukkit.craftbukkit.v1_15_R1.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, "entityCount"); + 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(); + } + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacket.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacket.java new file mode 100644 index 00000000..4d9b4e37 --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_15_R1.Packet; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacketList.java b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacketList.java new file mode 100644 index 00000000..ddfada1b --- /dev/null +++ b/nms/v1_15_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_15_R1/VersionNMSPacketList.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_15_R1; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherKey.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherKey.java new file mode 100644 index 00000000..6b5b49fb --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/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_16_R1; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_16_R1.DataWatcherRegistry; +import net.minecraft.server.v1_16_R1.DataWatcherSerializer; +import net.minecraft.server.v1_16_R1.IChatBaseComponent; +import net.minecraft.server.v1_16_R1.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.i; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.g; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f; + + 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<>(7, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(15, 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_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherPacketBuilder.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..6309d065 --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.fcommons.Strings; +import net.minecraft.server.v1_16_R1.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R1.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + if (!Strings.isEmpty(customName)) { + return Optional.of(CraftChatMessage.fromString(customName, false)[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_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityDestroyNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..953872db --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityLivingSpawnNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..0a32384b --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityLivingSpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + this.rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityMetadataNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..2c87bac1 --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/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_16_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityMountNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityMountNMSPacket.java new file mode 100644 index 00000000..50b0db19 --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/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_16_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntitySpawnNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..48714af6 --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTeleportNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..79ea4838 --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTypeID.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/EntityTypeID.java new file mode 100644 index 00000000..5e475d7b --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/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_16_R1; + +class EntityTypeID { + + static final int ARMOR_STAND = 1; + static final int ITEM = 37; + static final int SLIME = 75; + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/InboundPacketHandler.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/InboundPacketHandler.java new file mode 100644 index 00000000..f6eed9dd --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/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_16_R1; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_16_R1.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/PacketByteBuffer.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/PacketByteBuffer.java new file mode 100644 index 00000000..c29e9e7d --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_16_R1.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSManager.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSManager.java index b25769ed..0253aac5 100644 --- a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSManager.java +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSManager.java @@ -5,32 +5,97 @@ */ package me.filoghost.holographicdisplays.nms.v1_16_R1; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_16_R1.Entity; +import net.minecraft.server.v1_16_R1.NetworkManager; +import net.minecraft.server.v1_16_R1.PlayerConnection; +import org.bukkit.craftbukkit.v1_16_R1.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, "entityCount"); + 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(); + } + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacket.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacket.java new file mode 100644 index 00000000..712e3a17 --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_16_R1.Packet; +import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacketList.java b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacketList.java new file mode 100644 index 00000000..f4e6370d --- /dev/null +++ b/nms/v1_16_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R1/VersionNMSPacketList.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R1; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherKey.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherKey.java new file mode 100644 index 00000000..60368f84 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/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_16_R2; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_16_R2.DataWatcherRegistry; +import net.minecraft.server.v1_16_R2.DataWatcherSerializer; +import net.minecraft.server.v1_16_R2.IChatBaseComponent; +import net.minecraft.server.v1_16_R2.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.i; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.g; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f; + + 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<>(7, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(15, 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_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherPacketBuilder.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..7d4c73b6 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.fcommons.Strings; +import net.minecraft.server.v1_16_R2.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R2.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + if (!Strings.isEmpty(customName)) { + return Optional.of(CraftChatMessage.fromString(customName, false)[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_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityDestroyNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..7db5d9ec --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityLivingSpawnNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..3dba25aa --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityLivingSpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + this.rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityMetadataNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..6c011d83 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/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_16_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityMountNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityMountNMSPacket.java new file mode 100644 index 00000000..e0d677c6 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/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_16_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntitySpawnNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..a6a11c27 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTeleportNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..427d6c08 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTypeID.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/EntityTypeID.java new file mode 100644 index 00000000..881ea3f4 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/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_16_R2; + +class EntityTypeID { + + static final int ARMOR_STAND = 1; + static final int ITEM = 37; + static final int SLIME = 75; + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/InboundPacketHandler.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/InboundPacketHandler.java new file mode 100644 index 00000000..5c8d04a1 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/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_16_R2; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_16_R2.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/PacketByteBuffer.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/PacketByteBuffer.java new file mode 100644 index 00000000..4044f88b --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_16_R2.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSManager.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSManager.java index 60a3d01f..4234c0a8 100644 --- a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSManager.java +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSManager.java @@ -5,32 +5,97 @@ */ package me.filoghost.holographicdisplays.nms.v1_16_R2; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_16_R2.Entity; +import net.minecraft.server.v1_16_R2.NetworkManager; +import net.minecraft.server.v1_16_R2.PlayerConnection; +import org.bukkit.craftbukkit.v1_16_R2.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, "entityCount"); + 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(); + } + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacket.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacket.java new file mode 100644 index 00000000..df21fe31 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_16_R2.Packet; +import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacketList.java b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacketList.java new file mode 100644 index 00000000..5ef98842 --- /dev/null +++ b/nms/v1_16_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R2/VersionNMSPacketList.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R2; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherKey.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherKey.java new file mode 100644 index 00000000..e2cd7c39 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_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_16_R3; + +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_16_R3.DataWatcherRegistry; +import net.minecraft.server.v1_16_R3.DataWatcherSerializer; +import net.minecraft.server.v1_16_R3.IChatBaseComponent; +import net.minecraft.server.v1_16_R3.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.i; + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = DataWatcherRegistry.g; + private static final DataWatcherSerializer> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f; + + 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<>(7, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(15, 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_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherPacketBuilder.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..98300791 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.fcommons.Strings; +import net.minecraft.server.v1_16_R3.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R3.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + 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_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityDestroyNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..12a3cb42 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityLivingSpawnNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..8691a864 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityLivingSpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + this.rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityMetadataNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..e62556d9 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_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_16_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityMountNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityMountNMSPacket.java new file mode 100644 index 00000000..90d8ee25 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_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_16_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntitySpawnNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..91e495cf --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeVarInt(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTeleportNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..15cffe1c --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_16_R3.Packet; +import net.minecraft.server.v1_16_R3.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTypeID.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/EntityTypeID.java new file mode 100644 index 00000000..91293b2f --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_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_16_R3; + +class EntityTypeID { + + static final int ARMOR_STAND = 1; + static final int ITEM = 37; + static final int SLIME = 75; + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/InboundPacketHandler.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/InboundPacketHandler.java new file mode 100644 index 00000000..e473b9cd --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_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_16_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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_16_R3.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/PacketByteBuffer.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/PacketByteBuffer.java new file mode 100644 index 00000000..e4107bb3 --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_16_R3.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSManager.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSManager.java index d018d0f7..e8efbd3b 100644 --- a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSManager.java +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSManager.java @@ -5,32 +5,97 @@ */ package me.filoghost.holographicdisplays.nms.v1_16_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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_16_R3.Entity; +import net.minecraft.server.v1_16_R3.NetworkManager; +import net.minecraft.server.v1_16_R3.PlayerConnection; +import org.bukkit.craftbukkit.v1_16_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, "entityCount"); + 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(); + } + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacket.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacket.java new file mode 100644 index 00000000..f9a71ede --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_16_R3.Packet; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacketList.java b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacketList.java new file mode 100644 index 00000000..7f4ee1ba --- /dev/null +++ b/nms/v1_16_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_16_R3/VersionNMSPacketList.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_16_R3; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityLivingSpawnNMSPacket(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/DataWatcherPacketBuilder.java b/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..6baf351b --- /dev/null +++ b/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/DataWatcherPacketBuilder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_17_R1; + +import me.filoghost.fcommons.Strings; +import net.minecraft.network.chat.IChatBaseComponent; +import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_17_R1.util.CraftChatMessage; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, 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, 300); + 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_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntityMetadataNMSPacket.java b/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntityMetadataNMSPacket.java index da211c19..d50c5b13 100644 --- a/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntityMetadataNMSPacket.java +++ b/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntityMetadataNMSPacket.java @@ -5,16 +5,9 @@ */ package me.filoghost.holographicdisplays.nms.v1_17_R1; -import me.filoghost.fcommons.Strings; import me.filoghost.holographicdisplays.common.nms.EntityID; -import net.minecraft.network.chat.IChatBaseComponent; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata; -import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; -import org.bukkit.craftbukkit.v1_17_R1.util.CraftChatMessage; -import org.bukkit.inventory.ItemStack; - -import java.util.Optional; class EntityMetadataNMSPacket extends VersionNMSPacket { @@ -29,59 +22,21 @@ class EntityMetadataNMSPacket extends VersionNMSPacket { return rawPacket; } - public static Builder builder(EntityID entityID) { - return new Builder(entityID); + public static DataWatcherPacketBuilder builder(EntityID entityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + packetByteBuffer.writeVarInt(entityID.getNumericID()); + return new Builder(packetByteBuffer); } - static class Builder { + private static class Builder extends DataWatcherPacketBuilder { - private final PacketByteBuffer packetByteBuffer; - - private Builder(EntityID entityID) { - this.packetByteBuffer = PacketByteBuffer.get(); - - packetByteBuffer.writeVarInt(entityID.getNumericID()); + private Builder(PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); } - Builder setInvisible() { - packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ENTITY_STATUS, (byte) 0x20); // Invisible - return this; - } - - Builder setArmorStandMarker() { - setInvisible(); - packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ARMOR_STAND_STATUS, (byte) (0x01 | 0x08 | 0x10)); // Small, no base plate, marker - return this; - } - - Builder 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, 300); - if (!Strings.isEmpty(customName)) { - return Optional.of(CraftChatMessage.fromString(customName, false, true)[0]); - } else { - return Optional.empty(); - } - } - - Builder setItemStack(ItemStack itemStack) { - packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, CraftItemStack.asNMSCopy(itemStack)); - return this; - } - - Builder setSlimeSmall() { - packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.SLIME_SIZE, 1); - return this; - } - - EntityMetadataNMSPacket build() { - packetByteBuffer.writeDataWatcherEntriesEnd(); + @Override + EntityMetadataNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { return new EntityMetadataNMSPacket(packetByteBuffer); } diff --git a/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntitySpawnNMSPacket.java b/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntitySpawnNMSPacket.java index 95e4c5bd..a00d7705 100644 --- a/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntitySpawnNMSPacket.java +++ b/nms/v1_17_r1/src/main/java/me/filoghost/holographicdisplays/nms/v1_17_R1/EntitySpawnNMSPacket.java @@ -30,7 +30,11 @@ class EntitySpawnNMSPacket extends VersionNMSPacket { packetByteBuffer.writeByte(0); // Object data - packetByteBuffer.writeInt(0); + 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); diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherKey.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherKey.java new file mode 100644 index 00000000..623e5432 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherKey.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import net.minecraft.server.v1_8_R3.ItemStack; + +class DataWatcherKey { + + private static final DataWatcherSerializer BYTE_SERIALIZER = new DataWatcherSerializer<>(0, PacketByteBuffer::writeByte); + private static final DataWatcherSerializer STRING_SERIALIZER = new DataWatcherSerializer<>(4, PacketByteBuffer::writeString); + private static final DataWatcherSerializer ITEM_STACK_SERIALIZER = new DataWatcherSerializer<>(5, PacketByteBuffer::writeItemStack); + + static final DataWatcherKey ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BYTE_SERIALIZER); + static final DataWatcherKey ITEM_STACK = new DataWatcherKey<>(10, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(10, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(16, BYTE_SERIALIZER); + + private final int index; + private final DataWatcherSerializer serializer; + + private DataWatcherKey(int index, DataWatcherSerializer serializer) { + this.index = index; + this.serializer = serializer; + } + + int getIndex() { + return index; + } + + DataWatcherSerializer getSerializer() { + return serializer; + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherPacketBuilder.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..263327a0 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherPacketBuilder.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.fcommons.Strings; +import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, no base plate, marker + return this; + } + + DataWatcherPacketBuilder setCustomName(String customName) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300)); + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, Strings.isEmpty(customName) ? (byte) 0 : (byte) 1); + return this; + } + + DataWatcherPacketBuilder setItemStack(ItemStack itemStack) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, CraftItemStack.asNMSCopy(itemStack)); + return this; + } + + DataWatcherPacketBuilder setSlimeSmall() { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.SLIME_SIZE, (byte) 1); + return this; + } + + T build() { + packetByteBuffer.writeDataWatcherEntriesEnd(); + return createPacket(packetByteBuffer); + } + + abstract T createPacket(PacketByteBuffer packetByteBuffer); + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherSerializer.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherSerializer.java new file mode 100644 index 00000000..7501c69e --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/DataWatcherSerializer.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import java.util.function.BiConsumer; + +class DataWatcherSerializer { + + private final int typeID; + private final BiConsumer serializeFunction; + + DataWatcherSerializer(int typeID, BiConsumer serializeFunction) { + this.typeID = typeID; + this.serializeFunction = serializeFunction; + } + + int getTypeID() { + return typeID; + } + + void write(PacketByteBuffer packetByteBuffer, T value) { + serializeFunction.accept(packetByteBuffer, value); + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityDestroyNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..db84495c --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityLivingSpawnNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..7ba118b5 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_8_R3.DataWatcher; +import net.minecraft.server.v1_8_R3.MathHelper; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketDataSerializer; +import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "l"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeInt(MathHelper.floor(positionX * 32)); + packetByteBuffer.writeInt(MathHelper.floor(positionY * 32)); + packetByteBuffer.writeInt(MathHelper.floor(positionZ * 32)); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMetadataNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..33e03750 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_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_8_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMountNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMountNMSPacket.java new file mode 100644 index 00000000..d8b6614e --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityMountNMSPacket.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutAttachEntity; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeInt(passengerEntityID.getNumericID()); + packetByteBuffer.writeInt(vehicleEntityID.getNumericID()); + + packetByteBuffer.writeByte(0); // Leash + + this.rawPacket = writeData(new PacketPlayOutAttachEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntitySpawnNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..096f7288 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_8_R3.MathHelper; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeInt(MathHelper.floor(positionX * 32)); + packetByteBuffer.writeInt(MathHelper.floor(positionY * 32)); + packetByteBuffer.writeInt(MathHelper.floor(positionZ * 32)); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityTeleportNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..ffd16eb5 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_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_8_R3; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_8_R3.MathHelper; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeInt(MathHelper.floor(positionX * 32)); + packetByteBuffer.writeInt(MathHelper.floor(positionY * 32)); + packetByteBuffer.writeInt(MathHelper.floor(positionZ * 32)); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityTypeID.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/EntityTypeID.java new file mode 100644 index 00000000..1ce39b43 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_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_8_R3; + +class EntityTypeID { + + static final int ARMOR_STAND = 78; + static final int ITEM = 2; + static final int SLIME = 55; + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/InboundPacketHandler.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/InboundPacketHandler.java new file mode 100644 index 00000000..715191e0 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_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_8_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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_8_R3.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/PacketByteBuffer.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/PacketByteBuffer.java new file mode 100644 index 00000000..6b5d8845 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/PacketByteBuffer.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_8_R3.ItemStack; +import net.minecraft.server.v1_8_R3.PacketDataSerializer; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeByte(byte b) { + super.writeByte(b); + } + + void writeVarInt(int i) { + super.b(i); + } + + void writeString(String s) { + super.a(s); + } + + void writeItemStack(ItemStack itemStack) { + super.a(itemStack); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + int typeIDAndIndex = (key.getSerializer().getTypeID() << 5 | key.getIndex() & 0x1F) & 0xFF; + writeByte(typeIDAndIndex); + key.getSerializer().write(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(0x7F); + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSManager.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSManager.java index a2d1f2bf..adaf475e 100644 --- a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSManager.java +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSManager.java @@ -5,32 +5,110 @@ */ package me.filoghost.holographicdisplays.nms.v1_8_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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_8_R3.Entity; +import net.minecraft.server.v1_8_R3.NetworkManager; +import net.minecraft.server.v1_8_R3.PlayerConnection; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.Player; +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(int.class, Entity.class, "entityCount"); + private final Supplier fallbackEntityIDGenerator; + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.fallbackEntityIDGenerator = new FallbackEntityIDGenerator(); + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + testStaticFieldReadWrite(ENTITY_ID_COUNTER_FIELD); + + return () -> { + try { + int nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + ENTITY_ID_COUNTER_FIELD.setStatic(nmsEntityIDCounter + 1); + return nmsEntityIDCounter; + } catch (ReflectiveOperationException e) { + // Should not happen, access is tested beforehand + return fallbackEntityIDGenerator.get(); + } + }; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return fallbackEntityIDGenerator; + } + } + + private void testStaticFieldReadWrite(ReflectField field) throws ReflectiveOperationException { + T value = field.getStatic(); + field.setStatic(value); + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacket.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacket.java new file mode 100644 index 00000000..e7880fa3 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_8_R3.Packet; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacketList.java b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacketList.java new file mode 100644 index 00000000..791d1c43 --- /dev/null +++ b/nms/v1_8_r3/src/main/java/me/filoghost/holographicdisplays/nms/v1_8_R3/VersionNMSPacketList.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_8_R3; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherKey.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherKey.java new file mode 100644 index 00000000..6a12881d --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherKey.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import com.google.common.base.Optional; +import io.netty.handler.codec.EncoderException; +import net.minecraft.server.v1_9_R2.DataWatcherRegistry; +import net.minecraft.server.v1_9_R2.DataWatcherSerializer; +import net.minecraft.server.v1_9_R2.ItemStack; + +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.h; + private static final DataWatcherSerializer> ITEM_STACK_SERIALIZER = DataWatcherRegistry.f; + private static final DataWatcherSerializer STRING_SERIALIZER = DataWatcherRegistry.d; + + static final DataWatcherKey ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER); + static final DataWatcherKey CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER); + static final DataWatcherKey> ITEM_STACK = new DataWatcherKey<>(5, ITEM_STACK_SERIALIZER); + static final DataWatcherKey ARMOR_STAND_STATUS = new DataWatcherKey<>(10, BYTE_SERIALIZER); + static final DataWatcherKey SLIME_SIZE = new DataWatcherKey<>(11, 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_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherPacketBuilder.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherPacketBuilder.java new file mode 100644 index 00000000..6b04aec4 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/DataWatcherPacketBuilder.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import com.google.common.base.Optional; +import me.filoghost.fcommons.Strings; +import org.bukkit.craftbukkit.v1_9_R2.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +abstract class DataWatcherPacketBuilder { + + 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 | 0x08 | 0x10)); // Small, no base plate, marker + return this; + } + + DataWatcherPacketBuilder setCustomName(String customName) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300)); + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName)); + return this; + } + + DataWatcherPacketBuilder setItemStack(ItemStack itemStack) { + packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, Optional.fromNullable(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_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityDestroyNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityDestroyNMSPacket.java new file mode 100644 index 00000000..8b66c7b0 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityDestroyNMSPacket.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketPlayOutEntityDestroy; + +class EntityDestroyNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityDestroyNMSPacket(EntityID... entityIDs) { + int[] entityIDsArray = new int[entityIDs.length]; + for (int i = 0; i < entityIDs.length; i++) { + entityIDsArray[i] = entityIDs[i].getNumericID(); + } + + this.rawPacket = new PacketPlayOutEntityDestroy(entityIDsArray); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityLivingSpawnNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityLivingSpawnNMSPacket.java new file mode 100644 index 00000000..e4e84073 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityLivingSpawnNMSPacket.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import me.filoghost.fcommons.reflection.ReflectField; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_9_R2.DataWatcher; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketDataSerializer; +import net.minecraft.server.v1_9_R2.PacketPlayOutSpawnEntityLiving; + +class EntityLivingSpawnNMSPacket extends VersionNMSPacket { + + private static final ReflectField DATA_WATCHER_FIELD = + ReflectField.lookup(DataWatcher.class, PacketPlayOutSpawnEntityLiving.class, "m"); + + private final Packet rawPacket; + + private EntityLivingSpawnNMSPacket(Packet rawPacket, DataWatcher dataWatcher) { + this.rawPacket = rawPacket; + try { + DATA_WATCHER_FIELD.set(rawPacket, dataWatcher); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + + public static DataWatcherPacketBuilder builder( + EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // Head rotation + packetByteBuffer.writeByte(0); + + // Velocity + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + packetByteBuffer.writeShort(0); + + // Entries are deserialized and saved into a field which is not used during the re-serialization, set as empty + packetByteBuffer.writeDataWatcherEntriesEnd(); + + Packet rawPacket = writeData(new PacketPlayOutSpawnEntityLiving(), packetByteBuffer); + return new Builder(rawPacket, PacketByteBuffer.get()); + } + + + private static class Builder extends DataWatcherPacketBuilder { + + private final Packet rawPacket; + + private Builder(Packet rawPacket, PacketByteBuffer packetByteBuffer) { + super(packetByteBuffer); + this.rawPacket = rawPacket; + } + + @Override + EntityLivingSpawnNMSPacket createPacket(PacketByteBuffer packetByteBuffer) { + return new EntityLivingSpawnNMSPacket(rawPacket, new SerializedDataWatcher(packetByteBuffer)); + } + + } + + + private static class SerializedDataWatcher extends DataWatcher { + + private final byte[] serializedValue; + + SerializedDataWatcher(PacketByteBuffer dataByteBuffer) { + super(null); + serializedValue = new byte[dataByteBuffer.readableBytes()]; + dataByteBuffer.readBytes(serializedValue); + } + + @Override + public void a(PacketDataSerializer packetDataSerializer) { + packetDataSerializer.writeBytes(serializedValue); + } + + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityMetadataNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityMetadataNMSPacket.java new file mode 100644 index 00000000..8d7fe9f4 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/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_9_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketPlayOutEntityMetadata; + +class EntityMetadataNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) { + this.rawPacket = writeData(new PacketPlayOutEntityMetadata(), packetByteBuffer); + } + + @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_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityMountNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityMountNMSPacket.java new file mode 100644 index 00000000..33a74973 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/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_9_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketPlayOutMount; + +class EntityMountNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID()); + packetByteBuffer.writeIntArray(passengerEntityID.getNumericID()); + + this.rawPacket = writeData(new PacketPlayOutMount(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntitySpawnNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntitySpawnNMSPacket.java new file mode 100644 index 00000000..f5abfc5e --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntitySpawnNMSPacket.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketPlayOutSpawnEntity; + +class EntitySpawnNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + packetByteBuffer.writeUUID(entityID.getUUID()); + packetByteBuffer.writeByte(entityTypeID); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // 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 = writeData(new PacketPlayOutSpawnEntity(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTeleportNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTeleportNMSPacket.java new file mode 100644 index 00000000..e4fa3ed9 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTeleportNMSPacket.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import me.filoghost.holographicdisplays.common.nms.EntityID; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketPlayOutEntityTeleport; + +class EntityTeleportNMSPacket extends VersionNMSPacket { + + private final Packet rawPacket; + + EntityTeleportNMSPacket(EntityID entityID, double positionX, double positionY, double positionZ) { + PacketByteBuffer packetByteBuffer = PacketByteBuffer.get(); + + packetByteBuffer.writeVarInt(entityID.getNumericID()); + + // Position + packetByteBuffer.writeDouble(positionX); + packetByteBuffer.writeDouble(positionY); + packetByteBuffer.writeDouble(positionZ); + + // Rotation + packetByteBuffer.writeByte(0); + packetByteBuffer.writeByte(0); + + // On ground + packetByteBuffer.writeBoolean(false); + + this.rawPacket = writeData(new PacketPlayOutEntityTeleport(), packetByteBuffer); + } + + @Override + Packet getRawPacket() { + return rawPacket; + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTypeID.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/EntityTypeID.java new file mode 100644 index 00000000..5f0f9b16 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/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_9_R2; + +class EntityTypeID { + + static final int ARMOR_STAND = 78; + static final int ITEM = 2; + static final int SLIME = 55; + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/InboundPacketHandler.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/InboundPacketHandler.java new file mode 100644 index 00000000..162f2a9b --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/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_9_R2; + +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.common.nms.NMSErrors; +import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_9_R2.PacketPlayInUseEntity; +import org.bukkit.entity.Player; + +public 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; + + public 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_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/PacketByteBuffer.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/PacketByteBuffer.java new file mode 100644 index 00000000..f8461ae7 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/PacketByteBuffer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import io.netty.buffer.Unpooled; +import net.minecraft.server.v1_9_R2.PacketDataSerializer; + +import java.util.UUID; + +class PacketByteBuffer extends PacketDataSerializer { + + private static final PacketByteBuffer INSTANCE = new PacketByteBuffer(); + + static PacketByteBuffer get() { + INSTANCE.clear(); + return INSTANCE; + } + + private PacketByteBuffer() { + super(Unpooled.buffer()); + } + + void writeVarInt(int i) { + super.d(i); + } + + void writeUUID(UUID uuid) { + super.a(uuid); + } + + void writeIntArray(int... array) { + super.a(array); + } + + void writeDataWatcherEntry(DataWatcherKey key, T value) { + writeByte(key.getIndex()); + writeVarInt(key.getSerializerTypeID()); + key.getSerializer().a(this, value); + } + + void writeDataWatcherEntriesEnd() { + writeByte(255); + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSManager.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSManager.java index 62a56ac3..c8af8be1 100644 --- a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSManager.java +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSManager.java @@ -5,32 +5,113 @@ */ package me.filoghost.holographicdisplays.nms.v1_9_R2; +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.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.FallbackEntityIDGenerator; +import me.filoghost.holographicdisplays.common.nms.NMSErrors; import me.filoghost.holographicdisplays.common.nms.NMSManager; import me.filoghost.holographicdisplays.common.nms.NMSPacketList; import me.filoghost.holographicdisplays.common.nms.PacketListener; +import net.minecraft.server.v1_9_R2.Entity; +import net.minecraft.server.v1_9_R2.NetworkManager; +import net.minecraft.server.v1_9_R2.PlayerConnection; +import org.bukkit.craftbukkit.v1_9_R2.entity.CraftPlayer; import org.bukkit.entity.Player; +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(int.class, Entity.class, "entityCount"); + private final Supplier fallbackEntityIDGenerator; + private final Supplier entityIDGenerator; + + public VersionNMSManager(ErrorCollector errorCollector) { + this.fallbackEntityIDGenerator = new FallbackEntityIDGenerator(); + this.entityIDGenerator = getEntityIDGenerator(errorCollector); + + // Force initialization of class to eventually throw exceptions early + DataWatcherKey.ENTITY_STATUS.getIndex(); + } + + private Supplier getEntityIDGenerator(ErrorCollector errorCollector) { + try { + testStaticFieldReadWrite(ENTITY_ID_COUNTER_FIELD); + + return () -> { + try { + int nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic(); + ENTITY_ID_COUNTER_FIELD.setStatic(nmsEntityIDCounter + 1); + return nmsEntityIDCounter; + } catch (ReflectiveOperationException e) { + // Should not happen, access is tested beforehand + return fallbackEntityIDGenerator.get(); + } + }; + } catch (ReflectiveOperationException e) { + errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR); + return fallbackEntityIDGenerator; + } + } + + private void testStaticFieldReadWrite(ReflectField field) throws ReflectiveOperationException { + T value = field.getStatic(); + field.setStatic(value); + } + @Override public EntityID newEntityID() { - return null; + return new EntityID(entityIDGenerator); } @Override public NMSPacketList createPacketList() { - return null; + return new VersionNMSPacketList(); } @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().playerConnection; + NetworkManager networkManager = playerConnection.a(); + Channel channel = networkManager.channel; + + channel.eventLoop().execute(() -> { + try { + pipelineModifierTask.accept(channel.pipeline()); + } catch (Exception e) { + Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e); + } + }); } } diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacket.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacket.java new file mode 100644 index 00000000..86d90182 --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacket.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import me.filoghost.holographicdisplays.common.nms.NMSPacket; +import net.minecraft.server.v1_9_R2.Packet; +import org.bukkit.craftbukkit.v1_9_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.io.IOException; + +abstract class VersionNMSPacket implements NMSPacket { + + @Override + public void sendTo(Player player) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(getRawPacket()); + } + + abstract Packet getRawPacket(); + + protected static > T writeData(T packet, PacketByteBuffer packetByteBuffer) { + try { + packet.a(packetByteBuffer); + return packet; + } catch (IOException e) { + // Never thrown by the implementations + throw new RuntimeException(e); + } + } + +} diff --git a/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacketList.java b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacketList.java new file mode 100644 index 00000000..425d89df --- /dev/null +++ b/nms/v1_9_r2/src/main/java/me/filoghost/holographicdisplays/nms/v1_9_R2/VersionNMSPacketList.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.holographicdisplays.nms.v1_9_R2; + +import me.filoghost.holographicdisplays.common.nms.AbstractNMSPacketList; +import me.filoghost.holographicdisplays.common.nms.EntityID; +import me.filoghost.holographicdisplays.common.nms.IndividualCustomName; +import me.filoghost.holographicdisplays.common.nms.IndividualNMSPacket; +import org.bukkit.inventory.ItemStack; + +class VersionNMSPacketList extends AbstractNMSPacketList { + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, String customName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, IndividualCustomName individualCustomName) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ARMOR_STAND, positionX, positionY, positionZ)); + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setArmorStandMarker() + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, String customName) { + add(EntityMetadataNMSPacket.builder(entityID) + .setCustomName(customName) + .build() + ); + } + + @Override + public void addArmorStandNameChangePackets(EntityID entityID, IndividualCustomName individualCustomName) { + add(new IndividualNMSPacket(player -> EntityMetadataNMSPacket.builder(entityID) + .setCustomName(individualCustomName.get(player)) + .build() + )); + } + + @Override + public void addItemSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ, ItemStack itemStack) { + add(new EntitySpawnNMSPacket(entityID, EntityTypeID.ITEM, positionX, positionY, positionZ)); + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addItemStackChangePackets(EntityID entityID, ItemStack itemStack) { + add(EntityMetadataNMSPacket.builder(entityID) + .setItemStack(itemStack) + .build() + ); + } + + @Override + public void addSlimeSpawnPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(EntityLivingSpawnNMSPacket.builder(entityID, EntityTypeID.SLIME, positionX, positionY, positionZ) + .setInvisible() + .setSlimeSmall() // Required for a correct client-side collision box + .build() + ); + } + + @Override + public void addEntityDestroyPackets(EntityID... entityIDs) { + add(new EntityDestroyNMSPacket(entityIDs)); + } + + @Override + public void addTeleportPackets(EntityID entityID, double positionX, double positionY, double positionZ) { + add(new EntityTeleportNMSPacket(entityID, positionX, positionY, positionZ)); + } + + @Override + public void addMountPackets(EntityID vehicleEntityID, EntityID passengerEntityID) { + add(new EntityMountNMSPacket(vehicleEntityID, passengerEntityID)); + } + +} diff --git a/plugin/src/main/java/me/filoghost/holographicdisplays/plugin/util/NMSVersion.java b/plugin/src/main/java/me/filoghost/holographicdisplays/plugin/util/NMSVersion.java index 27d4c5f2..d3a8a485 100644 --- a/plugin/src/main/java/me/filoghost/holographicdisplays/plugin/util/NMSVersion.java +++ b/plugin/src/main/java/me/filoghost/holographicdisplays/plugin/util/NMSVersion.java @@ -20,19 +20,19 @@ public enum NMSVersion { // Not using shorter method reference syntax here because it initializes the class, causing a ClassNotFoundException v1_8_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_8_R2.VersionNMSManager()), - v1_8_R3((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_8_R3.VersionNMSManager()), + v1_8_R3((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_8_R3.VersionNMSManager(errorCollector)), v1_9_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_9_R1.VersionNMSManager()), - v1_9_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_9_R2.VersionNMSManager()), - v1_10_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_10_R1.VersionNMSManager()), - v1_11_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_11_R1.VersionNMSManager()), - v1_12_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_12_R1.VersionNMSManager()), + v1_9_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_9_R2.VersionNMSManager(errorCollector)), + v1_10_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_10_R1.VersionNMSManager(errorCollector)), + v1_11_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_11_R1.VersionNMSManager(errorCollector)), + v1_12_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_12_R1.VersionNMSManager(errorCollector)), v1_13_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_13_R1.VersionNMSManager()), - v1_13_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_13_R2.VersionNMSManager()), - v1_14_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_14_R1.VersionNMSManager()), - v1_15_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_15_R1.VersionNMSManager()), - v1_16_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_16_R1.VersionNMSManager()), - v1_16_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_16_R2.VersionNMSManager()), - v1_16_R3((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_16_R3.VersionNMSManager()), + v1_13_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_13_R2.VersionNMSManager(errorCollector)), + v1_14_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_14_R1.VersionNMSManager(errorCollector)), + v1_15_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_15_R1.VersionNMSManager(errorCollector)), + v1_16_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_16_R1.VersionNMSManager(errorCollector)), + v1_16_R2((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_16_R2.VersionNMSManager(errorCollector)), + v1_16_R3((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_16_R3.VersionNMSManager(errorCollector)), v1_17_R1((errorCollector) -> new me.filoghost.holographicdisplays.nms.v1_17_R1.VersionNMSManager(errorCollector)); private static final NMSVersion CURRENT_VERSION = extractCurrentVersion();