From 5d872b1830c1531665a7957830b3c43194b4dcd1 Mon Sep 17 00:00:00 2001 From: Kostiantyn Fedorenko Date: Sun, 23 Aug 2026 20:58:37 +0300 Subject: [PATCH] fix: stop double chests double-counting their viewers The Bukkit-level viewer hooks on CompoundContainer called the vanilla startOpen/stopOpen, which drive ContainerOpenersCounter. ChestMenu already calls startOpen once per open and CompoundContainer fans it out to both halves, so every viewer of a large chest was counted twice. The five-tick recheck corrects the count to 1 while the chest is still open, so the two decrements on close take the counter to -1: the first plays the close sound, and the pending recheck then sees 0 openers against a stored -1 and fires onClose a second time. Held open for less than five ticks the sequence happens to balance, which is why the symptom is erratic - a second close sound, or a lid and redstone output stuck in the wrong state, since a counter left negative never makes the 0 -> 1 transition that fires onOpen and schedules the next recheck. Delegate to the halves' onOpen/onClose instead, as CraftBukkit does, so each half still tracks the viewer for getViewers() while only vanilla touches the opener count. Also fix the InventoryOpenEvent cancel path, which leaked an opener per half on large chests: a large chest arrives as the MenuProvider that DoubleBlockCombiner builds, so neither the Container nor the CompoundContainer branch matched and the constructor's startOpen was never undone. The CompoundContainer branch was unreachable anyway - CompoundContainer is a Container, so the first branch would have claimed it - and it only stopped container1. Take the container from the menu that was created instead, and let CompoundContainer.stopOpen fan out to both halves. Follow-up to #570, which fixed the same double count on the other path. Co-Authored-By: Claude Opus 5 --- .../mixin/server/level/ServerPlayerMixin.java | 8 +++++--- .../mixin/world/CompoundContainerMixin.java | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/cardboardpowered/mixin/server/level/ServerPlayerMixin.java b/src/main/java/org/cardboardpowered/mixin/server/level/ServerPlayerMixin.java index a462177e2..aa52d4889 100644 --- a/src/main/java/org/cardboardpowered/mixin/server/level/ServerPlayerMixin.java +++ b/src/main/java/org/cardboardpowered/mixin/server/level/ServerPlayerMixin.java @@ -88,11 +88,11 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer.RespawnConfig; import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.CompoundContainer; import net.minecraft.world.Container; import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; import net.minecraft.world.level.Level; import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.phys.Vec3; @@ -339,13 +339,15 @@ public void openHandledScreen_c(MenuProvider factory, CallbackInfoReturnable result = org.bukkit.craftbukkit.event.CraftEventFactory.callInventoryOpenEventWithTitle(((ServerPlayer)(Object)this), container, cancelled); container = result.getSecond(); if (container == null && !cancelled) { if (factory instanceof Container) { ((Container) factory).stopOpen((ServerPlayer)(Object)this); - } else if (factory instanceof CompoundContainer) - ((CompoundContainer) factory).container1.stopOpen((ServerPlayer)(Object)this); + } else if (created instanceof ChestMenu) { + ((ChestMenu) created).getContainer().stopOpen((ServerPlayer)(Object)this); + } ci.setReturnValue(OptionalInt.empty()); } diff --git a/src/main/java/org/cardboardpowered/mixin/world/CompoundContainerMixin.java b/src/main/java/org/cardboardpowered/mixin/world/CompoundContainerMixin.java index 538cb3e59..e6702be88 100644 --- a/src/main/java/org/cardboardpowered/mixin/world/CompoundContainerMixin.java +++ b/src/main/java/org/cardboardpowered/mixin/world/CompoundContainerMixin.java @@ -37,15 +37,15 @@ public ItemStack getItem(int i) { @Override public void onOpen(CraftHumanEntity who) { - this.container1.startOpen(who.getHandle()); - this.container2.startOpen(who.getHandle()); + ((ContainerBridge) this.container1).onOpen(who); + ((ContainerBridge) this.container2).onOpen(who); transaction.add(who); } @Override public void onClose(CraftHumanEntity who) { - this.container1.stopOpen(who.getHandle()); - this.container2.stopOpen(who.getHandle()); + ((ContainerBridge) this.container1).onClose(who); + ((ContainerBridge) this.container2).onClose(who); transaction.remove(who); }