From 082f1adcb6b11ade4cb99646b50d926c7408d71f Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 21 Jul 2026 23:30:29 +0800 Subject: [PATCH] fix(interact): widen click dedup window to stop cross-tick double-firing PlayerUseUnknownEntityEvent is not Cancellable, so Paper keeps firing a follow-up PlayerInteractEvent for the same physical click. The previous 40ms dedup window is shorter than one server tick (50ms), so when the two events land on different ticks the duplicate slips through and the hand-tile toggle (select then discard) triggers on a single click. Raise the dedup windows in TableEventCoordinator and MahjongTableManager to 150ms (3 ticks) so the follow-up event is always caught. Sichuan exchange phase already bypasses hand-tile dedup, so rapid exchange clicks are unaffected. --- .../top/ellan/mahjong/table/core/MahjongTableManager.java | 4 ++-- .../top/ellan/mahjong/table/core/TableEventCoordinator.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/top/ellan/mahjong/table/core/MahjongTableManager.java b/src/main/java/top/ellan/mahjong/table/core/MahjongTableManager.java index eebe0ee..14e78b7 100644 --- a/src/main/java/top/ellan/mahjong/table/core/MahjongTableManager.java +++ b/src/main/java/top/ellan/mahjong/table/core/MahjongTableManager.java @@ -46,8 +46,8 @@ public final class MahjongTableManager implements Listener { private static final String ADMIN_PERMISSION = "mahjongpaper.admin"; - private static final long DUPLICATE_DISPLAY_ACTION_WINDOW_NANOS = 40_000_000L; - private static final long DUPLICATE_HAND_TILE_CLICK_WINDOW_NANOS = 40_000_000L; + private static final long DUPLICATE_DISPLAY_ACTION_WINDOW_NANOS = 150_000_000L; + private static final long DUPLICATE_HAND_TILE_CLICK_WINDOW_NANOS = 150_000_000L; private static final long RECENT_DISPLAY_ACTION_TTL_SECONDS = 60L; private static final long RECENT_HAND_TILE_CLICK_TTL_SECONDS = 60L; private static final double PERSISTED_TABLE_CLEANUP_RADIUS_XZ = 4.5D; diff --git a/src/main/java/top/ellan/mahjong/table/core/TableEventCoordinator.java b/src/main/java/top/ellan/mahjong/table/core/TableEventCoordinator.java index f1e6304..b5a86ca 100644 --- a/src/main/java/top/ellan/mahjong/table/core/TableEventCoordinator.java +++ b/src/main/java/top/ellan/mahjong/table/core/TableEventCoordinator.java @@ -41,13 +41,13 @@ import org.bukkit.util.RayTraceResult; final class TableEventCoordinator implements Listener { - private static final long DUPLICATE_DISPLAY_ACTION_WINDOW_NANOS = 40_000_000L; + private static final long DUPLICATE_DISPLAY_ACTION_WINDOW_NANOS = 150_000_000L; private static final long OVERHEAD_EXIT_GUARD_SECONDS = 2L; private static final double FLAT_INTERACTION_MAX_DISTANCE = 6.0D; private static final double FLAT_INTERACTION_BLOCK_EPSILON = 0.05D; /** * Bounded TTL on the per-player recent-action cache. The dedup window is - * 40ms (DUPLICATE_DISPLAY_ACTION_WINDOW_NANOS), so anything older than a + * 150ms (DUPLICATE_DISPLAY_ACTION_WINDOW_NANOS), so anything older than a * few seconds is by definition not a duplicate of a fresh click — but the * previous bare ConcurrentHashMap only evicted on PlayerQuitEvent, which * on Folia can occasionally be missed when the player's entity region