232 lines
5.3 KiB
Go
232 lines
5.3 KiB
Go
// Package dynbt is a library that provides dynamic NBT operation APIs.
|
|
//
|
|
// Dynamically represented NBT value is useful in many cases, for example,
|
|
// - You want to store custom structural values at runtime.
|
|
// - You want to query or modify the data later. (Otherwise use the [nbt.RawMessage])
|
|
// - You don't know what type the data is at compile time. (Otherwise use the [nbt.RawMessage] too)
|
|
//
|
|
// The [*Value] provides a group of APIs on top of the [nbt] package.
|
|
// [*Value] implements [nbt.Marshaler] and [nbt.Unmarshaler] interfaces.
|
|
// It can be used as a field of struct, or element of slice, map, etc.
|
|
// The pointer type should always be used, unless used as fields for structures
|
|
//
|
|
// Notice that querying Tags in Compound use a linear search, so it's not recommended to use it in a large Compound.
|
|
// The better choice is map[string]*Value for dynamic accessing a large Compound.
|
|
//
|
|
// This package tries its best to not copy data if possible.
|
|
// It returns the underlying data in some cases. Don't modify them!
|
|
package dynbt
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"math"
|
|
|
|
"git.konjactw.dev/falloutBot/go-mc/nbt"
|
|
)
|
|
|
|
type Value struct {
|
|
comp Compound
|
|
list []*Value
|
|
data []byte
|
|
tag byte // nbt.Tag*
|
|
}
|
|
|
|
func NewBoolean(v bool) *Value {
|
|
data := byte(0)
|
|
if v {
|
|
data = 1
|
|
}
|
|
return &Value{tag: nbt.TagByte, data: []byte{data}}
|
|
}
|
|
|
|
func NewByte(v int8) *Value {
|
|
return &Value{tag: nbt.TagByte, data: []byte{byte(v)}}
|
|
}
|
|
|
|
func NewShort(v int16) *Value {
|
|
data := make([]byte, 2)
|
|
binary.BigEndian.PutUint16(data, uint16(v))
|
|
return &Value{tag: nbt.TagShort, data: data}
|
|
}
|
|
|
|
func NewInt(v int32) *Value {
|
|
data := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(data, uint32(v))
|
|
return &Value{tag: nbt.TagInt, data: data}
|
|
}
|
|
|
|
func NewLong(v int64) *Value {
|
|
data := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(data, uint64(v))
|
|
return &Value{tag: nbt.TagLong, data: data}
|
|
}
|
|
|
|
func NewFloat(f float32) *Value {
|
|
data := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(data, math.Float32bits(f))
|
|
return &Value{tag: nbt.TagFloat, data: data}
|
|
}
|
|
|
|
func NewDouble(d float64) *Value {
|
|
data := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(data, math.Float64bits(d))
|
|
return &Value{tag: nbt.TagDouble, data: data}
|
|
}
|
|
|
|
func NewByteArray(v []byte) *Value {
|
|
data := make([]byte, 4, 4+len(v))
|
|
binary.BigEndian.PutUint32(data, uint32(len(v)))
|
|
return &Value{tag: nbt.TagByteArray, data: append(data, v...)}
|
|
}
|
|
|
|
func NewIntArray(v []int32) *Value {
|
|
data := make([]byte, 4+len(v)*4)
|
|
binary.BigEndian.PutUint32(data, uint32(len(v)))
|
|
for i, j := 0, 4; i < len(v); i, j = i+1, j+4 {
|
|
binary.BigEndian.PutUint32(data[j:], uint32(v[i]))
|
|
}
|
|
return &Value{tag: nbt.TagIntArray, data: data}
|
|
}
|
|
|
|
func NewLongArray(v []int64) *Value {
|
|
data := make([]byte, 4+len(v)*8)
|
|
binary.BigEndian.PutUint32(data, uint32(len(v)))
|
|
for i, j := 0, 4; i < len(v); i, j = i+1, j+8 {
|
|
binary.BigEndian.PutUint64(data[j:], uint64(v[i]))
|
|
}
|
|
return &Value{tag: nbt.TagLongArray, data: data}
|
|
}
|
|
|
|
func NewString(str string) *Value {
|
|
data := make([]byte, 2, 2+len(str))
|
|
binary.BigEndian.PutUint16(data, uint16(len(str)))
|
|
return &Value{tag: nbt.TagString, data: append(data, str...)}
|
|
}
|
|
|
|
func NewList(elems ...*Value) *Value {
|
|
return &Value{tag: nbt.TagList, list: elems}
|
|
}
|
|
|
|
func NewCompound() *Value {
|
|
return &Value{tag: nbt.TagCompound}
|
|
}
|
|
|
|
func (v *Value) Boolean() bool {
|
|
if v.tag != nbt.TagByte {
|
|
return false
|
|
}
|
|
return v.data[0] != 0
|
|
}
|
|
|
|
func (v *Value) Byte() int8 {
|
|
if v.tag != nbt.TagByte {
|
|
return 0
|
|
}
|
|
return int8(v.data[0])
|
|
}
|
|
|
|
func (v *Value) Short() int16 {
|
|
if v.tag != nbt.TagShort {
|
|
return 0
|
|
}
|
|
return int16(binary.BigEndian.Uint16(v.data))
|
|
}
|
|
|
|
func (v *Value) Int() int32 {
|
|
if v.tag != nbt.TagInt {
|
|
return 0
|
|
}
|
|
return int32(binary.BigEndian.Uint32(v.data))
|
|
}
|
|
|
|
func (v *Value) Long() int64 {
|
|
if v.tag != nbt.TagLong {
|
|
return 0
|
|
}
|
|
return int64(binary.BigEndian.Uint64(v.data))
|
|
}
|
|
|
|
func (v *Value) Float() float32 {
|
|
if v.tag != nbt.TagFloat {
|
|
return float32(math.NaN())
|
|
}
|
|
return math.Float32frombits(binary.BigEndian.Uint32(v.data))
|
|
}
|
|
|
|
func (v *Value) Double() float64 {
|
|
if v.tag != nbt.TagDouble {
|
|
return math.NaN()
|
|
}
|
|
return math.Float64frombits(binary.BigEndian.Uint64(v.data))
|
|
}
|
|
|
|
func (v *Value) List() []*Value {
|
|
if v.tag != nbt.TagList {
|
|
return nil
|
|
}
|
|
return v.list
|
|
}
|
|
|
|
func (v *Value) Compound() *Compound {
|
|
if v.tag != nbt.TagCompound {
|
|
return nil
|
|
}
|
|
return &v.comp
|
|
}
|
|
|
|
func (v *Value) ByteArray() []byte {
|
|
if v.tag != nbt.TagByteArray {
|
|
return nil
|
|
}
|
|
return v.data[4:]
|
|
}
|
|
|
|
func (v *Value) IntArray() []int32 {
|
|
if v.tag != nbt.TagIntArray {
|
|
return nil
|
|
}
|
|
length := binary.BigEndian.Uint32(v.data)
|
|
ret := make([]int32, length)
|
|
for i, j := uint32(0), 4; i < length; i, j = i+1, j+4 {
|
|
ret[i] = int32(binary.BigEndian.Uint32(v.data[j:]))
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (v *Value) LongArray() []int64 {
|
|
if v.tag != nbt.TagLongArray {
|
|
return nil
|
|
}
|
|
length := binary.BigEndian.Uint32(v.data)
|
|
ret := make([]int64, length)
|
|
for i, j := uint32(0), 4; i < length; i, j = i+1, j+8 {
|
|
ret[i] = int64(binary.BigEndian.Uint64(v.data[j:]))
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (v *Value) String() string {
|
|
if v.tag != nbt.TagString {
|
|
return ""
|
|
}
|
|
return string(v.data[2:])
|
|
}
|
|
|
|
type Compound struct {
|
|
kvs []kv
|
|
}
|
|
|
|
type kv struct {
|
|
tag string
|
|
v *Value
|
|
}
|
|
|
|
func (c *Compound) Visit(f func(tag string, v *Value)) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
for _, kv := range c.kvs {
|
|
f(kv.tag, kv.v)
|
|
}
|
|
}
|