# 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](https://github.com/GriefPrevention/GriefPrevention/pull/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:**
```yaml
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:
```java
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's `RestoreNatureProcessingTask` treats its `miny` parameter as an array
index rather than a world Y, so the snapshot array itself starts at that floor and `0` is passed
in — the manual tool, which captures the full column, passes a world minimum that clamps to the
same `0`.
- The boundary corners describe the chunk proper, `+1` and `+16` off the snapshot origin, not the
18-wide array. The outer ring is reference data the execution task never writes to, and
`cleanupEntities` uses those corners to decide which entities are in scope.
## Notes
- Nothing happens until `Expiration.ChestClaimDays` or `Expiration.AllClaims.DaysInactive` is
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 `RestoreNatureExecutionTask` runs
`getClaimAt` per 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
```
Optional dependencies
QuickShop-Hikari
Any compatible version
PlaceholderAPI
Any compatible version
VaultUnlocked
Any compatible version
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }