support block state & nbt improvement
This commit is contained in:
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user