From 06c1de696c4da6ea4c58c90712e6ca212dd370e2 Mon Sep 17 00:00:00 2001 From: Raqbit Date: Sun, 14 Mar 2021 11:12:11 +0100 Subject: [PATCH 1/4] Add new UUID encoding --- encoding/uuid.go | 20 ++---------- encoding/uuid_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 encoding/uuid_test.go diff --git a/encoding/uuid.go b/encoding/uuid.go index 4a1703b..b7e9de0 100644 --- a/encoding/uuid.go +++ b/encoding/uuid.go @@ -1,6 +1,7 @@ package encoding import ( + "encoding/binary" "github.com/google/uuid" "io" ) @@ -8,25 +9,10 @@ import ( type UUID uuid.UUID func (u *UUID) Write(w io.Writer) error { - str := String(uuid.UUID(*u).String()) - return str.Write(w) + return binary.Write(w, binary.BigEndian, u) } func (u *UUID) Read(r io.Reader) error { - var err error - var str String + return binary.Read(r, binary.BigEndian, u) - if err = str.Read(r); err != nil { - return err - } - - uid, err := uuid.Parse(string(str)) - - if err != nil { - return err - } - - *u = UUID(uid) - - return nil } diff --git a/encoding/uuid_test.go b/encoding/uuid_test.go new file mode 100644 index 0000000..eca163b --- /dev/null +++ b/encoding/uuid_test.go @@ -0,0 +1,71 @@ +package encoding + +import ( + "bytes" + "github.com/google/uuid" + "testing" +) + +func TestWriteUUID(t *testing.T) { + tests := []struct { + Value UUID + Expected []byte + }{ + { + Value: UUID(uuid.Nil), + Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + }, + { + Value: UUID(uuid.MustParse("70d651be-e8c1-4214-951e-fe92dea07cb6")), + Expected: []byte{0x70, 0xd6, 0x51, 0xbe, 0xe8, 0xc1, 0x42, 0x14, 0x95, 0x1e, 0xfe, 0x92, 0xde, 0xa0, 0x7c, 0xb6}, + }, + } + + var buff bytes.Buffer + + for _, test := range tests { + if err := test.Value.Write(&buff); err != nil { + t.Error(err) + } + + actual := buff.Bytes() + + if bytes.Compare(test.Expected, actual) != 0 { + // Not equal + t.Errorf("Unable to convert %d: %v != %v", test.Value, actual, test.Expected) + } + } +} + +func TestReadUUID(t *testing.T) { + tests := []struct { + Expected UUID + Value []byte + }{ + { + Expected: UUID(uuid.Nil), + Value: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + }, + { + Expected: UUID(uuid.MustParse("70d651be-e8c1-4214-951e-fe92dea07cb6")), + Value: []byte{0x70, 0xd6, 0x51, 0xbe, 0xe8, 0xc1, 0x42, 0x14, 0x95, 0x1e, 0xfe, 0x92, 0xde, 0xa0, 0x7c, 0xb6}, + }, + } + var buff bytes.Buffer + + for _, test := range tests { + buff.Write(test.Value) + + var actual UUID + if err := actual.Read(&buff); err != nil { + t.Error(err) + } + + if actual != test.Expected { + // Not equal + t.Errorf("Unable to convert %v: %d != %d", test.Value, actual, test.Expected) + } + + buff.Reset() + } +} From ae729cad1dce15ae49e63727cc8d8f1c5c3282ef Mon Sep 17 00:00:00 2001 From: Raqbit Date: Sun, 14 Mar 2021 11:14:42 +0100 Subject: [PATCH 2/4] Add sender to chat message packet --- packet/chat.go | 1 + packet/chat_gen.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packet/chat.go b/packet/chat.go index d436031..8c3e350 100644 --- a/packet/chat.go +++ b/packet/chat.go @@ -13,6 +13,7 @@ const ChatMessagePacketID = 0x0f type ChatMessagePacket struct { Message types.TextComponent Position enc.Byte + Sender enc.UUID } func (c *ChatMessagePacket) Info() PacketInfo { diff --git a/packet/chat_gen.go b/packet/chat_gen.go index 5e6097b..1a16eff 100644 --- a/packet/chat_gen.go +++ b/packet/chat_gen.go @@ -12,6 +12,9 @@ func (c *ChatMessagePacket) Write(w io.Writer) error { if err = c.Position.Write(w); err != nil { return err } + if err = c.Sender.Write(w); err != nil { + return err + } return nil } func (c *ChatMessagePacket) Read(r io.Reader) error { @@ -22,5 +25,8 @@ func (c *ChatMessagePacket) Read(r io.Reader) error { if err = c.Position.Read(r); err != nil { return err } + if err = c.Sender.Read(r); err != nil { + return err + } return nil } From 31b9302aceda8a58124fcc3861e747e0712df615 Mon Sep 17 00:00:00 2001 From: Raqbit Date: Sun, 14 Mar 2021 11:15:41 +0100 Subject: [PATCH 3/4] [wip] Add NBT encoding --- encoding/short.go | 16 ++++++++++ encoding/short_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++ nbt/byte.go | 6 ++++ nbt/byte_array.go | 21 ++++++++++++++ nbt/compound.go | 6 ++++ nbt/double.go | 6 ++++ nbt/float.go | 6 ++++ nbt/int.go | 6 ++++ nbt/int_array.go | 29 +++++++++++++++++++ nbt/long.go | 6 ++++ nbt/long_array.go | 29 +++++++++++++++++++ nbt/short.go | 6 ++++ nbt/string.go | 23 +++++++++++++++ nbt/tags.go | 22 ++++++++++++++ nbt/tags_string.go | 35 ++++++++++++++++++++++ 15 files changed, 283 insertions(+) create mode 100644 encoding/short.go create mode 100644 encoding/short_test.go create mode 100644 nbt/byte.go create mode 100644 nbt/byte_array.go create mode 100644 nbt/compound.go create mode 100644 nbt/double.go create mode 100644 nbt/float.go create mode 100644 nbt/int.go create mode 100644 nbt/int_array.go create mode 100644 nbt/long.go create mode 100644 nbt/long_array.go create mode 100644 nbt/short.go create mode 100644 nbt/string.go create mode 100644 nbt/tags.go create mode 100644 nbt/tags_string.go diff --git a/encoding/short.go b/encoding/short.go new file mode 100644 index 0000000..eb7599a --- /dev/null +++ b/encoding/short.go @@ -0,0 +1,16 @@ +package encoding + +import ( + "encoding/binary" + "io" +) + +type Short int16 + +func (s *Short) Write(w io.Writer) error { + return binary.Write(w, binary.BigEndian, s) +} + +func (s *Short) Read(r io.Reader) error { + return binary.Read(r, binary.BigEndian, s) +} diff --git a/encoding/short_test.go b/encoding/short_test.go new file mode 100644 index 0000000..e43abca --- /dev/null +++ b/encoding/short_test.go @@ -0,0 +1,66 @@ +package encoding + +import ( + "bytes" + "math" + "testing" +) + +func TestWriteShort(t *testing.T) { + tests := []struct { + Value Short + Expected []byte + }{ + {Value: 0, Expected: []byte{0x00, 0x00}}, + {Value: 5, Expected: []byte{0x00, 0x05}}, + {Value: math.MaxInt8 + 1, Expected: []byte{0x00, 0x80}}, + {Value: math.MaxInt16 / 2, Expected: []byte{0x3f, 0xff}}, + {Value: math.MaxInt16, Expected: []byte{0x7f, 0xff}}, + } + + var buff bytes.Buffer + + for _, test := range tests { + if err := test.Value.Write(&buff); err != nil { + t.Error(err) + } + + actual := buff.Bytes() + + if bytes.Compare(test.Expected, actual) != 0 { + // Not equal + t.Errorf("Unable to convert %d: %v != %v", test.Value, actual, test.Expected) + } + } +} + +func TestReadShort(t *testing.T) { + tests := []struct { + Expected Short + Value []byte + }{ + {Expected: 0, Value: []byte{0x00, 0x00}}, + {Expected: 5, Value: []byte{0x00, 0x05}}, + {Expected: math.MaxInt8 + 1, Value: []byte{0x00, 0x80}}, + {Expected: math.MaxInt16 / 2, Value: []byte{0x3f, 0xff}}, + {Expected: math.MaxInt16, Value: []byte{0x7f, 0xff}}, + } + + var buff bytes.Buffer + + for _, test := range tests { + buff.Write(test.Value) + + var actual Short + if err := actual.Read(&buff); err != nil { + t.Error(err) + } + + if actual != test.Expected { + // Not equal + t.Errorf("Unable to convert %v: %d != %d", test.Value, actual, test.Expected) + } + + buff.Reset() + } +} diff --git a/nbt/byte.go b/nbt/byte.go new file mode 100644 index 0000000..0de7a97 --- /dev/null +++ b/nbt/byte.go @@ -0,0 +1,6 @@ +package nbt + +import enc "github.com/Raqbit/mcproto/encoding" + +var WriteTagByte = enc.WriteByte +var ReadTagByte = enc.ReadByte diff --git a/nbt/byte_array.go b/nbt/byte_array.go new file mode 100644 index 0000000..67279b3 --- /dev/null +++ b/nbt/byte_array.go @@ -0,0 +1,21 @@ +package nbt + +import "io" + +func WriteTagByteArray(value []byte) []byte { + b := WriteTagInt(int32(len(value))) + b = append(b, value...) + return b +} + +func ReadTagByteArray(r io.Reader) ([]byte, error) { + l, err := ReadTagInt(r) + + if err != nil { + return nil, err + } + + buf := make([]byte, int(l)) + _, err = io.ReadFull(r, buf) + return buf, err +} diff --git a/nbt/compound.go b/nbt/compound.go new file mode 100644 index 0000000..661ac32 --- /dev/null +++ b/nbt/compound.go @@ -0,0 +1,6 @@ +package nbt + +type NamedTag struct { + typ TagType + name string +} diff --git a/nbt/double.go b/nbt/double.go new file mode 100644 index 0000000..586454a --- /dev/null +++ b/nbt/double.go @@ -0,0 +1,6 @@ +package nbt + +import enc "github.com/Raqbit/mcproto/encoding" + +var WriteTagDouble = enc.WriteDouble +var ReadTagDouble = enc.ReadDouble diff --git a/nbt/float.go b/nbt/float.go new file mode 100644 index 0000000..72cd811 --- /dev/null +++ b/nbt/float.go @@ -0,0 +1,6 @@ +package nbt + +import enc "github.com/Raqbit/mcproto/encoding" + +var WriteTagFloat = enc.WriteFloat +var ReadTagFloat = enc.ReadFloat diff --git a/nbt/int.go b/nbt/int.go new file mode 100644 index 0000000..67b9323 --- /dev/null +++ b/nbt/int.go @@ -0,0 +1,6 @@ +package nbt + +import enc "github.com/Raqbit/mcproto/encoding" + +var WriteTagInt = enc.WriteInt +var ReadTagInt = enc.ReadInt diff --git a/nbt/int_array.go b/nbt/int_array.go new file mode 100644 index 0000000..56e5adf --- /dev/null +++ b/nbt/int_array.go @@ -0,0 +1,29 @@ +package nbt + +import "io" + +func WriteTagIntArray(value []int32) []byte { + b := WriteTagInt(int32(len(value))) + for _, i := range value { + b = append(b, WriteTagInt(i)...) + } + return b +} + +func ReadTagIntArray(r io.Reader) ([]int32, error) { + length, err := ReadTagInt(r) + + if err != nil { + return nil, err + } + + buf := make([]int32, int(length)) + + for i := 0; i < int(length); i++ { + if buf[i], err = ReadTagInt(r); err != nil { + return nil, err + } + } + + return buf, err +} diff --git a/nbt/long.go b/nbt/long.go new file mode 100644 index 0000000..a971aae --- /dev/null +++ b/nbt/long.go @@ -0,0 +1,6 @@ +package nbt + +import enc "github.com/Raqbit/mcproto/encoding" + +var WriteTagLong = enc.WriteLong +var ReadTagLong = enc.ReadLong diff --git a/nbt/long_array.go b/nbt/long_array.go new file mode 100644 index 0000000..5df1086 --- /dev/null +++ b/nbt/long_array.go @@ -0,0 +1,29 @@ +package nbt + +import "io" + +func WriteTagLongArray(value []int64) []byte { + b := WriteTagInt(int32(len(value))) + for _, i := range value { + b = append(b, WriteTagLong(i)...) + } + return b +} + +func ReadTagLongArray(r io.Reader) ([]int64, error) { + length, err := ReadTagInt(r) + + if err != nil { + return nil, err + } + + buf := make([]int64, int(length)) + + for i := 0; i < int(length); i++ { + if buf[i], err = ReadTagLong(r); err != nil { + return nil, err + } + } + + return buf, err +} diff --git a/nbt/short.go b/nbt/short.go new file mode 100644 index 0000000..b3ceffa --- /dev/null +++ b/nbt/short.go @@ -0,0 +1,6 @@ +package nbt + +import enc "github.com/Raqbit/mcproto/encoding" + +var WriteTagShort = enc.WriteShort +var ReadTagShort = enc.ReadShort diff --git a/nbt/string.go b/nbt/string.go new file mode 100644 index 0000000..0aec75d --- /dev/null +++ b/nbt/string.go @@ -0,0 +1,23 @@ +package nbt + +import ( + enc "github.com/Raqbit/mcproto/encoding" + "io" +) + +func WriteTagString(str string) []byte { + b := enc.WriteUnsignedShort(uint16(len(str))) + b = append(b, []byte(str)...) + return b +} + +func ReadTagString(r io.Reader) (string, error) { + s, err := enc.ReadUnsignedByte(r) + if err != nil { + return "", nil + } + + stringBuff := make([]byte, int(s)) + _, err = io.ReadFull(r, stringBuff) + return string(stringBuff), err +} diff --git a/nbt/tags.go b/nbt/tags.go new file mode 100644 index 0000000..061f812 --- /dev/null +++ b/nbt/tags.go @@ -0,0 +1,22 @@ +package nbt + +//go:generate stringer -type=TagType -output tags_string.go -linecomment + +// TagType is the type of an NBT tag +type TagType byte + +const ( + TagTypeEnd TagType = 0 // End + TagTypeByte TagType = 1 // Byte + TagTypeShort TagType = 2 // Short + TagTypeInt TagType = 3 // Int + TagTypeLong TagType = 4 // Long + TagTypeFloat TagType = 5 // Float + TagTypeDouble TagType = 6 // Double + TagTypeByteArray TagType = 7 // Byte Array + TagTypeString TagType = 8 // String + TagTypeList TagType = 9 // List + TagTypeCompound TagType = 10 // Compound + TagTypeIntArray TagType = 11 // Int Array + TagTypeLongArray TagType = 12 // Long Array +) diff --git a/nbt/tags_string.go b/nbt/tags_string.go new file mode 100644 index 0000000..f1c4727 --- /dev/null +++ b/nbt/tags_string.go @@ -0,0 +1,35 @@ +// Code generated by "stringer -type=TagType -output tags_string.go -linecomment"; DO NOT EDIT. + +package nbt + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[TagTypeEnd-0] + _ = x[TagTypeByte-1] + _ = x[TagTypeShort-2] + _ = x[TagTypeInt-3] + _ = x[TagTypeLong-4] + _ = x[TagTypeFloat-5] + _ = x[TagTypeDouble-6] + _ = x[TagTypeByteArray-7] + _ = x[TagTypeString-8] + _ = x[TagTypeList-9] + _ = x[TagTypeCompound-10] + _ = x[TagTypeIntArray-11] + _ = x[TagTypeLongArray-12] +} + +const _TagType_name = "EndByteShortIntLongFloatDoubleByte ArrayStringListCompoundInt ArrayLong Array" + +var _TagType_index = [...]uint8{0, 3, 7, 12, 15, 19, 24, 30, 40, 46, 50, 58, 67, 77} + +func (i TagType) String() string { + if i >= TagType(len(_TagType_index)-1) { + return "TagType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _TagType_name[_TagType_index[i]:_TagType_index[i+1]] +} From 15e10e95992889331a17783798b10cd104251fe5 Mon Sep 17 00:00:00 2001 From: Raqbit Date: Sun, 21 Mar 2021 16:20:46 +0100 Subject: [PATCH 4/4] [wip] Update Join Game packet --- go.mod | 1 + go.sum | 2 + packet/client_settings.go | 2 +- packet/client_settings_gen.go | 4 +- packet/handshake.go | 2 +- packet/join_game.go | 19 +++++--- packet/join_game_gen.go | 68 -------------------------- packet/login_start.go | 2 +- packet/login_success.go | 2 +- tools/genpacket/genpacket.go | 89 +++++++++++++++++++++++------------ tools/genpacket/packet.go | 78 ++++++++++++++++++++++++++++++ 11 files changed, 160 insertions(+), 109 deletions(-) delete mode 100644 packet/join_game_gen.go create mode 100644 tools/genpacket/packet.go diff --git a/go.mod b/go.mod index 4723330..de3d85a 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,6 @@ require ( github.com/dave/jennifer v1.4.1 github.com/google/uuid v1.1.1 github.com/stretchr/testify v1.7.0 + github.com/yuin/stagparser v0.0.0-20181218160030-e10a81132760 golang.org/x/tools v0.1.0 ) diff --git a/go.sum b/go.sum index b70c530..bc22507 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/stagparser v0.0.0-20181218160030-e10a81132760 h1:i+m5+M2renk/OmDHvzRjlBQXm+5X6WR6xPjWfHNzvcM= +github.com/yuin/stagparser v0.0.0-20181218160030-e10a81132760/go.mod h1:+qbo7cNcx8dT/77C41x4MZbasDrLuUDI/04ZGR/7IqM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/packet/client_settings.go b/packet/client_settings.go index be179c3..72c492a 100644 --- a/packet/client_settings.go +++ b/packet/client_settings.go @@ -10,7 +10,7 @@ import ( const ClientSettingsPacketID int32 = 0x05 type ClientSettingsPacket struct { - Lang enc.String + Locale enc.String `pkt:"strLen(16)"` ViewDistance enc.Byte ChatVisibility enc.VarInt EnableChatColors enc.Bool diff --git a/packet/client_settings_gen.go b/packet/client_settings_gen.go index 26c7b3c..2c1560e 100644 --- a/packet/client_settings_gen.go +++ b/packet/client_settings_gen.go @@ -6,7 +6,7 @@ import "io" func (c *ClientSettingsPacket) Write(w io.Writer) error { var err error - if err = c.Lang.Write(w); err != nil { + if err = c.Locale.Write(w); err != nil { return err } if err = c.ViewDistance.Write(w); err != nil { @@ -28,7 +28,7 @@ func (c *ClientSettingsPacket) Write(w io.Writer) error { } func (c *ClientSettingsPacket) Read(r io.Reader) error { var err error - if err = c.Lang.Read(r); err != nil { + if err = c.Locale.Read(r); err != nil { return err } if err = c.ViewDistance.Read(r); err != nil { diff --git a/packet/handshake.go b/packet/handshake.go index 6cddb40..15ebd39 100644 --- a/packet/handshake.go +++ b/packet/handshake.go @@ -12,7 +12,7 @@ const HandshakePacketID int32 = 0x00 // https://wiki.vg/Protocol#Handshake type HandshakePacket struct { ProtoVer enc.VarInt - ServerAddr enc.String + ServerAddr enc.String `pkt:"strLen(255)"` ServerPort enc.UnsignedShort NextState types.ConnectionState } diff --git a/packet/join_game.go b/packet/join_game.go index 4cc8966..526e27d 100644 --- a/packet/join_game.go +++ b/packet/join_game.go @@ -5,20 +5,27 @@ import ( "github.com/Raqbit/mcproto/types" ) -//go:generate go run ../tools/genpacket/genpacket.go -packet=JoinGamePacket -output=join_game_gen.go +//go:generate go run ../tools/genpacket -packet=JoinGamePacket -output=join_game_gen.go const JoinGamePacketID int32 = 0x26 type JoinGamePacket struct { - PlayerID enc.Int - GameMode enc.UnsignedByte - Dimension enc.Int + PlayerID enc.Int + IsHardcore enc.Bool + GameMode enc.UnsignedByte + PreviousGameMode enc.Byte + WorldCount enc.VarInt + WorldNames []types.Identifier `pkt:"lenFrom(WorldCount)"` + //DimensionCodec nbt.Something // TODO + //Dimension nbt.Something // TODO + WorldName types.Identifier HashedSeed enc.Long MaxPlayers enc.UnsignedByte - LevelType enc.String `len:"16"` ViewDistance enc.VarInt - ReducedDebug enc.Bool + ReducedDebugInfo enc.Bool EnableRespawnScreen enc.Bool + IsDebug enc.Bool + IsFlat enc.Bool } func (*JoinGamePacket) String() string { diff --git a/packet/join_game_gen.go b/packet/join_game_gen.go deleted file mode 100644 index 002b34d..0000000 --- a/packet/join_game_gen.go +++ /dev/null @@ -1,68 +0,0 @@ -// Code generated by "genpacket -packet=JoinGamePacket -output=join_game_gen.go"; DO NOT EDIT. - -package packet - -import "io" - -func (j *JoinGamePacket) Write(w io.Writer) error { - var err error - if err = j.PlayerID.Write(w); err != nil { - return err - } - if err = j.GameMode.Write(w); err != nil { - return err - } - if err = j.Dimension.Write(w); err != nil { - return err - } - if err = j.HashedSeed.Write(w); err != nil { - return err - } - if err = j.MaxPlayers.Write(w); err != nil { - return err - } - if err = j.LevelType.Write(w); err != nil { - return err - } - if err = j.ViewDistance.Write(w); err != nil { - return err - } - if err = j.ReducedDebug.Write(w); err != nil { - return err - } - if err = j.EnableRespawnScreen.Write(w); err != nil { - return err - } - return nil -} -func (j *JoinGamePacket) Read(r io.Reader) error { - var err error - if err = j.PlayerID.Read(r); err != nil { - return err - } - if err = j.GameMode.Read(r); err != nil { - return err - } - if err = j.Dimension.Read(r); err != nil { - return err - } - if err = j.HashedSeed.Read(r); err != nil { - return err - } - if err = j.MaxPlayers.Read(r); err != nil { - return err - } - if err = j.LevelType.Read(r); err != nil { - return err - } - if err = j.ViewDistance.Read(r); err != nil { - return err - } - if err = j.ReducedDebug.Read(r); err != nil { - return err - } - if err = j.EnableRespawnScreen.Read(r); err != nil { - return err - } - return nil -} diff --git a/packet/login_start.go b/packet/login_start.go index 1548a1f..75c9fc8 100644 --- a/packet/login_start.go +++ b/packet/login_start.go @@ -11,7 +11,7 @@ const LoginStartPacketID int32 = 0x00 // https://wiki.vg/Protocol#Login_Start type LoginStartPacket struct { - Name enc.String + Name enc.String `pkt:"strLen(16)"` } func (*LoginStartPacket) Info() PacketInfo { diff --git a/packet/login_success.go b/packet/login_success.go index 4603cb0..9fd9f3c 100644 --- a/packet/login_success.go +++ b/packet/login_success.go @@ -12,7 +12,7 @@ const LoginSuccessPacketID = 0x02 // https://wiki.vg/Protocol#Login_Success type LoginSuccessPacket struct { UUID enc.UUID - Username enc.String + Username enc.String `pkt:"strLen(16)"` } func (*LoginSuccessPacket) Info() PacketInfo { diff --git a/tools/genpacket/genpacket.go b/tools/genpacket/genpacket.go index 2d2dc7c..2bb06b7 100644 --- a/tools/genpacket/genpacket.go +++ b/tools/genpacket/genpacket.go @@ -20,6 +20,7 @@ const ( WriterName = "w" ReaderName = "r" Directory = "." + SliceItemName = "v" ) var ( @@ -124,14 +125,19 @@ func (g *Generator) generate(typeName string) error { return fmt.Errorf("%s is not a struct type", typeName) } - name := obj.Name() - idName := strings.ToLower(name[:1]) + receiverName := strings.ToLower(typeName[:1]) + + //_, err := parsePacket(struc, typeName) + + //if err != nil { + // return fmt.Errorf("could not parse packet struct %s: %w", typeName, err) + //} g.file. Func(). Params( - jen.Id(idName). - Id("*" + obj.Name()), + jen.Id(receiverName). + Id("*" + typeName), ). Id(FuncMarshal). Params(jen.Id(WriterName).Qual("io", "Writer")). @@ -143,18 +149,13 @@ func (g *Generator) generate(typeName string) error { for i := 0; i < struc.NumFields(); i++ { field := struc.Field(i) + _, isSlice := field.Type().(*types.Slice) - group.If( - jen.Err(). - Op("="). - Id(idName). - Dot(field.Name()). - Dot("Write"). - Call( - jen.Id(WriterName), - ), - jen.Err().Op("!=").Nil(), - ).Block(jen.Return().Err()) + if isSlice { + genSliceFieldWrite(group, receiverName, field) + } else { + genFieldWrite(group, receiverName, field) + } } group.Return().Nil() @@ -163,7 +164,7 @@ func (g *Generator) generate(typeName string) error { g.file. Func(). Params( - jen.Id(idName). + jen.Id(receiverName). Id("*" + obj.Name()), ). Id(FuncUnmarshal). @@ -177,17 +178,7 @@ func (g *Generator) generate(typeName string) error { for i := 0; i < struc.NumFields(); i++ { field := struc.Field(i) - group.If( - jen.Err(). - Op("="). - Id(idName). - Dot(field.Name()). - Dot("Read"). - Call( - jen.Id(ReaderName), - ), - jen.Err().Op("!=").Nil(), - ).Block(jen.Return().Err()) + genFieldRead(group, receiverName, field) } group.Return().Nil() @@ -196,6 +187,46 @@ func (g *Generator) generate(typeName string) error { return nil } -func unqualified(*types.Package) string { - return "" +func genSliceFieldWrite(group *jen.Group, receiverName string, field *types.Var) { + group.For(jen.List(jen.Id("_"), jen.Id(SliceItemName)). + Op(":="). + Range(). + Id(receiverName). + Dot(field.Name()). + BlockFunc(func(group *jen.Group) { + group.If(jen.Err().Op("=").Id(SliceItemName).Dot("Write"). + Call( + jen.Id(WriterName), + ), + jen.Err().Op("!=").Nil(), + ).Block(jen.Return().Err()) + })) +} + +func genFieldWrite(group *jen.Group, receiverName string, field *types.Var) { + group.If( + jen.Err(). + Op("="). + Id(receiverName). + Dot(field.Name()). + Dot("Write"). + Call( + jen.Id(WriterName), + ), + jen.Err().Op("!=").Nil(), + ).Block(jen.Return().Err()) +} + +func genFieldRead(group *jen.Group, receiverName string, field *types.Var) { + group.If( + jen.Err(). + Op("="). + Id(receiverName). + Dot(field.Name()). + Dot("Read"). + Call( + jen.Id(ReaderName), + ), + jen.Err().Op("!=").Nil(), + ).Block(jen.Return().Err()) } diff --git a/tools/genpacket/packet.go b/tools/genpacket/packet.go new file mode 100644 index 0000000..6a6c1b9 --- /dev/null +++ b/tools/genpacket/packet.go @@ -0,0 +1,78 @@ +package main + +import ( + "fmt" + "github.com/yuin/stagparser" + "go/types" + "strings" +) + +type param struct { + name string + value interface{} +} + +type field struct { + name string + params []param +} + +type packet struct { + name string + fields []field +} + +func (p *packet) String() string { + sb := strings.Builder{} + sb.WriteString(fmt.Sprintf("packet %s {\n", p.name)) + for _, fld := range p.fields { + sb.WriteString(fmt.Sprintf("\t%s (", fld.name)) + for _, prm := range fld.params { + sb.WriteString(fmt.Sprintf("%s=%s,", prm.name, prm.value)) + } + sb.WriteString(")") + } + sb.WriteString("}\n") + + return sb.String() +} + +func parsePacket(packetType *types.Struct, structName string) (packet, error) { + fields := make([]field, packetType.NumFields()) + + for i := 0; i < packetType.NumFields(); i++ { + f := packetType.Field(i) + t := packetType.Tag(i) + + // TODO: stagparser expects us to give it one "value" of a struct tag (`key:"value"`) + // TODO: this is hard to do as the parser for it is only really present in reflect, not AST. + // TODO: a custom tag parser will probably have to be implemented + defs, err := stagparser.ParseTag(t, structName) + + if err != nil { + return packet{}, fmt.Errorf("could not parse struct tag for field %s: %w", f.Name(), err) + } + + params := make([]param, len(defs)) + + for j, def := range defs { + for name, value := range def.Attributes() { + params[j] = param{ + name: name, + value: value, + } + } + } + + fields[i] = field{ + name: f.Name(), + params: make([]param, 0), + } + + } + + return packet{ + name: structName, + fields: fields, + }, nil +}