From 1fdf21da7bba929235e7b5a5a255464f629612d7 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Thu, 18 Jul 2024 15:49:55 +0100 Subject: [PATCH] Migrate ArmorStand meta to using entity tag Our handling of this class causes some headaches due to ItemMeta being lossy, we can double patch this by properly storing the data we care about, there is still some stuff to be addressed, i.e. we really should ensure that the entity tag is stored to the tag properly --- .../0169-Add-ArmorStand-Item-Meta.patch | 227 +++++++----------- ...ve-checking-handled-tags-in-itemmeta.patch | 4 +- 2 files changed, 92 insertions(+), 139 deletions(-) diff --git a/patches/server/0169-Add-ArmorStand-Item-Meta.patch b/patches/server/0169-Add-ArmorStand-Item-Meta.patch index 554e001492b8..46624a41d4e1 100644 --- a/patches/server/0169-Add-ArmorStand-Item-Meta.patch +++ b/patches/server/0169-Add-ArmorStand-Item-Meta.patch @@ -33,10 +33,10 @@ index eef3517833ff5c0cf41b89973ebc972b8ed31e0f..c9fbc01be0b0e7fd1cafb091d06496f4 (type, meta) -> meta instanceof CraftMetaArmorStand armorStand ? armorStand : new CraftMetaArmorStand(meta)); diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java -index c4f12f96e39cb6189799a796b4cb2cb4f0b92392..59bdac414e8205ed608f79ef0d1502acd826d216 100644 +index c4f12f96e39cb6189799a796b4cb2cb4f0b92392..ecce5d0da946ca279c5608068442cc53437dd2a5 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java -@@ -11,9 +11,22 @@ import org.bukkit.Material; +@@ -11,9 +11,17 @@ import org.bukkit.Material; import org.bukkit.configuration.serialization.DelegateDeserialization; @DelegateDeserialization(SerializableMeta.class) @@ -45,229 +45,182 @@ index c4f12f96e39cb6189799a796b4cb2cb4f0b92392..59bdac414e8205ed608f79ef0d1502ac static final ItemMetaKeyType ENTITY_TAG = new ItemMetaKeyType<>(DataComponents.ENTITY_DATA, "entity-tag"); + // Paper start ++ static final ItemMetaKey ENTITY_ID = new ItemMetaKey("id", "entity-id"); + static final ItemMetaKey INVISIBLE = new ItemMetaKey("Invisible", "invisible"); + static final ItemMetaKey NO_BASE_PLATE = new ItemMetaKey("NoBasePlate", "no-base-plate"); + static final ItemMetaKey SHOW_ARMS = new ItemMetaKey("ShowArms", "show-arms"); + static final ItemMetaKey SMALL = new ItemMetaKey("Small", "small"); + static final ItemMetaKey MARKER = new ItemMetaKey("Marker", "marker"); -+ -+ private Boolean invisible = null; -+ private Boolean noBasePlate = null; -+ private Boolean showArms = null; -+ private Boolean small = null; -+ private Boolean marker = null; + // Paper end CompoundTag entityTag; CraftMetaArmorStand(CraftMetaItem meta) { -@@ -24,6 +37,13 @@ public class CraftMetaArmorStand extends CraftMetaItem { - } - - CraftMetaArmorStand armorStand = (CraftMetaArmorStand) meta; -+ // Paper start -+ this.invisible = armorStand.invisible; -+ this.noBasePlate = armorStand.noBasePlate; -+ this.showArms = armorStand.showArms; -+ this.small = armorStand.small; -+ this.marker = armorStand.marker; -+ // Paper end - this.entityTag = armorStand.entityTag; - } - -@@ -32,11 +52,39 @@ public class CraftMetaArmorStand extends CraftMetaItem { - - getOrEmpty(tag, CraftMetaArmorStand.ENTITY_TAG).ifPresent((nbt) -> { - this.entityTag = nbt.copyTag(); -+ // Paper start -+ if (entityTag.contains(INVISIBLE.NBT)) { -+ invisible = entityTag.getBoolean(INVISIBLE.NBT); -+ } -+ -+ if (entityTag.contains(NO_BASE_PLATE.NBT)) { -+ noBasePlate = entityTag.getBoolean(NO_BASE_PLATE.NBT); -+ } -+ -+ if (entityTag.contains(SHOW_ARMS.NBT)) { -+ showArms = entityTag.getBoolean(SHOW_ARMS.NBT); -+ } -+ -+ if (entityTag.contains(SMALL.NBT)) { -+ small = entityTag.getBoolean(SMALL.NBT); -+ } -+ -+ if (entityTag.contains(MARKER.NBT)) { -+ marker = entityTag.getBoolean(MARKER.NBT); -+ } -+ // Paper end - }); - } +@@ -37,6 +45,42 @@ public class CraftMetaArmorStand extends CraftMetaItem { CraftMetaArmorStand(Map map) { super(map); + // Paper start -+ this.invisible = SerializableMeta.getBoolean(map, INVISIBLE.BUKKIT); -+ this.noBasePlate = SerializableMeta.getBoolean(map, NO_BASE_PLATE.BUKKIT); -+ this.showArms = SerializableMeta.getBoolean(map, SHOW_ARMS.BUKKIT); -+ this.small = SerializableMeta.getBoolean(map, SMALL.BUKKIT); -+ this.marker = SerializableMeta.getBoolean(map, MARKER.BUKKIT); ++ String entityTag = SerializableMeta.getString(map, ENTITY_TAG.BUKKIT, true); ++ if (entityTag != null) { ++ java.io.ByteArrayInputStream buf = new java.io.ByteArrayInputStream(java.util.Base64.getDecoder().decode(entityTag)); ++ try { ++ this.entityTag = net.minecraft.nbt.NbtIo.readCompressed(buf, net.minecraft.nbt.NbtAccounter.unlimitedHeap()); ++ } catch (java.io.IOException ex) { ++ java.util.logging.Logger.getLogger(CraftMetaItem.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); ++ } ++ return; ++ } ++ SerializableMeta.getObjectOptionally(Boolean.class, map, INVISIBLE.BUKKIT, true).ifPresent((value) -> { ++ populateTagIfNull(); ++ this.entityTag.putBoolean(INVISIBLE.NBT, value); ++ }); ++ SerializableMeta.getObjectOptionally(Boolean.class, map, NO_BASE_PLATE.BUKKIT, true).ifPresent((value) -> { ++ populateTagIfNull(); ++ this.entityTag.putBoolean(NO_BASE_PLATE.NBT, value); ++ }); ++ SerializableMeta.getObjectOptionally(Boolean.class, map, SHOW_ARMS.BUKKIT, true).ifPresent((value) -> { ++ populateTagIfNull(); ++ this.entityTag.putBoolean(SHOW_ARMS.NBT, value); ++ }); ++ SerializableMeta.getObjectOptionally(Boolean.class, map, SMALL.BUKKIT, true).ifPresent((value) -> { ++ populateTagIfNull(); ++ this.entityTag.putBoolean(SMALL.NBT, value); ++ }); ++ SerializableMeta.getObjectOptionally(Boolean.class, map, MARKER.BUKKIT, true).ifPresent((value) -> { ++ populateTagIfNull(); ++ this.entityTag.putBoolean(MARKER.NBT, value); ++ }); ++ SerializableMeta.getObjectOptionally(String.class, map, ENTITY_ID, true).ifPresent((value) -> { ++ populateTagIfNull(); ++ this.entityTag.putString(ENTITY_ID.NBT, value); ++ }); + // Paper end } @Override -@@ -59,6 +107,31 @@ public class CraftMetaArmorStand extends CraftMetaItem { - void applyToItem(CraftMetaItem.Applicator tag) { - super.applyToItem(tag); +@@ -45,12 +89,13 @@ public class CraftMetaArmorStand extends CraftMetaItem { -+ // Paper start -+ if (!isArmorStandEmpty() && this.entityTag == null) { -+ this.entityTag = new CompoundTag(); -+ } -+ -+ if (this.invisible != null) { -+ this.entityTag.putBoolean(INVISIBLE.NBT, this.invisible); -+ } -+ -+ if (this.noBasePlate != null) { -+ this.entityTag.putBoolean(NO_BASE_PLATE.NBT, this.noBasePlate); -+ } -+ -+ if (this.showArms != null) { -+ this.entityTag.putBoolean(SHOW_ARMS.NBT, this.showArms); -+ } -+ -+ if (this.small != null) { -+ this.entityTag.putBoolean(SMALL.NBT, this.small); -+ } -+ -+ if (this.marker != null) { -+ this.entityTag.putBoolean(MARKER.NBT, this.marker); -+ } -+ // Paper end - if (this.entityTag != null) { - tag.put(CraftMetaArmorStand.ENTITY_TAG, CustomData.of(this.entityTag)); + if (tag.contains(CraftMetaArmorStand.ENTITY_TAG.NBT)) { + this.entityTag = tag.getCompound(CraftMetaArmorStand.ENTITY_TAG.NBT); ++ if (!this.entityTag.contains(ENTITY_ID.NBT)) entityTag.putString(ENTITY_ID.NBT, "minecraft:armor_stand"); // Paper - fixup legacy armorstand metas that did not include this. } -@@ -75,7 +148,7 @@ public class CraftMetaArmorStand extends CraftMetaItem { + } + + @Override + void serializeInternal(Map internalTags) { +- if (this.entityTag != null && !this.entityTag.isEmpty()) { ++ if (false && this.entityTag != null && !this.entityTag.isEmpty()) { // Paper - now correctly serialised as entity tag + internalTags.put(CraftMetaArmorStand.ENTITY_TAG.NBT, this.entityTag); + } + } +@@ -75,7 +120,7 @@ public class CraftMetaArmorStand extends CraftMetaItem { } boolean isArmorStandEmpty() { - return !(this.entityTag != null); -+ return !(this.invisible != null || this.noBasePlate != null || this.showArms != null || this.small != null || this.marker != null || this.entityTag != null); // Paper ++ return entityTag == null || entityTag.isEmpty(); // Paper - consider armor stand empty if tag is empty. } @Override -@@ -86,7 +159,14 @@ public class CraftMetaArmorStand extends CraftMetaItem { +@@ -86,7 +131,9 @@ public class CraftMetaArmorStand extends CraftMetaItem { if (meta instanceof CraftMetaArmorStand) { CraftMetaArmorStand that = (CraftMetaArmorStand) meta; - return this.entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : this.entityTag == null; + // Paper start -+ return java.util.Objects.equals(this.entityTag, that.entityTag) && -+ this.invisible == that.invisible && -+ this.noBasePlate == that.noBasePlate && -+ this.showArms == that.showArms && -+ this.small == that.small && -+ this.marker == that.marker; ++ return java.util.Objects.equals(this.entityTag, that.entityTag); + // Paper end } return true; } -@@ -104,6 +184,13 @@ public class CraftMetaArmorStand extends CraftMetaItem { - if (this.entityTag != null) { - hash = 73 * hash + this.entityTag.hashCode(); - } -+ // Paper start -+ hash = 61 * hash + (this.invisible != null ? Boolean.hashCode(this.isInvisible()) : 0); -+ hash = 61 * hash + (this.noBasePlate != null ? Boolean.hashCode(this.hasNoBasePlate()) : 0); -+ hash = 61 * hash + (this.showArms != null ? Boolean.hashCode(this.shouldShowArms()) : 0); -+ hash = 61 * hash + (this.small != null ? Boolean.hashCode(this.isSmall()) : 0); -+ hash = 61 * hash + (this.marker != null ? Boolean.hashCode(this.isMarker()) : 0); -+ // Paper end - - return original != hash ? CraftMetaArmorStand.class.hashCode() ^ hash : hash; - } -@@ -112,6 +199,28 @@ public class CraftMetaArmorStand extends CraftMetaItem { +@@ -112,6 +159,21 @@ public class CraftMetaArmorStand extends CraftMetaItem { Builder serialize(Builder builder) { super.serialize(builder); + // Paper start -+ if (invisible != null) { -+ builder.put(INVISIBLE.BUKKIT, invisible); -+ } -+ -+ if (noBasePlate != null) { -+ builder.put(NO_BASE_PLATE.BUKKIT, noBasePlate); -+ } -+ -+ if (showArms != null) { -+ builder.put(SHOW_ARMS.BUKKIT, showArms); -+ } -+ -+ if (small != null) { -+ builder.put(SMALL.BUKKIT, small); -+ } -+ -+ if (marker != null) { -+ builder.put(MARKER.BUKKIT, marker); ++ if (entityTag == null) { ++ return builder; ++ } else if (true) { ++ java.io.ByteArrayOutputStream buf = new java.io.ByteArrayOutputStream(); ++ try { ++ net.minecraft.nbt.NbtIo.writeCompressed(entityTag, buf); ++ } catch (java.io.IOException ex) { ++ java.util.logging.Logger.getLogger(CraftMetaItem.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); ++ } ++ builder.put(ENTITY_TAG.BUKKIT, java.util.Base64.getEncoder().encodeToString(buf.toByteArray())); ++ return builder; + } + // Paper end + return builder; } -@@ -125,4 +234,56 @@ public class CraftMetaArmorStand extends CraftMetaItem { +@@ -125,4 +187,68 @@ public class CraftMetaArmorStand extends CraftMetaItem { return clone; } + + // Paper start ++ private void populateTagIfNull() { ++ if (this.entityTag == null) { ++ this.entityTag = new CompoundTag(); ++ this.entityTag.putString(ENTITY_ID.NBT, "minecraft:armor_stand"); ++ } ++ } ++ + @Override + public boolean isInvisible() { -+ return invisible != null && invisible; ++ return entityTag != null && entityTag.contains(INVISIBLE.NBT) && entityTag.getBoolean(INVISIBLE.NBT); + } + + @Override + public boolean hasNoBasePlate() { -+ return noBasePlate != null && noBasePlate; ++ return entityTag != null && entityTag.contains(NO_BASE_PLATE.NBT) && entityTag.getBoolean(NO_BASE_PLATE.NBT); + } + + @Override + public boolean shouldShowArms() { -+ return showArms != null && showArms; ++ return entityTag != null && entityTag.contains(SHOW_ARMS.NBT) && entityTag.getBoolean(SHOW_ARMS.NBT); + } + + @Override + public boolean isSmall() { -+ return small != null && small; ++ return entityTag != null && entityTag.contains(SMALL.NBT) && entityTag.getBoolean(SMALL.NBT); + } + + @Override + public boolean isMarker() { -+ return marker != null && marker; ++ return entityTag != null && entityTag.contains(MARKER.NBT) && entityTag.getBoolean(MARKER.NBT); + } + + @Override + public void setInvisible(boolean invisible) { -+ this.invisible = invisible; ++ populateTagIfNull(); ++ entityTag.putBoolean(INVISIBLE.NBT, invisible); + } + + @Override + public void setNoBasePlate(boolean noBasePlate) { -+ this.noBasePlate = noBasePlate; ++ populateTagIfNull(); ++ entityTag.putBoolean(NO_BASE_PLATE.NBT, noBasePlate); + } + + @Override + public void setShowArms(boolean showArms) { -+ this.showArms = showArms; ++ populateTagIfNull(); ++ entityTag.putBoolean(SHOW_ARMS.NBT, showArms); + } + + @Override + public void setSmall(boolean small) { -+ this.small = small; ++ populateTagIfNull(); ++ entityTag.putBoolean(SMALL.NBT, small); + } + + @Override + public void setMarker(boolean marker) { -+ this.marker = marker; ++ populateTagIfNull(); ++ entityTag.putBoolean(MARKER.NBT, marker); + } + // Paper end } diff --git a/patches/server/0965-improve-checking-handled-tags-in-itemmeta.patch b/patches/server/0965-improve-checking-handled-tags-in-itemmeta.patch index 64ca3a97f846..367f1b1a4afc 100644 --- a/patches/server/0965-improve-checking-handled-tags-in-itemmeta.patch +++ b/patches/server/0965-improve-checking-handled-tags-in-itemmeta.patch @@ -246,10 +246,10 @@ index 865977ce17fbb8793a1eefd71079729e83f5cfaf..889d43acf4cf7a5917f110105ed05838 getOrEmpty(tag, CraftMetaArmor.TRIM).ifPresent((trimCompound) -> { TrimMaterial trimMaterial = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(io.papermc.paper.registry.RegistryKey.TRIM_MATERIAL, trimCompound.material()).orElse(null); // Paper - fix upstream not being correct diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java -index 59bdac414e8205ed608f79ef0d1502acd826d216..53df7e876c9f3e67aa2326fa1a5ce5e90ab7efd6 100644 +index b675326f6a4572c60f20efab01f577804eda9221..10d8c2d2d45905745d8dc09b45933307c0aacdc4 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java -@@ -47,8 +47,8 @@ public class CraftMetaArmorStand extends CraftMetaItem implements com.destroysto +@@ -35,8 +35,8 @@ public class CraftMetaArmorStand extends CraftMetaItem implements com.destroysto this.entityTag = armorStand.entityTag; }