# RegionIO — working notes A Minecraft Java Edition server core in Go, targeting **26.1.2 / protocol 775**. The goal is vanilla fidelity, not a lookalike: where vanilla behaviour is known, match it exactly. README.md describes what the server does. This file is about how to work on it. ## Commands ``` go build ./... && go vet ./... make test # go test ./... make test-race # the race-sensitive subset make verify # both go run ./cmd/regionio -seed 12345 # serves on 0.0.0.0:25565 go run ./cmd/regionio -seed 12345 -world "" # in-memory world, nothing read from or written to disk go run ./cmd/gendump # client-free generator diagnostics ``` ## Hard rules **No third-party dependencies.** `go.mod` has none. NBT, zlib framing, MD5 seeding, noise, the density-function interpreter — all in-tree. Keep it that way. **Bump `generatorVersion` whenever the generator's output changes.** It lives in `internal/world/store.go` and is stamped into every saved chunk; a mismatch makes the chunk regenerate. Without a bump, the chunks already on disk keep their old terrain and your change looks like it did nothing in exactly the area you are standing in — `chunkAt` prefers the store over the generator. `TestGeneratorVersionStampRejectsStaleChunks` covers the mechanism. For quick iteration, `-world ""` sidesteps persistence entirely. (The seed is guarded separately, by the world metadata file, and a seed mismatch is a hard error rather than a regeneration.) **Verify vanilla behaviour against the jar; don't recall it.** See below. ## Vanilla ground truth None of these are redistributable, so all are gitignored. Obtain `server.jar` from Mojang. | Source | What it gives you | |---|---| | `.refjava/` | Decompiled classes for the parts we port: `Climate`, `SurfaceRules`, `SurfaceSystem`, `Aquifer`, `DensityFunctions`, `NoiseRouterData`, `NoiseBasedChunkGenerator`, `TerrainProvider`, `PalettedContainer`, `LevelChunkSection`, `ClientboundLevelChunkPacketData` | | `versions/26.1.2/server-26.1.2.jar` | The real (deobfuscated) server. The outer `server.jar` is only a bundler | | `generated/reports/` | Datagen output: `blocks.json`, `registries.json`, `packets.json`, `biome_parameters/` | The inner jar also carries the **complete worldgen datapack**, which is the source for everything we still approximate: 259 `placed_feature`, 222 `configured_feature`, 66 `biome` (with per-stage `features` and `carvers`), 5 `configured_carver`, 35 `structure`, 1359 structure NBTs — 648 KB for the feature/biome/carver set. Three ways in, cheapest first: ``` unzip -p versions/26.1.2/server-26.1.2.jar data/minecraft/worldgen/biome/plains.json javap -p -c -classpath versions/26.1.2/server-26.1.2.jar net.minecraft.world.level.chunk.Strategy ``` `javap` settles questions the decompiled subset does not cover. It is how the biome palette threshold was pinned down: `Strategy$2` switches `{0..3}` and everything above falls through to the global palette, which `.refjava/` alone could not show. When a constant has to come from vanilla's *runtime* rather than its source or reports, dump it with a throwaway Java program run against the jar. `tools/VanillaLightDump.java` is the precedent — it walks the block-state registry and emits opacity, emission and voxel face shapes into `internal/world/light_properties.bin`. Substring-matching block names is how the light table was wrong before (`grass_block` matched "grass", `bedrock` matched "bed"); don't reintroduce that shape of guess anywhere. ## Layout ``` cmd/regionio/ entry point (flags, listener, graceful shutdown) cmd/gendump/ client-free generator diagnostics — biome spread, surface blocks, subsurface banding, deep-layer composition, bedrock band, cross-section cmd/genblocks/ generates internal/worldgen/generated_blocks.go from the block report cmd/genlight/ legacy light-table generator, superseded by tools/VanillaLightDump.java tools/ Java dumpers run against the jar, plus their Go-side fixtures internal/protocol/ VarInt, framing, compression, packet IDs internal/nbt/ NBT codec (modified UTF-8) internal/registry/ 28 embedded synced registries + tags, verbatim from vanilla internal/world/ chunk model, wire encoder, cache + tickets, Anvil store, lighting, the chunk generator itself (vanilla.go), decoration internal/worldgen/ the library: noise, density-function interpreter, surface rules, climate/biome finder, embedded datapack under data/ internal/network/ per-connection state machine internal/server/ shared core: config, sessions, status, profiles, entity loops ``` Note the split: `internal/worldgen` is a *library* over the datapack; the chunk generator that drives it is `internal/world/vanilla.go`. ## Fidelity status Bit-exact and parity-tested — treat as settled, change only with a vanilla reference in hand: `random.go` (Xoroshiro128++, `upgradeSeedTo128bit`, MD5 seeding), `improved_noise.go`, `perlin_noise.go`, `normal_noise.go`, `blended.go`, `spline.go`, `density.go`, the 4×8×4 cell grid with trilinear interpolation, the climate/biome finder, and the chunk wire encoder (`TestGoldenAgainstVanilla` compares bytes against a real vanilla chunk). Known gaps, roughly in order of how visible they are: - **Aquifers are absent.** `vanilla.go` floods every air block below sea level with water, so every cave below y=63 is solid water and there are no lava lakes. Vanilla resolves fluid per position through `Aquifer.computeSubstance`; the datapack ships all four router keys (`barrier`, `fluid_level_floodedness`, `fluid_level_spread`, `lava`) and they are not parsed. - **`above_preliminary_surface` is wrong**, so there is no subsurface banding: every land column is one grass block directly on stone, no dirt, no sandstone under sand. Vanilla is `blockY >= preliminarySurfaceLevel + surfaceDepth - 8`; we compare against the actual top block, which gates the whole biome surface subtree to a single block per column. `gendump` prints this. - **Several surface-rule conditions are stubs**: `hole` is hardcoded false (vanilla is `surfaceDepth <= 0`), `steep` is never assigned, `water` uses sea level instead of the column's real water height, and 6 of the 7 `noise_threshold` noises are unsupported so calcite, ice, packed ice, powder snow, swamp water and gravel patches never appear. `bandlands` is a 4-colour cycle rather than the 192-band array. - **No carvers and no ore veins.** Caves come only from the density router; `configured_carver` and the `vein_*` router keys are unimplemented. - **Decoration is hand-written heuristics**, not the vanilla feature system: oak trees only and without a biome check (so oaks grow in deserts), ores that cannot generate below y≈0 because they only replace stone and never deepslate, and no grass, flowers, lakes or springs. ## Testing worldgen `make verify` is the gate, but most generator defects are invisible to it — they show up as terrain that looks wrong. `cmd/gendump` exists for that: biome distribution, top surface blocks, subsurface banding, deep-layer composition, the bedrock band, and an ASCII cross-section, with no client involved. Add an assertion to it whenever you fix a class of defect; the bedrock-band check is the model — it prints per-layer counts and fails loudly on any air or water in the floor. `internal/world/vanilla_parity_test.go` compares surface heights against a capture from the official server and skips when the capture is absent. Note it reads a hardcoded `/tmp` path, so on Windows it never runs. A capture is produced by running the vanilla server headless at a known seed and reading its region files back with our own `regionfile.go` + `nbt`.