From 241017ff1584c3960e6bfd4733b77be75bae6c57 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 11 May 2026 20:38:17 -0700 Subject: [PATCH] feat: add donations-only mode to skip block scan (#430) Adds a `donations-only` config option that bypasses the island chunk scan entirely. The island level is computed only from blocks donated via `/island donate`, removing the per-recalculation CPU cost of scanning. When the option is enabled: - IslandLevelCalculator.scanIsland short-circuits to tidyUp(), which adds the donated points (`LevelsManager.getDonatedPoints`) and runs the configured level-calc formula on those points alone. - The /island detail command is not registered, since there are no scanned blocks to break down per the issue thread. - /island level, /island top, /island value, and /island donate continue to work; /island level reports the level based on donations only. Closes #430. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main/java/world/bentobox/level/Level.java | 6 ++++- .../calculators/IslandLevelCalculator.java | 8 ++++++ .../bentobox/level/config/ConfigSettings.java | 26 +++++++++++++++++++ src/main/resources/config.yml | 13 ++++++++-- .../java/world/bentobox/level/LevelTest.java | 15 +++++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java index 96a2cf6..82f6702 100644 --- a/src/main/java/world/bentobox/level/Level.java +++ b/src/main/java/world/bentobox/level/Level.java @@ -275,7 +275,11 @@ private void registerCommands(GameModeAddon gm) { new IslandLevelCommand(this, playerCmd); new IslandTopCommand(this, playerCmd); new IslandValueCommand(this, playerCmd); - new IslandDetailCommand(this, playerCmd); + // In donations-only mode, there are no scanned blocks to break down, + // so the detail command is not registered. + if (!getSettings().isDonationsOnly()) { + new IslandDetailCommand(this, playerCmd); + } new IslandDonateCommand(this, playerCmd); }); } diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java index 8ef95dc..0ec2c29 100644 --- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -794,6 +794,14 @@ boolean isNotZeroIsland() { } public void scanIsland(Pipeliner pipeliner) { + // In donations-only mode, skip the chunk scan entirely. tidyUp() will add + // the donated points and compute the level from those alone. + if (addon.getSettings().isDonationsOnly()) { + pipeliner.getInProcessQueue().remove(this); + this.tidyUp(); + this.getR().complete(getResults()); + return; + } // Scan the next chunk scanNextChunk().thenAccept(result -> { if (!Bukkit.isPrimaryThread()) { diff --git a/src/main/java/world/bentobox/level/config/ConfigSettings.java b/src/main/java/world/bentobox/level/config/ConfigSettings.java index f7879a3..ef47003 100644 --- a/src/main/java/world/bentobox/level/config/ConfigSettings.java +++ b/src/main/java/world/bentobox/level/config/ConfigSettings.java @@ -54,6 +54,17 @@ public class ConfigSettings implements ConfigObject { @ConfigEntry(path = "zero-new-island-levels") private boolean zeroNewIslandLevels = true; + @ConfigComment("") + @ConfigComment("Donations-only mode") + @ConfigComment("If true, the island block scan is skipped entirely and the island level") + @ConfigComment("is computed only from blocks donated via /island donate. This removes the") + @ConfigComment("per-recalculation CPU cost of scanning the island.") + @ConfigComment("The /island detail command is not registered in this mode, since there") + @ConfigComment("are no scanned blocks to break down. /island level still works and reports") + @ConfigComment("the level based on donated blocks.") + @ConfigEntry(path = "donations-only") + private boolean donationsOnly = false; + @ConfigComment("") @ConfigComment("Calculate island level on login") @@ -509,4 +520,19 @@ public boolean isDisableItemsAdder() { public void setDisableItemsAdder(boolean disableItemsAdder) { this.disableItemsAdder = disableItemsAdder; } + + /** + * @return true if donations-only mode is enabled (block scan skipped, level + * computed from donations only, /island detail disabled) + */ + public boolean isDonationsOnly() { + return donationsOnly; + } + + /** + * @param donationsOnly the donationsOnly to set + */ + public void setDonationsOnly(boolean donationsOnly) { + this.donationsOnly = donationsOnly; + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 11aacec..c101ad8 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -20,13 +20,22 @@ concurrent-island-calcs: 1 # If an island takes longer that this time to calculate, then the calculation will abort. # Generally, calculation should only take a few seconds, so if this ever triggers then something is not right. calculation-timeout: 5 -# +# # Zero island levels on new island or island reset # If true, Level will calculate the starter island's level and remove it from any future level calculations. # If false, the player's starter island blocks will count towards their level. # This will reduce CPU if false. zero-new-island-levels: true -# +# +# Donations-only mode +# If true, the island block scan is skipped entirely and the island level +# is computed only from blocks donated via /island donate. This removes the +# per-recalculation CPU cost of scanning the island. +# The /island detail command is not registered in this mode, since there +# are no scanned blocks to break down. /island level still works and reports +# the level based on donated blocks. +donations-only: false +# # Calculate island level on login # This silently calculates the player's island level when they login # This applies to all islands the player has on the server, e.g., BSkyBlock, AcidIsland diff --git a/src/test/java/world/bentobox/level/LevelTest.java b/src/test/java/world/bentobox/level/LevelTest.java index 288fec9..6323bf6 100644 --- a/src/test/java/world/bentobox/level/LevelTest.java +++ b/src/test/java/world/bentobox/level/LevelTest.java @@ -247,4 +247,19 @@ void testGetSettings() { assertEquals(100, s.getLevelCost()); } + /** + * Donations-only mode must not register the IslandDetailCommand, so we expect + * four player commands instead of the usual five. + */ + @Test + void testAllLoadedDonationsOnlySkipsDetailCommand() { + mockedBukkit.when(() -> Bukkit.getWorld("acidisland_world")).thenReturn(null); + addon.getSettings().setDonationsOnly(true); + addon.allLoaded(); + // 4 player commands (level, top, value, donate) — no detail + verify(cmd, times(4)).getAddon(); + // Admin command count is unchanged + verify(adminCmd, times(5)).getAddon(); + } + }