Add support for Minecraft 1.20

This commit is contained in:
Sascha Bartl 2023-06-10 11:04:09 +02:00 committed by filoghost
parent 2fa71ad3e0
commit a3c82bfddf
21 changed files with 846 additions and 2 deletions

View File

@ -109,6 +109,11 @@
<artifactId>holographicdisplays-nms-v1_19_r3</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>holographicdisplays-nms-v1_20_r1</artifactId>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>

View File

@ -40,7 +40,7 @@ public class HolographicDisplaysCore {
try {
nmsManager = NMSVersion.getCurrent().createNMSManager(errorCollector);
} catch (UnknownVersionException e) {
throw new PluginEnableException("Holographic Displays only supports Spigot from 1.8 to 1.19.3.");
throw new PluginEnableException("Holographic Displays only supports Spigot from 1.8 to 1.20.");
} catch (OutdatedVersionException e) {
throw new PluginEnableException("Holographic Displays only supports " + e.getMinimumSupportedVersion() + " and above.");
} catch (Throwable t) {

View File

@ -40,7 +40,8 @@ public enum NMSVersion {
/* 1.18.2 */ v1_18_R2(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_18_R2.VersionNMSManager(errorCollector)),
/* 1.19 - 1.19.2 */ v1_19_R1(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_19_R1.VersionNMSManager(errorCollector)),
/* 1.19.3 */ v1_19_R2(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_19_R2.VersionNMSManager(errorCollector)),
/* 1.19.4 - ? */ v1_19_R3(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_19_R3.VersionNMSManager(errorCollector)),
/* 1.19.4 */ v1_19_R3(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_19_R3.VersionNMSManager(errorCollector)),
/* 1.20 - ? */ v1_20_R1(errorCollector -> new me.filoghost.holographicdisplays.nms.v1_20_R1.VersionNMSManager(errorCollector)),
/* Other versions */ UNKNOWN(NMSManagerFactory.unknownVersion());
private static final NMSVersion CURRENT_VERSION = detectCurrentVersion();

View File

@ -30,6 +30,7 @@
<module>v1_19_r1</module>
<module>v1_19_r2</module>
<module>v1_19_r3</module>
<module>v1_20_r1</module>
</modules>
<dependencies>

36
nms/v1_20_r1/pom.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>me.filoghost.holographicdisplays</groupId>
<artifactId>holographicdisplays-nms</artifactId>
<version>3.0.3-SNAPSHOT</version>
</parent>
<artifactId>holographicdisplays-nms-v1_20_r1</artifactId>
<name>HolographicDisplays NMS v1_20_R1</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>holographicdisplays-nms-common</artifactId>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.20-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.joml</groupId>
<artifactId>joml</artifactId>
<version>1.10.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import io.netty.handler.codec.EncoderException;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.syncher.DataWatcherRegistry;
import net.minecraft.network.syncher.DataWatcherSerializer;
import net.minecraft.world.item.ItemStack;
import java.util.Optional;
class DataWatcherKey<T> {
private static final DataWatcherSerializer<Byte> BYTE_SERIALIZER = DataWatcherRegistry.a;
private static final DataWatcherSerializer<Integer> INT_SERIALIZER = DataWatcherRegistry.b;
private static final DataWatcherSerializer<Boolean> BOOLEAN_SERIALIZER = DataWatcherRegistry.k;
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.h;
private static final DataWatcherSerializer<Optional<IChatBaseComponent>> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.g;
static final DataWatcherKey<Byte> ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER);
static final DataWatcherKey<Optional<IChatBaseComponent>> CUSTOM_NAME = new DataWatcherKey<>(2, OPTIONAL_CHAT_COMPONENT_SERIALIZER);
static final DataWatcherKey<Boolean> CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER);
static final DataWatcherKey<ItemStack> ITEM_STACK = new DataWatcherKey<>(8, ITEM_STACK_SERIALIZER);
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(15, BYTE_SERIALIZER);
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(16, INT_SERIALIZER);
private final int index;
private final DataWatcherSerializer<T> serializer;
private final int serializerTypeID;
private DataWatcherKey(int index, DataWatcherSerializer<T> 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<T> getSerializer() {
return serializer;
}
int getSerializerTypeID() {
return serializerTypeID;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.fcommons.Strings;
import net.minecraft.network.chat.IChatBaseComponent;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R1.util.CraftChatMessage;
import org.bukkit.inventory.ItemStack;
import java.util.Optional;
abstract class DataWatcherPacketBuilder<T> {
private static final int MAX_CUSTOM_NAME_LENGTH = 5000;
private final PacketByteBuffer packetByteBuffer;
DataWatcherPacketBuilder(PacketByteBuffer packetByteBuffer) {
this.packetByteBuffer = packetByteBuffer;
}
DataWatcherPacketBuilder<T> setInvisible() {
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ENTITY_STATUS, (byte) 0x20); // Invisible
return this;
}
DataWatcherPacketBuilder<T> setArmorStandMarker() {
setInvisible();
packetByteBuffer.writeDataWatcherEntry(
DataWatcherKey.ARMOR_STAND_STATUS, (byte) (0x01 | 0x02 | 0x08 | 0x10)); // Small, no gravity, no base plate, marker
return this;
}
DataWatcherPacketBuilder<T> setCustomName(String customName) {
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, getCustomNameDataWatcherValue(customName));
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName));
return this;
}
private Optional<IChatBaseComponent> getCustomNameDataWatcherValue(String customName) {
customName = Strings.truncate(customName, MAX_CUSTOM_NAME_LENGTH);
if (!Strings.isEmpty(customName)) {
return Optional.of(CraftChatMessage.fromString(customName, false, true)[0]);
} else {
return Optional.empty();
}
}
DataWatcherPacketBuilder<T> setItemStack(ItemStack itemStack) {
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, CraftItemStack.asNMSCopy(itemStack));
return this;
}
DataWatcherPacketBuilder<T> setSlimeSmall() {
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.SLIME_SIZE, 1);
return this;
}
T build() {
packetByteBuffer.writeDataWatcherEntriesEnd();
return createPacket(packetByteBuffer);
}
abstract T createPacket(PacketByteBuffer packetByteBuffer);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy;
class EntityDestroyNMSPacket extends VersionNMSPacket {
private final Packet<?> rawPacket;
EntityDestroyNMSPacket(EntityID entityID) {
PacketByteBuffer packetByteBuffer = PacketByteBuffer.get();
packetByteBuffer.writeVarIntArray(entityID.getNumericID());
this.rawPacket = new PacketPlayOutEntityDestroy(packetByteBuffer.getInternalSerializer());
}
EntityDestroyNMSPacket(EntityID entityID1, EntityID entityID2) {
PacketByteBuffer packetByteBuffer = PacketByteBuffer.get();
packetByteBuffer.writeVarIntArray(entityID1.getNumericID(), entityID2.getNumericID());
this.rawPacket = new PacketPlayOutEntityDestroy(packetByteBuffer.getInternalSerializer());
}
@Override
Packet<?> getRawPacket() {
return rawPacket;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
class EntityMetadataNMSPacket extends VersionNMSPacket {
private final Packet<?> rawPacket;
private EntityMetadataNMSPacket(PacketByteBuffer packetByteBuffer) {
this.rawPacket = new PacketPlayOutEntityMetadata(packetByteBuffer.getInternalSerializer());
}
@Override
Packet<?> getRawPacket() {
return rawPacket;
}
public static DataWatcherPacketBuilder<EntityMetadataNMSPacket> builder(EntityID entityID) {
PacketByteBuffer packetByteBuffer = PacketByteBuffer.get();
packetByteBuffer.writeVarInt(entityID.getNumericID());
return new Builder(packetByteBuffer);
}
private static class Builder extends DataWatcherPacketBuilder<EntityMetadataNMSPacket> {
private Builder(PacketByteBuffer packetByteBuffer) {
super(packetByteBuffer);
}
@Override
EntityMetadataNMSPacket createPacket(PacketByteBuffer packetByteBuffer) {
return new EntityMetadataNMSPacket(packetByteBuffer);
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutMount;
class EntityMountNMSPacket extends VersionNMSPacket {
private final Packet<?> rawPacket;
EntityMountNMSPacket(EntityID vehicleEntityID, EntityID passengerEntityID) {
PacketByteBuffer packetByteBuffer = PacketByteBuffer.get();
packetByteBuffer.writeVarInt(vehicleEntityID.getNumericID());
packetByteBuffer.writeVarIntArray(passengerEntityID.getNumericID());
this.rawPacket = new PacketPlayOutMount(packetByteBuffer.getInternalSerializer());
}
@Override
Packet<?> getRawPacket() {
return rawPacket;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.common.PositionCoordinates;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity;
class EntitySpawnNMSPacket extends VersionNMSPacket {
private final Packet<?> rawPacket;
EntitySpawnNMSPacket(EntityID entityID, int entityTypeID, PositionCoordinates position, double positionOffsetY) {
PacketByteBuffer packetByteBuffer = PacketByteBuffer.get();
packetByteBuffer.writeVarInt(entityID.getNumericID());
packetByteBuffer.writeUUID(entityID.getUUID());
packetByteBuffer.writeVarInt(entityTypeID);
// Position
packetByteBuffer.writeDouble(position.getX());
packetByteBuffer.writeDouble(position.getY() + positionOffsetY);
packetByteBuffer.writeDouble(position.getZ());
// Rotation
packetByteBuffer.writeByte(0);
packetByteBuffer.writeByte(0);
// Object data
if (entityTypeID == EntityTypeID.ITEM) {
packetByteBuffer.writeInt(1); // Velocity is present and zero (otherwise by default a random velocity is applied)
} else {
packetByteBuffer.writeInt(0);
}
// Velocity
packetByteBuffer.writeShort(0);
packetByteBuffer.writeShort(0);
packetByteBuffer.writeShort(0);
this.rawPacket = new PacketPlayOutSpawnEntity(packetByteBuffer.getInternalSerializer());
}
@Override
Packet<?> getRawPacket() {
return rawPacket;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.common.PositionCoordinates;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport;
class EntityTeleportNMSPacket extends VersionNMSPacket {
private final Packet<?> rawPacket;
EntityTeleportNMSPacket(EntityID entityID, PositionCoordinates position, double positionOffsetY) {
PacketByteBuffer packetByteBuffer = PacketByteBuffer.get();
packetByteBuffer.writeVarInt(entityID.getNumericID());
// Position
packetByteBuffer.writeDouble(position.getX());
packetByteBuffer.writeDouble(position.getY() + positionOffsetY);
packetByteBuffer.writeDouble(position.getZ());
// Rotation
packetByteBuffer.writeByte(0);
packetByteBuffer.writeByte(0);
// On ground
packetByteBuffer.writeBoolean(false);
this.rawPacket = new PacketPlayOutEntityTeleport(packetByteBuffer.getInternalSerializer());
}
@Override
Packet<?> getRawPacket() {
return rawPacket;
}
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
class EntityTypeID {
static final int ARMOR_STAND = 2;
static final int ITEM = 54;
static final int SLIME = 88;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_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.nms.common.NMSErrors;
import me.filoghost.holographicdisplays.nms.common.PacketListener;
import net.minecraft.network.protocol.game.PacketPlayInUseEntity;
import org.bukkit.entity.Player;
class InboundPacketHandler extends ChannelInboundHandlerAdapter {
public static final String HANDLER_NAME = "holographic_displays_listener";
private static final ReflectField<Integer> ENTITY_ID_FIELD = ReflectField.lookup(int.class, PacketPlayInUseEntity.class, "a");
private final Player player;
private final PacketListener packetListener;
InboundPacketHandler(Player player, PacketListener packetListener) {
this.player = player;
this.packetListener = packetListener;
}
@Override
public void channelRead(ChannelHandlerContext context, Object packet) throws Exception {
try {
if (packet instanceof PacketPlayInUseEntity) {
int entityID = ENTITY_ID_FIELD.get(packet);
boolean cancel = packetListener.onAsyncEntityInteract(player, entityID);
if (cancel) {
return;
}
}
} catch (Throwable t) {
Log.warning(NMSErrors.EXCEPTION_ON_PACKET_READ, t);
}
super.channelRead(context, packet);
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import io.netty.buffer.Unpooled;
import net.minecraft.network.PacketDataSerializer;
import java.util.UUID;
class PacketByteBuffer {
private static final PacketByteBuffer INSTANCE = new PacketByteBuffer();
private final PacketDataSerializer serializer;
static PacketByteBuffer get() {
INSTANCE.clear();
return INSTANCE;
}
private PacketByteBuffer() {
this.serializer = new PacketDataSerializer(Unpooled.buffer());
}
void writeBoolean(boolean flag) {
serializer.writeBoolean(flag);
}
void writeByte(int i) {
serializer.writeByte(i);
}
void writeShort(int i) {
serializer.writeShort(i);
}
void writeInt(int i) {
serializer.writeInt(i);
}
void writeDouble(double d) {
serializer.writeDouble(d);
}
void writeVarInt(int i) {
serializer.d(i);
}
void writeVarIntArray(int i1) {
writeVarInt(1);
writeVarInt(i1);
}
void writeVarIntArray(int i1, int i2) {
writeVarInt(2);
writeVarInt(i1);
writeVarInt(i2);
}
void writeUUID(UUID uuid) {
serializer.a(uuid);
}
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
serializer.writeByte(key.getIndex());
writeVarInt(key.getSerializerTypeID());
key.getSerializer().a(serializer, value);
}
void writeDataWatcherEntriesEnd() {
serializer.writeByte(0xFF);
}
public PacketDataSerializer getInternalSerializer() {
return serializer;
}
void clear() {
serializer.clear();
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.common.PositionCoordinates;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import me.filoghost.holographicdisplays.nms.common.PacketGroup;
import me.filoghost.holographicdisplays.nms.common.entity.ClickableNMSPacketEntity;
class VersionClickableNMSPacketEntity implements ClickableNMSPacketEntity {
private final EntityID slimeID;
VersionClickableNMSPacketEntity(EntityID slimeID) {
this.slimeID = slimeID;
}
@Override
public EntityID getID() {
return slimeID;
}
@Override
public PacketGroup newSpawnPackets(PositionCoordinates position) {
return PacketGroup.of(
new EntitySpawnNMSPacket(slimeID, EntityTypeID.SLIME, position, SLIME_Y_OFFSET),
EntityMetadataNMSPacket.builder(slimeID)
.setInvisible()
.setSlimeSmall() // Required for a correct client-side collision box
.build()
);
}
@Override
public PacketGroup newTeleportPackets(PositionCoordinates position) {
return new EntityTeleportNMSPacket(slimeID, position, SLIME_Y_OFFSET);
}
@Override
public PacketGroup newDestroyPackets() {
return new EntityDestroyNMSPacket(slimeID);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.common.PositionCoordinates;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import me.filoghost.holographicdisplays.nms.common.PacketGroup;
import me.filoghost.holographicdisplays.nms.common.entity.ItemNMSPacketEntity;
import org.bukkit.inventory.ItemStack;
class VersionItemNMSPacketEntity implements ItemNMSPacketEntity {
private final EntityID itemID;
private final EntityID vehicleID;
VersionItemNMSPacketEntity(EntityID itemID, EntityID vehicleID) {
this.itemID = itemID;
this.vehicleID = vehicleID;
}
@Override
public PacketGroup newSpawnPackets(PositionCoordinates position, ItemStack itemStack) {
return PacketGroup.of(
new EntitySpawnNMSPacket(vehicleID, EntityTypeID.ARMOR_STAND, position, ITEM_Y_OFFSET),
EntityMetadataNMSPacket.builder(vehicleID)
.setArmorStandMarker()
.build(),
new EntitySpawnNMSPacket(itemID, EntityTypeID.ITEM, position, ITEM_Y_OFFSET),
EntityMetadataNMSPacket.builder(itemID)
.setItemStack(itemStack)
.build(),
new EntityMountNMSPacket(vehicleID, itemID)
);
}
@Override
public PacketGroup newChangePackets(ItemStack itemStack) {
return EntityMetadataNMSPacket.builder(itemID)
.setItemStack(itemStack)
.build();
}
@Override
public PacketGroup newTeleportPackets(PositionCoordinates position) {
return new EntityTeleportNMSPacket(vehicleID, position, ITEM_Y_OFFSET);
}
@Override
public PacketGroup newDestroyPackets() {
return new EntityDestroyNMSPacket(itemID, vehicleID);
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_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.nms.common.EntityID;
import me.filoghost.holographicdisplays.nms.common.FallbackEntityIDGenerator;
import me.filoghost.holographicdisplays.nms.common.NMSErrors;
import me.filoghost.holographicdisplays.nms.common.NMSManager;
import me.filoghost.holographicdisplays.nms.common.PacketListener;
import me.filoghost.holographicdisplays.nms.common.entity.ClickableNMSPacketEntity;
import me.filoghost.holographicdisplays.nms.common.entity.ItemNMSPacketEntity;
import me.filoghost.holographicdisplays.nms.common.entity.TextNMSPacketEntity;
import net.minecraft.network.NetworkManager;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_20_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<AtomicInteger> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(AtomicInteger.class, Entity.class, "d");
private static final ReflectField<NetworkManager> NETWORK_MANAGER_FIELD = ReflectField.lookup(NetworkManager.class, PlayerConnection.class, "h");
private final Supplier<Integer> entityIDGenerator;
public VersionNMSManager(ErrorCollector errorCollector) {
this.entityIDGenerator = getEntityIDGenerator(errorCollector);
// Force initialization of class to eventually throw exceptions early
DataWatcherKey.ENTITY_STATUS.getIndex();
}
private Supplier<Integer> getEntityIDGenerator(ErrorCollector errorCollector) {
try {
AtomicInteger nmsEntityIDCounter = ENTITY_ID_COUNTER_FIELD.getStatic();
return nmsEntityIDCounter::incrementAndGet;
} catch (ReflectiveOperationException e) {
errorCollector.add(e, NMSErrors.EXCEPTION_GETTING_ENTITY_ID_GENERATOR);
return new FallbackEntityIDGenerator();
}
}
private EntityID newEntityID() {
return new EntityID(entityIDGenerator);
}
@Override
public TextNMSPacketEntity newTextPacketEntity() {
return new VersionTextNMSPacketEntity(newEntityID());
}
@Override
public ItemNMSPacketEntity newItemPacketEntity() {
return new VersionItemNMSPacketEntity(newEntityID(), newEntityID());
}
@Override
public ClickableNMSPacketEntity newClickablePacketEntity() {
return new VersionClickableNMSPacketEntity(newEntityID());
}
@Override
public void injectPacketListener(Player player, PacketListener packetListener) {
modifyPipeline(player, (ChannelPipeline pipeline) -> {
ChannelHandler currentListener = pipeline.get(InboundPacketHandler.HANDLER_NAME);
if (currentListener != null) {
pipeline.remove(InboundPacketHandler.HANDLER_NAME);
}
pipeline.addBefore("packet_handler", InboundPacketHandler.HANDLER_NAME, new InboundPacketHandler(player, packetListener));
});
}
@Override
public void uninjectPacketListener(Player player) {
modifyPipeline(player, (ChannelPipeline pipeline) -> {
ChannelHandler currentListener = pipeline.get(InboundPacketHandler.HANDLER_NAME);
if (currentListener != null) {
pipeline.remove(InboundPacketHandler.HANDLER_NAME);
}
});
}
/*
* Modifying the pipeline in the main thread can cause deadlocks, delays and other concurrency issues,
* which can be avoided by using the event loop. Thanks to ProtocolLib for this insight.
*/
private void modifyPipeline(Player player, Consumer<ChannelPipeline> pipelineModifierTask) {
PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().c;
NetworkManager networkManager;
try {
networkManager = NETWORK_MANAGER_FIELD.get(playerConnection);
} catch (ReflectiveOperationException e) {
Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e);
return;
}
Channel channel = networkManager.m;
channel.eventLoop().execute(() -> {
if (!player.isOnline()) {
return;
}
try {
pipelineModifierTask.accept(channel.pipeline());
} catch (Exception e) {
Log.warning(NMSErrors.EXCEPTION_MODIFYING_CHANNEL_PIPELINE, e);
}
});
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.nms.common.PacketGroup;
import net.minecraft.network.protocol.Packet;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
abstract class VersionNMSPacket implements PacketGroup {
@Override
public void sendTo(Player player) {
((CraftPlayer) player).getHandle().c.a(getRawPacket());
}
abstract Packet<?> getRawPacket();
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.nms.v1_20_R1;
import me.filoghost.holographicdisplays.common.PositionCoordinates;
import me.filoghost.holographicdisplays.nms.common.EntityID;
import me.filoghost.holographicdisplays.nms.common.IndividualTextPacketGroup;
import me.filoghost.holographicdisplays.nms.common.PacketGroup;
import me.filoghost.holographicdisplays.nms.common.entity.TextNMSPacketEntity;
class VersionTextNMSPacketEntity implements TextNMSPacketEntity {
private final EntityID armorStandID;
VersionTextNMSPacketEntity(EntityID armorStandID) {
this.armorStandID = armorStandID;
}
@Override
public PacketGroup newSpawnPackets(PositionCoordinates position, String text) {
return PacketGroup.of(
new EntitySpawnNMSPacket(armorStandID, EntityTypeID.ARMOR_STAND, position, ARMOR_STAND_Y_OFFSET),
EntityMetadataNMSPacket.builder(armorStandID)
.setArmorStandMarker()
.setCustomName(text)
.build()
);
}
@Override
public IndividualTextPacketGroup newSpawnPackets(PositionCoordinates position) {
return IndividualTextPacketGroup.of(
new EntitySpawnNMSPacket(armorStandID, EntityTypeID.ARMOR_STAND, position, ARMOR_STAND_Y_OFFSET),
(String text) -> EntityMetadataNMSPacket.builder(armorStandID)
.setArmorStandMarker()
.setCustomName(text)
.build()
);
}
@Override
public PacketGroup newChangePackets(String text) {
return EntityMetadataNMSPacket.builder(armorStandID)
.setCustomName(text)
.build();
}
@Override
public IndividualTextPacketGroup newChangePackets() {
return IndividualTextPacketGroup.of(
(String text) -> EntityMetadataNMSPacket.builder(armorStandID)
.setCustomName(text)
.build()
);
}
@Override
public PacketGroup newTeleportPackets(PositionCoordinates position) {
return new EntityTeleportNMSPacket(armorStandID, position, ARMOR_STAND_Y_OFFSET);
}
@Override
public PacketGroup newDestroyPackets() {
return new EntityDestroyNMSPacket(armorStandID);
}
}

View File

@ -201,6 +201,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>holographicdisplays-nms-v1_20_r1</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>