Change FakeArmorStand structure to implement FakeItem

This commit is contained in:
Flowsqy 2021-12-29 12:24:51 +01:00
parent 2fda9bbf8d
commit a7bc054dce
8 changed files with 223 additions and 97 deletions

View File

@ -3,18 +3,10 @@ package de.epiceric.shopchest.nms;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.UUID;
public interface FakeArmorStand {
int getEntityId();
public interface FakeArmorStand extends FakeEntity {
void sendData(String name, Iterable<Player> receivers);
void remove(Iterable<Player> receivers);
void setLocation(Location location, Iterable<Player> receivers);
void spawn(UUID uuid, Location location, Iterable<Player> receivers);
}

View File

@ -0,0 +1,16 @@
package de.epiceric.shopchest.nms;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.UUID;
public interface FakeEntity {
int getEntityId();
void spawn(UUID uuid, Location location, Iterable<Player> receivers);
void remove(Iterable<Player> receivers);
}

View File

@ -0,0 +1,10 @@
package de.epiceric.shopchest.nms;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public interface FakeItem extends FakeEntity{
void sendData(ItemStack item, Iterable<Player> receivers);
}

View File

@ -4,4 +4,6 @@ public interface Platform {
FakeArmorStand createFakeArmorStand();
FakeItem createFakeItem();
}

View File

@ -6,115 +6,75 @@ import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundRemoveEntityPacket;
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class FakeArmorStandImpl implements FakeArmorStand {
public class FakeArmorStandImpl extends FakeEntityImpl<String> implements FakeArmorStand {
private final static byte INVISIBLE_FLAG = 0b100000;
private final static AtomicInteger ENTITY_COUNTER;
protected static final EntityDataAccessor<Byte> DATA_SHARED_FLAGS_ID;
private static final EntityDataAccessor<Boolean> DATA_NO_GRAVITY;
private static final EntityDataAccessor<Optional<Component>> DATA_CUSTOM_NAME;
private static final EntityDataAccessor<Boolean> DATA_CUSTOM_NAME_VISIBLE;
private static final EntityDataAccessor<Boolean> DATA_SILENT;
private final static Field packedItemField;
private final static float MARKER_ARMOR_STAND_OFFSET = 1.975f;
static {
try {
final Field entityCounterField = Entity.class.getDeclaredField("b"); // ENTITY_COUNTER
ENTITY_COUNTER = (AtomicInteger) entityCounterField.get(null);
final Field dataSharedFlagsId = Entity.class.getDeclaredField("Z"); // DATA_SHARED_FLAGS_ID
DATA_SHARED_FLAGS_ID = forceCast(dataSharedFlagsId.get(null));
final Field dataNoGravityField = Entity.class.getDeclaredField("aM"); // DATA_NO_GRAVITY
DATA_NO_GRAVITY = forceCast(dataNoGravityField.get(null));
DATA_SHARED_FLAGS_ID = FakeEntityImpl.forceCast(dataSharedFlagsId.get(null));
final Field dataCustomNameField = Entity.class.getDeclaredField("aJ"); // DATA_CUSTOM_NAME
DATA_CUSTOM_NAME = forceCast(dataCustomNameField.get(null));
DATA_CUSTOM_NAME = FakeEntityImpl.forceCast(dataCustomNameField.get(null));
final Field dataCustomNameVisibleField = Entity.class.getDeclaredField("aK"); // DATA_CUSTOM_NAME_VISIBLE
DATA_CUSTOM_NAME_VISIBLE = forceCast(dataCustomNameVisibleField.get(null));
final Field dataSilentField = Entity.class.getDeclaredField("aL"); // DATA_SILENT
DATA_SILENT = forceCast(dataSilentField.get(null));
packedItemField = ClientboundSetEntityDataPacket.class.getDeclaredField("b"); // packedItems
DATA_CUSTOM_NAME_VISIBLE = FakeEntityImpl.forceCast(dataCustomNameVisibleField.get(null));
} catch (ReflectiveOperationException e){
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
private static <T> T forceCast(Object o){
return (T) o;
}
private final int entityId;
public FakeArmorStandImpl() {
entityId = ENTITY_COUNTER.incrementAndGet();
}
@Override
public int getEntityId() {
return entityId;
}
private void sendPacket(Packet<?> packet, Iterable<Player> receivers){
for(Player receiver : receivers){
((CraftPlayer)receiver).getHandle().connection.send(packet);
}
super();
}
@Override
public void sendData(String name, Iterable<Player> receivers) {
// Create packet
final SynchedEntityData entityData = new SynchedEntityData(null);
final ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(entityId, entityData, false);
final List<SynchedEntityData.DataItem<?>> packedItems = new ArrayList<>(5);
// Setup data
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SHARED_FLAGS_ID, INVISIBLE_FLAG));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_NO_GRAVITY, true));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME, Optional.ofNullable(
Component.Serializer.fromJson(
ComponentSerializer.toString(
TextComponent.fromLegacyText(name)
)
)
)));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME_VISIBLE, true));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SILENT, true));
try {
packedItemField.set(dataPacket, packedItems);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
// Send packet
sendPacket(dataPacket, receivers);
sendData(receivers, name);
}
@Override
public void remove(Iterable<Player> receivers) {
final ClientboundRemoveEntityPacket removePacket = new ClientboundRemoveEntityPacket(entityId);
sendPacket(removePacket, receivers);
protected EntityType<?> getEntityType() {
return EntityType.ARMOR_STAND;
}
@Override
protected float getSpawnOffSet() {
return MARKER_ARMOR_STAND_OFFSET;
}
@Override
protected int getDataItemCount() {
return 4;
}
@Override
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, String name) {
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SHARED_FLAGS_ID, INVISIBLE_FLAG));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME, Optional.ofNullable(
Component.Serializer.fromJson(
ComponentSerializer.toString(
TextComponent.fromLegacyText(name)
)
)
)));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_CUSTOM_NAME_VISIBLE, true));
// TODO Add Marker (specific to ArmorStand)
}
@Override
@ -131,20 +91,4 @@ public class FakeArmorStandImpl implements FakeArmorStand {
sendPacket(positionPacket, receivers);
}
@Override
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(
entityId,
uuid,
location.getX(),
location.getY() + MARKER_ARMOR_STAND_OFFSET,
location.getZ(),
0f,
0f,
EntityType.ARMOR_STAND,
0,
Vec3.ZERO
);
sendPacket(spawnPacket, receivers);
}
}

View File

@ -0,0 +1,120 @@
package de.epiceric.shopchest.nms.v1_17_R1;
import de.epiceric.shopchest.nms.FakeEntity;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundRemoveEntityPacket;
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class FakeEntityImpl<T> implements FakeEntity {
private final static AtomicInteger ENTITY_COUNTER;
private final static EntityDataAccessor<Boolean> DATA_NO_GRAVITY;
private final static EntityDataAccessor<Boolean> DATA_SILENT;
private final static Field packedItemField;
static {
try {
final Field entityCounterField = Entity.class.getDeclaredField("b"); // ENTITY_COUNTER
ENTITY_COUNTER = (AtomicInteger) entityCounterField.get(null);
final Field dataNoGravityField = Entity.class.getDeclaredField("aM"); // DATA_NO_GRAVITY
DATA_NO_GRAVITY = forceCast(dataNoGravityField.get(null));
final Field dataSilentField = Entity.class.getDeclaredField("aL"); // DATA_SILENT
DATA_SILENT = forceCast(dataSilentField.get(null));
packedItemField = ClientboundSetEntityDataPacket.class.getDeclaredField("b"); // packedItems
}catch (ReflectiveOperationException e){
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
protected static <T> T forceCast(Object o){
return (T) o;
}
protected final int entityId;
public FakeEntityImpl() {
entityId = ENTITY_COUNTER.incrementAndGet();
}
@Override
public int getEntityId() {
return entityId;
}
protected void sendPacket(Packet<?> packet, Iterable<Player> receivers){
for(Player receiver : receivers){
((CraftPlayer)receiver).getHandle().connection.send(packet);
}
}
@Override
public void spawn(UUID uuid, Location location, Iterable<Player> receivers) {
final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(
entityId,
uuid,
location.getX(),
location.getY() + getSpawnOffSet(),
location.getZ(),
0f,
0f,
getEntityType(),
0,
Vec3.ZERO
);
sendPacket(spawnPacket, receivers);
}
@Override
public void remove(Iterable<Player> receivers) {
final ClientboundRemoveEntityPacket removePacket = new ClientboundRemoveEntityPacket(entityId);
sendPacket(removePacket, receivers);
}
protected void sendData(Iterable<Player> receivers, T data){
// Create packet
final SynchedEntityData entityData = new SynchedEntityData(null);
final ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(entityId, entityData, false);
final List<SynchedEntityData.DataItem<?>> packedItems = new ArrayList<>(2 + getDataItemCount());
// Setup data
packedItems.add(new SynchedEntityData.DataItem<>(DATA_NO_GRAVITY, true));
packedItems.add(new SynchedEntityData.DataItem<>(DATA_SILENT, true));
addSpecificData(packedItems, data);
try {
packedItemField.set(dataPacket, packedItems);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
// Send packet
sendPacket(dataPacket, receivers);
}
protected abstract EntityType<?> getEntityType();
protected float getSpawnOffSet(){
return 0f;
}
protected abstract int getDataItemCount();
protected abstract void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, T data);
}

View File

@ -0,0 +1,36 @@
package de.epiceric.shopchest.nms.v1_17_R1;
import de.epiceric.shopchest.nms.FakeItem;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class FakeItemImpl extends FakeEntityImpl<ItemStack> implements FakeItem {
public FakeItemImpl() {
super();
}
@Override
public void sendData(ItemStack item, Iterable<Player> receivers) {
}
@Override
protected EntityType<?> getEntityType() {
return EntityType.ITEM;
}
@Override
protected int getDataItemCount() {
return 0;
}
@Override
protected void addSpecificData(List<SynchedEntityData.DataItem<?>> packedItems, ItemStack data) {
}
}

View File

@ -1,6 +1,7 @@
package de.epiceric.shopchest.nms.v1_17_R1;
import de.epiceric.shopchest.nms.FakeArmorStand;
import de.epiceric.shopchest.nms.FakeItem;
import de.epiceric.shopchest.nms.Platform;
public class PlatformImpl implements Platform {
@ -10,4 +11,9 @@ public class PlatformImpl implements Platform {
return new FakeArmorStandImpl();
}
@Override
public FakeItem createFakeItem() {
return new FakeItemImpl();
}
}