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.
34 lines
754 B
Go
34 lines
754 B
Go
package world
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/zlib"
|
|
"io"
|
|
)
|
|
|
|
// compress.go wraps compress/zlib for region-file chunk payloads. zlib is the
|
|
// default compression for Anvil .mca chunk records (compression type 2).
|
|
|
|
// zlibDeflate compresses src into a new byte slice.
|
|
func zlibDeflate(src []byte) []byte {
|
|
var buf bytes.Buffer
|
|
w := zlib.NewWriter(&buf)
|
|
_, _ = w.Write(src)
|
|
_ = w.Close()
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// zlibInflate decompresses src (a zlib stream). It returns an error if src is
|
|
// not a valid zlib payload.
|
|
func zlibInflate(src []byte) ([]byte, error) {
|
|
r, err := zlib.NewReader(bytes.NewReader(src))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer r.Close()
|
|
out, err := io.ReadAll(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|