|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Spottedleaf <Spottedleaf@users.noreply.github.com> |
| 3 | +Date: Mon, 20 Apr 2026 02:56:22 -0700 |
| 4 | +Subject: [PATCH] Delay open/close callbacks for chests |
| 5 | + |
| 6 | +The logic for chests may invoke block updates, which may not |
| 7 | +be safe to perform. For example, inventory closing due to |
| 8 | +player logout (which restricts chunk loading due to being |
| 9 | +inside an entity status callback) or during shutdown (which has |
| 10 | +chunk loading restrictions due to the chunk system being halted). |
| 11 | + |
| 12 | +diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java |
| 13 | +index 9eb6cb86ff70f04863cae3def1006f82bcb4fa6b..da5fdf77dd26d97c8b92bac9a6af8d922c31eb9c 100644 |
| 14 | +--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java |
| 15 | ++++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java |
| 16 | +@@ -66,6 +66,13 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { |
| 17 | + Objects.requireNonNull(BarrelBlockEntity.this); |
| 18 | + } |
| 19 | + |
| 20 | ++ // Paper start - delay open/close callbacks |
| 21 | ++ @Override |
| 22 | ++ public boolean delayCallbacks() { |
| 23 | ++ return true; |
| 24 | ++ } |
| 25 | ++ // Paper end - delay open/close callbacks |
| 26 | ++ |
| 27 | + @Override |
| 28 | + protected void onOpen(final Level level, final BlockPos pos, final BlockState state) { |
| 29 | + BarrelBlockEntity.this.playSound(state, SoundEvents.BARREL_OPEN); |
| 30 | +diff --git a/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/net/minecraft/world/level/block/entity/ChestBlockEntity.java |
| 31 | +index 81a293cc13653a9a0336cb25d75273969fee4d16..8ebcce4c4971a11d367fb39e9c7fd280f63594e3 100644 |
| 32 | +--- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java |
| 33 | ++++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java |
| 34 | +@@ -35,6 +35,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement |
| 35 | + Objects.requireNonNull(ChestBlockEntity.this); |
| 36 | + } |
| 37 | + |
| 38 | ++ // Paper start - delay open/close callbacks |
| 39 | ++ @Override |
| 40 | ++ public boolean delayCallbacks() { |
| 41 | ++ return true; |
| 42 | ++ } |
| 43 | ++ // Paper end - delay open/close callbacks |
| 44 | ++ |
| 45 | + @Override |
| 46 | + protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) { |
| 47 | + if (blockState.getBlock() instanceof ChestBlock chestBlock) { |
| 48 | +diff --git a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java |
| 49 | +index 9af046cbe2c5024040057118a0e9ecd539815781..aa8b384b05738139fa3492b3c09084cda09448ce 100644 |
| 50 | +--- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java |
| 51 | ++++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java |
| 52 | +@@ -37,11 +37,23 @@ public abstract class ContainerOpenersCounter { |
| 53 | + |
| 54 | + protected abstract void openerCountChanged(final Level level, final BlockPos pos, final BlockState blockState, int previous, int current); |
| 55 | + |
| 56 | ++ // Paper start - delay open/close callbacks |
| 57 | ++ public boolean delayCallbacks() { |
| 58 | ++ return false; |
| 59 | ++ } |
| 60 | ++ // Paper end - delay open/close callbacks |
| 61 | ++ |
| 62 | + public abstract boolean isOwnContainer(final Player player); |
| 63 | + |
| 64 | + public void incrementOpeners( |
| 65 | + final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState, final double maxInteractionRange |
| 66 | + ) { |
| 67 | ++ // Paper start - delay open/close callbacks |
| 68 | ++ if (this.delayCallbacks()) { |
| 69 | ++ scheduleRecheck(level, pos, blockState, 1); |
| 70 | ++ return; |
| 71 | ++ } |
| 72 | ++ // Paper end - delay open/close callbacks |
| 73 | + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added |
| 74 | + int previous = this.openCount++; |
| 75 | + |
| 76 | +@@ -66,6 +78,12 @@ public abstract class ContainerOpenersCounter { |
| 77 | + } |
| 78 | + |
| 79 | + public void decrementOpeners(final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState) { |
| 80 | ++ // Paper start - delay open/close callbacks |
| 81 | ++ if (this.delayCallbacks()) { |
| 82 | ++ scheduleRecheck(level, pos, blockState, 1); |
| 83 | ++ return; |
| 84 | ++ } |
| 85 | ++ // Paper end - delay open/close callbacks |
| 86 | + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added |
| 87 | + if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative |
| 88 | + int previous = this.openCount--; |
| 89 | +@@ -151,6 +169,11 @@ public abstract class ContainerOpenersCounter { |
| 90 | + } |
| 91 | + |
| 92 | + private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState) { |
| 93 | +- level.scheduleTick(blockPos, blockState.getBlock(), 5); |
| 94 | ++ // Paper start - delay open/close callbacks |
| 95 | ++ scheduleRecheck(level, blockPos, blockState, 5); |
| 96 | ++ } |
| 97 | ++ private static void scheduleRecheck(final Level level, final BlockPos blockPos, final BlockState blockState, final int delay) { |
| 98 | ++ level.scheduleTick(blockPos, blockState.getBlock(), delay); |
| 99 | ++ // Paper end - delay open/close callbacks |
| 100 | + } |
| 101 | + } |
| 102 | +diff --git a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java |
| 103 | +index eeac644f60760570244380d3c454fd33e37b5a7f..751625124a87de63d933d9c7809e46361e3136a5 100644 |
| 104 | +--- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java |
| 105 | ++++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java |
| 106 | +@@ -18,6 +18,13 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity |
| 107 | + Objects.requireNonNull(EnderChestBlockEntity.this); |
| 108 | + } |
| 109 | + |
| 110 | ++ // Paper start - delay open/close callbacks |
| 111 | ++ @Override |
| 112 | ++ public boolean delayCallbacks() { |
| 113 | ++ return true; |
| 114 | ++ } |
| 115 | ++ // Paper end - delay open/close callbacks |
| 116 | ++ |
| 117 | + @Override |
| 118 | + protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) { |
| 119 | + level.playSound( |
0 commit comments