From e6199c0a7ad6945aad909e4ef91674c23f0609ae Mon Sep 17 00:00:00 2001 From: nossr50 Date: Fri, 4 Jul 2025 12:13:37 -0700 Subject: [PATCH] prevent simulateBlockBreak from calling itself fixes #5188 --- .../com/gmail/nossr50/util/EventUtils.java | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/gmail/nossr50/util/EventUtils.java b/src/main/java/com/gmail/nossr50/util/EventUtils.java index 984d24d8b..69016e65d 100644 --- a/src/main/java/com/gmail/nossr50/util/EventUtils.java +++ b/src/main/java/com/gmail/nossr50/util/EventUtils.java @@ -64,6 +64,13 @@ import org.jetbrains.annotations.NotNull; */ public final class EventUtils { + /** + * True when the current thread is already executing simulateBlockBreak(). + * Thread-local so parallel Folia regions / async tasks are isolated. + */ + private static final ThreadLocal IN_FAKE_BREAK = + ThreadLocal.withInitial(() -> false); + /** * This is a static utility class, therefore we don't want any instances of this class. Making * the constructor private prevents accidents like that. @@ -366,22 +373,31 @@ public final class EventUtils { * @param eventType The type of event to signal to other plugins * @return true if the event wasn't cancelled, false otherwise */ - public static boolean simulateBlockBreak(Block block, Player player, + private static boolean simulateBlockBreak(Block block, Player player, FakeBlockBreakEventType eventType) { - PluginManager pluginManager = mcMMO.p.getServer().getPluginManager(); - - FakeBlockDamageEvent damageEvent = new FakeBlockDamageEvent(player, block, - player.getInventory().getItemInMainHand(), true); - pluginManager.callEvent(damageEvent); - - BlockBreakEvent fakeBlockBreakEvent = null; - - switch (eventType) { - case FAKE -> fakeBlockBreakEvent = new FakeBlockBreakEvent(block, player); - case TREE_FELLER -> fakeBlockBreakEvent = new TreeFellerBlockBreakEvent(block, player); + if (IN_FAKE_BREAK.get()) { + return true; + } + IN_FAKE_BREAK.set(true); + + try { + final PluginManager pluginManager = mcMMO.p.getServer().getPluginManager(); + + final FakeBlockDamageEvent damageEvent = new FakeBlockDamageEvent(player, block, + player.getInventory().getItemInMainHand(), true); + pluginManager.callEvent(damageEvent); + + final BlockBreakEvent fakeBlockBreakEvent = switch (eventType) { + case FAKE -> new FakeBlockBreakEvent(block, player); + case TREE_FELLER -> new TreeFellerBlockBreakEvent(block, player); + }; + + pluginManager.callEvent(fakeBlockBreakEvent); + return !damageEvent.isCancelled() && !fakeBlockBreakEvent.isCancelled(); + } finally { + // always clear the flag + IN_FAKE_BREAK.set(false); } - pluginManager.callEvent(fakeBlockBreakEvent); - return !damageEvent.isCancelled() && !fakeBlockBreakEvent.isCancelled(); } public static void handlePartyTeleportEvent(Player teleportingPlayer, Player targetPlayer) {