mirror of
https://github.com/filoghost/HolographicDisplays.git
synced 2024-12-29 20:27:42 +01:00
Add other major NMS implementations
This commit is contained in:
parent
2d0969e171
commit
4f86a8e9c2
@ -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<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.h;
|
||||
private static final DataWatcherSerializer<Optional<ItemStack>> ITEM_STACK_SERIALIZER = DataWatcherRegistry.f;
|
||||
private static final DataWatcherSerializer<String> STRING_SERIALIZER = DataWatcherRegistry.d;
|
||||
|
||||
static final DataWatcherKey<Byte> ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<String> CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER);
|
||||
static final DataWatcherKey<Boolean> CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER);
|
||||
static final DataWatcherKey<Optional<ItemStack>> ITEM_STACK = new DataWatcherKey<>(6, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(12, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, no base plate, marker
|
||||
return this;
|
||||
}
|
||||
|
||||
DataWatcherPacketBuilder<T> setCustomName(String customName) {
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300));
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName));
|
||||
return this;
|
||||
}
|
||||
|
||||
DataWatcherPacketBuilder<T> setItemStack(ItemStack itemStack) {
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.ITEM_STACK, Optional.fromNullable(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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DataWatcher> 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<EntityLivingSpawnNMSPacket> 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<EntityLivingSpawnNMSPacket> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(int.class, Entity.class, "entityCount");
|
||||
private final Supplier<Integer> fallbackEntityIDGenerator;
|
||||
private final Supplier<Integer> 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<Integer> 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 <T> void testStaticFieldReadWrite(ReflectField<T> 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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.h;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.f;
|
||||
private static final DataWatcherSerializer<String> STRING_SERIALIZER = DataWatcherRegistry.d;
|
||||
|
||||
static final DataWatcherKey<Byte> ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<String> CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER);
|
||||
static final DataWatcherKey<Boolean> CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER);
|
||||
static final DataWatcherKey<ItemStack> ITEM_STACK = new DataWatcherKey<>(6, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(12, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, no base plate, marker
|
||||
return this;
|
||||
}
|
||||
|
||||
DataWatcherPacketBuilder<T> setCustomName(String customName) {
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300));
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName));
|
||||
return this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DataWatcher> 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<EntityLivingSpawnNMSPacket> 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<EntityLivingSpawnNMSPacket> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(int.class, Entity.class, "entityCount");
|
||||
private final Supplier<Integer> fallbackEntityIDGenerator;
|
||||
private final Supplier<Integer> 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<Integer> 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 <T> void testStaticFieldReadWrite(ReflectField<T> 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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.h;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.f;
|
||||
private static final DataWatcherSerializer<String> STRING_SERIALIZER = DataWatcherRegistry.d;
|
||||
|
||||
static final DataWatcherKey<Byte> ENTITY_STATUS = new DataWatcherKey<>(0, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<String> CUSTOM_NAME = new DataWatcherKey<>(2, STRING_SERIALIZER);
|
||||
static final DataWatcherKey<Boolean> CUSTOM_NAME_VISIBILITY = new DataWatcherKey<>(3, BOOLEAN_SERIALIZER);
|
||||
static final DataWatcherKey<ItemStack> ITEM_STACK = new DataWatcherKey<>(6, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(12, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, no base plate, marker
|
||||
return this;
|
||||
}
|
||||
|
||||
DataWatcherPacketBuilder<T> setCustomName(String customName) {
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME, Strings.truncate(customName, 300));
|
||||
packetByteBuffer.writeDataWatcherEntry(DataWatcherKey.CUSTOM_NAME_VISIBILITY, !Strings.isEmpty(customName));
|
||||
return this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DataWatcher> 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<EntityLivingSpawnNMSPacket> 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<EntityLivingSpawnNMSPacket> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(int.class, Entity.class, "entityCount");
|
||||
private final Supplier<Integer> fallbackEntityIDGenerator;
|
||||
private final Supplier<Integer> 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<Integer> 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 <T> void testStaticFieldReadWrite(ReflectField<T> 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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.i;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.g;
|
||||
private static final DataWatcherSerializer<Optional<IChatBaseComponent>> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f;
|
||||
|
||||
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<>(6, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(11, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(12, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, 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, 300);
|
||||
if (!Strings.isEmpty(customName)) {
|
||||
return Optional.of(CraftChatMessage.fromString(customName, false)[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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DataWatcher> 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<EntityLivingSpawnNMSPacket> 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<EntityLivingSpawnNMSPacket> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(int.class, Entity.class, "entityCount");
|
||||
private final Supplier<Integer> fallbackEntityIDGenerator;
|
||||
private final Supplier<Integer> 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<Integer> 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 <T> void testStaticFieldReadWrite(ReflectField<T> 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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.i;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.g;
|
||||
private static final DataWatcherSerializer<Optional<IChatBaseComponent>> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f;
|
||||
|
||||
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<>(7, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(13, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(14, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, 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, 300);
|
||||
if (!Strings.isEmpty(customName)) {
|
||||
return Optional.of(CraftChatMessage.fromString(customName, false)[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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<DataWatcher> 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<EntityLivingSpawnNMSPacket> 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<EntityLivingSpawnNMSPacket> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<AtomicInteger> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(AtomicInteger.class, Entity.class, "entityCount");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.i;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.g;
|
||||
private static final DataWatcherSerializer<Optional<IChatBaseComponent>> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f;
|
||||
|
||||
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<>(7, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(15, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, 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, 300);
|
||||
if (!Strings.isEmpty(customName)) {
|
||||
return Optional.of(CraftChatMessage.fromString(customName, false)[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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<AtomicInteger> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(AtomicInteger.class, Entity.class, "entityCount");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.i;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.g;
|
||||
private static final DataWatcherSerializer<Optional<IChatBaseComponent>> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f;
|
||||
|
||||
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<>(7, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(15, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, 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, 300);
|
||||
if (!Strings.isEmpty(customName)) {
|
||||
return Optional.of(CraftChatMessage.fromString(customName, false)[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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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<Integer> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
<T> void writeDataWatcherEntry(DataWatcherKey<T> key, T value) {
|
||||
writeByte(key.getIndex());
|
||||
writeVarInt(key.getSerializerTypeID());
|
||||
key.getSerializer().a(this, value);
|
||||
}
|
||||
|
||||
void writeDataWatcherEntriesEnd() {
|
||||
writeByte(255);
|
||||
}
|
||||
|
||||
}
|
@ -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<AtomicInteger> ENTITY_ID_COUNTER_FIELD = ReflectField.lookup(AtomicInteger.class, Entity.class, "entityCount");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@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<ChannelPipeline> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 extends Packet<?>> T writeData(T packet, PacketByteBuffer packetByteBuffer) {
|
||||
try {
|
||||
packet.a(packetByteBuffer);
|
||||
return packet;
|
||||
} catch (IOException e) {
|
||||
// Never thrown by the implementations
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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<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.i;
|
||||
private static final DataWatcherSerializer<ItemStack> ITEM_STACK_SERIALIZER = DataWatcherRegistry.g;
|
||||
private static final DataWatcherSerializer<Optional<IChatBaseComponent>> OPTIONAL_CHAT_COMPONENT_SERIALIZER = DataWatcherRegistry.f;
|
||||
|
||||
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<>(7, ITEM_STACK_SERIALIZER);
|
||||
static final DataWatcherKey<Byte> ARMOR_STAND_STATUS = new DataWatcherKey<>(14, BYTE_SERIALIZER);
|
||||
static final DataWatcherKey<Integer> SLIME_SIZE = new DataWatcherKey<>(15, 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<T> {
|
||||
|
||||
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 | 0x08 | 0x10)); // Small, 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, 300);
|
||||
if (!Strings.isEmpty(customName)) {
|
||||
return Optional.of(CraftChatMessage.fromString(customName, false)[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);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user