The world now survives restarts: chunks load from disk (read-through
cache) and player edits persist via async autosave + a final SaveAll on
shutdown. RegionIO finally does region I/O.
- world/regionfile.go: Anvil .mca container — 8192-byte header
(offset + timestamp tables), 4096-byte sectors, zlib chunk records.
- world/compress.go: zlib deflate/inflate for chunk payloads.
- world/store.go: chunk <-> Level-nested NBT (per-section
block_states/biomes palettes, WORLD_SURFACE heightmap, DataVersion
4790, yPos -4) via the existing nbt package; Store opens one
RegionFile per region with proper floor-division coords.
- world/state_names.go: id->name bridge from the embedded blocks.json
report so network int-IDs round-trip through the disk named palette.
- world/encode.go: GetBiome read accessor for serialization.
- world/cache.go: read-through (disk then generation), dirty tracking,
StartAutosave (returns a done channel so the saver exits before
Close), SaveAll, NewCacheWithStore.
- server.go + main.go: Config.WorldDir (default "world"), -world flag,
autosave loop every 30s, SaveAll + store Close on signal.
- Tests: region round-trip/absent/overwrite, chunk NBT round-trip,
end-to-end save-reload, negative chunk coords, autosave persistence.
Replaces the biome-blind fillVanillaColumn heuristics with a full
interpreter for the overworld surface_rule tree (already embedded in
overworld.json): block/sequence/condition/bandlands rules plus all 11
condition tests (biome, steep, hole, water, temperature, y_above,
stone_depth, noise_threshold, not, vertical_gradient,
above_preliminary_surface).
- worldgen/blockids.go: name(+Properties)→network-ID table for surface
blocks (grass/sand/terracotta/mycelium/podzol/coarse_dirt/sandstone/
calcite/snow/ice/...), with snowy property variants.
- worldgen/surface.go: rule-tree parser + interpreter + SurfaceContext;
LoadOverworldSurfaceRule caches the seed-independent tree.
- loader.go: OverworldDensity.SurfaceRule() exposes the parsed tree.
- biome_lookup.go: BiomeNameAt returns the biome name for biome tests.
- vanilla.go: samples the 2D climate + biome before column fill, threads
the rule tree and biome name into fillVanillaColumn, and applies it
top-down with stone as the default for non-matching (deeper) blocks.
The above_preliminary_surface gate uses an inclusive bound so the top
solid block reaches the biome dispatch.
- Performance: one per-column RNG and a reused SurfaceContext keep the
overhead to ~+13ms/chunk (71ms vs 58ms baseline), within the gate.
- Chunk stores per-section biome arrays (64 cells/section); flat generators
keep the uniform single-valued fallback.
- New writeBiomePalette uses min 1 bpe and direct at registry width (65 biomes).
- Climate sampler splits 2D axes (sampled once per column) from 3D depth
(per cell), keeping per-cell cost to a single density-function compute.
- Full biome parameter table (surface + underground twins + lush/dripstone/
deep_dark caves) with depth as a true range, not a binary layer.
- fillBiomes3D fills the 1536 cells/chunk in parallel; <0.3ms overhead vs
baseline chunk gen (benchmark-verified).
- Tests: cave-biome resolution, per-cell variation, flat-world regression,
registry-range validity, plus chunk-gen and per-cell benchmarks.