# GriefPrevention3D v18.2.12
**Wiki:** https://github.com/castledking/GriefPrevention3D/wiki
## Fixes
### Trust commands freeze the region/main thread on claims with many subdivisions
Reported in [issue #9](https://github.com/castledking/GriefPrevention3D/issues/9) on
Folia 26.1.2: running `/trust <player>` on a parent claim with a large number of
subdivisions stopped the affected region from ticking for 15+ seconds, with the Folia
watchdog dumping a stack parked inside SnakeYAML's emitter at 5s, 10s and 15s:
```
org.yaml.snakeyaml.emitter.Emitter.writeLineBreak(Emitter.java:1344)
...
org.bukkit.configuration.file.YamlConfiguration.saveToString(YamlConfiguration.java:92)
FlatFileDataStore.getYamlForClaim(FlatFileDataStore.java:731)
FlatFileDataStore.writeClaimToStorage(FlatFileDataStore.java:810)
FlatFileDataStore.writeClaimToStorage(FlatFileDataStore.java:804)
DataStore.saveClaim(DataStore.java:831)
GriefPrevention.propagateTrustToChildren(GriefPrevention.java:3699)
```
Two separate problems combined here, and both are fixed.
#### 1. Quadratic saves — one full claim-tree serialization per subdivision
Flat file storage keeps subdivisions **inside their root claim's `.yml` file**, not in
files of their own. `writeClaimToStorage` reflects that: given a subdivision, it walks up
to the root claim and serializes the *entire* tree — parent plus every child and
grandchild — into one document.
`propagateTrustToChildren` saved each child as it modified it:
```java
childClaim.setPermission(identifier, permissionLevel);
this.dataStore.saveClaim(childClaim); // rewrites the whole tree, once per child
```
So a claim with N subdivisions performed N full-tree YAML serializations and N file
writes, each one proportional to N — quadratic work, all of it on the calling thread,
which on Folia is the region thread that owns the player. That is the watchdog stall.
**Fix:** `propagateTrustToChildren` now only mutates children in memory and collects them
into a list. The caller hands that list to the data store in a single batch, at all three
call sites (`/trust`, `/accesstrust`, `/containertrust`, `/permissiontrust` via
`handleTrustCommand`, plus both `/untrust` paths).
Batching is done through a new `DataStore.saveClaims(Collection<Claim>)`:
- The **default** implementation saves each claim individually. This is required for the
database backend, which stores one row per claim — saving only the parent there would
silently drop the children's permission changes.
- `FlatFileDataStore` **overrides** it to collapse the batch down to the distinct root
claims involved and write each root exactly once. Claim IDs are still assigned per
claim, so nothing that depended on `saveClaim`'s ID assignment changes.
N subdivisions now cost **one** write instead of N.
#### 2. YAML serialization and disk I/O ran on the tick thread
Even a single write of a large claim tree does real work — SnakeYAML emitting, then a
blocking file write — and it happened inline on whichever thread ran the command.
**Fix:** `writeClaimToStorage` now builds the `YamlConfiguration` on the calling thread,
where the claim data cannot change underneath it, then hands that finished document to a
dedicated single daemon writer thread which performs `saveToString()` and the file write.
Claim data is never read off-thread, so this introduces no race with claim mutation.
The writer pipeline:
- **Coalesces** writes per claim — if a claim is saved several times before the writer
catches up, only the newest data is written and the superseded ones are dropped.
- **Orders deletes behind writes** on the same thread, and drops any pending write for a
claim being deleted, so a queued write can never resurrect a deleted claim file.
- **Drains on shutdown** — `FlatFileDataStore.close()` (called from `onDisable`) waits for
the writer, then writes anything still pending on the calling thread. If the writer is
already shut down, saves and deletes fall back to running synchronously, so no claim
data is lost during a normal stop or restart.
- Also flushes before `migrateData`, which renames the claim data folder out from under
the writer.
This benefits every claim save, not just trust changes — resizes, subdivisions, transfers
and abandons no longer serialize YAML or touch disk on a tick thread.
## Notes
Claim saves are now durable a few milliseconds after the command returns, rather than at
the moment it returns. A normal `stop`/restart, plugin disable, or `/reload` flushes
everything; only an abrupt `kill -9` could lose that sub-second window.
`DataStore.mergeClaims` still saves each moved subdivision individually when merging two
claims. That path is much cheaper now that serialization and I/O are off-thread, but it is
the same pattern and remains a candidate for `saveClaims` in a later release.
## Files Changed
```
M gradle.properties
M pom.xml
M src/main/java/me/ryanhamshire/GriefPrevention/DataStore.java
M src/main/java/me/ryanhamshire/GriefPrevention/FlatFileDataStore.java
M src/main/java/me/ryanhamshire/GriefPrevention/GriefPrevention.java
```
Optional dependencies
VaultUnlocked
Any compatible version
QuickShop-Hikari
Any compatible version
PlaceholderAPI
Any compatible version
DiscordSRV
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" }