From 26e90b9ffb811625a52e3819b667b08959f6b940 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 25 May 2024 16:37:35 -0700 Subject: [PATCH] Fix CraftMetaBlockState for data components (#10731) This will go on forever... --- ...-Fix-upstreams-block-state-factories.patch | 60 +++- .../server/1038-General-ItemMeta-fixes.patch | 316 ++++++++++++++++-- 2 files changed, 336 insertions(+), 40 deletions(-) diff --git a/patches/server/0638-Fix-upstreams-block-state-factories.patch b/patches/server/0638-Fix-upstreams-block-state-factories.patch index 2b35a7c6b3..caa81d33e3 100644 --- a/patches/server/0638-Fix-upstreams-block-state-factories.patch +++ b/patches/server/0638-Fix-upstreams-block-state-factories.patch @@ -13,7 +13,7 @@ the material type of the block at that location. public net.minecraft.world.level.block.entity.BlockEntityType validBlocks diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java -index 6e24673a793017ee857cf75bf9a74105ce76b773..e3c5f99b3ad91a9bb454f9ab95b1ccff0bb7b34c 100644 +index 5e4cc5f54e8bf6afdab3afdf7f25c7b494e0d53b..7fa16b8a99509cc8f28b25513f0a1595219fe607 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java @@ -371,7 +371,7 @@ public abstract class BlockEntity { @@ -56,7 +56,7 @@ index 92133f16c192f5caf9962a08401ff914550747f8..397eb1a101bd60f49dbb2fa8eddf28f6 // Paper start @Override diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java -index e40973b65ece11d9c5a76abad51f72e610bf02ab..411c2de93c71e480f95229c882cdf43b8801edc8 100644 +index 83ac5fc6cbbd249b5865ab203b150f53f01c9f05..b7ff7af2513204b151340538d50a65c850bdb75f 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.block.entity.BeehiveBlockEntity; @@ -67,6 +67,27 @@ index e40973b65ece11d9c5a76abad51f72e610bf02ab..411c2de93c71e480f95229c882cdf43b import net.minecraft.world.level.block.entity.BrewingStandBlockEntity; import net.minecraft.world.level.block.entity.BrushableBlockEntity; import net.minecraft.world.level.block.entity.CalibratedSculkSensorBlockEntity; +@@ -87,9 +88,9 @@ public final class CraftBlockStates { + private static class BlockEntityStateFactory> extends BlockStateFactory { + + private final BiFunction blockStateConstructor; +- private final BiFunction tileEntityConstructor; ++ private final BlockEntityType tileEntityConstructor; // Paper + +- protected BlockEntityStateFactory(Class blockStateType, BiFunction blockStateConstructor, BiFunction tileEntityConstructor) { ++ protected BlockEntityStateFactory(Class blockStateType, BiFunction blockStateConstructor, BlockEntityType tileEntityConstructor) { // Paper + super(blockStateType); + this.blockStateConstructor = blockStateConstructor; + this.tileEntityConstructor = tileEntityConstructor; +@@ -106,7 +107,7 @@ public final class CraftBlockStates { + } + + private T createTileEntity(BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData) { +- return this.tileEntityConstructor.apply(blockPosition, blockData); ++ return this.tileEntityConstructor.create(blockPosition, blockData); // Paper + } + + private B createBlockState(World world, T tileEntity) { @@ -118,228 +119,65 @@ public final class CraftBlockStates { private static final BlockStateFactory DEFAULT_FACTORY = new BlockStateFactory(CraftBlockState.class) { @Override @@ -354,26 +375,26 @@ index e40973b65ece11d9c5a76abad51f72e610bf02ab..411c2de93c71e480f95229c882cdf43b private static > void register( - Material blockType, -- Class blockStateType, -- BiFunction blockStateConstructor, -- BiFunction tileEntityConstructor -- ) { -- CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor); -- } -- -- private static > void register( -- List blockTypes, + net.minecraft.world.level.block.entity.BlockEntityType blockEntityType, // Paper Class blockStateType, - BiFunction blockStateConstructor, - BiFunction tileEntityConstructor + BiFunction blockStateConstructor // Paper ) { +- CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor); +- } +- +- private static > void register( +- List blockTypes, +- Class blockStateType, +- BiFunction blockStateConstructor, +- BiFunction tileEntityConstructor +- ) { - BlockStateFactory factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, tileEntityConstructor); - for (Material blockType : blockTypes) { - CraftBlockStates.register(blockType, factory); + // Paper start -+ BlockStateFactory factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, blockEntityType::create); ++ BlockStateFactory factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, blockEntityType); // Paper + for (net.minecraft.world.level.block.Block block : blockEntityType.validBlocks) { + CraftBlockStates.register(CraftBlockType.minecraftToBukkit(block), factory); } @@ -421,6 +442,21 @@ index e40973b65ece11d9c5a76abad51f72e610bf02ab..411c2de93c71e480f95229c882cdf43b } return factory.createBlockState(world, blockPosition, blockData, tileEntity); } +@@ -472,6 +320,14 @@ public final class CraftBlockStates { + return new CraftBlockState(CraftBlock.at(world, pos), flag); + } + ++ // Paper start ++ @Nullable ++ public static BlockEntityType getBlockEntityType(final Material material) { ++ final BlockStateFactory factory = org.bukkit.craftbukkit.block.CraftBlockStates.FACTORIES.get(material); ++ return factory instanceof final BlockEntityStateFactory blockEntityStateFactory ? blockEntityStateFactory.tileEntityConstructor : null; ++ } ++ // Paper end ++ + private CraftBlockStates() { + } + } diff --git a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java index 81d4c8867ebcba1b805be1828e0a6a476963a855..9ff1a8068533ba5fc2fb43188d9a5c544a907618 100644 --- a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java diff --git a/patches/server/1038-General-ItemMeta-fixes.patch b/patches/server/1038-General-ItemMeta-fixes.patch index 3594dcaefd..46729ac08c 100644 --- a/patches/server/1038-General-ItemMeta-fixes.patch +++ b/patches/server/1038-General-ItemMeta-fixes.patch @@ -6,6 +6,7 @@ Subject: [PATCH] General ItemMeta fixes == AT == private-f net/minecraft/world/item/ItemStack components public net/minecraft/world/food/FoodProperties DEFAULT_EAT_SECONDS +public org/bukkit/craftbukkit/block/CraftBlockStates getBlockState(Lorg/bukkit/World;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/entity/BlockEntity;)Lorg/bukkit/craftbukkit/block/CraftBlockState; diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java index 8e2b3dd109dca3089cbce82cd3788874613a3230..893efb2c4a07c33d41e934279dd914a9dbd4ef79 100644 @@ -69,7 +70,7 @@ index 397eb1a101bd60f49dbb2fa8eddf28f6f233167f..ce10aa64576716f530e69d2281c090cf protected void load(T tileEntity) { if (tileEntity != null && tileEntity != this.snapshot) { diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java -index 3e552a859846d206ba79c3ee740ae76a37ee7606..2626fee8cf8690dee8c6db9d503891b6fd882192 100644 +index 3e552a859846d206ba79c3ee740ae76a37ee7606..f1e1953f2dc65dc615b7b7b648c37b195d3b4c25 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -328,7 +328,14 @@ public final class CraftItemStack extends ItemStack { @@ -88,7 +89,7 @@ index 3e552a859846d206ba79c3ee740ae76a37ee7606..2626fee8cf8690dee8c6db9d503891b6 ((CraftMetaItem) itemMeta).applyToItem(tag); itemStack.applyComponents(tag.build()); } -@@ -689,15 +696,19 @@ public final class CraftItemStack extends ItemStack { +@@ -689,15 +696,20 @@ public final class CraftItemStack extends ItemStack { } if (!((CraftMetaItem) itemMeta).isEmpty()) { @@ -103,12 +104,14 @@ index 3e552a859846d206ba79c3ee740ae76a37ee7606..2626fee8cf8690dee8c6db9d503891b6 + // Paper end - support updating profile after resolving it ((CraftMetaItem) itemMeta).applyToItem(tag); - item.restorePatch(tag.build()); - } +- item.restorePatch(tag.build()); +- } - // SpigotCraft#463 this is required now by the Vanilla client, so mimic ItemStack constructor in ensuring it - if (item.getItem() != null && item.getMaxDamage() > 0) { - item.setDamageValue(item.getDamageValue()); -- } ++ item.restorePatch(DataComponentPatch.EMPTY); // Paper - properly apply the new patch from itemmeta ++ item.applyComponents(tag.build()); // Paper - properly apply the new patch from itemmeta + } + // Paper - this is no longer needed return true; @@ -147,41 +150,284 @@ index 1ac3bec02fce28d5ce698305a7482a9eccbb1867..b494568f833dc21d4e2447ac3e5c5002 for (Pattern p : this.patterns) { diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java -index 12911233c01d0ac1af9adbd157d56d28361fc76f..99ee41e79891d6017f065492efab5af95b1b4c38 100644 +index 12911233c01d0ac1af9adbd157d56d28361fc76f..e5c2ab243fd56ef32321cbf127ef7b9d21fad56c 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java -@@ -209,10 +209,19 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta - super.applyToItem(tag); +@@ -142,9 +142,17 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta - if (this.blockEntityTag != null) { -- tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(this.blockEntityTag.getSnapshotNBTWithoutComponents())); -+ // Paper start - accurately replicate logic for creating ItemStack from BlockEntity -+ // taken from BlockEntity#saveToItem and BlockItem#setBlockEntityData -+ CompoundTag nbt = this.blockEntityTag.getSnapshotCustomNbtOnly(); -+ nbt.remove("id"); -+ if (!nbt.isEmpty()) { -+ BlockEntity.addEntityType(nbt, this.blockEntityTag.getTileEntity().getType()); -+ tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(nbt)); -+ } + @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) + static final ItemMetaKeyType BLOCK_ENTITY_TAG = new ItemMetaKeyType<>(DataComponents.BLOCK_ENTITY_DATA, "BlockEntityTag"); ++ static final ItemMetaKey BLOCK_ENTITY_TAG_CUSTOM_DATA = new ItemMetaKey("block-entity-tag"); // Paper ++ static final ItemMetaKey BLOCK_ENTITY_COMPONENTS = new ItemMetaKey("block-entity-components"); // Paper + + final Material material; +- private CraftBlockEntityState blockEntityTag; ++ // Paper start - store data separately ++ DataComponentMap components = DataComponentMap.EMPTY; ++ CustomData blockEntityTag = CustomData.EMPTY; ++ private Material materialForBlockEntityType() { ++ return (this.material != Material.SHIELD) ? this.material : CraftMetaBlockState.shieldToBannerHack(); ++ } ++ // Paper end + private CompoundTag internalTag; + + CraftMetaBlockState(CraftMetaItem meta, Material material) { +@@ -153,41 +161,60 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + + if (!(meta instanceof CraftMetaBlockState) + || ((CraftMetaBlockState) meta).material != material) { +- this.blockEntityTag = null; ++ // Paper start ++ this.components = DataComponentMap.EMPTY; ++ this.blockEntityTag = CustomData.EMPTY; + // Paper end + return; + } - for (TypedDataComponent component : this.blockEntityTag.collectComponents()) { -- tag.putIfAbsent(component); -+ if (CraftMetaItem.DEFAULT_HANDLED_DCTS.contains(component.type())) continue; // Paper - if the component type was already handled by CraftMetaItem, don't add it again -+ tag.builder.set(component); + CraftMetaBlockState te = (CraftMetaBlockState) meta; ++ // Paper start ++ this.components = te.components; + this.blockEntityTag = te.blockEntityTag; ++ // Paper end + } + + CraftMetaBlockState(DataComponentPatch tag, Material material, final Set> extraHandledDcts) { // Paper + super(tag, extraHandledDcts); // Paper + this.material = material; + ++ // Paper start - move to separate method to be re-called ++ this.updateBlockState(tag); ++ } ++ ++ private void updateBlockState(final DataComponentPatch tag) { ++ // Paper end + getOrEmpty(tag, CraftMetaBlockState.BLOCK_ENTITY_TAG).ifPresent((nbt) -> { +- this.blockEntityTag = CraftMetaBlockState.getBlockState(material, nbt.copyTag()); ++ this.blockEntityTag = nbt; // Paper + }); + + if (!tag.isEmpty()) { +- CraftBlockEntityState blockEntityTag = this.blockEntityTag; +- if (blockEntityTag == null) { +- blockEntityTag = CraftMetaBlockState.getBlockState(material, null); +- } +- +- // Convert to map +- PatchedDataComponentMap map = new PatchedDataComponentMap(DataComponentMap.EMPTY); +- map.applyPatch(tag); +- // Apply +- Set> applied = blockEntityTag.applyComponents(map, tag); ++ // Paper start - store data in a DataComponentMap to be used to construct CraftBlockEntityStates ++ final DataComponentMap.Builder map = DataComponentMap.builder(); ++ final net.minecraft.world.level.block.entity.BlockEntity dummyBlockEntity = java.util.Objects.requireNonNull( ++ org.bukkit.craftbukkit.block.CraftBlockStates.createNewTileEntity(this.materialForBlockEntityType()) ++ ); ++ ++ // we don't care about what's in here, all ++ // we want is to know which data component types are referenced ++ Set> applied = dummyBlockEntity.applyComponentsSet(DataComponentMap.EMPTY, DataComponentPatch.EMPTY); ++ // Paper end - store data in a DataComponentMap to be used to construct CraftBlockEntityStates + // Mark applied components as handled + for (DataComponentType seen : applied) { + this.unhandledTags.clear(seen); } + // Only set blockEntityTag if something was applied + if (!applied.isEmpty()) { +- this.blockEntityTag = blockEntityTag; ++ // Paper start ++ for (final DataComponentType type : applied) { ++ getOrEmpty(tag, type).ifPresent(value -> { ++ map.set(type, value); ++ }); ++ } ++ // Paper end + } ++ this.components = map.build(); // Paper } } -@@ -332,6 +341,13 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + +@@ -200,7 +227,10 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + } else { + this.material = Material.AIR; + } +- this.blockEntityTag = CraftMetaBlockState.getBlockState(this.material, this.internalTag); ++ // Paper start ++ if (this.internalTag != null) { // legacy ++ this.setBlockState(CraftMetaBlockState.getBlockState(this.material, this.internalTag)); ++ } + this.internalTag = null; + } + +@@ -208,13 +238,20 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + void applyToItem(CraftMetaItem.Applicator tag) { + super.applyToItem(tag); + +- if (this.blockEntityTag != null) { +- tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(this.blockEntityTag.getSnapshotNBTWithoutComponents())); ++ // Paper start - accurately replicate logic for creating ItemStack from BlockEntity ++ // taken from BlockEntity#saveToItem and BlockItem#setBlockEntityData ++ final CompoundTag nbt = this.blockEntityTag.copyTag(); ++ nbt.remove("id"); ++ if (!nbt.isEmpty()) { ++ BlockEntity.addEntityType(nbt, java.util.Objects.requireNonNull(CraftBlockStates.getBlockEntityType(this.materialForBlockEntityType()))); ++ tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(nbt)); ++ } + +- for (TypedDataComponent component : this.blockEntityTag.collectComponents()) { +- tag.putIfAbsent(component); +- } ++ for (final TypedDataComponent component : this.components) { ++ if (CraftMetaItem.DEFAULT_HANDLED_DCTS.contains(component.type())) continue; // if the component type was already handled by CraftMetaItem, don't add it again ++ tag.builder.set(component); + } ++ // Paper end + } + + @Override +@@ -223,14 +260,29 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + + if (tag.contains(CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { + this.internalTag = tag.getCompound(CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT); ++ return; // Paper - if legacy, don't check anything else ++ } ++ // Paper start - new serialization format ++ if (tag.contains(CraftMetaBlockState.BLOCK_ENTITY_TAG_CUSTOM_DATA.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { ++ this.blockEntityTag = CustomData.of(tag.getCompound(CraftMetaBlockState.BLOCK_ENTITY_TAG_CUSTOM_DATA.NBT)); + } ++ if (tag.contains(CraftMetaBlockState.BLOCK_ENTITY_COMPONENTS.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { ++ this.components = DataComponentMap.CODEC.parse(org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), tag.getCompound(CraftMetaBlockState.BLOCK_ENTITY_COMPONENTS.NBT)).getOrThrow(); ++ } ++ // Paper end - new serialization format + } + + @Override + void serializeInternal(final Map internalTags) { +- if (this.blockEntityTag != null) { +- internalTags.put(CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, this.blockEntityTag.getSnapshotNBT()); ++ // Paper start - new serialization format ++ if (!this.blockEntityTag.isEmpty()) { ++ internalTags.put(CraftMetaBlockState.BLOCK_ENTITY_TAG_CUSTOM_DATA.NBT, this.blockEntityTag.getUnsafe()); // unsafe because it's serialized right away ++ } ++ if (!this.components.isEmpty()) { ++ final Tag componentsTag = DataComponentMap.CODEC.encodeStart(org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), this.components).getOrThrow(); ++ internalTags.put(CraftMetaBlockState.BLOCK_ENTITY_COMPONENTS.NBT, componentsTag); + } ++ // Paper end - new serialization format + } + + @Override +@@ -244,9 +296,10 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + int applyHash() { + final int original; + int hash = original = super.applyHash(); +- if (this.blockEntityTag != null) { +- hash = 61 * hash + this.blockEntityTag.hashCode(); +- } ++ // Paper start ++ hash = 61 * hash + this.blockEntityTag.hashCode(); ++ hash = 61 * hash + this.components.hashCode(); ++ // Paper end + return original != hash ? CraftMetaBlockState.class.hashCode() ^ hash : hash; + } + +@@ -258,19 +311,19 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + if (meta instanceof CraftMetaBlockState) { + CraftMetaBlockState that = (CraftMetaBlockState) meta; + +- return Objects.equal(this.blockEntityTag, that.blockEntityTag); ++ return Objects.equal(this.blockEntityTag, that.blockEntityTag) && Objects.equal(this.components, that.components); // Paper + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { +- return super.notUncommon(meta) && (meta instanceof CraftMetaBlockState || this.blockEntityTag == null); ++ return super.notUncommon(meta) && (meta instanceof CraftMetaBlockState || (this.blockEntityTag.isEmpty() && this.components.isEmpty())); // Paper + } + + @Override + boolean isEmpty() { +- return super.isEmpty() && this.blockEntityTag == null; ++ return super.isEmpty() && this.blockEntityTag.isEmpty() && this.components.isEmpty(); // Paper + } + + @Override +@@ -281,27 +334,50 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + @Override + public CraftMetaBlockState clone() { + CraftMetaBlockState meta = (CraftMetaBlockState) super.clone(); +- if (this.blockEntityTag != null) { +- meta.blockEntityTag = this.blockEntityTag.copy(); +- } ++ // Paper start - no need for "clone" because they are essentially immutables ++ meta.blockEntityTag = this.blockEntityTag; ++ meta.components = this.components; ++ // Paper end + return meta; + } + + @Override + public boolean hasBlockState() { +- return this.blockEntityTag != null; ++ return !this.blockEntityTag.isEmpty() || !this.components.isEmpty(); // Paper + } + + // Paper start - add method to clear block state + @Override + public void clearBlockState() { +- this.blockEntityTag = null; ++ // Paper start ++ this.blockEntityTag = CustomData.EMPTY; ++ this.components = DataComponentMap.EMPTY; ++ // Paper end + } + // Paper end - add method to clear block state + + @Override +- public BlockState getBlockState() { +- return (this.blockEntityTag != null) ? this.blockEntityTag.copy() : CraftMetaBlockState.getBlockState(this.material, null); ++ // Paper start - create blockstate on-demand ++ public CraftBlockEntityState getBlockState() { ++ BlockPos pos = BlockPos.ZERO; ++ final Material stateMaterial = this.materialForBlockEntityType(); ++ if (!this.blockEntityTag.isEmpty()) { ++ // Paper "id" field is always present now ++ pos = BlockEntity.getPosFromTag(this.blockEntityTag.getUnsafe()); // unsafe is fine here, just querying ++ } ++ final net.minecraft.world.level.block.entity.BlockEntityType type = java.util.Objects.requireNonNull(CraftBlockStates.getBlockEntityType(stateMaterial)); ++ final net.minecraft.world.level.block.state.BlockState nmsBlockState = ((org.bukkit.craftbukkit.block.data.CraftBlockData) this.getBlockData(stateMaterial)).getState(); ++ final net.minecraft.world.level.block.entity.BlockEntity blockEntity = java.util.Objects.requireNonNull(type.create(pos, nmsBlockState)); ++ if (!this.blockEntityTag.isEmpty()) { ++ this.blockEntityTag.loadInto(blockEntity, org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry()); ++ } ++ final PatchedDataComponentMap patchedMap = new PatchedDataComponentMap(nmsBlockState.getBlock().asItem().components()); ++ patchedMap.setAll(this.components); ++ blockEntity.applyComponents(nmsBlockState.getBlock().asItem().components(), patchedMap.asPatch()); ++ ++ // This is expected to always return a CraftBlockEntityState for the passed material: ++ return (CraftBlockEntityState) CraftBlockStates.getBlockState(null, pos, nmsBlockState, blockEntity); ++ // Paper end + } + + private static CraftBlockEntityState getBlockState(Material material, CompoundTag blockEntityTag) { +@@ -331,7 +407,19 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta + Class blockStateType = CraftBlockStates.getBlockStateType(stateMaterial); Preconditions.checkArgument(blockStateType == blockState.getClass() && blockState instanceof CraftBlockEntityState, "Invalid blockState for " + this.material); - this.blockEntityTag = (CraftBlockEntityState) blockState; +- this.blockEntityTag = (CraftBlockEntityState) blockState; + // Paper start - when a new BlockState is set, the components from that block entity + // have to be used to update the fields on CraftMetaItem -+ final PatchedDataComponentMap patchedMap = new net.minecraft.core.component.PatchedDataComponentMap(this.blockEntityTag.getHandle().getBlock().asItem().components()); -+ final net.minecraft.core.component.DataComponentMap map = this.blockEntityTag.collectComponents(); ++ final CraftBlockEntityState craftBlockState = (CraftBlockEntityState) blockState; ++ final CompoundTag data = craftBlockState.getSnapshotCustomNbtOnly(); ++ BlockEntity.addEntityType(data, craftBlockState.getTileEntity().getType()); ++ this.blockEntityTag = CustomData.of(data); ++ final PatchedDataComponentMap patchedMap = new net.minecraft.core.component.PatchedDataComponentMap(craftBlockState.getHandle().getBlock().asItem().components()); ++ final net.minecraft.core.component.DataComponentMap map = craftBlockState.collectComponents(); + patchedMap.setAll(map); -+ this.updateFromPatch(patchedMap.asPatch(), null); ++ final DataComponentPatch patch = patchedMap.asPatch(); ++ this.updateFromPatch(patch, null); ++ this.updateBlockState(patch); + // Paper end } @@ -563,7 +809,7 @@ index 8e0dd4b7a7a25a8beb27b507047bc48d8227627c..4c09c57c263b18ee2e0156cc4a49ac02 } } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java -index 12a193db7475870e5107c86c7611bb4b92feacb8..860bc5ec4baec5e09a456cc5559d0de2aa10797a 100644 +index 12a193db7475870e5107c86c7611bb4b92feacb8..c235b80b94fdb6c77766016114713cd501ffd67c 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -172,9 +172,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { @@ -832,6 +1078,20 @@ index 12a193db7475870e5107c86c7611bb4b92feacb8..860bc5ec4baec5e09a456cc5559d0de2 CraftMetaItem.NAME.TYPE, CraftMetaItem.ITEM_NAME.TYPE, CraftMetaItem.LORE.TYPE, +@@ -1971,7 +1989,12 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { + // Paper end - improve checking handled data component types + + protected static Optional getOrEmpty(DataComponentPatch tag, ItemMetaKeyType type) { +- Optional result = tag.get(type.TYPE); ++ // Paper start ++ return getOrEmpty(tag, type.TYPE); ++ } ++ protected static Optional getOrEmpty(final DataComponentPatch tag, final DataComponentType type) { ++ Optional result = tag.get(type); ++ // Paper end + + return (result != null) ? result : Optional.empty(); + } diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java index 157a7b7351f48e68d2923c72ed3bbe3dcae21383..051e3c4e5020fdf1392f888bfb4a633db69e5c5f 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java