Fix bugs with pk.Ary. Add tests and examples.
This commit is contained in:
@ -1,19 +1,31 @@
|
|||||||
package packet
|
package packet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ary is used to send or receive the packet field like "Array of X"
|
||||||
|
// which has a count must be known from the context.
|
||||||
|
//
|
||||||
|
// Typically, you must decode an integer representing the length. Then
|
||||||
|
// receive the corresponding amount of data according to the length.
|
||||||
|
// In this case, the field Len should be a pointer of integer type so
|
||||||
|
// the value can be updating when Packet.Scan() method is decoding the
|
||||||
|
// previous field.
|
||||||
|
// In some special cases, you might want to read an "Array of X" with a fix length.
|
||||||
|
// So it's allowed to directly set an integer type Len, but not a pointer.
|
||||||
|
//
|
||||||
|
// Note that Ary DO NOT read or write the Len. You are controlling it manually.
|
||||||
type Ary struct {
|
type Ary struct {
|
||||||
Len Field // Pointer of VarInt, VarLong, Int or Long
|
Len interface{} // Value or Pointer of any integer type, only needed in ReadFrom
|
||||||
Ary interface{} // Slice of FieldEncoder, FieldDecoder or both (Field)
|
Ary interface{} // Slice of FieldEncoder, FieldDecoder or both (Field)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Ary) WriteTo(r io.Writer) (n int64, err error) {
|
func (a Ary) WriteTo(r io.Writer) (n int64, err error) {
|
||||||
length := int(reflect.ValueOf(a.Len).Int())
|
|
||||||
array := reflect.ValueOf(a.Ary)
|
array := reflect.ValueOf(a.Ary)
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < array.Len(); i++ {
|
||||||
elem := array.Index(i)
|
elem := array.Index(i)
|
||||||
nn, err := elem.Interface().(FieldEncoder).WriteTo(r)
|
nn, err := elem.Interface().(FieldEncoder).WriteTo(r)
|
||||||
n += nn
|
n += nn
|
||||||
@ -24,8 +36,24 @@ func (a Ary) WriteTo(r io.Writer) (n int64, err error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a Ary) length() int {
|
||||||
|
v := reflect.ValueOf(a.Len)
|
||||||
|
for {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
v = v.Elem()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return int(v.Int())
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return int(v.Uint())
|
||||||
|
default:
|
||||||
|
panic(errors.New("unsupported Len value: " + v.Type().String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a Ary) ReadFrom(r io.Reader) (n int64, err error) {
|
func (a Ary) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
length := int(reflect.ValueOf(a.Len).Elem().Int())
|
length := a.length()
|
||||||
array := reflect.ValueOf(a.Ary).Elem()
|
array := reflect.ValueOf(a.Ary).Elem()
|
||||||
if array.Cap() < length {
|
if array.Cap() < length {
|
||||||
array.Set(reflect.MakeSlice(array.Type(), length, length))
|
array.Set(reflect.MakeSlice(array.Type(), length, length))
|
||||||
@ -42,19 +70,32 @@ func (a Ary) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Opt struct {
|
type Opt struct {
|
||||||
Has *Boolean
|
Has interface{} // Boolean, *Boolean or func() bool
|
||||||
Field interface{} // FieldEncoder, FieldDecoder or both (Field)
|
Field interface{} // FieldEncoder, FieldDecoder or both (Field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o Opt) has() bool {
|
||||||
|
switch o.Has.(type) {
|
||||||
|
case Boolean:
|
||||||
|
return bool(o.Has.(Boolean))
|
||||||
|
case *Boolean:
|
||||||
|
return bool(*o.Has.(*Boolean))
|
||||||
|
case func() bool:
|
||||||
|
return o.Has.(func() bool)()
|
||||||
|
default:
|
||||||
|
panic(errors.New("unsupported Has value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (o Opt) WriteTo(w io.Writer) (int64, error) {
|
func (o Opt) WriteTo(w io.Writer) (int64, error) {
|
||||||
if *o.Has {
|
if o.has() {
|
||||||
return o.Field.(FieldEncoder).WriteTo(w)
|
return o.Field.(FieldEncoder).WriteTo(w)
|
||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o Opt) ReadFrom(r io.Reader) (int64, error) {
|
func (o Opt) ReadFrom(r io.Reader) (int64, error) {
|
||||||
if *o.Has {
|
if o.has() {
|
||||||
return o.Field.(FieldDecoder).ReadFrom(r)
|
return o.Field.(FieldDecoder).ReadFrom(r)
|
||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
72
net/packet/util_test.go
Normal file
72
net/packet/util_test.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package packet_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleAry_WriteTo() {
|
||||||
|
data := []pk.Int{0, 1, 2, 3, 4, 5, 6}
|
||||||
|
// Len is completely ignored by WriteTo method.
|
||||||
|
// The length is inferred from the length of Ary.
|
||||||
|
pk.Marshal(
|
||||||
|
0x00,
|
||||||
|
// It's important to remember that
|
||||||
|
// typically the responsibility of
|
||||||
|
// sending the length field
|
||||||
|
// is on you.
|
||||||
|
pk.VarInt(len(data)),
|
||||||
|
pk.Ary{
|
||||||
|
Len: len(data), // this line can be removed
|
||||||
|
Ary: data,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAry_ReadFrom() {
|
||||||
|
var length pk.VarInt
|
||||||
|
var data []pk.String
|
||||||
|
|
||||||
|
var p pk.Packet // = conn.ReadPacket()
|
||||||
|
if err := p.Scan(
|
||||||
|
|
||||||
|
&length, // decode length first
|
||||||
|
pk.Ary{ // then decode Ary according to length
|
||||||
|
Len: &length,
|
||||||
|
Ary: &data,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAry_WriteTo(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
want := []byte{
|
||||||
|
0x00, 0x00, 0x00, 0x01,
|
||||||
|
0x00, 0x00, 0x00, 0x02,
|
||||||
|
0x00, 0x00, 0x00, 0x03,
|
||||||
|
}
|
||||||
|
int3, long3, varint3, varlong3 := pk.Int(3), pk.Long(3), pk.VarInt(3), pk.VarLong(3)
|
||||||
|
for _, item := range [...]pk.Ary{
|
||||||
|
{Len: int3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: long3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: varint3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: varlong3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: &int3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: &long3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: &varint3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
{Len: &varlong3, Ary: []pk.Int{1, 2, 3}},
|
||||||
|
} {
|
||||||
|
_, err := item.WriteTo(&buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf.Bytes(), want) {
|
||||||
|
t.Fatalf("Ary encoding error: got %#v, want %#v", buf.Bytes(), want)
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
}
|
@ -86,14 +86,19 @@ func ExampleColumn_send() {
|
|||||||
_, err := pk.Tuple{
|
_, err := pk.Tuple{
|
||||||
pk.Short(0), // Block count
|
pk.Short(0), // Block count
|
||||||
pk.UnsignedByte(bpb), // Bits Per Block
|
pk.UnsignedByte(bpb), // Bits Per Block
|
||||||
pk.Opt{
|
hasPalette, pk.Opt{
|
||||||
Has: &hasPalette,
|
Has: &hasPalette,
|
||||||
Field: pk.Ary{
|
Field: pk.Tuple{
|
||||||
Len: &paletteLength,
|
paletteLength, pk.Ary{
|
||||||
Ary: nil, // TODO: We need translate v.Palette (with type of []Block) to state ID
|
Len: &paletteLength,
|
||||||
|
Ary: nil, // TODO: We need translate v.Palette (with type of []Block) to state ID
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, // Palette
|
}, // Palette
|
||||||
pk.Ary{Len: &dataArrayLength, Ary: dataArray}, // Data Array
|
dataArrayLength, pk.Ary{
|
||||||
|
Len: &dataArrayLength,
|
||||||
|
Ary: dataArray,
|
||||||
|
}, // Data Array
|
||||||
}.WriteTo(&buf)
|
}.WriteTo(&buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -110,12 +115,12 @@ func ExampleColumn_send() {
|
|||||||
pk.Boolean(true), // Full chunk
|
pk.Boolean(true), // Full chunk
|
||||||
PrimaryBitMask, // PrimaryBitMask
|
PrimaryBitMask, // PrimaryBitMask
|
||||||
pk.NBT(c.Level.Heightmaps), // Heightmaps
|
pk.NBT(c.Level.Heightmaps), // Heightmaps
|
||||||
pk.Ary{
|
bal, pk.Ary{
|
||||||
Len: &bal, // Biomes array length
|
Len: bal, // Biomes array length
|
||||||
Ary: c.Level.Biomes, // Biomes
|
Ary: c.Level.Biomes, // Biomes
|
||||||
},
|
},
|
||||||
pk.Ary{
|
size, pk.Ary{
|
||||||
Len: &size, // Size
|
Len: size, // Size
|
||||||
Ary: pk.ByteArray(buf.Bytes()), // Data
|
Ary: pk.ByteArray(buf.Bytes()), // Data
|
||||||
},
|
},
|
||||||
pk.VarInt(0), // Block entities array length
|
pk.VarInt(0), // Block entities array length
|
||||||
|
Reference in New Issue
Block a user