rename fastnbt -> dynbt. add some NewXXX api
This commit is contained in:
31
nbt/dynbt/bigTest_test.snbt
Normal file
31
nbt/dynbt/bigTest_test.snbt
Normal file
File diff suppressed because one or more lines are too long
194
nbt/dynbt/decode.go
Normal file
194
nbt/dynbt/decode.go
Normal file
@ -0,0 +1,194 @@
|
||||
package dynbt
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/Tnze/go-mc/nbt"
|
||||
)
|
||||
|
||||
func (v *Value) UnmarshalNBT(tagType byte, r nbt.DecoderReader) error {
|
||||
v.tag = tagType
|
||||
var buf [8]byte
|
||||
switch tagType {
|
||||
case nbt.TagEnd:
|
||||
case nbt.TagByte:
|
||||
n, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.data = append(v.data[:0], n)
|
||||
|
||||
case nbt.TagShort:
|
||||
if _, err := r.Read(buf[:2]); err != nil {
|
||||
return err
|
||||
}
|
||||
v.data = append(v.data[:0], buf[:2]...)
|
||||
|
||||
case nbt.TagInt, nbt.TagFloat:
|
||||
if _, err := r.Read(buf[:4]); err != nil {
|
||||
return err
|
||||
}
|
||||
v.data = append(v.data[:0], buf[:4]...)
|
||||
|
||||
case nbt.TagLong, nbt.TagDouble:
|
||||
if _, err := r.Read(buf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
v.data = append(v.data[:0], buf[:]...)
|
||||
|
||||
case nbt.TagByteArray:
|
||||
n, err := readInt32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.data = append(v.data[:0], make([]byte, 4+n)...)
|
||||
binary.BigEndian.PutUint32(v.data, uint32(n))
|
||||
|
||||
_, err = io.ReadFull(r, v.data[4:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nbt.TagString:
|
||||
n, err := readInt16(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.data = append(v.data[:0], make([]byte, 2+n)...)
|
||||
binary.BigEndian.PutUint16(v.data, uint16(n))
|
||||
|
||||
_, err = io.ReadFull(r, v.data[2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nbt.TagList:
|
||||
t, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
length, err := readInt32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.list = v.list[:0]
|
||||
|
||||
for i := int32(0); i < length; i++ {
|
||||
field := new(Value)
|
||||
err = field.UnmarshalNBT(t, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.list = append(v.list, field)
|
||||
}
|
||||
|
||||
case nbt.TagCompound:
|
||||
for {
|
||||
t, name, err := readTag(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t == nbt.TagEnd {
|
||||
break
|
||||
}
|
||||
|
||||
field := new(Value)
|
||||
err = field.UnmarshalNBT(t, r)
|
||||
if err != nil {
|
||||
return decodeErr{name, err}
|
||||
}
|
||||
|
||||
v.comp.kvs = append(v.comp.kvs, kv{tag: name, v: field})
|
||||
}
|
||||
case nbt.TagIntArray:
|
||||
n, err := readInt32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.data = append(v.data[:0], make([]byte, 4+n*4)...)
|
||||
binary.BigEndian.PutUint32(v.data, uint32(n))
|
||||
|
||||
_, err = io.ReadFull(r, v.data[4:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nbt.TagLongArray:
|
||||
n, err := readInt32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.data = append(v.data[:0], make([]byte, 4+n*8)...)
|
||||
binary.BigEndian.PutUint32(v.data, uint32(n))
|
||||
|
||||
_, err = io.ReadFull(r, v.data[4:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readTag(r nbt.DecoderReader) (tagType byte, tagName string, err error) {
|
||||
tagType, err = r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch tagType {
|
||||
// case 0x1f, 0x78:
|
||||
case nbt.TagEnd:
|
||||
default: // Read Tag
|
||||
tagName, err = readString(r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readInt16(r nbt.DecoderReader) (int16, error) {
|
||||
var data [2]byte
|
||||
_, err := io.ReadFull(r, data[:])
|
||||
return int16(binary.BigEndian.Uint16(data[:])), err
|
||||
}
|
||||
|
||||
func readInt32(r nbt.DecoderReader) (int32, error) {
|
||||
var data [4]byte
|
||||
_, err := io.ReadFull(r, data[:])
|
||||
return int32(binary.BigEndian.Uint32(data[:])), err
|
||||
}
|
||||
|
||||
func readString(r nbt.DecoderReader) (string, error) {
|
||||
length, err := readInt16(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if length < 0 {
|
||||
return "", errors.New("string length less than 0")
|
||||
}
|
||||
|
||||
var str string
|
||||
if length > 0 {
|
||||
buf := make([]byte, length)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
str = string(buf)
|
||||
}
|
||||
return str, err
|
||||
}
|
||||
|
||||
type decodeErr struct {
|
||||
decoding string
|
||||
err error
|
||||
}
|
||||
|
||||
func (d decodeErr) Error() string {
|
||||
return fmt.Sprintf("fail to decode tag %q: %v", d.decoding, d.err)
|
||||
}
|
98
nbt/dynbt/decode_test.go
Normal file
98
nbt/dynbt/decode_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
package dynbt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"testing"
|
||||
|
||||
"github.com/Tnze/go-mc/nbt"
|
||||
)
|
||||
|
||||
//go:embed bigTest_test.snbt
|
||||
var bigTestSNBT string
|
||||
|
||||
func TestValue_UnmarshalNBT(t *testing.T) {
|
||||
data, err := nbt.Marshal(nbt.StringifiedMessage(bigTestSNBT))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var val Value
|
||||
err = nbt.Unmarshal(data, &val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v := val.Get("longTest"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.Long(), int64(9223372036854775807); got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("shortTest"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.Short(), int16(32767); got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("stringTest"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.String(), "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!"; got != want {
|
||||
t.Errorf("expect %s, got: %s", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("floatTest"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.Float(), float32(0.49823147); got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("byteTest"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.Byte(), int8(127); got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("intTest"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.Int(), int32(2147483647); got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("nested compound test"); v == nil {
|
||||
t.Fail()
|
||||
} else if v = v.Get("ham"); v == nil {
|
||||
t.Fail()
|
||||
} else if v = v.Get("name"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.String(), "Hampus"; got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("nested compound test", "ham", "name"); v == nil {
|
||||
t.Fail()
|
||||
} else if got, want := v.String(), "Hampus"; got != want {
|
||||
t.Errorf("expect %v, got: %v", want, got)
|
||||
}
|
||||
|
||||
if v := val.Get("listTest (long)"); v == nil {
|
||||
t.Fail()
|
||||
} else if list := v.List(); list == nil {
|
||||
t.Fail()
|
||||
} else if len(list) != 5 {
|
||||
t.Fail()
|
||||
} else if list[0].Long() != 11 || list[1].Long() != 12 || list[2].Long() != 13 || list[3].Long() != 14 || list[4].Long() != 15 {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
want := make([]byte, 1000)
|
||||
for n := 0; n < 1000; n++ {
|
||||
want[n] = byte((n*n*255 + n*7) % 100)
|
||||
}
|
||||
if v := val.Get("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"); v == nil {
|
||||
t.Fail()
|
||||
} else if got := v.ByteArray(); !bytes.Equal(got, want) {
|
||||
t.Errorf("expect %v", want)
|
||||
t.Errorf(" got: %v", got)
|
||||
}
|
||||
}
|
94
nbt/dynbt/encode.go
Normal file
94
nbt/dynbt/encode.go
Normal file
@ -0,0 +1,94 @@
|
||||
package dynbt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/Tnze/go-mc/nbt"
|
||||
)
|
||||
|
||||
func (v *Value) TagType() byte { return v.tag }
|
||||
|
||||
func (v *Value) MarshalNBT(w io.Writer) (err error) {
|
||||
switch v.tag {
|
||||
case nbt.TagEnd:
|
||||
_, err = w.Write([]byte{0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nbt.TagByte, nbt.TagShort, nbt.TagInt, nbt.TagLong, nbt.TagFloat, nbt.TagDouble,
|
||||
nbt.TagByteArray, nbt.TagString, nbt.TagIntArray, nbt.TagLongArray:
|
||||
_, err = w.Write(v.data)
|
||||
|
||||
case nbt.TagList:
|
||||
// Take a look at the first element's tag.
|
||||
// If length == 0, use TagEnd
|
||||
elemType := nbt.TagEnd
|
||||
length := len(v.list)
|
||||
if length > 0 {
|
||||
elemType = v.list[0].tag
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte{elemType})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = writeInt32(w, int32(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, val := range v.list {
|
||||
err = val.MarshalNBT(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case nbt.TagCompound:
|
||||
for _, field := range v.comp.kvs {
|
||||
err = writeTag(w, field.v.tag, field.tag)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = field.v.MarshalNBT(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = w.Write([]byte{nbt.TagEnd})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
err = errors.New("internal: unknown tag")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeTag(w io.Writer, tagType byte, tagName string) error {
|
||||
if _, err := w.Write([]byte{tagType}); err != nil {
|
||||
return err
|
||||
}
|
||||
bName := []byte(tagName)
|
||||
if err := writeInt16(w, int16(len(bName))); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := w.Write(bName)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeInt16(w io.Writer, n int16) error {
|
||||
_, err := w.Write([]byte{byte(n >> 8), byte(n)})
|
||||
return err
|
||||
}
|
||||
|
||||
func writeInt32(w io.Writer, n int32) error {
|
||||
_, err := w.Write([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
|
||||
return err
|
||||
}
|
93
nbt/dynbt/encode_test.go
Normal file
93
nbt/dynbt/encode_test.go
Normal file
@ -0,0 +1,93 @@
|
||||
package dynbt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Tnze/go-mc/nbt"
|
||||
)
|
||||
|
||||
func TestValue_new(t *testing.T) {
|
||||
if val := NewBoolean(true); val.Boolean() != true {
|
||||
t.Error("encode bool error")
|
||||
}
|
||||
if val := NewBoolean(false); val.Boolean() != false {
|
||||
t.Error("encode bool error")
|
||||
}
|
||||
if val := NewByte(127); val.Byte() != 127 {
|
||||
t.Error("encode byte error")
|
||||
}
|
||||
if val := NewShort(32767); val.Short() != 32767 {
|
||||
t.Error("encode short error")
|
||||
}
|
||||
if val := NewInt(2147483647); val.Int() != 2147483647 {
|
||||
t.Error("encode int error")
|
||||
}
|
||||
if val := NewLong(9223372036854775807); val.Long() != 9223372036854775807 {
|
||||
t.Error("encode long error")
|
||||
}
|
||||
if val := NewString("HELLO WORLD THIS IS A TEST STRING ÅÄÖ!"); val.String() != "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!" {
|
||||
t.Error("encode string error")
|
||||
}
|
||||
if val := NewFloat(0.49823147); val.Float() != 0.49823147 {
|
||||
t.Error("encode float error")
|
||||
}
|
||||
if val := NewDouble(0.4931287132182315); val.Double() != 0.4931287132182315 {
|
||||
t.Error("encode double error")
|
||||
}
|
||||
|
||||
byteArray := make([]byte, 1000)
|
||||
for n := range byteArray {
|
||||
byteArray[n] = byte((n*n*255 + n*7) % 100)
|
||||
}
|
||||
if val := NewByteArray(byteArray); !bytes.Equal(byteArray, val.ByteArray()) {
|
||||
t.Error("encode byteArray error")
|
||||
}
|
||||
|
||||
intArray := make([]int32, 250)
|
||||
for n := range intArray {
|
||||
intArray[n] = rand.Int31()
|
||||
}
|
||||
if val := NewIntArray(intArray); !reflect.DeepEqual(intArray, val.IntArray()) {
|
||||
t.Error("encode intArray error")
|
||||
}
|
||||
|
||||
longArray := make([]int64, 125)
|
||||
for n := range longArray {
|
||||
longArray[n] = rand.Int63()
|
||||
}
|
||||
if val := NewLongArray(longArray); !reflect.DeepEqual(longArray, val.LongArray()) {
|
||||
t.Error("encode longArray error")
|
||||
}
|
||||
|
||||
val := NewCompound()
|
||||
val.Set("a", NewString("tnze"))
|
||||
if val.Get("a").String() != "tnze" || val.Compound().Len() != 1 {
|
||||
t.Error("encode compound error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValue_bigTest(t *testing.T) {
|
||||
data, err := nbt.Marshal(nbt.StringifiedMessage(bigTestSNBT))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var val Value
|
||||
err = nbt.Unmarshal(data, &val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var data2 []byte
|
||||
data2, err = nbt.Marshal(&val)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(data, data2) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
222
nbt/dynbt/types.go
Normal file
222
nbt/dynbt/types.go
Normal file
@ -0,0 +1,222 @@
|
||||
// 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"
|
||||
|
||||
"github.com/Tnze/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
|
||||
}
|
47
nbt/dynbt/update.go
Normal file
47
nbt/dynbt/update.go
Normal file
@ -0,0 +1,47 @@
|
||||
package dynbt
|
||||
|
||||
import "github.com/Tnze/go-mc/nbt"
|
||||
|
||||
func (v *Value) Set(key string, val *Value) {
|
||||
if v.tag != nbt.TagCompound {
|
||||
panic("cannot set non-Compound Tag")
|
||||
}
|
||||
v.comp.Set(key, val)
|
||||
}
|
||||
|
||||
func (v *Value) Get(keys ...string) *Value {
|
||||
for _, key := range keys {
|
||||
if v.tag == nbt.TagCompound {
|
||||
v = v.comp.Get(key)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (c *Compound) Set(key string, val *Value) {
|
||||
for i := range c.kvs {
|
||||
if c.kvs[i].tag == key {
|
||||
c.kvs[i].v = val
|
||||
return
|
||||
}
|
||||
}
|
||||
c.kvs = append(c.kvs, kv{key, val})
|
||||
}
|
||||
|
||||
func (c *Compound) Get(key string) *Value {
|
||||
for _, tag := range c.kvs {
|
||||
if tag.tag == key {
|
||||
return tag.v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compound) Len() int {
|
||||
return len(c.kvs)
|
||||
}
|
Reference in New Issue
Block a user