RegionIO/internal/nbt/encode.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

95 lines
2.6 KiB
Go

package nbt
import (
"encoding/binary"
"fmt"
"math"
)
// Marshal encodes root in the network format: the root tag's type byte followed
// by its payload, with no root name (Minecraft 1.20.2+).
func Marshal(root Tag) []byte {
dst := []byte{root.ID()}
return appendPayload(dst, root)
}
// MarshalNamed encodes root in the classic named format: type byte, root name,
// then payload. Used for files and legacy framing.
func MarshalNamed(name string, root Tag) []byte {
dst := []byte{root.ID()}
dst = appendString(dst, name)
return appendPayload(dst, root)
}
// appendString writes a modified-UTF-8 string with an unsigned-short length.
func appendString(dst []byte, s string) []byte {
start := len(dst)
dst = append(dst, 0, 0) // length placeholder
dst = encodeModifiedUTF8(dst, s)
binary.BigEndian.PutUint16(dst[start:], uint16(len(dst)-start-2))
return dst
}
// appendPayload writes a tag's payload (no type byte, no name).
func appendPayload(dst []byte, t Tag) []byte {
switch v := t.(type) {
case Byte:
return append(dst, byte(v))
case Short:
return binary.BigEndian.AppendUint16(dst, uint16(v))
case Int:
return binary.BigEndian.AppendUint32(dst, uint32(v))
case Long:
return binary.BigEndian.AppendUint64(dst, uint64(v))
case Float:
return binary.BigEndian.AppendUint32(dst, math.Float32bits(float32(v)))
case Double:
return binary.BigEndian.AppendUint64(dst, math.Float64bits(float64(v)))
case ByteArray:
dst = binary.BigEndian.AppendUint32(dst, uint32(len(v)))
return append(dst, v...)
case String:
return appendString(dst, string(v))
case IntArray:
dst = binary.BigEndian.AppendUint32(dst, uint32(len(v)))
for _, n := range v {
dst = binary.BigEndian.AppendUint32(dst, uint32(n))
}
return dst
case LongArray:
dst = binary.BigEndian.AppendUint32(dst, uint32(len(v)))
for _, n := range v {
dst = binary.BigEndian.AppendUint64(dst, uint64(n))
}
return dst
case List:
return appendList(dst, v)
case *Compound:
return appendCompound(dst, v)
default:
panic(fmt.Sprintf("nbt: cannot encode %T", t))
}
}
func appendList(dst []byte, l List) []byte {
elemID := l.ElemID
if len(l.Elems) == 0 {
elemID = TagEnd // Java writes TagEnd for empty lists.
}
dst = append(dst, elemID)
dst = binary.BigEndian.AppendUint32(dst, uint32(len(l.Elems)))
for _, e := range l.Elems {
dst = appendPayload(dst, e)
}
return dst
}
func appendCompound(dst []byte, c *Compound) []byte {
for _, k := range c.keys {
t := c.m[k]
dst = append(dst, t.ID())
dst = appendString(dst, k)
dst = appendPayload(dst, t)
}
return append(dst, TagEnd)
}