A fork of GriefPrevention that adds 3D subdivisions
GriefPrevention3D v18.2.20
Wiki: https://github.com/castledking/GriefPrevention3D/wiki
Features
Automatic nature restoration is back
Upstream deleted the entire restore-nature feature in
GriefPrevention#2332 (5d0ebdec,
July 2024). That commit landed here through a routine upstream merge, so servers that had been
running automatic cleanup for years lost it on update with nothing in the changelog pointing at it.
The manual half came back in cab3d9cf — /restorenature, /restorenatureaggressive and
/restorenaturefill all work, along with RestoreNatureProcessingTask and
RestoreNatureExecutionTask. The automatic half did not. restoreClaim and restoreChunk existed
nowhere in the source, the AutomaticNatureRestoration config key was never read, and
CleanupUnusedClaimTask deleted expired claims and left the land exactly as the owner abandoned it.
There was no setting an admin could flip, because there was no setting.
This release reconnects the automatic path to the processing tasks that were already shipping.
Config:
GriefPrevention:
Claims:
Expiration:
AutomaticNatureRestoration:
SurvivalWorlds: false
Defaults to false, matching upstream, so no existing server starts editing terrain after updating.
It is written back on the next config save.
Where restoration now fires:
| Trigger | Restores when |
|---|---|
Chest claim expiry (Expiration.ChestClaimDays) |
creative world, or SurvivalWorlds: true |
Inactivity expiry (Expiration.AllClaims.DaysInactive) |
creative world, or SurvivalWorlds: true |
Staff /deleteclaim |
creative world, or SurvivalWorlds: true |
Player /abandonclaim |
creative world only |
That table is upstream's behaviour reproduced exactly, including the asymmetry on the last row.
SurvivalWorlds was never consulted on the abandon path — upstream gated it on creativeRulesApply
alone, and the field comment read "whether survival claims will be automatically restored to nature
when auto-deleted." A survival player abandoning a claim does not get their build flattened.
Restoration is silent when it fires automatically. RestoreNatureExecutionTask.showVisualization
returns immediately on a null player ID, so nothing is drawn and no message is sent; the only trace
is the existing expiry line in the admin log.
The size guards are upstream's: admin claims are skipped entirely, and so is anything over 10000
blocks of area, 250 wide or 250 deep. Claim#getArea resolves to ClaimBounds#area() — the 2D
footprint, or polygon.cellCount() for a shaped claim — and getWidth/getHeight are X and Z
lengths, so these thresholds mean here what they meant upstream despite this fork's 3D claims.
Claim#removeSurfaceFluids and Messages.UnclaimCleanupWarning restored
Both were collateral in the same upstream removal and are needed by the paths above.
removeSurfaceFluids clears lava and water above sea level before a claim is deleted, only in
creative-rules worlds, never in the Nether, and never for admin or oversized claims. It now bounds
its scan with GriefPrevention.getWorldMaxY instead of the old World#getMaxHeight.
UnclaimCleanupWarning is back in the enum and translated across all fifteen message files — cs_CZ,
de_DE, en, en_PT, es, fr_FR, ja_JP, ms_MY, pl_PL, pt_BR, ru_RU, tr_TR, uk_UA, zh_CN and the base
messages.yml. Each follows the quoting convention and level of formality already used in that
file, and reuses that locale's established word for a claim rather than introducing a new one.
Servers with a customised messages.yml on disk are unaffected either way: a key absent from the
file falls back to message.defaultValue in MessageLocalization.populateMessagesArray, so the
message appears in English rather than not at all.
Technical changes
Two deliberate departures from the code that was deleted, both because this fork supports region threading and upstream did not.
Chunk coordinates are derived arithmetically. Upstream's restoreClaim iterated
Claim#getChunks(), which calls world.getChunkAt(x, z) and loads chunks on the calling thread —
rejected under Folia and Canvas when that thread does not own the region. restoreClaim now walks
lesser.getBlockX() >> 4 to greater.getBlockX() >> 4 directly and never touches a Chunk object.
restoreChunk(Chunk, ...) is kept with upstream's signature for API compatibility and delegates to
the coordinate-based path.
Block capture happens on the owning region thread. Upstream scheduled the whole operation with
runTaskLaterAsynchronously and read blocks from the async task. The work is now split:
SchedulerUtil.runAtLocationLater(this, snapshotOrigin, () -> {
// snapshot 18x18 columns on the region thread
SchedulerUtil.runAsyncNow(this, task); // analysis off-thread
}, delayInTicks);
RestoreNatureProcessingTask already closes the loop itself, dispatching
RestoreNatureExecutionTask back through SchedulerUtil.runAtLocation before it writes anything.
This mirrors how the fork's existing /restorenature handler in PlayerEventHandler already worked,
rather than introducing a second convention.
Two smaller details:
- The vertical window starts at
seaLevel - 15, upstream's floor, so caves and mines under a claim are left alone. The fork'sRestoreNatureProcessingTasktreats itsminyparameter as an array index rather than a world Y, so the snapshot array itself starts at that floor and0is passed in — the manual tool, which captures the full column, passes a world minimum that clamps to the same0. - The boundary corners describe the chunk proper,
+1and+16off the snapshot origin, not the 18-wide array. The outer ring is reference data the execution task never writes to, andcleanupEntitiesuses those corners to decide which entities are in scope.
Notes
- Nothing happens until
Expiration.ChestClaimDaysorExpiration.AllClaims.DaysInactiveis non-zero. Restoration is driven by expiry; with expiry disabled the flag has no effect. - Non-aggressive mode is what the automatic path uses, so
RestoreNatureExecutionTaskrunsgetClaimAtper block and skips anything still inside a claim. Neighbouring claims that share a chunk with a deleted one are safe. - Restoration operates per chunk, so it reaches the whole chunk rather than the claim footprint, and for a 3D claim it spans the full column above the floor rather than the claim's Y bounds. The per-block claim check above is what keeps that from mattering.
- No data migration. No permission changes. No command changes.
Tests
No new tests. The full suite passes — 226 tests, 0 failures, 0 errors.
The restore path has no unit coverage and does not gain any here. It needs a live world to snapshot, a region scheduler to dispatch through, and real block writes to assert against; the mock harness provides none of the three.
What was verified statically: Claim#getArea, getWidth and getHeight were traced through
ClaimBounds to confirm the upstream size thresholds still describe a 2D footprint in this fork;
miny was traced through every use in RestoreNatureProcessingTask to confirm it is an array index
and unused in RestoreNatureExecutionTask; and both edited message files were parsed to confirm the
apostrophes in the restored string do not break YAML.
Files Changed
M gradle.properties
M pom.xml
M src/main/java/com/griefprevention/commands/UnifiedAdminClaimCommand.java
M src/main/java/me/ryanhamshire/GriefPrevention/Claim.java
M src/main/java/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java
M src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java
M src/main/java/me/ryanhamshire/GriefPrevention/Messages.java
M src/main/resources/messages.yml
M src/main/resources/messages_cs_CZ.yml
M src/main/resources/messages_de_DE.yml
M src/main/resources/messages_en.yml
M src/main/resources/messages_en_PT.yml
M src/main/resources/messages_es.yml
M src/main/resources/messages_fr_FR.yml
M src/main/resources/messages_ja_JP.yml
M src/main/resources/messages_ms_MY.yml
M src/main/resources/messages_pl_PL.yml
M src/main/resources/messages_pt_BR.yml
M src/main/resources/messages_ru_RU.yml
M src/main/resources/messages_tr_TR.yml
M src/main/resources/messages_uk_UA.yml
M src/main/resources/messages_zh_CN.yml