From e3a122f3ac150ad8140453ca3f28cf24c2ecd6c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 May 2026 07:49:52 +0000 Subject: [PATCH 1/2] fix: toggle button always showing OFF state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After toggle actions ran, the inventory was never refreshed — the player saw the initial item regardless of tag state. Fix: after toggle action chain completes (without a close/navigate action breaking it), reopen the same GUI page so buildInventory re-evaluates the tag and renders the correct item/name/lore. --- .../dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java b/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java index e49e6f1..b2b4a67 100644 --- a/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java +++ b/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java @@ -113,10 +113,18 @@ public static boolean handleClick(ServerPlayerEntity player, GuiDefinition def, }; if (!matches) continue; + boolean isToggle = btn.toggle().isPresent(); List actions = resolveActions(player, btn); + boolean chainBroken = false; for (GuiDefinition.ButtonAction action : actions) { boolean shouldBreak = executeAction(player, def, page, action); - if (shouldBreak) break; + if (shouldBreak) { chainBroken = true; break; } + } + // Toggle buttons must reopen the GUI to reflect the new state. + // Only reopen if no action already closed/navigated the screen. + if (isToggle && !chainBroken) { + player.closeHandledScreen(); + open(player, def, page); } return true; } From 7f8aeb76f7f9b4cccb2f86159de763b86e51bff9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 17 May 2026 07:50:36 +0000 Subject: [PATCH 2/2] fix: toggle applies tag via Java API before reopen, not command dispatcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Command dispatcher execution is async relative to inventory rebuild — tag @s add/remove was not guaranteed to be reflected by the time buildInventory re-evaluated player.getCommandTags(). Fix: flip the tag directly with player.addCommandTag()/removeCommandTag() synchronously, then skip the auto-generated default tag commands in the action chain, then reopen. Custom side-effect actions (non-tag commands, messages) still execute normally. --- .../guiapi/gui/BarrelGuiHandler.java | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java b/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java index b2b4a67..1f804f7 100644 --- a/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java +++ b/src/main/java/dev/toolkitmc/guiapi/gui/BarrelGuiHandler.java @@ -115,16 +115,40 @@ public static boolean handleClick(ServerPlayerEntity player, GuiDefinition def, boolean isToggle = btn.toggle().isPresent(); List actions = resolveActions(player, btn); - boolean chainBroken = false; + + // For toggle buttons: apply the tag flip directly in Java (synchronous) + // before running custom actions and reopening. This avoids the race + // between command dispatcher execution and inventory rebuild. + if (isToggle) { + GuiDefinition.ToggleDefinition tgl = btn.toggle().get(); + boolean wasOn = player.getCommandTags().contains(tgl.tag()); + if (wasOn) { + player.removeCommandTag(tgl.tag()); + } else { + player.addCommandTag(tgl.tag()); + } + // Run any custom side-effect actions (skip default tag commands) + boolean chainBroken = false; + for (GuiDefinition.ButtonAction action : actions) { + // Skip the auto-generated tag add/remove defaults + if (action.runWith() == GuiDefinition.RunWith.CONSOLE + && (action.value().startsWith("tag @s add " + tgl.tag()) + || action.value().startsWith("tag @s remove " + tgl.tag()))) { + continue; + } + boolean shouldBreak = executeAction(player, def, page, action); + if (shouldBreak) { chainBroken = true; break; } + } + if (!chainBroken) { + player.closeHandledScreen(); + open(player, def, page); + } + return true; + } + for (GuiDefinition.ButtonAction action : actions) { boolean shouldBreak = executeAction(player, def, page, action); - if (shouldBreak) { chainBroken = true; break; } - } - // Toggle buttons must reopen the GUI to reflect the new state. - // Only reopen if no action already closed/navigated the screen. - if (isToggle && !chainBroken) { - player.closeHandledScreen(); - open(player, def, page); + if (shouldBreak) break; } return true; }