Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: PaperMC <developers@papermc.io>
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
Original file line number Diff line number Diff line change
@@ -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;
}
}
}