From ec59c033796ea2cf24c28e4bf1e59e486a0d1aa2 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Thu, 20 Jun 2024 11:08:52 +0800 Subject: [PATCH] lil refactor --- messages7/cl_start_info.go | 19 ++- messages7/ctrl_connect.go | 35 +++++ messages7/ctrl_token.go | 38 ++++++ messages7/enter_game.go | 28 ++++ messages7/info.go | 33 +++++ messages7/net_message.go | 11 ++ messages7/ready.go | 28 ++++ messages7/sv_client_info.go | 21 ++- network7/network7.go | 36 ++--- packer/packer.go | 6 - protocol7/connection.go | 186 ++++++++++---------------- {packet7 => protocol7}/packet.go | 77 ++++++++++- {packet7 => protocol7}/packet_test.go | 2 +- teeworlds.go | 11 +- 14 files changed, 386 insertions(+), 145 deletions(-) create mode 100644 messages7/ctrl_connect.go create mode 100644 messages7/ctrl_token.go create mode 100644 messages7/enter_game.go create mode 100644 messages7/info.go create mode 100644 messages7/net_message.go create mode 100644 messages7/ready.go rename {packet7 => protocol7}/packet.go (56%) rename {packet7 => protocol7}/packet_test.go (99%) diff --git a/messages7/cl_start_info.go b/messages7/cl_start_info.go index 6d2a44e..e4b4956 100644 --- a/messages7/cl_start_info.go +++ b/messages7/cl_start_info.go @@ -3,6 +3,7 @@ package messages7 import ( "slices" + "github.com/teeworlds-go/teeworlds/network7" "github.com/teeworlds-go/teeworlds/packer" ) @@ -30,7 +31,23 @@ type ClStartInfo struct { ColorEyes int } -func (info *ClStartInfo) Pack() []byte { +func (info ClStartInfo) MsgId() int { + return network7.MsgGameClStartInfo +} + +func (info ClStartInfo) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (info ClStartInfo) System() bool { + return false +} + +func (info ClStartInfo) Vital() bool { + return true +} + +func (info ClStartInfo) Pack() []byte { return slices.Concat( packer.PackStr(info.Name), packer.PackStr(info.Clan), diff --git a/messages7/ctrl_connect.go b/messages7/ctrl_connect.go new file mode 100644 index 0000000..23d3db5 --- /dev/null +++ b/messages7/ctrl_connect.go @@ -0,0 +1,35 @@ +package messages7 + +import ( + "slices" + + "github.com/teeworlds-go/teeworlds/network7" +) + +type CtrlConnect struct { + Token [4]byte +} + +func (msg CtrlConnect) MsgId() int { + return network7.MsgCtrlConnect +} + +func (msg CtrlConnect) MsgType() network7.MsgType { + return network7.TypeControl +} + +func (msg CtrlConnect) System() bool { + return false +} + +func (msg CtrlConnect) Vital() bool { + return false +} + +func (msg CtrlConnect) Pack() []byte { + return slices.Concat( + []byte{network7.MsgCtrlConnect}, + msg.Token[:], + []byte{512: 0}, + ) +} diff --git a/messages7/ctrl_token.go b/messages7/ctrl_token.go new file mode 100644 index 0000000..724b2ea --- /dev/null +++ b/messages7/ctrl_token.go @@ -0,0 +1,38 @@ +package messages7 + +// this message is shared between client and server +// but this implementation is assuming we are sending from a client + +import ( + "slices" + + "github.com/teeworlds-go/teeworlds/network7" +) + +type CtrlToken struct { + Token [4]byte +} + +func (msg CtrlToken) MsgId() int { + return network7.MsgCtrlToken +} + +func (msg CtrlToken) MsgType() network7.MsgType { + return network7.TypeControl +} + +func (msg CtrlToken) System() bool { + return false +} + +func (msg CtrlToken) Vital() bool { + return false +} + +func (msg CtrlToken) Pack() []byte { + return slices.Concat( + []byte{network7.MsgCtrlToken}, + msg.Token[:], + []byte{512: 0}, + ) +} diff --git a/messages7/enter_game.go b/messages7/enter_game.go new file mode 100644 index 0000000..2572098 --- /dev/null +++ b/messages7/enter_game.go @@ -0,0 +1,28 @@ +package messages7 + +import ( + "github.com/teeworlds-go/teeworlds/network7" +) + +type EnterGame struct { +} + +func (msg EnterGame) MsgId() int { + return network7.MsgSysEnterGame +} + +func (msg EnterGame) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (msg EnterGame) System() bool { + return true +} + +func (msg EnterGame) Vital() bool { + return true +} + +func (msg EnterGame) Pack() []byte { + return []byte{} +} diff --git a/messages7/info.go b/messages7/info.go new file mode 100644 index 0000000..a60e2e8 --- /dev/null +++ b/messages7/info.go @@ -0,0 +1,33 @@ +package messages7 + +import ( + "github.com/teeworlds-go/teeworlds/network7" +) + +type Info struct { +} + +func (msg Info) MsgId() int { + return network7.MsgSysInfo +} + +func (info Info) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (msg Info) System() bool { + return true +} + +func (msg Info) Vital() bool { + return true +} + +func (msg Info) Pack() []byte { + return []byte{ + 0x30, 0x2E, 0x37, 0x20, 0x38, 0x30, 0x32, 0x66, + 0x31, 0x62, 0x65, 0x36, 0x30, 0x61, 0x30, 0x35, 0x36, 0x36, 0x35, 0x66, + 0x00, 0x6D, 0x79, 0x5F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, + 0x5F, 0x31, 0x32, 0x33, 0x00, 0x85, 0x1C, 0x00, + } +} diff --git a/messages7/net_message.go b/messages7/net_message.go new file mode 100644 index 0000000..3016c74 --- /dev/null +++ b/messages7/net_message.go @@ -0,0 +1,11 @@ +package messages7 + +import "github.com/teeworlds-go/teeworlds/network7" + +type NetMessage interface { + MsgId() int + MsgType() network7.MsgType + System() bool + Vital() bool + Pack() []byte +} diff --git a/messages7/ready.go b/messages7/ready.go new file mode 100644 index 0000000..e58fff5 --- /dev/null +++ b/messages7/ready.go @@ -0,0 +1,28 @@ +package messages7 + +import ( + "github.com/teeworlds-go/teeworlds/network7" +) + +type Ready struct { +} + +func (msg Ready) MsgId() int { + return network7.MsgSysReady +} + +func (msg Ready) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (msg Ready) System() bool { + return true +} + +func (msg Ready) Vital() bool { + return true +} + +func (msg Ready) Pack() []byte { + return []byte{} +} diff --git a/messages7/sv_client_info.go b/messages7/sv_client_info.go index 9b91f1c..d99a919 100644 --- a/messages7/sv_client_info.go +++ b/messages7/sv_client_info.go @@ -1,6 +1,9 @@ package messages7 -import "github.com/teeworlds-go/teeworlds/packer" +import ( + "github.com/teeworlds-go/teeworlds/network7" + "github.com/teeworlds-go/teeworlds/packer" +) type SvClientInfo struct { ClientId int @@ -29,6 +32,22 @@ type SvClientInfo struct { Silent bool } +func (info *SvClientInfo) MsgId() int { + return network7.MsgGameSvClientInfo +} + +func (info *SvClientInfo) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (info *SvClientInfo) System() bool { + return false +} + +func (info *SvClientInfo) Vital() bool { + return true +} + func (info *SvClientInfo) Unpack(u *packer.Unpacker) { info.ClientId = u.GetInt() info.Local = u.GetInt() != 0 diff --git a/network7/network7.go b/network7/network7.go index d676f89..c106ee4 100644 --- a/network7/network7.go +++ b/network7/network7.go @@ -3,22 +3,28 @@ package network7 const ( MaxClients = 64 - MsgCtrlKeepAlive ControlMsg = 0x00 - MsgCtrlConnect ControlMsg = 0x01 - MsgCtrlAccept ControlMsg = 0x02 - MsgCtrlToken ControlMsg = 0x05 - MsgCtrlClose ControlMsg = 0x04 + MsgCtrlKeepAlive = 0x00 + MsgCtrlConnect = 0x01 + MsgCtrlAccept = 0x02 + MsgCtrlToken = 0x05 + MsgCtrlClose = 0x04 - MsgSysMapChange NetMsg = 2 - MsgSysConReady NetMsg = 5 - MsgSysSnapSingle NetMsg = 8 + MsgSysInfo = 1 + MsgSysMapChange = 2 + MsgSysConReady = 5 + MsgSysSnapSingle = 8 + MsgSysReady = 18 + MsgSysEnterGame = 19 - MsgGameSvMotd NetMsg = 1 - MsgGameSvChat NetMsg = 3 - MsgGameReadyToEnter NetMsg = 8 - MsgGameSvClientInfo NetMsg = 18 - MsgGameClStartInfo NetMsg = 27 + MsgGameSvMotd = 1 + MsgGameSvChat = 3 + MsgGameReadyToEnter = 8 + MsgGameSvClientInfo = 18 + MsgGameClStartInfo = 27 + + TypeControl MsgType = 1 + TypeNet MsgType = 2 + TypeConnless MsgType = 3 ) -type ControlMsg int -type NetMsg int +type MsgType int diff --git a/packer/packer.go b/packer/packer.go index 5b51647..852d8fa 100644 --- a/packer/packer.go +++ b/packer/packer.go @@ -2,8 +2,6 @@ package packer import ( "slices" - - "github.com/teeworlds-go/teeworlds/network7" ) type Unpacker struct { @@ -127,10 +125,6 @@ func PackBool(b bool) []byte { return []byte{0x00} } -func PackMsg(msg network7.NetMsg) []byte { - return PackInt(int(msg)) -} - func PackInt(num int) []byte { res := []byte{0x00} idx := 0 diff --git a/protocol7/connection.go b/protocol7/connection.go index a1814bd..2ba7633 100644 --- a/protocol7/connection.go +++ b/protocol7/connection.go @@ -12,7 +12,6 @@ import ( "github.com/teeworlds-go/teeworlds/messages7" "github.com/teeworlds-go/teeworlds/network7" "github.com/teeworlds-go/teeworlds/packer" - "github.com/teeworlds-go/teeworlds/packet7" ) type Player struct { @@ -36,17 +35,38 @@ type Connection struct { Players []Player } -func (c *Connection) SendCtrlToken(myToken []byte) { - header := []byte{0x04, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff} - ctrlToken := append([]byte{0x05}, myToken...) - zeros := []byte{512: 0} - data := slices.Concat(header, ctrlToken, zeros) - c.Conn.Write(data) +func (connection *Connection) BuildResponse() *Packet { + return &Packet{ + Header: PacketHeader{ + Flags: PacketFlags{ + Connless: false, + Compression: false, + Resend: false, + Control: false, + }, + Ack: connection.Ack, + NumChunks: 0, // will be set in Packet.Pack() + Token: connection.ServerToken, + }, + } +} + +func (connection *Connection) CtrlToken() *Packet { + response := connection.BuildResponse() + response.Header.Flags.Control = true + response.Messages = append( + response.Messages, + messages7.CtrlToken{ + Token: connection.ClientToken, + }, + ) + + return response } func (c *Connection) SendCtrlMsg(data []byte) { - header := packet7.PacketHeader{ - Flags: packet7.PacketFlags{ + header := PacketHeader{ + Flags: PacketFlags{ Connless: false, Compression: false, Resend: false, @@ -65,75 +85,8 @@ func (c *Connection) SendKeepAlive() { c.SendCtrlMsg([]byte{byte(network7.MsgCtrlKeepAlive)}) } -func (c *Connection) SendReady() { - ready := []byte{0x40, 0x01, 0x02, 0x25} - - c.Sequence++ - c.SendPacket(ready, 1) -} - -type ChunkArgs struct { - MsgId network7.NetMsg - System bool - Flags chunk7.ChunkFlags - Payload []byte -} - -func (client *Connection) PackChunk(c ChunkArgs) []byte { - c.MsgId <<= 1 - if c.System { - c.MsgId |= 1 - } - - client.Sequence++ - msgAndSys := packer.PackMsg(c.MsgId) - - chunkHeader := chunk7.ChunkHeader{ - Flags: c.Flags, - Size: len(msgAndSys) + len(c.Payload), - Seq: client.Sequence, - } - - data := slices.Concat( - chunkHeader.Pack(), - msgAndSys, - c.Payload, - ) - - fmt.Printf("packed chunk: %x\n", data) - - return data -} - -func (client *Connection) SendPacket(payload []byte, numChunks int) { - header := packet7.PacketHeader{ - Flags: packet7.PacketFlags{ - Connless: false, - Compression: false, - Resend: false, - Control: false, - }, - Ack: client.Ack, - NumChunks: numChunks, - Token: client.ServerToken, - } - - packet := slices.Concat(header.Pack(), payload) - client.Conn.Write(packet) -} - -func (client *Connection) SendInfo() { - info := []byte{0x40, 0x28, 0x01, 0x03, 0x30, 0x2E, 0x37, 0x20, 0x38, 0x30, 0x32, 0x66, - 0x31, 0x62, 0x65, 0x36, 0x30, 0x61, 0x30, 0x35, 0x36, 0x36, 0x35, 0x66, - 0x00, 0x6D, 0x79, 0x5F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, - 0x5F, 0x31, 0x32, 0x33, 0x00, 0x85, 0x1C, 0x00} - - client.Sequence++ - client.SendPacket(info, 1) -} - -func (client *Connection) SendStartInfo() { - info := messages7.ClStartInfo{ +func (client *Connection) MsgStartInfo() messages7.ClStartInfo { + return messages7.ClStartInfo{ Name: "gopher", Clan: "", Country: 0, @@ -156,24 +109,6 @@ func (client *Connection) SendStartInfo() { ColorFeet: 0, ColorEyes: 0, } - - payload := client.PackChunk(ChunkArgs{ - MsgId: network7.MsgGameClStartInfo, - Flags: chunk7.ChunkFlags{ - Vital: true, - }, - Payload: info.Pack(), - }) - - client.SendPacket(payload, 1) -} - -func (client *Connection) SendEnterGame() { - enter := []byte{ - 0x40, 0x01, 0x04, 0x27, - } - - client.SendPacket(enter, 1) } func byteSliceToString(s []byte) string { @@ -184,17 +119,17 @@ func byteSliceToString(s []byte) string { return string(s) } -func (client *Connection) OnSystemMsg(msg network7.NetMsg, chunk chunk7.Chunk, u *packer.Unpacker) { +func (connection *Connection) OnSystemMsg(msg int, chunk chunk7.Chunk, u *packer.Unpacker, response *Packet) { if msg == network7.MsgSysMapChange { fmt.Println("got map change") - client.SendReady() + response.Messages = append(response.Messages, messages7.Ready{}) } else if msg == network7.MsgSysConReady { fmt.Println("got ready") - client.SendStartInfo() + response.Messages = append(response.Messages, connection.MsgStartInfo()) } else if msg == network7.MsgSysSnapSingle { // tick := u.GetInt() // fmt.Printf("got snap single tick=%d\n", tick) - client.SendKeepAlive() + connection.SendKeepAlive() } else { fmt.Printf("unknown system message id=%d data=%x\n", msg, chunk.Data) } @@ -209,10 +144,10 @@ func (client *Connection) OnMotd(motd string) { fmt.Printf("[motd] %s\n", motd) } -func (client *Connection) OnGameMsg(msg network7.NetMsg, chunk chunk7.Chunk, u *packer.Unpacker) { +func (client *Connection) OnGameMsg(msg int, chunk chunk7.Chunk, u *packer.Unpacker, response *Packet) { if msg == network7.MsgGameReadyToEnter { fmt.Println("got ready to enter") - client.SendEnterGame() + response.Messages = append(response.Messages, messages7.EnterGame{}) } else if msg == network7.MsgGameSvMotd { motd := u.GetString() if motd != "" { @@ -234,7 +169,7 @@ func (client *Connection) OnGameMsg(msg network7.NetMsg, chunk chunk7.Chunk, u * } } -func (client *Connection) OnMessage(chunk chunk7.Chunk) { +func (client *Connection) OnMessage(chunk chunk7.Chunk, response *Packet) { // fmt.Printf("got chunk size=%d data=%v\n", chunk.Header.Size, chunk.Data) if chunk.Header.Flags.Vital { @@ -250,36 +185,47 @@ func (client *Connection) OnMessage(chunk chunk7.Chunk) { msg >>= 1 if sys { - client.OnSystemMsg(network7.NetMsg(msg), chunk, &u) + client.OnSystemMsg(msg, chunk, &u, response) } else { - client.OnGameMsg(network7.NetMsg(msg), chunk, &u) + client.OnGameMsg(msg, chunk, &u, response) } } -func (client *Connection) OnPacketPayload(header []byte, data []byte) { +func (connection *Connection) OnPacketPayload(header []byte, data []byte, response *Packet) (*Packet, error) { chunks := chunk7.UnpackChunks(data) for _, c := range chunks { - client.OnMessage(c) + connection.OnMessage(c, response) } + return response, nil + } -func (client *Connection) OnPacket(data []byte) { - header := packet7.PacketHeader{} +// the response packet might be nil even if there is no error +func (connection *Connection) OnPacket(data []byte) (*Packet, error) { + header := PacketHeader{} headerRaw := data[:7] payload := data[7:] header.Unpack(headerRaw) + response := connection.BuildResponse() if header.Flags.Control { - ctrlMsg := network7.ControlMsg(payload[0]) + ctrlMsg := int(payload[0]) fmt.Printf("got ctrl msg %d\n", ctrlMsg) if ctrlMsg == network7.MsgCtrlToken { - copy(client.ServerToken[:], payload[1:5]) - fmt.Printf("got server token %x\n", client.ServerToken) - client.SendCtrlMsg(slices.Concat([]byte{byte(network7.MsgCtrlConnect)}, client.ClientToken[:])) + copy(connection.ServerToken[:], payload[1:5]) + response.Header.Token = connection.ServerToken + fmt.Printf("got server token %x\n", connection.ServerToken) + response.Messages = append( + response.Messages, + messages7.CtrlConnect{ + Token: connection.ClientToken, + }, + ) } else if ctrlMsg == network7.MsgCtrlAccept { fmt.Println("got accept") - client.SendInfo() + // TODO: don't hardcode info + response.Messages = append(response.Messages, messages7.Info{}) } else if ctrlMsg == network7.MsgCtrlClose { // TODO: get length from packet header to determine if a reason is set or not // len(data) -> is 1400 (maxPacketLen) @@ -291,7 +237,12 @@ func (client *Connection) OnPacket(data []byte) { } else { fmt.Printf("unknown control message: %x\n", data) } - return + + if len(response.Messages) == 0 { + return nil, nil + } + + return response, nil } if header.Flags.Compression { @@ -300,9 +251,10 @@ func (client *Connection) OnPacket(data []byte) { payload, err = huff.Decompress(payload) if err != nil { fmt.Printf("huffman error: %v\n", err) - return + return nil, nil } } - client.OnPacketPayload(headerRaw, payload) + response, err := connection.OnPacketPayload(headerRaw, payload, response) + return response, err } diff --git a/packet7/packet.go b/protocol7/packet.go similarity index 56% rename from packet7/packet.go rename to protocol7/packet.go index a9e9485..a710370 100644 --- a/packet7/packet.go +++ b/protocol7/packet.go @@ -1,6 +1,13 @@ -package packet7 +package protocol7 -import "slices" +import ( + "slices" + + "github.com/teeworlds-go/teeworlds/chunk7" + "github.com/teeworlds-go/teeworlds/messages7" + "github.com/teeworlds-go/teeworlds/network7" + "github.com/teeworlds-go/teeworlds/packer" +) const ( packetFlagControl = 1 @@ -26,6 +33,72 @@ type PacketHeader struct { ResponseToken [4]byte } +type Packet struct { + Header PacketHeader + Messages []messages7.NetMessage +} + +func PackChunk(msg messages7.NetMessage, connection *Connection) []byte { + if msg.MsgType() == network7.TypeControl { + return msg.Pack() + } + + msgId := msg.MsgId() << 1 + if msg.System() { + msgId |= 1 + } + + if msg.Vital() { + connection.Sequence++ + } + + msgAndSys := packer.PackInt(msgId) + payload := msg.Pack() + + // TODO: support resend + chunkHeader := chunk7.ChunkHeader{ + Flags: chunk7.ChunkFlags{ + Vital: msg.Vital(), + }, + Size: len(msgAndSys) + len(payload), + Seq: connection.Sequence, + } + + data := slices.Concat( + chunkHeader.Pack(), + msgAndSys, + payload, + ) + + return data +} + +func (packet *Packet) Pack(connection *Connection) []byte { + payload := []byte{} + control := false + + for _, msg := range packet.Messages { + payload = append(payload, PackChunk(msg, connection)...) + if msg.MsgType() == network7.TypeControl { + control = true + } + } + + packet.Header.NumChunks = len(packet.Messages) + + if control { + packet.Header.Flags.Connless = false + packet.Header.Flags.Compression = false + packet.Header.Flags.Resend = false + packet.Header.Flags.Control = true + } + + return slices.Concat( + packet.Header.Pack(), + payload, + ) +} + func (header *PacketHeader) Pack() []byte { flags := 0 if header.Flags.Control { diff --git a/packet7/packet_test.go b/protocol7/packet_test.go similarity index 99% rename from packet7/packet_test.go rename to protocol7/packet_test.go index 0d245d7..e6732e4 100644 --- a/packet7/packet_test.go +++ b/protocol7/packet_test.go @@ -1,4 +1,4 @@ -package packet7 +package protocol7 import ( "reflect" diff --git a/teeworlds.go b/teeworlds.go index 0cdcac0..cefc356 100644 --- a/teeworlds.go +++ b/teeworlds.go @@ -60,13 +60,20 @@ func main() { go readNetwork(ch, client.Conn) - client.SendCtrlToken([]byte{0xff, 0xff, 0xff, 0xff}) + packet := client.CtrlToken() + conn.Write(packet.Pack(client)) for { time.Sleep(10_000_000) select { case msg := <-ch: - client.OnPacket(msg) + packet, err = client.OnPacket(msg) + if err != nil { + panic(err) + } + if packet != nil { + conn.Write(packet.Pack(client)) + } default: // do nothing }