Skip to content

Release 3.15#2945

Merged
tastybento merged 60 commits intomasterfrom
develop
Apr 26, 2026
Merged

Release 3.15#2945
tastybento merged 60 commits intomasterfrom
develop

Conversation

@tastybento
Copy link
Copy Markdown
Member

No description provided.

tastybento and others added 30 commits April 8, 2026 23:43
Scaffolding for the shift away from chunk-copy island deletion. No behavior
change yet — reset and admin delete still go through the old pipeline.

- Settings: add island.deletion.housekeeping.{enabled,interval-days,
  region-age-days} (defaults off/30/60). Deprecate keep-previous-island-on-reset
  and slow-deletion config entries (unbound from config; getters/setters kept
  as @deprecated(forRemoval=true) for binary compat until Phase 4).
- PurgeRegionsService: extract scan/filter/delete/player-cleanup logic out
  of AdminPurgeRegionsCommand so the command and the scheduler share one
  code path. Handles both pre-26.1 (DIM-1/DIM1 subfolders) and 26.1.1+
  (sibling world folders) dimension layouts.
- AdminPurgeRegionsCommand: reduced to ~180 LOC, delegates to the service
  and retains only the two-step confirmation UX + per-island display.
- HousekeepingManager: new manager wired in BentoBox.onEnable(). Hourly
  wall-clock check; runs the purge service across every gamemode overworld
  if enabled and interval has elapsed. Last-run timestamp persisted to
  <plugin-data-folder>/database/housekeeping.yml regardless of DB backend,
  so the schedule survives restarts. Progress logged to console.
- AdminPurgeRegionsCommandTest: stub plugin.getPurgeRegionsService() with
  a real service over the mocked plugin so the extraction is exercised
  exactly as the command runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Running /bbox purge regions confirm on Paper 26.1.1 tripped AsyncCatcher
because PurgeRegionsService.delete() was saving worlds from the async
worker thread, and World.save() is main-thread-only:

    IllegalStateException: Asynchronous world save!
      at PurgeRegionsService.delete(PurgeRegionsService.java:151)

The pre-refactor command ran the save on the main thread inside execute()
but I collapsed it into the service. Move the save back out of the
service so all callers are responsible for flushing on the main thread
before dispatching the async delete.

- PurgeRegionsService.delete(): no longer calls Bukkit.getWorlds().save().
  Javadoc updated to state the caller contract.
- AdminPurgeRegionsCommand.deleteEverything(): call Bukkit.getWorlds()
  .forEach(World::save) before scheduling the async delete. Runs on the
  main thread since execute() is invoked there.
- HousekeepingManager.executeCycle(): the existing runTask() save was
  fire-and-forget — the async cycle could start scanning/deleting before
  the save finished. Block via CompletableFuture.join() until the
  main-thread save completes.
- AdminPurgeRegionsCommandTest: add regression asserting the service
  never calls Bukkit.getWorlds() itself (would have caught this bug).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Paper rate-limits its built-in "plugin-induced save detected" warning,
so after the scan save fired once, the confirm-path save was silent and
looked like it wasn't running. Add explicit plugin.log lines on both
sides of every World.save() call in the purge code paths (scan, confirm,
housekeeping) so operators always see when the save is happening.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds /bbox admin purge age-regions <days> to rewrite per-chunk
timestamp tables in .mca files so regions become purgable without
waiting wall-clock time. The purge scanner reads timestamps from the
region header, not file mtime, so `touch` cannot fake ageing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
IslandsManager.deleteIsland() used to branch on keepPreviousIslandOnReset:
false -> evict from cache, enqueue IslandChunkDeletionManager, MultiLib
notify, delete DB row. true -> save with deletable=true and fire the
deletion event.

With the new region-file purge flow (Phase 1), physical cleanup no
longer happens inline at all - old islands are left in place with
deletable=true and reaped later by PurgeRegionsService /
HousekeepingManager. So the hard-path branch goes away entirely:
every call with removeBlocks=true now soft-deletes.

Consequences in this commit:
- AdminDeleteCommand also soft-deletes until Phase 3 splits it on
  GameModeAddon.isUsesNewChunkGeneration() (new-gen -> soft-delete,
  void gamemodes -> ChunkGenerator regen).
- Nether/End cascade is a no-op in the soft path (nothing touches
  chunks); PurgeRegionsService.scan already gates nether/end on
  isNetherIslands/isEndIslands so vanilla-owned dimensions are
  skipped when the regions are eventually reaped.
- keepPreviousIslandOnReset setter/getter remain as deprecated shims
  (no longer consulted at runtime); Phase 4 removes the field.
- The bentobox-deleteIsland MultiLib subscriber is now unreachable
  from this server's publishers but stays until Phase 4 deletes the
  deletion infrastructure wholesale.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2 made reset leave orphaned islands in place with
deletable=true until the region purge reaps them. That meant admins
walking around a server had no way to tell an orphan from a normal
unowned island — /bbox admin info just showed "Unowned" and entering
the area was silent.

Two visible cues now:

- IslandInfo.showAdminInfo() prints a new "deletable: flagged for
  deletion and awaiting region purge" line when island.isDeletable()
  is true, right after the purge-protected line.

- LockAndBanListener notifies ops (once per entry, same pattern as
  the existing lock notification) when they step onto an island
  flagged deletable. Non-ops still see nothing; this is strictly an
  admin heads-up. The notification state is cleared when the op
  leaves the island, so walking back in re-triggers it.

New locale keys commands.admin.info.deletable and
protection.deletable-island-admin in en-US.yml.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/bbox admin delete used to always call deleteIsland(island, true, uuid),
which after Phase 2 soft-deletes unconditionally. That is the right
behavior for new-chunk-generation gamemodes like Boxed where chunks
are expensive and the region-file purge reaps them later on the
HousekeepingManager schedule. For void/simple-generator gamemodes it
is the wrong behavior — chunks are cheap, admins expect "delete" to
actually delete, and soft-deleted rows would linger forever because
the repainted region files always look fresh to the purge scan.

Branch on GameModeAddon.isUsesNewChunkGeneration():

  - true (new-gen): soft-delete via IslandsManager.deleteIsland(),
    same as /is reset. Physical cleanup happens later via
    PurgeRegionsService / HousekeepingManager.

  - false (void/simple): kick off DeleteIslandChunks (which
    routes to WorldRegenerator.regenerateSimple with correct
    nether/end cascade gating) to repaint the chunks via the
    addon's own ChunkGenerator, then hard-delete the island row
    immediately. DeleteIslandChunks snapshots the bounds in its
    constructor so the row can be removed before the async regen
    completes.

Adds IslandsManager.hardDeleteIsland(island): fires the pre-delete
event, kicks members, nulls owner, evicts from cache, deletes the DB
row. Does not touch world chunks — caller handles physical cleanup.

Phase 4 will remove DeleteIslandChunks, IslandDeletion, and the
CopyWorldRegenerator.regenerateCopy seed-world path; regenerateSimple
and the split here survive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a second purge mode that reaps region files for any island already
flagged as deletable, regardless of region-file age. Exposed as
/bbox admin purge deleted and run from HousekeepingManager on a
configurable hourly cadence (default 24h) alongside the existing
monthly age sweep. Closes the post-reset gap where orphan island
regions sat on disk for 60+ days waiting for the age threshold.

Fix: evict in-memory chunks via World.unloadChunk(cx, cz, false) on
the main thread before the async file delete, otherwise Paper's
autosave re-flushes the deleted region files with the stale chunks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Paper's internal chunk cache keeps serving stale block data even after
the .mca region files are deleted from disk. The chunks only clear on
server restart when Paper discards its cache. Deleting the island DB
row immediately left a window where players see old blocks but BentoBox
reports no island at that location.

The deleted sweep (days==0) now adds island IDs to a pendingDeletions
set instead of removing them from the DB inline. On plugin shutdown
(BentoBox.onDisable), flushPendingDeletions() processes the set. If
the server crashes before a clean shutdown, the islands stay
deletable=true and the next purge cycle retries safely.

The age-based sweep (days>0) keeps immediate DB removal with the
existing residual-region completeness check, since old regions won't
be in Paper's memory cache.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip the diagnostic logging added during development that printed
file size, removed status, and existsAfter for every .mca deletion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This setting was made obsolete by Phase 2 which changed /is reset to
always soft-delete. The only remaining references were in
AdminPurgeCommand for conditional logging — now simplified to always
use tier-based progress reporting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…vice.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…/AdminPurgeDeletedCommand.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…DeleteCommand.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Rename `deleteableRegions` → `deletableRegions` in PurgeScanResult
  and all callers (fixes API surface typo)
- Distinguish "none-found" from "purge failed" in
  AdminPurgeRegionsCommand.deleteEverything() with dedicated locale key
- Replace CompletableFuture.runAsync() with Bukkit scheduler in
  AdminPurgeAgeRegionsCommand (ties task to plugin lifecycle)
- Clear notifiedPlayers and deletableNotified on PlayerQuitEvent in
  LockAndBanListener (prevents unbounded set growth)
- Add `commands.admin.purge.failed` locale key in en-US.yml

Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/99688668-0dd8-455e-9002-3f229fbefbdc

Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
The minimumRank validation was clamping SETTING flags that use -1
(disabled) as their defaultRank. Since -1 is a valid disabled state
for SETTING/WORLD_SETTING flags (Island.isAllowed checks >= 0),
restrict the validation to PROTECTION flags only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bundle icon

The "icon" field in blueprint bundle JSON files now accepts three formats:
- Plain material name: "DIAMOND"
- Vanilla namespaced material: "minecraft:diamond"
- Custom item model key: "myserver:island_tropical"

Material.matchMaterial() is tried first, handling plain names and vanilla
namespaced keys. If the string contains a colon and is not a recognised
vanilla material, it is treated as a custom item model key applied to a
PAPER base item via ItemMeta.setItemModel().

getIcon() (returning Material) is preserved for binary compatibility.
getIconItemStack() is the new method that returns the full ItemStack with
model data applied. Panels now call getIconItemStack() to render icons.

Closes #2940

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When an admin clicks an item from their inventory to set a blueprint bundle
icon, IconChanger now checks ItemMeta.hasItemModel() first. If the item
carries a custom model key (e.g. paper[item_model="myserver:island_tropical"]),
that NamespacedKey string is stored instead of the plain Material name,
preserving the full item model through to getIconItemStack().

Plain material items continue to work exactly as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Applies the same logic as BlueprintBundle to Blueprint.java:
- icon field changed from Material to String internally
- getIcon() preserved (returns Material, backward compatible)
- getIconItemStack() added — resolves plain names, minecraft: keys,
  and custom item-model keys (PAPER base + ItemMeta.setItemModel)
- setIcon(Material) and setIcon(String) both fluently return Blueprint

BlueprintManagementPanel now calls blueprint.getIconItemStack() so
custom-model icons are visible to admins in the management GUI.

IconChanger also applies item-model detection in the blueprint branch,
so admins can click a datapacked item to set a blueprint icon the same
way as a bundle icon.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation

SonarCloud flagged 8.9% code duplication and repeated "PAPER" literals.

- Add ItemParser.parseIconMaterial() and parseIconItemStack() as the
  single source of truth for icon string resolution
- Both BlueprintBundle and Blueprint now delegate to these methods
  instead of duplicating the logic
- Replace repeated "PAPER" string literals with a DEFAULT_ICON constant
  in each class
- Remove unused Sound import from IconChangerTest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…item-model-icon

feat: support item model keys and namespaced materials for blueprint bundle icon
tastybento and others added 16 commits April 18, 2026 06:11
…y /purge unowned

The old /purge command only soft-flagged islands as deletable and left the
.mca region files in place. Disk wasn't freed until the next housekeeping
sweep (or a manual /purge regions). This merges the region-file deletion
directly into /purge <days> so admins get the full reap in one step, and
removes now-redundant subcommands.

Changes:
- /purge <days> now delegates to PurgeRegionsService: scans for region files
  older than N days whose islands aren't protected/spawn/unowned, lists each
  island location to the log, and reaps the .mca files on confirm.
- Dropped /purge regions (merged in), /purge status, /purge stop (regions
  approach is synchronous from the admin's POV — no state to supervise).
- /purge unowned: simplified to flag orphan islands deletable via
  IslandsManager.deleteIsland(island, true, null); logs each orphan's
  location; tells the admin to run /purge deleted to reap.
- /purge deleted unchanged — still reaps anything already flagged.
- Locale cleanup: removed keys for the dropped subcommands; updated
  description to reflect disk-freeing behavior; added unowned.flagged.
- Tests: AdminPurgeCommandTest rewritten end-to-end against a real
  PurgeRegionsService with a tempDir region layout; AdminPurgeRegionsCommandTest
  removed; AdminPurgeUnownedCommandTest gains coverage for already-deletable
  islands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Drop always-true `this.user.equals(user)` guards in AdminPurgeCommand
  and AdminPurgeDeletedCommand — `this.user = user` was assigned on the
  prior line, so the check could never fail.
- Parameterize the three days-validation tests (notanumber / 0 / -3) as
  a single @ParameterizedTest.
- Extract `wireIsland(id, deletable, purgeProtected, spawn)`,
  `wireEmptyGrid()`, and `createRegionFile()` helpers in
  AdminPurgeCommandTest to collapse ~15-line setup blocks duplicated
  across the 8 scenario tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ommands

AdminPurgeCommand and AdminPurgeDeletedCommand each carried their own
inPurge/toBeConfirmed/lastScan state machine and near-identical
canExecute / scan-and-prompt / deleteEverything bodies (~75 lines apiece,
diverging only in scan source, log prefix, confirm message and an optional
chunk-evict step). Pull the shared scaffolding into a package-private
AbstractPurgeCommand, with subclasses supplying:

- logPrefix() — log line prefix
- successMessageKey() — locale key sent on a successful delete
- sendConfirmPrompt() — main-thread prompt(s) before user types confirm
- beforeDelete(scan) — optional pre-delete hook (used by /purge deleted to
  evict in-memory chunks before the async file delete)
- logScanContents(islands, scan) — optional scan-time logging

Behaviour is preserved: same locale keys, same log prefixes, same async
threading. The "after a non-empty scan" tail on the failed-delete log line
in /purge deleted is dropped — it was always true (we'd already returned
on empty) and made the message harder to share.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Merge /purge regions into /purge; drop status/stop; simplify /purge unowned
- PurgeRegionsService: log a warning when scan() or scanDeleted() finds
  no island grid for a world, instead of silently returning empty. Helps
  admins diagnose misconfigured worlds.

- AdminDeleteCommandTest: add tests for the isUsesNewChunkGeneration()
  branch in deleteIsland() — verify soft-delete path (new chunk gen)
  calls deleteIsland(island, true, uuid) and hard-delete path (simple
  gen) calls hardDeleteIsland(island).

- PurgeRegionsServiceTest: add testScanNullGrid() to match existing
  testScanDeletedNullGrid() coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a soft-deleted island's protection box spans two region files and one
of those regions is blocked by an active neighbour island, the deleted-sweep
previously removed the island's DB row at shutdown even though its chunks
remained on disk in the blocked region. On the next server start those
orphaned blocks loaded from the still-present .mca file and appeared in-game
with no island record to track them ("ghost blocks").

Fix: filterForDeletedSweep now collects the IDs of deletable islands found in
blocked regions and returns them via a new DeleteSweepFilter record. scanDeleted
propagates these as PurgeScanResult.straddlingIslandIds. In delete(), any island
in that set is skipped for pendingDeletions — its DB row stays (deletable=true)
so the next housekeeping sweep retries once the blocking island is itself deleted.

Three new tests cover: straddling island DB row is retained, non-straddling
island is still deferred normally, and scanDeleted populates straddlingIslandIds
correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nd-ghost-blocks

fix: retain DB row for deleted-sweep islands straddling a blocked region
…s; translate uk.yml parameters

- Add 15 missing keys (purge failed, age-regions, deleted, unowned.flagged,
  info.deletable, OBSIDIAN_SCOOPING.lavaTip, deletable-island-admin,
  ISLAND_DEFAULTS panel) to all 22 non-English locale files with AI-assisted
  translations
- Translate 48 untranslated parameters: lines in uk.yml: <player>→<гравець>,
  [player]→[гравець], [home name]→[назва дому], [language]→[мова>,
  player's island→острів гравця, etc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e files

Translate player-facing parameter strings (e.g. [home name], [language],
<player>, [flag]) that are pure display text looked up via getTranslationOrNothing.
Parsed keyword arguments ([list|add|remove], [air] [biome] [nowater]) are
intentionally left in English as they are matched by Java command parsers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions

feat(locale): add 3.15 purge/delete keys and translate parameter strings
Centralises CraftEngine custom block access in a BentoBox hook,
following the same pattern as ItemsAdderHook and OraxenHook.
Provides static utility methods for block ID detection, registry
validation, and block placement so that addons do not need a direct
CraftEngine compile-time dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: add CraftEngineHook for centralised custom block support
… error message

YamlConfiguration.loadConfiguration(File) internally catches parse
exceptions and logs them at SEVERE with a full stack trace via
Bukkit.getLogger(), bypassing BentoBox's own try-catch entirely.

Switch to new YamlConfiguration() + load(file) so BentoBox owns the
error handling. Split the catch into InvalidConfigurationException
(bad YAML syntax — tells the admin to delete/fix the file and notes
English fallback) and a generic Exception fallback. Includes the
locale folder name in the message so the admin knows which addon's
file is at fault.

Related: BentoBoxWorld/Chat#58

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-stacktrace

fix(locale): suppress stack trace for malformed locale files, improve error message
Vanilla Minecraft hardcodes the end arrival platform to
ServerLevel.END_SPAWN_POINT (~100, 50, 0). When BentoBox redirects the
player via PlayerPortalEvent#setTo to the island portal location, the
platform sits at the fixed coords but the player lands elsewhere and
falls into the void.

Add createEndPlatform(Location) on AbstractTeleportListener that lays a
5x5 obsidian floor with 3 layers of clear space above, and call it from
both PlayerTeleportListener and EntityTeleportListener when the
destination is THE_END and portal creation is enabled.

Fixes BentoBoxWorld/AOneBlock#433

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…-at-destination

fix: build end obsidian platform at destination, not fixed spawn point
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

Release 3.15.0 introduces a new “soft-delete + region-file reap” workflow for island deletion/purging, expands icon support to item-model based icons, and adds CraftEngine integration.

Changes:

  • Added region-file purge infrastructure (PurgeRegionsService) with new admin subcommands (purge deleted, purge age-regions) and periodic housekeeping (HousekeepingManager).
  • Switched blueprint/bundle icons from Material to string-backed icons that can represent custom item models, and updated panels + click handling accordingly.
  • Added CraftEngine hook + dependency wiring and updated locale/config/resources to match new commands and messaging.

Reviewed changes

Copilot reviewed 77 out of 80 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/test/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanelTest.java Updates tests for icon ItemStack usage.
src/test/java/world/bentobox/bentobox/panels/IconChangerTest.java New tests for item-model vs material icon selection.
src/test/java/world/bentobox/bentobox/panels/BlueprintManagementPanelTest.java Updates tests for icon ItemStack usage.
src/test/java/world/bentobox/bentobox/managers/HousekeepingManagerTest.java New tests for housekeeping scheduling/state persistence.
src/test/java/world/bentobox/bentobox/listeners/teleports/PlayerTeleportListenerTest.java Adds regression tests for End platform creation behavior.
src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListenerTest.java Adds targeting-cancel behavior tests (XP orb exception).
src/test/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBundleTest.java New tests for icon string parsing + ItemStack rendering.
src/test/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommandTest.java Updates purge-unowned tests for new deletable-flag flow.
src/test/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeDeletedCommandTest.java New tests for deleted-sweep purge behavior.
src/test/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommandTest.java Updates tests for hard-delete vs soft-delete branching.
src/test/java/world/bentobox/bentobox/SettingsTest.java Removes keep-previous-island tests in line with deprecation.
src/main/resources/plugin.yml Adds CraftEngine as a soft dependency.
src/main/resources/locales/en-US.yml Updates purge messaging; adds deleted/age-regions/unowned flagging keys.
src/main/resources/locales/zh-HK.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/zh-CN.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/vi.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/tr.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/ru.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/ro.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/pt.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/pt-BR.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/pl.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/nl.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/lv.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/ko.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/ja.yml Adds new purge-related keys and related messaging (incl. typo fix).
src/main/resources/locales/it.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/id.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/hu.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/hr.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/fr.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/es.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/de.yml Adds new purge-related keys and related messaging.
src/main/resources/locales/cs.yml Adds new purge-related keys and related messaging.
src/main/resources/config.yml Removes deprecated keep-previous-island-on-reset config block.
src/main/java/world/bentobox/bentobox/util/ItemParser.java Adds icon string → Material/ItemStack parsing helpers (item model aware).
src/main/java/world/bentobox/bentobox/util/IslandInfo.java Displays “deletable” state in admin island info output.
src/main/java/world/bentobox/bentobox/panels/settings/IslandDefaultSettingsTab.java Updates @since tag for 3.15.0.
src/main/java/world/bentobox/bentobox/panels/customizable/IslandCreationPanel.java Uses bundle icon ItemStack rather than Material.
src/main/java/world/bentobox/bentobox/panels/IconChanger.java Stores item-model key icons when present.
src/main/java/world/bentobox/bentobox/panels/BlueprintManagementPanel.java Uses bundle/blueprint icon ItemStacks in UI.
src/main/java/world/bentobox/bentobox/managers/LocalesManager.java Improves locale YAML load error handling and messaging.
src/main/java/world/bentobox/bentobox/managers/IslandsManager.java Soft-delete always marks deletable + adds hardDeleteIsland.
src/main/java/world/bentobox/bentobox/managers/HousekeepingManager.java New scheduled housekeeping for age/deleted sweeps with persisted state.
src/main/java/world/bentobox/bentobox/managers/ChunkPregenManager.java Updates @since tag for 3.15.0.
src/main/java/world/bentobox/bentobox/listeners/teleports/PlayerTeleportListener.java Builds End platform at redirected destination to prevent void fall.
src/main/java/world/bentobox/bentobox/listeners/teleports/EntityTeleportListener.java Same End platform behavior for entities.
src/main/java/world/bentobox/bentobox/listeners/teleports/AbstractTeleportListener.java Adds createEndPlatform() helper implementation.
src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/InvincibleVisitorsListener.java Prevents IV targeting cancel for ExperienceOrbs.
src/main/java/world/bentobox/bentobox/listeners/flags/protection/LockAndBanListener.java Notifies ops when on deletable islands; cleans up tracking on quit.
src/main/java/world/bentobox/bentobox/listeners/BentoBoxListenerRegistrar.java Updates @since tag for 3.15.0.
src/main/java/world/bentobox/bentobox/hooks/MythicMobsHook.java Updates @since tag for 3.15.0.
src/main/java/world/bentobox/bentobox/hooks/CraftEngineHook.java New hook wrapper around CraftEngine custom block API.
src/main/java/world/bentobox/bentobox/hooks/BentoBoxHookRegistrar.java Registers CraftEngine as a late hook.
src/main/java/world/bentobox/bentobox/database/objects/Island.java Updates @since tags for 3.15.0.
src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintBundle.java Switches icon storage to string + adds ItemStack accessor.
src/main/java/world/bentobox/bentobox/blueprints/Blueprint.java Switches icon storage to string + adds ItemStack accessor.
src/main/java/world/bentobox/bentobox/api/flags/clicklisteners/IslandDefaultCycleClick.java Updates @since tag for 3.15.0.
src/main/java/world/bentobox/bentobox/api/events/island/IslandEvent.java Clarifies DELETED semantics and adds new PURGED reason.
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeUnownedCommand.java Changes unowned purge to flag islands deletable (with confirm).
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStopCommand.java Removes stop subcommand implementation.
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeStatusCommand.java Removes status subcommand implementation.
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeDeletedCommand.java New “deleted sweep” purge command.
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeCommand.java Refactors purge into service-driven scan + confirm flow.
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AdminPurgeAgeRegionsCommand.java New debug command to age region timestamps for testing.
src/main/java/world/bentobox/bentobox/api/commands/admin/purge/AbstractPurgeCommand.java New shared scan/confirm/delete scaffolding for purge commands.
src/main/java/world/bentobox/bentobox/api/commands/admin/AdminDeleteCommand.java Branches between hard-delete + chunk regen vs soft-delete based on gamemode.
src/main/java/world/bentobox/bentobox/api/addons/GameModeAddon.java Updates @since tag for 3.15.0.
src/main/java/world/bentobox/bentobox/Settings.java Introduces housekeeping settings; deprecates prior reset-deletion toggles.
src/main/java/world/bentobox/bentobox/BentoBox.java Instantiates PurgeRegionsService + housekeeping and flushes pending deletions on shutdown.
build.gradle.kts Bumps version to 3.15.0; adds CraftEngine dependencies/repo.
CLAUDE.md Updates build version documentation.
.gitignore Adds .paper-nms/ ignore.

Comment thread src/main/java/world/bentobox/bentobox/util/ItemParser.java
Comment thread src/main/java/world/bentobox/bentobox/blueprints/Blueprint.java
Comment on lines +256 to +261
Duration elapsed = Duration.ofMillis(System.currentTimeMillis() - startMillis);
plugin.log("Housekeeping age sweep: complete — " + totalWorlds + " world(s) processed, "
+ totalRegionsPurged + " region(s) purged in " + elapsed.toSeconds() + "s");
lastAgeRunMillis = System.currentTimeMillis();
saveState();
}
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

executeAgeCycle() updates lastAgeRunMillis (and persists it) unconditionally, even when delete(scan) reports errors via runDeleteIfNonEmpty() returning 0. This contradicts getLastAgeRunMillis()'s "last successful" contract and can cause the scheduler to skip retries after a failed purge. Track overall success and only advance/persist the timestamp when the cycle completes without errors (or explicitly document that partial failures still advance the schedule).

Copilot uses AI. Check for mistakes.
Comment thread src/main/java/world/bentobox/bentobox/managers/HousekeepingManager.java Outdated
@tastybento
Copy link
Copy Markdown
Member Author

@copilot apply changes based on the open comments in this thread. Make a branch for these changes that will apply to develop.

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 26, 2026

@tastybento I can’t apply these changes because the pull request’s branch is the repository’s default branch, which I’m not allowed to push to directly.

You can ask me to create a new pull request with the requested changes instead and I'll open a separate PR on a new branch.

@tastybento
Copy link
Copy Markdown
Member Author

@tastybento I can’t apply these changes because the pull request’s branch is the repository’s default branch, which I’m not allowed to push to directly.

You can ask me to create a new pull request with the requested changes instead and I'll open a separate PR on a new branch.

@copilot I did ask you to create a new pull request with the requested changes and open a separate PR on a new branch. But anyway, do that.

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 26, 2026

@tastybento I've opened a new pull request, #2959, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits April 26, 2026 20:05
Fix inPurge race condition and housekeeping timestamp-on-success semantics
@sonarqubecloud
Copy link
Copy Markdown

@tastybento tastybento merged commit c4e3c08 into master Apr 26, 2026
5 checks passed
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.

3 participants