From 241260a762f97aa4b0af34f33f236a652200ff19 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Mon, 28 Sep 2026 07:03:58 -0400 Subject: [PATCH] Support latched tree felling - Add a run hook controlling continued sneaking. - Preserve latched runs when players release sneak. --- .../iris/api/tree/TreeFellerRunHooks.java | 4 +++ .../arcane/iris/world/tree/TreeFellerSVC.java | 2 +- .../iris/world/tree/TreeFellingRunner.java | 31 ++++++++++++++++--- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/api/tree/TreeFellerRunHooks.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/api/tree/TreeFellerRunHooks.java index ba4ee17ee..74ad544df 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/api/tree/TreeFellerRunHooks.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/api/tree/TreeFellerRunHooks.java @@ -1,6 +1,10 @@ package art.arcane.iris.api.tree; public interface TreeFellerRunHooks { + default boolean requiresSneaking() { + return true; + } + TreeFellerRunHooks NONE = new TreeFellerRunHooks() { @Override public void onActivationAccepted() { diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellerSVC.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellerSVC.java index a04c123f5..60b22b603 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellerSVC.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellerSVC.java @@ -219,7 +219,7 @@ public class TreeFellerSVC implements IrisService, IrisTreeFellerService { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void haltWhenSneakingStops(PlayerToggleSneakEvent event) { if (!event.isSneaking()) { - runner.finishRuns(event.getPlayer().getUniqueId()); + runner.finishSneakRuns(event.getPlayer().getUniqueId()); } } diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellingRunner.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellingRunner.java index 5775321c2..c0c0ddbce 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellingRunner.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/tree/TreeFellingRunner.java @@ -523,10 +523,15 @@ final class TreeFellingRunner { } private boolean isRunControlActive(FellingRun run, Player player) { - return player.isOnline() - && player.getGameMode() == GameMode.SURVIVAL - && player.isSneaking() - && player.getWorld().equals(run.candidate.world()); + try { + return player.isOnline() + && player.getGameMode() == GameMode.SURVIVAL + && (!run.runHooks.requiresSneaking() || player.isSneaking()) + && player.getWorld().equals(run.candidate.world()); + } catch (RuntimeException error) { + IrisLogging.reportError("An Iris tree-feller integration control check failed.", error); + return false; + } } private boolean reserveLogCost(FellingRun run) { @@ -622,6 +627,24 @@ final class TreeFellingRunner { } } + void finishSneakRuns(UUID playerId) { + Set runs = service.activeRuns.get(playerId); + if (runs == null) { + return; + } + for (FellingRun run : List.copyOf(runs)) { + try { + if (!run.runHooks.requiresSneaking()) { + continue; + } + } catch (RuntimeException error) { + IrisLogging.reportError("An Iris tree-feller integration control check failed.", error); + } + run.abortReason = "halted"; + finish(run); + } + } + private Map> groupByChunk( List positions ) {