From ae4acc6aeb299c7d9dd0bed182611cd5f05eaea1 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 10 Jul 2023 16:10:15 -0700
Subject: [PATCH] improve checking handled tags in itemmeta

---
 .../craftbukkit/inventory/CraftItemMetas.java |  58 +++----
 .../craftbukkit/inventory/CraftItemStack.java |  25 ++-
 .../craftbukkit/inventory/CraftItemType.java  |   4 +-
 .../craftbukkit/inventory/CraftMetaArmor.java |   4 +-
 .../inventory/CraftMetaArmorStand.java        |   4 +-
 .../inventory/CraftMetaAxolotlBucket.java     |   4 +-
 .../inventory/CraftMetaBanner.java            |   4 +-
 .../inventory/CraftMetaBlockState.java        |   4 +-
 .../craftbukkit/inventory/CraftMetaBook.java  |   4 +-
 .../inventory/CraftMetaBookSigned.java        |   4 +-
 .../inventory/CraftMetaBundle.java            |   4 +-
 .../inventory/CraftMetaCharge.java            |   4 +-
 .../inventory/CraftMetaColorableArmor.java    |   4 +-
 .../inventory/CraftMetaCompass.java           |   4 +-
 .../inventory/CraftMetaCrossbow.java          |   4 +-
 .../inventory/CraftMetaEnchantedBook.java     |   4 +-
 .../inventory/CraftMetaEntityTag.java         |   4 +-
 .../inventory/CraftMetaFirework.java          |   4 +-
 .../craftbukkit/inventory/CraftMetaItem.java  | 151 ++++++++++--------
 .../inventory/CraftMetaKnowledgeBook.java     |   4 +-
 .../inventory/CraftMetaLeatherArmor.java      |   4 +-
 .../craftbukkit/inventory/CraftMetaMap.java   |   4 +-
 .../inventory/CraftMetaMusicInstrument.java   |   4 +-
 .../inventory/CraftMetaOminousBottle.java     |   4 +-
 .../inventory/CraftMetaPotion.java            |   4 +-
 .../inventory/CraftMetaShield.java            |   4 +-
 .../craftbukkit/inventory/CraftMetaSkull.java |   4 +-
 .../inventory/CraftMetaSpawnEgg.java          |   4 +-
 .../inventory/CraftMetaSuspiciousStew.java    |   4 +-
 .../CraftMetaTropicalFishBucket.java          |   4 +-
 .../DeprecatedItemMetaCustomValueTest.java    |   2 +-
 .../inventory/MetaHandledTagsTest.java        |  33 ++++
 .../PersistentDataContainerTest.java          |   6 +-
 33 files changed, 225 insertions(+), 158 deletions(-)
 create mode 100644 paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java

diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemMetas.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemMetas.java
index d29f4dd980..1fd6e5a1ad 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemMetas.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemMetas.java
@@ -40,120 +40,120 @@ import org.bukkit.inventory.meta.TropicalFishBucketMeta;
 
 public final class CraftItemMetas {
 
-    public record ItemMetaData<I extends ItemMeta>(Class<I> metaClass, Function<ItemStack, I> fromItemStack,
+    public record ItemMetaData<I extends ItemMeta>(Class<I> metaClass, BiFunction<ItemStack, java.util.Set<net.minecraft.core.component.DataComponentType<?>>, I> fromItemStack,
                                                    BiFunction<ItemType.Typed<I>, CraftMetaItem, I> fromItemMeta) {
     }
 
     private static final ItemMetaData<ItemMeta> EMPTY_META_DATA = new ItemMetaData<>(ItemMeta.class,
-            item -> null,
+            (item, extras) -> null,
             (type, meta) -> null);
 
     private static final ItemMetaData<ItemMeta> ITEM_META_DATA = new ItemMetaData<>(ItemMeta.class,
-            item -> new CraftMetaItem(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaItem(item.getComponentsPatch(), extras),
             (type, meta) -> new CraftMetaItem(meta));
 
     private static final ItemMetaData<BookMeta> SIGNED_BOOK_META_DATA = new ItemMetaData<>(BookMeta.class,
-            item -> new CraftMetaBookSigned(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaBookSigned(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaBookSigned signed ? signed : new CraftMetaBookSigned(meta));
 
     private static final ItemMetaData<BookMeta> WRITABLE_BOOK_META_DATA = new ItemMetaData<>(BookMeta.class,
-            item -> new CraftMetaBook(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaBook(item.getComponentsPatch(), extras),
             (type, meta) -> meta != null && meta.getClass().equals(CraftMetaBook.class) ? (BookMeta) meta : new CraftMetaBook(meta));
 
     private static final ItemMetaData<SkullMeta> SKULL_META_DATA = new ItemMetaData<>(SkullMeta.class,
-            item -> new CraftMetaSkull(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaSkull(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaSkull skull ? skull : new CraftMetaSkull(meta));
 
     private static final ItemMetaData<ArmorMeta> ARMOR_META_DATA = new ItemMetaData<>(ArmorMeta.class,
-            item -> new CraftMetaArmor(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaArmor(item.getComponentsPatch(), extras),
             (type, meta) -> meta != null && meta.getClass().equals(CraftMetaArmor.class) ? (ArmorMeta) meta : new CraftMetaArmor(meta));
 
     private static final ItemMetaData<ColorableArmorMeta> COLORABLE_ARMOR_META_DATA = new ItemMetaData<>(ColorableArmorMeta.class,
-            item -> new CraftMetaColorableArmor(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaColorableArmor(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof ColorableArmorMeta colorable ? colorable : new CraftMetaColorableArmor(meta));
 
     private static final ItemMetaData<LeatherArmorMeta> LEATHER_ARMOR_META_DATA = new ItemMetaData<>(LeatherArmorMeta.class,
-            item -> new CraftMetaLeatherArmor(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaLeatherArmor(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaLeatherArmor leather ? leather : new CraftMetaLeatherArmor(meta));
 
     private static final ItemMetaData<PotionMeta> POTION_META_DATA = new ItemMetaData<>(PotionMeta.class,
-            item -> new CraftMetaPotion(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaPotion(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaPotion potion ? potion : new CraftMetaPotion(meta));
 
     private static final ItemMetaData<MapMeta> MAP_META_DATA = new ItemMetaData<>(MapMeta.class,
-            item -> new CraftMetaMap(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaMap(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaMap map ? map : new CraftMetaMap(meta));
 
     private static final ItemMetaData<FireworkMeta> FIREWORK_META_DATA = new ItemMetaData<>(FireworkMeta.class,
-            item -> new CraftMetaFirework(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaFirework(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaFirework firework ? firework : new CraftMetaFirework(meta));
 
     private static final ItemMetaData<FireworkEffectMeta> CHARGE_META_DATA = new ItemMetaData<>(FireworkEffectMeta.class,
-            item -> new CraftMetaCharge(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaCharge(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaCharge charge ? charge : new CraftMetaCharge(meta));
 
     private static final ItemMetaData<EnchantmentStorageMeta> ENCHANTED_BOOK_META_DATA = new ItemMetaData<>(EnchantmentStorageMeta.class,
-            item -> new CraftMetaEnchantedBook(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaEnchantedBook(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaEnchantedBook enchantedBook ? enchantedBook : new CraftMetaEnchantedBook(meta));
 
     private static final ItemMetaData<BannerMeta> BANNER_META_DATA = new ItemMetaData<>(BannerMeta.class,
-            item -> new CraftMetaBanner(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaBanner(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaBanner banner ? banner : new CraftMetaBanner(meta));
 
     private static final ItemMetaData<SpawnEggMeta> SPAWN_EGG_META_DATA = new ItemMetaData<>(SpawnEggMeta.class,
-            item -> new CraftMetaSpawnEgg(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaSpawnEgg(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaSpawnEgg spawnEgg ? spawnEgg : new CraftMetaSpawnEgg(meta));
 
     private static final ItemMetaData<ArmorStandMeta> ARMOR_STAND_META_DATA = new ItemMetaData<>(ArmorStandMeta.class, // paper
-            item -> new CraftMetaArmorStand(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaArmorStand(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaArmorStand armorStand ? armorStand : new CraftMetaArmorStand(meta));
 
     private static final ItemMetaData<KnowledgeBookMeta> KNOWLEDGE_BOOK_META_DATA = new ItemMetaData<>(KnowledgeBookMeta.class,
-            item -> new CraftMetaKnowledgeBook(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaKnowledgeBook(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaKnowledgeBook knowledgeBook ? knowledgeBook : new CraftMetaKnowledgeBook(meta));
 
     private static final ItemMetaData<BlockStateMeta> BLOCK_STATE_META_DATA = new ItemMetaData<>(BlockStateMeta.class,
-            item -> new CraftMetaBlockState(item.getComponentsPatch(), CraftItemType.minecraftToBukkit(item.getItem())),
+            (item, extras) -> new CraftMetaBlockState(item.getComponentsPatch(), CraftItemType.minecraftToBukkit(item.getItem()), extras),
             (type, meta) -> new CraftMetaBlockState(meta, type.asMaterial()));
 
     private static final ItemMetaData<ShieldMeta> SHIELD_META_DATA = new ItemMetaData<>(ShieldMeta.class,
-            item -> new CraftMetaShield(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaShield(item.getComponentsPatch(), extras),
             (type, meta) -> new CraftMetaShield(meta));
 
     private static final ItemMetaData<TropicalFishBucketMeta> TROPICAL_FISH_BUCKET_META_DATA = new ItemMetaData<>(TropicalFishBucketMeta.class,
-            item -> new CraftMetaTropicalFishBucket(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaTropicalFishBucket(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaTropicalFishBucket tropicalFishBucket ? tropicalFishBucket : new CraftMetaTropicalFishBucket(meta));
 
     private static final ItemMetaData<AxolotlBucketMeta> AXOLOTL_BUCKET_META_DATA = new ItemMetaData<>(AxolotlBucketMeta.class,
-            item -> new CraftMetaAxolotlBucket(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaAxolotlBucket(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaAxolotlBucket axolotlBucket ? axolotlBucket : new CraftMetaAxolotlBucket(meta));
 
     private static final ItemMetaData<CrossbowMeta> CROSSBOW_META_DATA = new ItemMetaData<>(CrossbowMeta.class,
-            item -> new CraftMetaCrossbow(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaCrossbow(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaCrossbow crossbow ? crossbow : new CraftMetaCrossbow(meta));
 
     private static final ItemMetaData<SuspiciousStewMeta> SUSPICIOUS_STEW_META_DATA = new ItemMetaData<>(SuspiciousStewMeta.class,
-            item -> new CraftMetaSuspiciousStew(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaSuspiciousStew(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaSuspiciousStew suspiciousStew ? suspiciousStew : new CraftMetaSuspiciousStew(meta));
 
     private static final ItemMetaData<ItemMeta> ENTITY_TAG_META_DATA = new ItemMetaData<>(ItemMeta.class,
-            item -> new CraftMetaEntityTag(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaEntityTag(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaEntityTag entityTag ? entityTag : new CraftMetaEntityTag(meta));
 
     private static final ItemMetaData<CompassMeta> COMPASS_META_DATA = new ItemMetaData<>(CompassMeta.class,
-            item -> new CraftMetaCompass(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaCompass(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaCompass compass ? compass : new CraftMetaCompass(meta));
 
     private static final ItemMetaData<BundleMeta> BUNDLE_META_DATA = new ItemMetaData<>(BundleMeta.class,
-            item -> new CraftMetaBundle(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaBundle(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaBundle bundle ? bundle : new CraftMetaBundle(meta));
 
     private static final ItemMetaData<MusicInstrumentMeta> MUSIC_INSTRUMENT_META_DATA = new ItemMetaData<>(MusicInstrumentMeta.class,
-            item -> new CraftMetaMusicInstrument(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaMusicInstrument(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaMusicInstrument musicInstrument ? musicInstrument : new CraftMetaMusicInstrument(meta));
 
     private static final ItemMetaData<OminousBottleMeta> OMINOUS_BOTTLE_META_DATA = new ItemMetaData<>(OminousBottleMeta.class,
-            item -> new CraftMetaOminousBottle(item.getComponentsPatch()),
+            (item, extras) -> new CraftMetaOminousBottle(item.getComponentsPatch(), extras),
             (type, meta) -> meta instanceof CraftMetaOminousBottle musicInstrument ? musicInstrument : new CraftMetaOminousBottle(meta));
 
     // We use if instead of a set, since the result gets cached in CraftItemType,
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index 7228d43d33..dfdabf8643 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -173,10 +173,11 @@ public final class CraftItemStack extends ItemStack {
         } else if (this.handle == null) {
             this.handle = new net.minecraft.world.item.ItemStack(CraftItemType.bukkitToMinecraft(type), 1);
         } else {
+            final Material oldType = CraftMagicNumbers.getMaterial(this.handle.getItem()); // Paper
             this.handle.setItem(CraftItemType.bukkitToMinecraft(type));
             if (this.hasItemMeta()) {
                 // This will create the appropriate item meta, which will contain all the data we intend to keep
-                CraftItemStack.setItemMeta(this.handle, CraftItemStack.getItemMeta(this.handle));
+                this.adjustTagForItemMeta(oldType); // Paper
             }
         }
         this.setData(null);
@@ -337,6 +338,19 @@ public final class CraftItemStack extends ItemStack {
     public ItemMeta getItemMeta() {
         return CraftItemStack.getItemMeta(this.handle);
     }
+    // Paper start - improve handled tags on type change
+    public void adjustTagForItemMeta(final Material oldType) {
+        final CraftMetaItem oldMeta = (CraftMetaItem) CraftItemFactory.instance().getItemMeta(oldType);
+        final ItemMeta newMeta;
+        if (oldMeta == null) {
+            newMeta = getItemMeta(this.handle);
+        } else {
+            final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts = new java.util.HashSet<>(CraftMetaItem.getTopLevelHandledDcts(oldMeta.getClass()));
+            newMeta = getItemMeta(this.handle, CraftItemType.minecraftToBukkitNew(this.handle.getItem()), extraHandledDcts);
+        }
+        this.setItemMeta(newMeta);
+    }
+    // Paper end - improve handled tags on type change
     // Paper start
     public static void applyMetaToItem(net.minecraft.world.item.ItemStack itemStack, ItemMeta itemMeta) {
         final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator();
@@ -349,12 +363,17 @@ public final class CraftItemStack extends ItemStack {
     }
     public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org.bukkit.inventory.ItemType metaForType) {
         // Paper end
+        // Paper start - handled tags on type change
+        return getItemMeta(item, metaForType, null);
+    }
+    public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org.bukkit.inventory.ItemType metaForType, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) {
+        // Paper end - handled tags on type change
         if (!CraftItemStack.hasItemMeta(item)) {
             return CraftItemFactory.instance().getItemMeta(CraftItemStack.getType(item));
         }
 
-        if (metaForType != null) { return ((CraftItemType<?>) metaForType).getItemMeta(item); } // Paper
-        return ((CraftItemType<?>) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item);
+        if (metaForType != null) { return ((CraftItemType<?>) metaForType).getItemMeta(item, extraHandledDcts); } // Paper
+        return ((CraftItemType<?>) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item, extraHandledDcts); // Paper
     }
 
     static Material getType(net.minecraft.world.item.ItemStack item) {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
index 6d76cc1db3..f4a6ee6dfc 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
@@ -114,8 +114,8 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
         return this.item;
     }
 
-    public M getItemMeta(net.minecraft.world.item.ItemStack itemStack) {
-        return this.itemMetaData.get().fromItemStack().apply(itemStack);
+    public M getItemMeta(net.minecraft.world.item.ItemStack itemStack, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) {
+        return this.itemMetaData.get().fromItemStack().apply(itemStack, extraHandledDcts);
     }
 
     public M getItemMeta(ItemMeta itemMeta) {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
index 0fdd9dd475..6532bdaf6c 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
@@ -34,8 +34,8 @@ public class CraftMetaArmor extends CraftMetaItem implements ArmorMeta {
         }
     }
 
-    CraftMetaArmor(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaArmor.TRIM).ifPresent((trimCompound) -> {
             TrimMaterial trimMaterial = CraftTrimMaterial.minecraftHolderToBukkit(trimCompound.material());
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
index ecce5d0da9..00b5c4ab61 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
@@ -35,8 +35,8 @@ public class CraftMetaArmorStand extends CraftMetaItem implements com.destroysto
         this.entityTag = armorStand.entityTag;
     }
 
-    CraftMetaArmorStand(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaArmorStand(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaArmorStand.ENTITY_TAG).ifPresent((nbt) -> {
             this.entityTag = nbt.copyTag();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
index c4beb94d8e..169fefb64e 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
@@ -36,8 +36,8 @@ public class CraftMetaAxolotlBucket extends CraftMetaItem implements AxolotlBuck
         this.bucketEntityTag = bucket.bucketEntityTag;
     }
 
-    CraftMetaAxolotlBucket(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaAxolotlBucket(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaAxolotlBucket.ENTITY_TAG).ifPresent((nbt) -> {
             this.entityTag = nbt.copyTag();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
index eb44c19f6a..d0a8cd89da 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
@@ -34,8 +34,8 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta {
         this.patterns = new ArrayList<Pattern>(banner.patterns);
     }
 
-    CraftMetaBanner(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaBanner(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaBanner.PATTERNS).ifPresent((entityTag) -> {
             List<BannerPatternLayers.Layer> patterns = entityTag.layers();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
index 3985e5b4e2..413e41f113 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
@@ -73,8 +73,8 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
         this.position = te.position;
     }
 
-    CraftMetaBlockState(DataComponentPatch tag, Material material) {
-        super(tag);
+    CraftMetaBlockState(DataComponentPatch tag, Material material, final Set<DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         this.material = material;
 
         getOrEmpty(tag, CraftMetaBlockState.BLOCK_ENTITY_TAG).ifPresent((blockTag) -> {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
index 32e5188442..257c835bc2 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
@@ -64,8 +64,8 @@ public class CraftMetaBook extends CraftMetaItem implements BookMeta, WritableBo
         }
     }
 
-    CraftMetaBook(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaBook(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaBook.BOOK_CONTENT).ifPresent((writable) -> {
             List<Filterable<String>> pages = writable.pages();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
index fd3b12477c..cbb3d80cc7 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
@@ -78,8 +78,8 @@ public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta {
         }
     }
 
-    CraftMetaBookSigned(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaBookSigned(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaBookSigned.BOOK_CONTENT).ifPresent((written) -> {
             this.title = written.title().raw();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
index 30533ce683..2736a87a6c 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
@@ -34,8 +34,8 @@ public class CraftMetaBundle extends CraftMetaItem implements BundleMeta {
         }
     }
 
-    CraftMetaBundle(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaBundle(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaBundle.ITEMS).ifPresent((bundle) -> {
             bundle.items().forEach((item) -> {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java
index 72340e7269..56c6784e29 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java
@@ -29,8 +29,8 @@ class CraftMetaCharge extends CraftMetaItem implements FireworkEffectMeta {
         this.setEffect(SerializableMeta.getObject(FireworkEffect.class, map, CraftMetaCharge.EXPLOSION.BUKKIT, true));
     }
 
-    CraftMetaCharge(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaCharge(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaCharge.EXPLOSION).ifPresent((f) -> {
             try {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
index 366fec1aee..6517ec4933 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
@@ -18,8 +18,8 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
         CraftMetaLeatherArmor.readColor(this, meta);
     }
 
-    CraftMetaColorableArmor(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaColorableArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         CraftMetaLeatherArmor.readColor(this, tag);
     }
 
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
index 607e230403..69a112b3a9 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
@@ -50,8 +50,8 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta {
         this.tracked = compassMeta.tracked;
     }
 
-    CraftMetaCompass(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaCompass(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         getOrEmpty(tag, CraftMetaCompass.LODESTONE_TARGET).ifPresent((lodestoneTarget) -> {
             lodestoneTarget.target().ifPresent((target) -> {
                 this.lodestoneWorld = target.dimension();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
index c278af5193..0807c2172c 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
@@ -36,8 +36,8 @@ public class CraftMetaCrossbow extends CraftMetaItem implements CrossbowMeta {
         }
     }
 
-    CraftMetaCrossbow(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaCrossbow(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaCrossbow.CHARGED_PROJECTILES).ifPresent((p) -> {
             List<net.minecraft.world.item.ItemStack> list = p.getItems();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
index 19af55ec2b..c93f769ee6 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
@@ -32,8 +32,8 @@ class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorage
         }
     }
 
-    CraftMetaEnchantedBook(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaEnchantedBook(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaEnchantedBook.STORED_ENCHANTMENTS).ifPresent((itemEnchantments) -> {
             this.enchantments = buildEnchantments(itemEnchantments);
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
index 3ff0340c40..3f6c5cbbf6 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
@@ -39,8 +39,8 @@ public class CraftMetaEntityTag extends CraftMetaItem {
         this.entityTag = entity.entityTag;
     }
 
-    CraftMetaEntityTag(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaEntityTag(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaEntityTag.ENTITY_TAG).ifPresent((nbt) -> {
             this.entityTag = nbt.copyTag();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
index 4941e0afff..566d893a41 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
@@ -60,8 +60,8 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
         }
     }
 
-    CraftMetaFirework(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaFirework(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaFirework.FIREWORKS).ifPresent((fireworks) -> {
             this.power = fireworks.flightDuration();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
index cf85cd7426..22f2376365 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
@@ -393,7 +393,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
         // Paper end
     }
 
-    CraftMetaItem(DataComponentPatch tag) {
+    CraftMetaItem(DataComponentPatch tag, Set<DataComponentType<?>> extraHandledTags) { // Paper - improve handled tags on type changes
         CraftMetaItem.getOrEmpty(tag, CraftMetaItem.NAME).ifPresent((component) -> {
             this.displayName = component;
         });
@@ -525,9 +525,16 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
         });
         // Paper end - fix ItemFlags
 
+        // Paper start - improve checking handled data component types
+        Set<DataComponentType<?>> handledTags = getTopLevelHandledDcts(this.getClass());
+        if (extraHandledTags != null) {
+            extraHandledTags.addAll(handledTags);
+            handledTags = extraHandledTags;
+        }
+        // Paper end - improve checking handled data component types
         Set<Map.Entry<DataComponentType<?>, Optional<?>>> keys = tag.entrySet();
         for (Map.Entry<DataComponentType<?>, Optional<?>> key : keys) {
-            if (!CraftMetaItem.getHandledTags().contains(key.getKey())) {
+            if (!handledTags.contains(key.getKey())) { // Paper - improve checking handled data component types
                 key.getValue().ifPresent((value) -> {
                     this.unhandledTags.set((DataComponentType) key.getKey(), value);
                 });
@@ -2372,75 +2379,83 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
         this.version = version;
     }
 
-    public static Set<DataComponentType> getHandledTags() {
-        synchronized (CraftMetaItem.HANDLED_TAGS) {
-            if (CraftMetaItem.HANDLED_TAGS.isEmpty()) {
-                CraftMetaItem.HANDLED_TAGS.addAll(Arrays.asList(
-                        CraftMetaItem.NAME.TYPE,
-                        CraftMetaItem.ITEM_NAME.TYPE,
-                        CraftMetaItem.LORE.TYPE,
-                        CraftMetaItem.CUSTOM_MODEL_DATA.TYPE,
-                        CraftMetaItem.ENCHANTABLE.TYPE,
-                        CraftMetaItem.BLOCK_DATA.TYPE,
-                        CraftMetaItem.REPAIR.TYPE,
-                        CraftMetaItem.ENCHANTMENTS.TYPE,
-                        CraftMetaItem.HIDE_ADDITIONAL_TOOLTIP.TYPE,
-                        CraftMetaItem.HIDE_TOOLTIP.TYPE,
-                        CraftMetaItem.TOOLTIP_STYLE.TYPE,
-                        CraftMetaItem.ITEM_MODEL.TYPE,
-                        CraftMetaItem.UNBREAKABLE.TYPE,
-                        CraftMetaItem.ENCHANTMENT_GLINT_OVERRIDE.TYPE,
-                        CraftMetaItem.GLIDER.TYPE,
-                        CraftMetaItem.DAMAGE_RESISTANT.TYPE,
-                        CraftMetaItem.MAX_STACK_SIZE.TYPE,
-                        CraftMetaItem.RARITY.TYPE,
-                        CraftMetaItem.USE_REMAINDER.TYPE,
-                        CraftMetaItem.USE_COOLDOWN.TYPE,
-                        CraftMetaItem.FOOD.TYPE,
-                        CraftMetaItem.TOOL.TYPE,
-                        CraftMetaItem.EQUIPPABLE.TYPE,
-                        CraftMetaItem.JUKEBOX_PLAYABLE.TYPE,
-                        CraftMetaItem.DAMAGE.TYPE,
-                        CraftMetaItem.MAX_DAMAGE.TYPE,
-                        CraftMetaItem.CUSTOM_DATA.TYPE,
-                        CraftMetaItem.ATTRIBUTES.TYPE,
-                        CraftMetaItem.CAN_PLACE_ON.TYPE, // Paper
-                        CraftMetaItem.CAN_BREAK.TYPE, // Paper
-                        CraftMetaArmor.TRIM.TYPE,
-                        CraftMetaArmorStand.ENTITY_TAG.TYPE,
-                        CraftMetaBanner.PATTERNS.TYPE,
-                        CraftMetaEntityTag.ENTITY_TAG.TYPE,
-                        CraftMetaLeatherArmor.COLOR.TYPE,
-                        CraftMetaMap.MAP_POST_PROCESSING.TYPE,
-                        CraftMetaMap.MAP_COLOR.TYPE,
-                        CraftMetaMap.MAP_ID.TYPE,
-                        CraftMetaPotion.POTION_CONTENTS.TYPE,
-                        CraftMetaShield.BASE_COLOR.TYPE,
-                        CraftMetaSkull.SKULL_PROFILE.TYPE,
-                        CraftMetaSkull.NOTE_BLOCK_SOUND.TYPE,
-                        CraftMetaSpawnEgg.ENTITY_TAG.TYPE,
-                        CraftMetaBlockState.BLOCK_ENTITY_TAG.TYPE,
-                        CraftMetaBook.BOOK_CONTENT.TYPE,
-                        CraftMetaBookSigned.BOOK_CONTENT.TYPE,
-                        CraftMetaFirework.FIREWORKS.TYPE,
-                        CraftMetaEnchantedBook.STORED_ENCHANTMENTS.TYPE,
-                        CraftMetaCharge.EXPLOSION.TYPE,
-                        CraftMetaKnowledgeBook.BOOK_RECIPES.TYPE,
-                        CraftMetaTropicalFishBucket.ENTITY_TAG.TYPE,
-                        CraftMetaTropicalFishBucket.BUCKET_ENTITY_TAG.TYPE,
-                        CraftMetaAxolotlBucket.ENTITY_TAG.TYPE,
-                        CraftMetaAxolotlBucket.BUCKET_ENTITY_TAG.TYPE,
-                        CraftMetaCrossbow.CHARGED_PROJECTILES.TYPE,
-                        CraftMetaSuspiciousStew.EFFECTS.TYPE,
-                        CraftMetaCompass.LODESTONE_TARGET.TYPE,
-                        CraftMetaBundle.ITEMS.TYPE,
-                        CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT.TYPE,
-                        CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER.TYPE
-                ));
+    // Paper start - improve checking handled tags
+    @org.jetbrains.annotations.VisibleForTesting
+    public static final Map<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> HANDLED_DCTS_PER_TYPE = new HashMap<>();
+    private static final Set<DataComponentType<?>> DEFAULT_HANDLED_DCTS = Set.of(
+        CraftMetaItem.NAME.TYPE,
+        CraftMetaItem.ITEM_NAME.TYPE,
+        CraftMetaItem.LORE.TYPE,
+        CraftMetaItem.CUSTOM_MODEL_DATA.TYPE,
+        CraftMetaItem.ENCHANTABLE.TYPE,
+        CraftMetaItem.BLOCK_DATA.TYPE,
+        CraftMetaItem.REPAIR.TYPE,
+        CraftMetaItem.ENCHANTMENTS.TYPE,
+        CraftMetaItem.HIDE_ADDITIONAL_TOOLTIP.TYPE,
+        CraftMetaItem.HIDE_TOOLTIP.TYPE,
+        CraftMetaItem.TOOLTIP_STYLE.TYPE,
+        CraftMetaItem.ITEM_MODEL.TYPE,
+        CraftMetaItem.UNBREAKABLE.TYPE,
+        CraftMetaItem.ENCHANTMENT_GLINT_OVERRIDE.TYPE,
+        CraftMetaItem.GLIDER.TYPE,
+        CraftMetaItem.DAMAGE_RESISTANT.TYPE,
+        CraftMetaItem.MAX_STACK_SIZE.TYPE,
+        CraftMetaItem.RARITY.TYPE,
+        CraftMetaItem.USE_REMAINDER.TYPE,
+        CraftMetaItem.USE_COOLDOWN.TYPE,
+        CraftMetaItem.FOOD.TYPE,
+        CraftMetaItem.TOOL.TYPE,
+        CraftMetaItem.EQUIPPABLE.TYPE,
+        CraftMetaItem.JUKEBOX_PLAYABLE.TYPE,
+        CraftMetaItem.DAMAGE.TYPE,
+        CraftMetaItem.MAX_DAMAGE.TYPE,
+        CraftMetaItem.CUSTOM_DATA.TYPE,
+        CraftMetaItem.ATTRIBUTES.TYPE,
+        CraftMetaItem.CAN_PLACE_ON.TYPE, // Paper
+        CraftMetaItem.CAN_BREAK.TYPE // Paper
+    );
+    public static Set<DataComponentType<?>> getTopLevelHandledDcts(final Class<? extends CraftMetaItem> clazz) {
+        synchronized (HANDLED_DCTS_PER_TYPE) {
+            if (HANDLED_DCTS_PER_TYPE.isEmpty()) {
+                final Map<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> map = new HashMap<>();
+                map.put(CraftMetaArmor.class, Set.of(CraftMetaArmor.TRIM.TYPE));
+                map.put(CraftMetaArmorStand.class, Set.of(CraftMetaArmorStand.ENTITY_TAG.TYPE));
+                map.put(CraftMetaAxolotlBucket.class, Set.of(CraftMetaAxolotlBucket.ENTITY_TAG.TYPE, CraftMetaAxolotlBucket.BUCKET_ENTITY_TAG.TYPE));
+                map.put(CraftMetaBanner.class, Set.of(CraftMetaBanner.PATTERNS.TYPE)); // banner uses same tag as block state
+                map.put(CraftMetaShield.class, Set.of(CraftMetaShield.BASE_COLOR.TYPE, CraftMetaBanner.PATTERNS.TYPE));
+                map.put(CraftMetaBlockState.class, Set.of(CraftMetaBlockState.BLOCK_ENTITY_TAG.TYPE));
+                map.put(CraftMetaBook.class, Set.of(CraftMetaBook.BOOK_CONTENT.TYPE));
+                map.put(CraftMetaBookSigned.class, Set.of(CraftMetaBookSigned.BOOK_CONTENT.TYPE));
+                map.put(CraftMetaBundle.class, Set.of(CraftMetaBundle.ITEMS.TYPE));
+                map.put(CraftMetaCharge.class, Set.of(CraftMetaCharge.EXPLOSION.TYPE));
+                map.put(CraftMetaColorableArmor.class, Set.of(CraftMetaArmor.TRIM.TYPE, CraftMetaLeatherArmor.COLOR.TYPE));
+                map.put(CraftMetaCompass.class, Set.of(CraftMetaCompass.LODESTONE_TARGET.TYPE));
+                map.put(CraftMetaCrossbow.class, Set.of(CraftMetaCrossbow.CHARGED_PROJECTILES.TYPE));
+                map.put(CraftMetaEnchantedBook.class, Set.of(CraftMetaEnchantedBook.STORED_ENCHANTMENTS.TYPE));
+                map.put(CraftMetaEntityTag.class, Set.of(CraftMetaEntityTag.ENTITY_TAG.TYPE));
+                map.put(CraftMetaFirework.class, Set.of(CraftMetaFirework.FIREWORKS.TYPE));
+                map.put(CraftMetaKnowledgeBook.class, Set.of(CraftMetaKnowledgeBook.BOOK_RECIPES.TYPE));
+                map.put(CraftMetaLeatherArmor.class, Set.of(CraftMetaLeatherArmor.COLOR.TYPE));
+                map.put(CraftMetaMap.class, Set.of(CraftMetaMap.MAP_COLOR.TYPE, CraftMetaMap.MAP_POST_PROCESSING.TYPE, CraftMetaMap.MAP_ID.TYPE));
+                map.put(CraftMetaMusicInstrument.class, Set.of(CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT.TYPE));
+                map.put(CraftMetaOminousBottle.class, Set.of(CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER.TYPE));
+                map.put(CraftMetaPotion.class, Set.of(CraftMetaPotion.POTION_CONTENTS.TYPE));
+                map.put(CraftMetaSkull.class, Set.of(CraftMetaSkull.SKULL_PROFILE.TYPE, CraftMetaSkull.NOTE_BLOCK_SOUND.TYPE));
+                map.put(CraftMetaSpawnEgg.class, Set.of(CraftMetaSpawnEgg.ENTITY_TAG.TYPE));
+                map.put(CraftMetaSuspiciousStew.class, Set.of(CraftMetaSuspiciousStew.EFFECTS.TYPE));
+                map.put(CraftMetaTropicalFishBucket.class, Set.of(CraftMetaTropicalFishBucket.ENTITY_TAG.TYPE, CraftMetaTropicalFishBucket.BUCKET_ENTITY_TAG.TYPE));
+
+                for (final Map.Entry<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> entry : map.entrySet()) {
+                    final ArrayList<DataComponentType<?>> topLevelTags = new ArrayList<>(entry.getValue());
+                    // add tags common to CraftMetaItem to all
+                    topLevelTags.addAll(DEFAULT_HANDLED_DCTS);
+                    HANDLED_DCTS_PER_TYPE.put(entry.getKey(), Set.copyOf(topLevelTags));
+                }
             }
-            return CraftMetaItem.HANDLED_TAGS;
+            return HANDLED_DCTS_PER_TYPE.getOrDefault(clazz, DEFAULT_HANDLED_DCTS);
         }
     }
+    // Paper end - improve checking handled data component types
 
     protected static <T> Optional<? extends T> getOrEmpty(DataComponentPatch tag, ItemMetaKeyType<T> type) {
         Optional<? extends T> result = tag.get(type.TYPE);
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java
index 68c0a6d5e0..4dc7adb626 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java
@@ -32,8 +32,8 @@ public class CraftMetaKnowledgeBook extends CraftMetaItem implements KnowledgeBo
         }
     }
 
-    CraftMetaKnowledgeBook(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaKnowledgeBook(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaKnowledgeBook.BOOK_RECIPES).ifPresent((pages) -> {
             for (int i = 0; i < pages.size(); i++) {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
index 75a8ad6990..e8c950aa74 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
@@ -25,8 +25,8 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
         CraftMetaLeatherArmor.readColor(this, meta);
     }
 
-    CraftMetaLeatherArmor(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaLeatherArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         CraftMetaLeatherArmor.readColor(this, tag);
     }
 
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
index 08e18dcabb..149356981e 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
@@ -44,8 +44,8 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
         this.color = map.color;
     }
 
-    CraftMetaMap(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaMap(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaMap.MAP_ID).ifPresent((mapId) -> {
             this.mapId = mapId.id();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
index 478059eb3a..8423c6b67a 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
@@ -26,8 +26,8 @@ public class CraftMetaMusicInstrument extends CraftMetaItem implements MusicInst
         }
     }
 
-    CraftMetaMusicInstrument(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaMusicInstrument(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT).ifPresent((instrument) -> {
             this.instrument = CraftMusicInstrument.minecraftHolderToBukkit(instrument);
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
index ed272c7eb4..b118d8ecb5 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
@@ -24,8 +24,8 @@ public class CraftMetaOminousBottle extends CraftMetaItem implements OminousBott
         this.ominousBottleAmplifier = bottleMeta.ominousBottleAmplifier;
     }
 
-    CraftMetaOminousBottle(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaOminousBottle(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         getOrEmpty(tag, CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER).ifPresent((amplifier) -> {
             this.ominousBottleAmplifier = amplifier.value();
         });
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
index 96ad0f53d3..fd7b7f53c5 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
@@ -54,8 +54,8 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
         }
     }
 
-    CraftMetaPotion(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaPotion(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         getOrEmpty(tag, CraftMetaPotion.POTION_CONTENTS).ifPresent((potionContents) -> {
             potionContents.potion().ifPresent((potion) -> {
                 this.type = CraftPotionType.minecraftHolderToBukkit(potion);
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java
index bcd6cc29e4..967d8940ae 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaShield.java
@@ -42,8 +42,8 @@ public class CraftMetaShield extends CraftMetaItem implements ShieldMeta, BlockS
         }
     }
 
-    CraftMetaShield(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaShield(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper - improve checking handled tags in item meta
+        super(tag, extraHandledDcts); // Paper - improve checking handled tags in item meta
 
         getOrEmpty(tag, CraftMetaShield.BASE_COLOR).ifPresent((color) -> {
             this.banner = CraftMetaShield.getBlockState(DyeColor.getByWoolData((byte) color.getId()));
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
index ca714e165e..302906467b 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
@@ -48,8 +48,8 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
         this.noteBlockSound = skullMeta.noteBlockSound;
     }
 
-    CraftMetaSkull(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaSkull(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaSkull.SKULL_PROFILE).ifPresent(this::setProfile);
 
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
index 6c2c3b514b..8ddf091b3f 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
@@ -33,8 +33,8 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
         this.entityTag = egg.entityTag;
     }
 
-    CraftMetaSpawnEgg(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaSpawnEgg(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaSpawnEgg.ENTITY_TAG).ifPresent((nbt) -> {
             this.entityTag = nbt.copyTag();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java
index 248efddbad..7a43e326e5 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java
@@ -33,8 +33,8 @@ public class CraftMetaSuspiciousStew extends CraftMetaItem implements Suspicious
         }
     }
 
-    CraftMetaSuspiciousStew(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaSuspiciousStew(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
         getOrEmpty(tag, CraftMetaSuspiciousStew.EFFECTS).ifPresent((suspiciousStewEffects) -> {
             List<SuspiciousStewEffects.Entry> list = suspiciousStewEffects.effects();
             int length = list.size();
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
index a514fe98d3..17705059b8 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
@@ -38,8 +38,8 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB
         this.bucketEntityTag = bucket.bucketEntityTag;
     }
 
-    CraftMetaTropicalFishBucket(DataComponentPatch tag) {
-        super(tag);
+    CraftMetaTropicalFishBucket(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
+        super(tag, extraHandledDcts); // Paper
 
         getOrEmpty(tag, CraftMetaTropicalFishBucket.ENTITY_TAG).ifPresent((nbt) -> {
             this.entityTag = nbt.copyTag();
diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
index 91cfa32272..9cc1ef5c92 100644
--- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
+++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
@@ -97,7 +97,7 @@ public class DeprecatedItemMetaCustomValueTest {
         CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator();
         itemMeta.applyToItem(compound);
 
-        assertEquals(itemMeta, new CraftMetaItem(compound.build()));
+        assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper
     }
 
     @Test
diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java
new file mode 100644
index 0000000000..df20446af3
--- /dev/null
+++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java
@@ -0,0 +1,33 @@
+package org.bukkit.craftbukkit.inventory;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.ClassInfoList;
+import io.github.classgraph.ScanResult;
+import org.bukkit.support.environment.AllFeatures;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+// in cb package because of package-private stuff
+@AllFeatures
+class MetaHandledTagsTest {
+
+    @Test
+    public void checkAllMetasHaveHandledTags() {
+        try (final ScanResult result = new ClassGraph()
+            .whitelistPackages("org.bukkit.craftbukkit.inventory")
+            .enableClassInfo().scan()) {
+            final ClassInfoList subclasses = result.getSubclasses(CraftMetaItem.class.getName());
+            assertFalse(subclasses.isEmpty(), "found 0 sub types");
+            for (final ClassInfo subclass : subclasses) {
+                final Class<CraftMetaItem> clazz = subclass.loadClass(CraftMetaItem.class);
+                CraftMetaItem.getTopLevelHandledDcts(clazz); // load into map
+                assertTrue(CraftMetaItem.HANDLED_DCTS_PER_TYPE.containsKey(clazz), subclass.getName() + " not found in handled tags map");
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
index 5b16e6f5e5..130c4500a5 100644
--- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
+++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
@@ -131,7 +131,7 @@ public class PersistentDataContainerTest {
         CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator();
         itemMeta.applyToItem(compound);
 
-        assertEquals(itemMeta, new CraftMetaItem(compound.build()));
+        assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper
     }
 
     @Test
@@ -464,7 +464,7 @@ public class PersistentDataContainerTest {
 
     @Test
     public void testEmptyListApplicationToAnyType() throws IOException {
-        final CraftMetaItem craftItem = new CraftMetaItem(DataComponentPatch.EMPTY);
+        final CraftMetaItem craftItem = new CraftMetaItem(DataComponentPatch.EMPTY, null); // Paper
         final PersistentDataContainer container = craftItem.getPersistentDataContainer();
 
         container.set(PersistentDataContainerTest.requestKey("list"), PersistentDataType.LIST.strings(), List.of());
@@ -477,7 +477,7 @@ public class PersistentDataContainerTest {
         final CraftMetaItem.Applicator storage = new CraftMetaItem.Applicator();
         craftItem.applyToItem(storage);
 
-        final CraftMetaItem readItem = new CraftMetaItem(storage.build());
+        final CraftMetaItem readItem = new CraftMetaItem(storage.build(), null); // Paper
         final PersistentDataContainer readContainer = readItem.getPersistentDataContainer();
 
         assertTrue(readContainer.has(PersistentDataContainerTest.requestKey("list"), PersistentDataType.LIST.strings()));