
BetterAutoSave
BetterAutoSave - серверный мод для Forge 1.20.1 и NeoForge 1.21.1, который выносит автосохранение чанков, сущностей и SavedData в фоновые потоки. Убирает лаг-спайки от автосейва и телепортов.
BetterAutoSave v0.17.0 (Forge 1.20.1)
Список изменений
v0.17.0 — Main-thread save capture cost roughly halved
0.17.0 is a performance release. The main-thread cost of capturing a chunk for saving drops by roughly half, by removing a full-chunk scan that ran on every snapshot but whose result was never used. This build also closes a data-safety gap where a third-party biome container could be referenced directly into an async snapshot
Removing the dead block recount during capture (issue #24)
Main-thread capture previously wrapped each section's two paletted containers in a LevelChunkSection. That constructor unconditionally calls recalcBlockCounts(), which walks all 4096 cells of every section whose palette holds more than one entry, feeding each into a hash map — about 98,000 hash writes for a 24-section chunk
The three counts it produces (non-empty blocks, randomly ticking blocks, randomly ticking fluids) only serve live-chunk tick scheduling and network sync. Vanilla reads none of them when serializing a section — a copy with completely wrong counts serializes byte-for-byte identically to a correct one. In other words, the result of that scan was never used anywhere on the save path
This build carries only the two containers the worker actually needs for encoding. The on-disk NBT is unchanged, and no mixin or vanilla behavior is involved
Before/after on a real production server (Forge 1.20.1, 137 mods, AMD Ryzen 9 9950X3D2, ZGC, 60-second sample, eventCompatMode = PARTIAL):
| Main-thread frame | Before | After |
|---|---|---|
Capture entry captureWithGeneration | 6692ms (19.1% of busy time) | 2948ms (8.2%) |
Section capture copySections within it | 4096ms | 1024ms |
Block recount recalcBlockCounts | 3152ms | 0 (call no longer exists) |
| BetterAutoSave total main-thread footprint | 25.8% of busy time | 15.2% |
The number of chunks saved in each sample was cross-checked against frames unaffected by this change; the "after" run actually processed about 15% more, so the reductions above are not inflated by a lighter workload
A side effect: with the per-section temporary hash map gone, ZGC relocation read barriers (forwarding_find) fell 32%. The paletted container deep copy itself became 20% faster as a result, even though that code was not touched
To be clear, this does not raise TPS on an already healthy server — both samples ran at TPS 20. What it buys is main-thread headroom: the same save throughput now occupies less main-thread time, making it less likely to crowd the tick budget under heavy load or with large worlds
No more duplicate capture in FULL compatibility mode
Under eventCompatMode = FULL, snapshot capture (section copies, light layer clones, heightmap clones, block entity NBT, structure data copies) previously ran unconditionally before the mode branch — even though that mode actually uses the complete NBT produced by vanilla ChunkSerializer.write, which gathers the same data itself. That gather-then-discard work is now skipped
Users who switched to FULL to work around issue #8 no longer pay double the main-thread capture cost
Data safety
If a live section's biome container is not vanilla's PalettedContainer implementation — which vanilla never produces, and only occurs when a third-party mod supplies a read-only container — it was previously referenced directly into the async snapshot. The read-only interface has no copy method, so it could not be detached, meaning the main thread could still mutate the same object while a worker encoded it. This build encodes it to NBT on the main thread instead, fully separating it from the live container
Upgrading
- Drop-in jar replacement, no config changes, no save format changes
- The on-disk NBT is byte-for-byte identical to before, so rolling back to 0.16.3 is safe at any time
