diff --git a/paper-server/patches/features/0019-Delay-open-close-callbacks-for-chests.patch b/paper-server/patches/features/0019-Delay-open-close-callbacks-for-chests.patch index 6c493b1103bb..75fef0c36d46 100644 --- a/paper-server/patches/features/0019-Delay-open-close-callbacks-for-chests.patch +++ b/paper-server/patches/features/0019-Delay-open-close-callbacks-for-chests.patch @@ -28,7 +28,7 @@ index aa4e5a8977fd437780675b173397988e37303f46..118af4e35eec652e7eb8a484a44652cf protected void onOpen(final Level level, final BlockPos pos, final BlockState state) { BarrelBlockEntity.this.playSound(state, SoundEvents.BARREL_OPEN); diff --git a/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/net/minecraft/world/level/block/entity/ChestBlockEntity.java -index 06878204e0df42fc35cfa380413986a045ee229d..a9273aebb5c8efe78e88158f11e1ee2af0dff3f1 100644 +index 58e532e913f360280f5bee461d1e86a63f4f76ed..13ba29eaaea8bdc8d8dff14a9504b7918006d9fc 100644 --- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java @@ -30,6 +30,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement diff --git a/paper-server/patches/features/0031-Optimize-Hoppers.patch b/paper-server/patches/features/0031-Optimize-Hoppers.patch index 7488e12727d5..878944958a8b 100644 --- a/paper-server/patches/features/0031-Optimize-Hoppers.patch +++ b/paper-server/patches/features/0031-Optimize-Hoppers.patch @@ -84,7 +84,7 @@ index a8da0c5b2f9b75216d6b93addabff744c6b10431..44a7590558fa73fb570f76ba7a4e7db1 return copy; } diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java -index bb61b97a9619e76344bae5458bbdf2bc96e5494c..ae0df53c3be7ebcf48886adbedfaa2c071a94023 100644 +index 378d72b7f0e02ea19e5e96f7fe1359e0cb6310bb..cc5c7cc3c0c5c28ebc36bf532e61759efbc4544a 100644 --- a/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/net/minecraft/world/level/block/entity/BlockEntity.java @@ -39,6 +39,7 @@ import org.jspecify.annotations.Nullable; @@ -95,10 +95,10 @@ index bb61b97a9619e76344bae5458bbdf2bc96e5494c..ae0df53c3be7ebcf48886adbedfaa2c0 // CraftBukkit start - data containers private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer; -@@ -209,6 +210,7 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance> TYPE_CODEC = BuiltInRegistries.BLOCK_ENTITY_TYPE.byNameCodec(); private static final Logger LOGGER = LogUtils.getLogger(); private final BlockEntityType type; -@@ -53,6 +_,7 @@ +@@ -47,12 +_,14 @@ + protected boolean remove; + private BlockState blockState; + private DataComponentMap components = DataComponentMap.EMPTY; ++ public boolean real = true; // Paper - mark block entities that aren't actually in the level + + public BlockEntity(final BlockEntityType type, final BlockPos worldPosition, final BlockState blockState) { + this.type = type; this.worldPosition = worldPosition.immutable(); this.validateBlockState(blockState); this.blockState = blockState; @@ -65,6 +72,15 @@ } public final CompoundTag saveWithFullMetadata(final HolderLookup.Provider registries) { +@@ -190,7 +_,7 @@ + } + + public void setChanged() { +- if (this.level != null) { ++ if (this.level != null && this.real) { // Paper - don't broadcast for fake block entities + setChanged(this.level, this.worldPosition, this.blockState); + } + } @@ -278,6 +_,12 @@ } @@ -100,7 +116,7 @@ + } + + public org.bukkit.inventory.@Nullable InventoryHolder getOwner(boolean useSnapshot) { -+ if (this.level == null) return null; ++ if (this.level == null || !this.real) return null; // Paper - fake block entities have no owner + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition); + org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper + return state instanceof final org.bukkit.inventory.InventoryHolder inventoryHolder ? inventoryHolder : null; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch index e5c42fe9bcde..63eb6a005aaf 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch @@ -41,3 +41,19 @@ protected ChestBlockEntity(final BlockEntityType type, final BlockPos worldPosition, final BlockState blockState) { super(type, worldPosition, blockState); } +@@ -129,6 +_,7 @@ + + @Override + public void startOpen(final ContainerUser containerUser) { ++ if (!this.real) return; // Paper - don't broadcast for fake block entities + if (!this.remove && !containerUser.getLivingEntity().isSpectator()) { + this.openersCounter + .incrementOpeners( +@@ -139,6 +_,7 @@ + + @Override + public void stopOpen(final ContainerUser containerUser) { ++ if (!this.real) return; // Paper - don't broadcast for fake block entities + if (!this.remove && !containerUser.getLivingEntity().isSpectator()) { + this.openersCounter.decrementOpeners(containerUser.getLivingEntity(), this.getLevel(), this.getBlockPos(), this.getBlockState()); + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch index 2a4f512c9356..3add8fc26c25 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch @@ -61,7 +61,7 @@ + // CraftBukkit start + if (slot == 0) { + LecternBlockEntity.this.setBook(itemStack); -+ if (LecternBlockEntity.this.getLevel() != null) { ++ if (LecternBlockEntity.this.getLevel() != null && LecternBlockEntity.this.real) { // Paper - don't update the block for fake block entities + LecternBlock.resetBookState(null, LecternBlockEntity.this.getLevel(), LecternBlockEntity.this.getBlockPos(), LecternBlockEntity.this.getBlockState(), LecternBlockEntity.this.hasBook()); + } + } @@ -75,12 +75,21 @@ } @Override +@@ -146,7 +_,7 @@ + private void onBookItemRemove() { + this.page = 0; + this.pageCount = 0; +- LecternBlock.resetBookState(null, this.getLevel(), this.getBlockPos(), this.getBlockState(), false); ++ if (this.level != null && this.real) LecternBlock.resetBookState(null, this.getLevel(), this.getBlockPos(), this.getBlockState(), false); // Paper - don't update the block for fake block entities + } + + public void setBook(final ItemStack book, final @Nullable Player resolutionContext) { @@ -161,7 +_,7 @@ if (newPage != this.page) { this.page = newPage; this.setChanged(); - LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); -+ if (this.level != null) LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); // CraftBukkit ++ if (this.level != null && this.real) LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); // CraftBukkit // Paper - don't update the block for fake block entities } } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch index fd3bfc5ed4e2..7800e50fc12f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch @@ -47,7 +47,7 @@ } this.openCount++; -+ if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call ++ if (this.opened || !this.real) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call // Paper - don't animate fake block entities this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), EVENT_SET_OPEN_COUNT, this.openCount); if (this.openCount == 1) { this.level.gameEvent(containerUser.getLivingEntity(), GameEvent.CONTAINER_OPEN, this.worldPosition); @@ -55,7 +55,7 @@ public void stopOpen(final ContainerUser containerUser) { if (!this.remove && !containerUser.getLivingEntity().isSpectator()) { this.openCount--; -+ if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call. ++ if (this.opened || !this.real) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call. // Paper - don't animate fake block entities this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), EVENT_SET_OPEN_COUNT, this.openCount); if (this.openCount <= 0) { this.level.gameEvent(containerUser.getLivingEntity(), GameEvent.CONTAINER_CLOSE, this.worldPosition); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java index e303dea052fa..7df2b8dd12cd 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftMenus.java @@ -82,7 +82,7 @@ public static > MenuT return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftDoubleChestInventoryViewBuilder<>(handle))); } if (menuType == MenuType.GENERIC_9X3) { - return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, ChestBlockEntity::new, false))); + return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, ChestBlockEntity::new))); } // this isn't ideal as both dispenser and dropper are 3x3, InventoryType can't currently handle generic 3x3s with size 9 // this needs to be removed when inventory creation is overhauled diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftBlockEntityInventoryViewBuilder.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftBlockEntityInventoryViewBuilder.java index 333f554ce155..c1a8f655adef 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftBlockEntityInventoryViewBuilder.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/view/builder/CraftBlockEntityInventoryViewBuilder.java @@ -16,25 +16,14 @@ public class CraftBlockEntityInventoryViewBuilder extends CraftAbstractLocationInventoryViewBuilder { private final Block block; - private final boolean useFakeBlockEntity; private final @Nullable CraftBlockInventoryBuilder builder; public CraftBlockEntityInventoryViewBuilder( final MenuType handle, final Block block, final @Nullable CraftBlockInventoryBuilder builder - ) { - this(handle, block, builder, true); - } - - public CraftBlockEntityInventoryViewBuilder( - final MenuType handle, - final Block block, - final @Nullable CraftBlockInventoryBuilder builder, - final boolean useFakeBlockEntity ) { super(handle); - this.useFakeBlockEntity = useFakeBlockEntity; this.block = block; this.builder = builder; } @@ -72,19 +61,17 @@ private AbstractContainerMenu buildFakeBlockEntity(final ServerPlayer player) { final MenuProvider inventory = this.builder.build(this.position, this.block.defaultBlockState()); if (inventory instanceof final BlockEntity blockEntity) { blockEntity.setLevel(this.world); + // marks this as "not in world" for gating general level broadcasts + blockEntity.real = false; super.defaultTitle = inventory.getDisplayName(); } - if (!this.useFakeBlockEntity) { // gets around open noise for chest - return handle.create(player.nextContainerCounter(), player.getInventory()); - } - return inventory.createMenu(player.nextContainerCounter(), player.getInventory(), player); } @Override public LocationInventoryViewBuilder copy() { - final CraftBlockEntityInventoryViewBuilder copy = new CraftBlockEntityInventoryViewBuilder<>(super.handle, this.block, this.builder, this.useFakeBlockEntity); + final CraftBlockEntityInventoryViewBuilder copy = new CraftBlockEntityInventoryViewBuilder<>(super.handle, this.block, this.builder); copy.world = this.world; copy.position = this.position; copy.checkReachable = super.checkReachable;