synvael/docs/adr/0009-baseline-relative-sparse-chunk-persistence.md

3 KiB

0009. Baseline-relative sparse chunk persistence

  • Status: Accepted
  • Date: 2026-07-10

Context

Worldgen is seed-deterministic (ADR-0003): any unmodified chunk is reproducible bit-for-bit from (seed, chunk_coord, worldgen_version). The world is procedurally generated, unbounded in Y, and viewed at large horizontal distance in both single-player and multiplayer, so the set of chunks a session visits is effectively unbounded.

Persisting the full voxel contents of every visited chunk, the naive model, makes save size scale with the volume explored rather than the volume changed. In a half-scale voxel grid (ADR-0002), where a unit volume holds roughly eight times the voxels of a 1 m grid, that cost is compounded. The overwhelming majority of visited chunks are never modified, so storing them at all duplicates data the generator can reproduce on demand.

The decision that is hard to reverse is the on-disk representation of a chunk: the SYNC record format and the ChunkData type are both shaped by it, and changing the representation later requires a save-format migration.

Decision

A chunk is persisted only when its contents diverge from its deterministic baseline.

  • Representation. Both on disk (the SYNC record) and in memory (ChunkData), a modified chunk is stored as a sparse local_index → BlockId edit map layered over the regenerated baseline, together with the worldgen_version the baseline is pinned to. An unmodified chunk stores no voxel data and is omitted from its region file entirely.
  • Load. A load resolves the baseline by regenerating it from the seed, then applies the stored diff when a record exists (a hit). A miss means the chunk was never modified, so the regenerated baseline is the chunk.
  • Version pinning. Each persisted chunk records the worldgen_version its baseline was generated under, so a later generator update does not silently shift the baseline beneath an already-modified chunk. A region pins a base_worldgen_version and stores only per-chunk exceptions.

Consequences

  • Save size scales with the volume modified, not the volume explored. A session that walks across untouched terrain writes nothing.
  • Revisiting an unmodified chunk re-runs worldgen instead of reading it back. This CPU cost is mitigated by an LRU cache of regenerated baselines, which is a pure performance layer and does not affect authority or determinism.
  • Worldgen determinism is promoted from a worldgen-local property to a hard invariant of the persistence layer: if the generator ceased to be reproducible, every unmodified chunk and every stored diff's baseline would be corrupted. Determinism regressions are therefore guarded aggressively by tests.
  • A per-chunk worldgen_version stamp is mandatory metadata, and the load and write-back paths must both honour it once more than one worldgen version exists. Until then a single version is assumed, tracked as follow-on work.