RegionIO/internal/world/encode_test.go
Master290 a7bb9496ae Initial commit: RegionIO Minecraft server core (26.1.2/protocol 775)
Vanilla-faithful overworld generator (final_density + multi-noise biomes),
full connection lifecycle (status/login/configuration/play), chunk streaming,
creative block editing, and the protocol/nbt/registry infrastructure.
2026-06-24 00:32:51 +03:00

154 lines
4.1 KiB
Go

package world
import (
"testing"
"regionio/internal/protocol"
)
// parsePalettedContainer consumes one paletted container of entryCount entries.
// The long-array length is derived from bits-per-entry, not length-prefixed.
func parsePalettedContainer(t *testing.T, r *protocol.Reader, maxBits, entryCount int) {
t.Helper()
bpe, err := r.ReadByte()
if err != nil {
t.Fatalf("bpe: %v", err)
}
if bpe == 0 {
if _, err := r.VarInt(); err != nil { // single value
t.Fatalf("single value: %v", err)
}
return
}
if int(bpe) <= maxBits { // indirect: palette precedes data
n, err := r.VarInt()
if err != nil || n < 0 {
t.Fatalf("palette len: %v", err)
}
for i := int32(0); i < n; i++ {
if _, err := r.VarInt(); err != nil {
t.Fatalf("palette entry: %v", err)
}
}
}
perLong := 64 / int(bpe)
longs := (entryCount + perLong - 1) / perLong
for i := 0; i < longs; i++ {
if _, err := r.Int64(); err != nil {
t.Fatalf("data long: %v", err)
}
}
}
func skipBitSet(t *testing.T, r *protocol.Reader) {
t.Helper()
n, err := r.VarInt()
if err != nil || n < 0 {
t.Fatalf("bitset len: %v", err)
}
for i := int32(0); i < n; i++ {
if _, err := r.Int64(); err != nil {
t.Fatalf("bitset long: %v", err)
}
}
}
// TestFlatChunkEncodesCleanly fully parses an encoded flat chunk and asserts
// the byte stream is consumed exactly, with the expected high-level structure.
func TestFlatChunkEncodesCleanly(t *testing.T) {
body := GenerateFlat(2, -3).Encode()
// X and Z are plain big-endian ints.
if got := readInt32(t, body[0:4]); got != 2 {
t.Fatalf("chunkX = %d, want 2", got)
}
if got := readInt32(t, body[4:8]); got != -3 {
t.Fatalf("chunkZ = %d, want -3", got)
}
r := protocol.NewReader(body[8:])
// Heightmaps: 3 entries, each 37 longs of packed 9-bit heights.
hmCount, err := r.VarInt()
if err != nil || hmCount != 3 {
t.Fatalf("heightmap count = %d (err %v), want 3", hmCount, err)
}
for i := int32(0); i < hmCount; i++ {
if _, err := r.VarInt(); err != nil { // type
t.Fatalf("hm type: %v", err)
}
longs, err := r.VarInt()
if err != nil || longs != 37 {
t.Fatalf("hm longs = %d (err %v), want 37", longs, err)
}
for j := int32(0); j < longs; j++ {
if _, err := r.Int64(); err != nil {
t.Fatalf("hm long: %v", err)
}
}
}
// Section data block.
dataLen, err := r.VarInt()
if err != nil || dataLen <= 0 {
t.Fatalf("data len = %d (err %v)", dataLen, err)
}
nonAirSections := 0
for s := 0; s < SectionCount; s++ {
count, err := r.Uint16()
if err != nil {
t.Fatalf("section %d count: %v", s, err)
}
if _, err := r.Uint16(); err != nil { // reserved 2-byte field
t.Fatalf("section %d reserved: %v", s, err)
}
if count > 0 {
nonAirSections++
}
parsePalettedContainer(t, r, 8, 4096) // blocks
parsePalettedContainer(t, r, 3, 64) // biomes
}
if nonAirSections != 1 {
t.Fatalf("non-air sections = %d, want 1 (flat layers live in section 0)", nonAirSections)
}
// Block entities.
if be, err := r.VarInt(); err != nil || be != 0 {
t.Fatalf("block entities = %d (err %v), want 0", be, err)
}
// Light: four bitsets, then sky arrays, then block arrays.
skipBitSet(t, r) // sky mask
skipBitSet(t, r) // block mask
skipBitSet(t, r) // empty sky mask
skipBitSet(t, r) // empty block mask
skyArrays, err := r.VarInt()
if err != nil || skyArrays != lightSections {
t.Fatalf("sky arrays = %d (err %v), want %d", skyArrays, err, lightSections)
}
for i := int32(0); i < skyArrays; i++ {
n, err := r.VarInt()
if err != nil || n != 2048 {
t.Fatalf("sky array len = %d (err %v), want 2048", n, err)
}
for j := int32(0); j < n; j++ {
if _, err := r.ReadByte(); err != nil {
t.Fatalf("sky byte: %v", err)
}
}
}
if blockArrays, err := r.VarInt(); err != nil || blockArrays != 0 {
t.Fatalf("block arrays = %d (err %v), want 0", blockArrays, err)
}
if rem := r.Remaining(); rem != 0 {
t.Fatalf("trailing bytes after parse: %d", rem)
}
}
func readInt32(t *testing.T, b []byte) int32 {
t.Helper()
if len(b) < 4 {
t.Fatal("short int32")
}
return int32(uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]))
}