diff --git a/paper-server/patches/features/0034-Restore-synchronous-container-opening.patch b/paper-server/patches/features/0034-Restore-synchronous-container-opening.patch new file mode 100644 index 000000000000..fad84b6f5c8b --- /dev/null +++ b/paper-server/patches/features/0034-Restore-synchronous-container-opening.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: PaperMC +Date: Sat, 18 Jul 2026 00:00:00 +0300 +Subject: [PATCH] Restore synchronous container opening + +Container state changes are observable from the advancement callback that +follows a block use. Delaying an opening therefore makes the opened block +appear closed until the next tick, breaking datapacks that use containers as +GUIs. + +Only defer the close path, which can run while handling player logout or +server shutdown. Keep opening synchronous so the block state matches vanilla +at the time its use callback runs. + +diff --git a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java +index 52737172ccfa0c7c977e4f2fe1db242e0bb92d25..25b530d8880ff4fbe29a722079d1c3538f1511de 100644 +--- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java ++++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java +@@ -48,12 +48,6 @@ public abstract class ContainerOpenersCounter { + public void incrementOpeners( + final LivingEntity entity, final Level level, final BlockPos pos, final BlockState blockState, final double maxInteractionRange + ) { +- // Paper start - delay open/close callbacks +- if (this.delayCallbacks()) { +- scheduleRecheck(level, pos, blockState, 1); +- return; +- } +- // Paper end - delay open/close callbacks + int previous = this.openCount++; + + // Paper start - Call BlockRedstoneEvent diff --git a/paper-server/src/test/java/io/papermc/paper/world/ContainerOpenersCounterTest.java b/paper-server/src/test/java/io/papermc/paper/world/ContainerOpenersCounterTest.java new file mode 100644 index 000000000000..95dde6fc5521 --- /dev/null +++ b/paper-server/src/test/java/io/papermc/paper/world/ContainerOpenersCounterTest.java @@ -0,0 +1,57 @@ +package io.papermc.paper.world; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.ContainerOpenersCounter; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.support.environment.Normal; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +@Normal +class ContainerOpenersCounterTest { + + @Test + void delayedCallbackContainersOpenSynchronously() { + final RecordingOpenersCounter counter = new RecordingOpenersCounter(); + final LivingEntity entity = Mockito.mock(LivingEntity.class); + final Level level = Mockito.mock(Level.class); + final BlockState state = Mockito.mock(BlockState.class); + + counter.incrementOpeners(entity, level, BlockPos.ZERO, state, 0.0); + + assertTrue(counter.openedSynchronously); + assertEquals(1, counter.getOpenerCount()); + } + + private static final class RecordingOpenersCounter extends ContainerOpenersCounter { + private boolean openedSynchronously; + + @Override + protected void onOpen(final Level level, final BlockPos pos, final BlockState blockState) { + this.openedSynchronously = true; + } + + @Override + protected void onClose(final Level level, final BlockPos pos, final BlockState blockState) { + } + + @Override + protected void openerCountChanged(final Level level, final BlockPos pos, final BlockState blockState, final int previous, final int current) { + } + + @Override + public boolean delayCallbacks() { + return true; + } + + @Override + public boolean isOwnContainer(final Player player) { + return false; + } + } +}