Skip to content

feat(limits): add configurable auto-refresh and harden limit persistence#260

Closed
matheus-fsc wants to merge 1 commit intoBentoBoxWorld:developfrom
matheus-fsc:develop
Closed

feat(limits): add configurable auto-refresh and harden limit persistence#260
matheus-fsc wants to merge 1 commit intoBentoBoxWorld:developfrom
matheus-fsc:develop

Conversation

@matheus-fsc
Copy link
Copy Markdown

Introduce a configurable auto-refresh loop for permission-based limits and add runtime auditing for islands above their effective limits.

Changes:

  • add auto-refresh-seconds to config (default 3600, 0 disables scheduler)

  • add Settings#getAutoRefreshSeconds() and parse value safely

  • start/stop JoinListener auto-refresh task in addon lifecycle

  • refresh online owners' island permission limits on schedule

  • audit block/entity/entity-group limit breaches and emit console warnings for admins

  • fix recount world resolution to evaluate material limits against the scanned world (normal/nether/end)

  • fix IslandBlockCount null-handling for entity maps so deserialized null maps are reattached

  • persist owner limit clears and admin offset changes immediately via setIsland()

  • add Settings tests for auto-refresh default and override behavior

Validation:

  • full suite passed with JDK 21: mvn test (222 tests, 0 failures, 0 errors)

Introduce a configurable auto-refresh loop for permission-based limits and add runtime auditing for islands above their effective limits.

Changes:

- add auto-refresh-seconds to config (default 3600, 0 disables scheduler)

- add Settings#getAutoRefreshSeconds() and parse value safely

- start/stop JoinListener auto-refresh task in addon lifecycle

- refresh online owners' island permission limits on schedule

- audit block/entity/entity-group limit breaches and emit console warnings for admins

- fix recount world resolution to evaluate material limits against the scanned world (normal/nether/end)

- fix IslandBlockCount null-handling for entity maps so deserialized null maps are reattached

- persist owner limit clears and admin offset changes immediately via setIsland()

- add Settings tests for auto-refresh default and override behavior

Validation:

- full suite passed with JDK 21: mvn test (222 tests, 0 failures, 0 errors)
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a configurable scheduled refresh to re-evaluate permission-based island limits and introduces runtime auditing to detect and warn about islands exceeding effective block/entity limits, while also hardening persistence and fixing recount world resolution.

Changes:

  • Add auto-refresh-seconds config + Settings#getAutoRefreshSeconds() and tests.
  • Start/stop an auto-refresh task in addon lifecycle to refresh owner permission limits and audit limit breaches.
  • Fix recount limit evaluation world selection and improve persistence/null-handling for deserialized island limit maps and admin offset updates.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/test/java/world/bentobox/limits/SettingsTest.java Adds tests for the new auto-refresh configuration behavior.
src/main/resources/config.yml Introduces auto-refresh-seconds configuration with documentation and default.
src/main/java/world/bentobox/limits/Settings.java Parses/stores the auto-refresh interval and exposes it via a getter.
src/main/java/world/bentobox/limits/objects/IslandBlockCount.java Ensures deserialized null entity maps are reattached and clears don’t NPE.
src/main/java/world/bentobox/limits/listeners/JoinListener.java Implements scheduled refresh + auditing and persists owner-permission removals.
src/main/java/world/bentobox/limits/Limits.java Wires auto-refresh start/stop into addon enable/disable lifecycle.
src/main/java/world/bentobox/limits/commands/admin/OffsetCommand.java Persists offset changes immediately via setIsland().
src/main/java/world/bentobox/limits/calculators/RecountCalculator.java Evaluates material limits against the correct scanned world (normal/nether/end).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +177 to +196
private Map<EntityType, Integer> countEntitiesInIslandSpace(Island island) {
Map<EntityType, Integer> counts = new EnumMap<>(EntityType.class);
World world = island.getWorld();
collectEntityCounts(island, world, counts);
IslandWorldManager iwm = addon.getPlugin().getIWM();
if (iwm.isNetherIslands(world) && iwm.getNetherWorld(world) != null) {
collectEntityCounts(island, iwm.getNetherWorld(world), counts);
}
if (iwm.isEndIslands(world) && iwm.getEndWorld(world) != null) {
collectEntityCounts(island, iwm.getEndWorld(world), counts);
}
return counts;
}

private void collectEntityCounts(Island island, World world, Map<EntityType, Integer> counts) {
for (Entity entity : world.getEntities()) {
if (island.inIslandSpace(entity.getLocation())) {
counts.merge(entity.getType(), 1, Integer::sum);
}
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scheduled auto-refresh runs on the main thread and the audit path does an O(totalEntitiesInWorld) scan per island via world.getEntities() + island.inIslandSpace(). This can cause noticeable server lag spikes on large servers. Prefer the existing pattern used in EntityLimitListener (e.g., EntityLimitListener.java:454-503) using world.getNearbyEntities(island.getBoundingBox()) (and then filtering/grouping) so the query is spatially bounded and doesn’t require iterating every entity in the world.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +111
private void refreshPlayerIslands(GameModeAddon gameMode, Player player) {
addon.getIslands().getIslands(gameMode.getOverWorld(), player.getUniqueId()).stream()
.filter(island -> player.getUniqueId().equals(island.getOwner()))
.forEach(island -> {
String islandId = island.getUniqueId();
IslandBlockCount islandBlockCount = addon.getBlockLimitListener().getIsland(islandId);
if (!joinEventCheck(player, islandId, islandBlockCount)) {
checkPerms(player, gameMode.getPermissionPrefix() + "island.limit.", islandId,
gameMode.getDescription().getName());
}
auditIslandLimitBreaches(island, player.getName());
});
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-refresh calls checkPerms() for each owned island, and checkPerms() always persists via BlockLimitsListener.setIsland() (which unconditionally triggers handler.saveObjectAsync; see BlockLimitsListener.java:574-577). With the scheduler enabled, this can create periodic database writes even when permissions/limits didn’t change. Consider tracking whether any permission-derived limits actually changed (or gating persistence behind IslandBlockCount#isChanged / map-diff) before calling setIsland during scheduled refreshes.

Copilot uses AI. Check for mistakes.
@matheus-fsc matheus-fsc closed this Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants