diff --git a/src/main/java/world/bentobox/chunkblock/Settings.java b/src/main/java/world/bentobox/chunkblock/Settings.java index bc5a338..4defcee 100644 --- a/src/main/java/world/bentobox/chunkblock/Settings.java +++ b/src/main/java/world/bentobox/chunkblock/Settings.java @@ -314,8 +314,9 @@ public class Settings implements WorldSettings { @ConfigComment("Admins can change protection sizes for players individually using /chadmin range set ") @ConfigComment("or set this permission: chunkblock.island.range.") @ConfigComment("ChunkBlock: this must cover the largest unlockable ring of chunks (see chunkblock.max-chunks).") + @ConfigComment("With max-chunks 441 (21x21, ring 10) the minimum needed is 168.") @ConfigEntry(path = "world.protection-range") - private int islandProtectionRange = 240; + private int islandProtectionRange = 168; @ConfigComment("Start islands at these coordinates. This is where new islands will start in the") @ConfigComment("world. These must be a factor of your island distance, but the plugin will auto") diff --git a/src/main/java/world/bentobox/chunkblock/listeners/LevelListener.java b/src/main/java/world/bentobox/chunkblock/listeners/LevelListener.java index a76f81b..e491057 100644 --- a/src/main/java/world/bentobox/chunkblock/listeners/LevelListener.java +++ b/src/main/java/world/bentobox/chunkblock/listeners/LevelListener.java @@ -27,6 +27,7 @@ import world.bentobox.chunkblock.events.ChunkUnlockEvent; import world.bentobox.chunkblock.events.RingCompleteEvent; import world.bentobox.level.events.IslandLevelCalculatedEvent; +import world.bentobox.level.events.IslandPreLevelEvent; /** * Watches island level changes from the Level addon. Levels are chunk currency here: @@ -44,6 +45,27 @@ public LevelListener(ChunkBlock addon) { this.addon = addon; } + /** + * Shrinks the island's protection range to cover only the unlocked chunks so the + * Level addon scans the playable area instead of the entire 240-block default. + * The calculator reads the range in the same tick; the original is restored on + * the next tick. + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onIslandPreLevel(IslandPreLevelEvent e) { + Island island = e.getIsland(); + if (island == null || !addon.inWorld(island.getWorld())) { + return; + } + int ring = addon.getChunkManager().currentRing(island); + int needed = ring * 16 + ChunkManager.CHUNK_CENTER; + int stored = island.getProtectionRange(); + if (needed < stored) { + island.setProtectionRange(needed); + Bukkit.getScheduler().runTask(addon.getPlugin(), () -> island.setProtectionRange(stored)); + } + } + /** * Fires after every island level calculation, before results are saved. Never * cancelled here — we only read the level. diff --git a/src/test/java/world/bentobox/chunkblock/SettingsTest.java b/src/test/java/world/bentobox/chunkblock/SettingsTest.java index 30cf8f1..ede1583 100644 --- a/src/test/java/world/bentobox/chunkblock/SettingsTest.java +++ b/src/test/java/world/bentobox/chunkblock/SettingsTest.java @@ -80,7 +80,7 @@ void testGetIslandDistance() { */ @Test void testGetIslandProtectionRange() { - assertEquals(240, s.getIslandProtectionRange()); + assertEquals(168, s.getIslandProtectionRange()); } /** diff --git a/src/test/java/world/bentobox/chunkblock/listeners/LevelListenerTest.java b/src/test/java/world/bentobox/chunkblock/listeners/LevelListenerTest.java index 334ed61..aaab902 100644 --- a/src/test/java/world/bentobox/chunkblock/listeners/LevelListenerTest.java +++ b/src/test/java/world/bentobox/chunkblock/listeners/LevelListenerTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -28,6 +29,7 @@ import world.bentobox.chunkblock.events.ChunkRelockEvent; import world.bentobox.chunkblock.events.ChunkUnlockEvent; import world.bentobox.chunkblock.events.RingCompleteEvent; +import world.bentobox.level.events.IslandPreLevelEvent; /** * Tests the credit-announcement and LIFO re-lock flows in {@link LevelListener} and the @@ -216,6 +218,45 @@ void testIslandResetClearsRingRewards() { assertEquals(1, data.getUnlockedChunkCount()); } + @Test + void testPreLevelShrinksProtectionRangeToUnlockedArea() { + when(island.getProtectionRange()).thenReturn(240); + // Only center chunk is unlocked → ring 0 → needed = 8 + IslandPreLevelEvent pre = new IslandPreLevelEvent(uuid, island); + listener.onIslandPreLevel(pre); + verify(island).setProtectionRange(ChunkManager.CHUNK_CENTER); + // Restore scheduled for next tick + verify(sch).runTask(any(), any(Runnable.class)); + } + + @Test + void testPreLevelRangeMatchesCurrentRing() { + when(island.getProtectionRange()).thenReturn(240); + level = 8; + claimRingOne(); + // Ring 1 unlocked → needed = 1 * 16 + 8 = 24 + IslandPreLevelEvent pre = new IslandPreLevelEvent(uuid, island); + listener.onIslandPreLevel(pre); + verify(island).setProtectionRange(24); + } + + @Test + void testPreLevelSkipsWhenRangeAlreadySmallEnough() { + when(island.getProtectionRange()).thenReturn(8); + IslandPreLevelEvent pre = new IslandPreLevelEvent(uuid, island); + listener.onIslandPreLevel(pre); + verify(island, never()).setProtectionRange(anyInt()); + } + + @Test + void testPreLevelIgnoresOtherWorlds() { + when(addon.inWorld(world)).thenReturn(false); + when(island.getProtectionRange()).thenReturn(240); + IslandPreLevelEvent pre = new IslandPreLevelEvent(uuid, island); + listener.onIslandPreLevel(pre); + verify(island, never()).setProtectionRange(anyInt()); + } + /** Claims and celebrates all eight chunks of ring 1, closing it with the last one */ private void claimRingOne() { for (int[] offset : new int[][] { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { -1, 1 },