support block state & nbt improvement

This commit is contained in:
Tnze
2022-03-09 16:12:47 +08:00
parent 86592931c6
commit aa8e611644
915 changed files with 6566 additions and 8605 deletions

View File

@ -2,6 +2,7 @@ package nbt
import (
"bytes"
"encoding"
"errors"
"fmt"
"io"
@ -23,7 +24,7 @@ func Unmarshal(data []byte, v interface{}) error {
// into a struct or map, but not a string.
//
// This method also return tag name of the root tag.
// In real world, it is often empty, but the API should allows you to get it when ever you want.
// In real world, it is often empty, but the API should allow you to get it when ever you want.
func (d *Decoder) Decode(v interface{}) (string, error) {
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr {
@ -39,7 +40,7 @@ func (d *Decoder) Decode(v interface{}) (string, error) {
return tagName, fmt.Errorf("nbt: unknown Tag, maybe compressed by %s, uncompress it first", c)
}
// We decode val not val.Elem because the NBTDecoder interface
// We decode val not val.Elem because the Unmarshaler interface
// test must be applied at the top level of the value.
err = d.unmarshal(val, tagType)
if err != nil {
@ -64,9 +65,12 @@ func (d *Decoder) checkCompressed(head byte) (compress string) {
var ErrEND = errors.New("unexpected TAG_End")
func (d *Decoder) unmarshal(val reflect.Value, tagType byte) error {
u, val := indirect(val, tagType == TagEnd)
u, t, val, assign := indirect(val, tagType == TagEnd)
if assign != nil {
defer assign()
}
if u != nil {
return u.Decode(tagType, d.r)
return u.UnmarshalNBT(tagType, d.r)
}
switch tagType {
@ -177,13 +181,20 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte) error {
if err != nil {
return err
}
switch vk := val.Kind(); vk {
default:
return errors.New("cannot parse TagString as " + vk.String())
case reflect.String:
val.SetString(s)
case reflect.Interface:
val.Set(reflect.ValueOf(s))
if t != nil {
err := t.UnmarshalText([]byte(s))
if err != nil {
return err
}
} else {
switch vk := val.Kind(); vk {
default:
return errors.New("cannot parse TagString as " + vk.String())
case reflect.String:
val.SetString(s)
case reflect.Interface:
val.Set(reflect.ValueOf(s))
}
}
case TagByteArray:
@ -270,7 +281,7 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte) error {
}
// If we need parse TAG_List into slice, make a new with right length.
// Otherwise if we need parse into array, we check if len(array) are enough.
// Otherwise, if we need parse into array, we check if len(array) are enough.
var buf reflect.Value
vk := val.Kind()
switch vk {
@ -370,14 +381,15 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte) error {
// indirect walks down v allocating pointers as needed,
// until it gets to a non-pointer.
// If it encounters an NBTDecoder, indirect stops and returns that.
// If decodingNull is true, indirect stops at the first settable pointer so it
// If it encounters an Unmarshaler, indirect stops and returns that.
// If decodingNull is true, indirect stops at the first settable pointer, so it
// can be set to nil.
//
// This function is copied and modified from encoding/json
func indirect(v reflect.Value, decodingNull bool) (NBTDecoder, reflect.Value) {
func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value, func()) {
v0 := v
haveAddr := false
var assign func()
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
@ -389,12 +401,19 @@ func indirect(v reflect.Value, decodingNull bool) (NBTDecoder, reflect.Value) {
for {
// Load value from interface, but only if the result will be
// usefully addressable.
// Otherwise, try init a new value
if v.Kind() == reflect.Interface && !v.IsNil() {
e := v.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
haveAddr = false
v = e
continue
} else if v.CanSet() {
e = reflect.New(e.Type())
cv := v
assign = func() { cv.Set(e.Elem()) }
v = e
continue
}
}
@ -417,8 +436,12 @@ func indirect(v reflect.Value, decodingNull bool) (NBTDecoder, reflect.Value) {
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(NBTDecoder); ok {
return u, reflect.Value{}
i := v.Interface()
if u, ok := i.(Unmarshaler); ok {
return u, nil, reflect.Value{}, assign
}
if u, ok := i.(encoding.TextUnmarshaler); ok {
return nil, u, v, assign
}
}
@ -429,7 +452,7 @@ func indirect(v reflect.Value, decodingNull bool) (NBTDecoder, reflect.Value) {
v = v.Elem()
}
}
return nil, v
return nil, nil, v, assign
}
// rawRead read and discard a value

View File

@ -6,6 +6,7 @@ import (
"io"
"math"
"reflect"
"strconv"
"testing"
)
@ -380,6 +381,31 @@ func TestDecoder_Decode_ErrorString(t *testing.T) {
}
type TextBool bool
func (b TextBool) MarshalText() (text []byte, err error) {
return []byte(strconv.FormatBool(bool(b))), nil
}
func (b *TextBool) UnmarshalText(text []byte) (err error) {
*((*bool)(b)), err = strconv.ParseBool(string(text))
return
}
func TestDecoder_Decode_textUnmarshaler(t *testing.T) {
var b TextBool
data := []byte{
TagString, 0, 0,
0, 4, 't', 'r', 'u', 'e',
}
_, err := NewDecoder(bytes.NewReader(data)).Decode(&b)
if err != nil {
t.Fatal(err)
}
if b != true {
t.Errorf("b should be true")
}
}
func TestRawMessage_Decode(t *testing.T) {
data := []byte{
TagCompound, 0, 2, 'a', 'b',
@ -435,16 +461,16 @@ func TestStringifiedMessage_Decode(t *testing.T) {
t.Fatal(tag, err)
} else {
if tag != "ab" {
t.Fatalf("Decode tag name error: want %s, get: %s", "ab", tag)
t.Fatalf("UnmarshalNBT tag name error: want %s, get: %s", "ab", tag)
}
if container.Key != 12 {
t.Fatalf("Decode Key error: want %v, get: %v", 12, container.Key)
t.Fatalf("UnmarshalNBT Key error: want %v, get: %v", 12, container.Key)
}
if container.Value != `"Tn ze"` {
t.Fatalf("Decode Key error: get: %v", container.Value)
t.Fatalf("UnmarshalNBT Key error: get: %v", container.Value)
}
if container.List != "[{},{}]" {
t.Fatalf("Decode List error: get: %v", container.List)
t.Fatalf("UnmarshalNBT List error: get: %v", container.List)
}
}
}

View File

@ -49,8 +49,8 @@ func (e *Encoder) marshal(val reflect.Value, tagType byte, tagName string) error
return err
}
if val.CanInterface() {
if encoder, ok := val.Interface().(NBTEncoder); ok {
return encoder.Encode(e.w)
if encoder, ok := val.Interface().(Marshaler); ok {
return encoder.MarshalNBT(e.w)
}
}
return e.writeValue(val, tagType)
@ -205,7 +205,7 @@ func getTagType(v reflect.Value) (byte, reflect.Value) {
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(NBTEncoder); ok {
if u, ok := v.Interface().(Marshaler); ok {
return u.TagType(), v
}
}
@ -214,7 +214,7 @@ func getTagType(v reflect.Value) (byte, reflect.Value) {
}
if v.CanInterface() {
if encoder, ok := v.Interface().(NBTEncoder); ok {
if encoder, ok := v.Interface().(Marshaler); ok {
return encoder.TagType(), v
}
}

View File

@ -2,11 +2,11 @@ package nbt
import "io"
type NBTDecoder interface {
Decode(tagType byte, r DecoderReader) error
type Unmarshaler interface {
UnmarshalNBT(tagType byte, r DecoderReader) error
}
type NBTEncoder interface {
type Marshaler interface {
TagType() byte
Encode(w io.Writer) error
MarshalNBT(w io.Writer) error
}

View File

@ -21,12 +21,12 @@ func (m RawMessage) TagType() byte {
return m.Type
}
func (m RawMessage) Encode(w io.Writer) error {
func (m RawMessage) MarshalNBT(w io.Writer) error {
_, err := w.Write(m.Data)
return err
}
func (m *RawMessage) Decode(tagType byte, r DecoderReader) error {
func (m *RawMessage) UnmarshalNBT(tagType byte, r DecoderReader) error {
if tagType == TagEnd {
return ErrEND
}
@ -66,7 +66,7 @@ func (m RawMessage) Unmarshal(v interface{}) error {
d := NewDecoder(bytes.NewReader(m.Data))
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr {
return errors.New("nbt: non-pointer passed to Decode")
return errors.New("nbt: non-pointer passed to UnmarshalNBT")
}
return d.unmarshal(val, m.Type)
}

View File

@ -56,13 +56,13 @@ func (m StringifiedMessage) TagType() byte {
}
}
func (m StringifiedMessage) Encode(w io.Writer) error {
func (m StringifiedMessage) MarshalNBT(w io.Writer) error {
d := decodeState{data: []byte(m)}
d.scan.reset()
return writeValue(NewEncoder(w), &d, false, "")
}
func (m *StringifiedMessage) Decode(tagType byte, r DecoderReader) error {
func (m *StringifiedMessage) UnmarshalNBT(tagType byte, r DecoderReader) error {
if tagType == TagEnd {
return ErrEND
}

View File

@ -159,7 +159,7 @@ func TestStringifiedMessage_Encode(t *testing.T) {
{`[B; 1B, 2B, 3B]`, []byte{0, 0, 0, 3, 1, 2, 3}},
{`[{},{}]`, []byte{TagCompound, 0, 0, 0, 2, 0, 0}},
} {
if err := StringifiedMessage(v.snbt).Encode(&buff); err != nil {
if err := StringifiedMessage(v.snbt).MarshalNBT(&buff); err != nil {
t.Errorf("Encode SNBT error: %v", err)
}
if !bytes.Equal(buff.Bytes(), v.data) {