diff --git a/messages/game.go b/messages/game.go new file mode 100644 index 0000000..4e32a53 --- /dev/null +++ b/messages/game.go @@ -0,0 +1,57 @@ +package message + +import "github.com/teeworlds-go/teeworlds/packer" + +type SvClientInfo struct { + ClientId int + Local bool + Team int + Name string + Clan string + Country int + Body string + Marking string + Decoration string + Hands string + Feet string + Eyes string + CustomColorBody bool + CustomColorMarking bool + CustomColorDecoration bool + CustomColorHands bool + CustomColorFeet bool + CustomColorEyes bool + ColorBody int + ColorMarking int + ColorHands int + ColorFeet int + ColorEyes int + Silent bool +} + +func (info *SvClientInfo) Unpack(u *packer.Unpacker) { + info.ClientId = u.GetInt() + info.Local = u.GetInt() != 0 + info.Team = u.GetInt() + info.Name = u.GetString() + info.Clan = u.GetString() + info.Country = u.GetInt() + info.Body = u.GetString() + info.Marking = u.GetString() + info.Decoration = u.GetString() + info.Hands = u.GetString() + info.Feet = u.GetString() + info.Eyes = u.GetString() + info.CustomColorBody = u.GetInt() != 0 + info.CustomColorMarking = u.GetInt() != 0 + info.CustomColorDecoration = u.GetInt() != 0 + info.CustomColorHands = u.GetInt() != 0 + info.CustomColorFeet = u.GetInt() != 0 + info.CustomColorEyes = u.GetInt() != 0 + info.ColorBody = u.GetInt() + info.ColorMarking = u.GetInt() + info.ColorHands = u.GetInt() + info.ColorFeet = u.GetInt() + info.ColorEyes = u.GetInt() + info.Silent = u.GetInt() != 0 +} diff --git a/messages/game_test.go b/messages/game_test.go new file mode 100644 index 0000000..4305851 --- /dev/null +++ b/messages/game_test.go @@ -0,0 +1,76 @@ +package message + +import ( + "reflect" + "testing" + + "github.com/teeworlds-go/teeworlds/packer" +) + +func TestUnpackClientInfo(t *testing.T) { + u := packer.Unpacker{} + u.Reset([]byte{ + 0x00, 0x01, 0x00, 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, 0x00, + 0x00, 0x40, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x77, 0x61, 0x72, + 0x64, 0x00, 0x64, 0x75, 0x6f, 0x64, 0x6f, 0x6e, 0x6e, 0x79, 0x00, + 0x00, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x00, 0x73, + 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x00, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x72, 0x64, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xfc, 0xaf, 0x05, 0xeb, 0x83, 0xd0, 0x0a, 0x80, 0xfe, + 0x07, 0x80, 0xfe, 0x07, 0x80, 0xfe, 0x07, 0x80, 0xfe, 0x07, 0x00, + }) + + info := SvClientInfo{} + info.Unpack(&u) + + { + got := info.Eyes + want := "standard" + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + + got = info.Decoration + want = "" + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + + got = info.Marking + want = "duodonny" + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + } + + wantedInfo := SvClientInfo{ + ClientId: 0, + Local: true, + Team: 0, + Name: "gopher", + Clan: "", + Country: -1, + Body: "greensward", + Marking: "duodonny", + Decoration: "", + Hands: "standard", + Feet: "standard", + Eyes: "standard", + CustomColorBody: true, + CustomColorMarking: true, + CustomColorDecoration: false, + CustomColorHands: false, + CustomColorFeet: false, + CustomColorEyes: false, + ColorBody: 5635840, + ColorMarking: -11141356, + ColorHands: 65408, + ColorFeet: 65408, + ColorEyes: 65408, + Silent: true, + } + + if !reflect.DeepEqual(info, wantedInfo) { + t.Errorf("got %v, wanted %v", info, wantedInfo) + } +} diff --git a/packer/packer.go b/packer/packer.go index 83b186c..0734dfd 100644 --- a/packer/packer.go +++ b/packer/packer.go @@ -26,6 +26,10 @@ func (u *Unpacker) getByte() byte { return b } +func (u *Unpacker) Data() []byte { + return u.data +} + const ( Sanitize = 1 SanitizeCC = 2 diff --git a/packer/packer_state_test.go b/packer/packer_state_test.go index c1b411f..24c9d04 100644 --- a/packer/packer_state_test.go +++ b/packer/packer_state_test.go @@ -5,6 +5,79 @@ import ( "testing" ) +func TestUnpackClientInfo(t *testing.T) { + u := Unpacker{} + u.Reset([]byte{ + 0x24, 0x00, 0x01, 0x00, 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, 0x00, + 0x00, 0x40, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x77, 0x61, 0x72, + 0x64, 0x00, 0x64, 0x75, 0x6f, 0x64, 0x6f, 0x6e, 0x6e, 0x79, 0x00, + 0x00, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x00, 0x73, + 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x00, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x72, 0x64, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xfc, 0xaf, 0x05, 0xeb, 0x83, 0xd0, 0x0a, 0x80, 0xfe, + 0x07, 0x80, 0xfe, 0x07, 0x80, 0xfe, 0x07, 0x80, 0xfe, 0x07, 0x00, + }) + + { + // message id + got := u.GetInt() + want := 36 + + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + + // client id + got = u.GetInt() + want = 0 + + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + + u.GetInt() // Local bool + u.GetInt() // Team int + } + + { + // name + got := u.GetString() + want := "gopher" + + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + + // clan + got = u.GetString() + want = "" + + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + } + + { + // country + got := u.GetInt() + want := -1 + + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + } + + { + // body + got := u.GetString() + want := "greensward" + + if got != want { + t.Errorf("got %v, wanted %v", got, want) + } + } +} + // unpack with state func TestUnpackSimpleInts(t *testing.T) { diff --git a/teeworlds.go b/teeworlds.go index 281eab3..cbf0e8f 100644 --- a/teeworlds.go +++ b/teeworlds.go @@ -11,6 +11,7 @@ import ( "github.com/teeworlds-go/huffman" "github.com/teeworlds-go/teeworlds/chunk" + message "github.com/teeworlds-go/teeworlds/messages" "github.com/teeworlds-go/teeworlds/packer" "github.com/teeworlds-go/teeworlds/packet" ) @@ -18,6 +19,8 @@ import ( const ( maxPacksize = 1400 + MaxClients = 64 + msgCtrlKeepAlive = 0x00 msgCtrlConnect = 0x01 msgCtrlAccept = 0x02 @@ -28,9 +31,10 @@ const ( msgSysConReady = 5 msgSysSnapSingle = 8 - msgGameMotd = 1 + msgGameSvMotd = 1 msgGameSvChat = 3 msgGameReadyToEnter = 8 + msgGameSvClientInfo = 18 ) func ctrlToken(myToken []byte) []byte { @@ -67,12 +71,18 @@ func readNetwork(ch chan<- []byte, conn net.Conn) { conn.Close() } +type Player struct { + info message.SvClientInfo +} + type TeeworldsClient struct { clientToken [4]byte serverToken [4]byte conn net.Conn Ack int + + Players []Player } func (client TeeworldsClient) sendCtrlMsg(data []byte) { @@ -204,20 +214,35 @@ func (client *TeeworldsClient) onSystemMsg(msg int, chunk chunk.Chunk, u *packer } } -func onChatMessage(mode int, clientId int, targetId int, message string) { - fmt.Printf("[chat] %d: %s\n", clientId, message) +func (client *TeeworldsClient) OnChatMessage(mode int, clientId int, targetId int, message string) { + name := client.Players[clientId].info.Name + fmt.Printf("[chat] <%s> %s\n", name, message) +} + +func (client *TeeworldsClient) OnMotd(motd string) { + fmt.Printf("[motd] %s\n", motd) } func (client *TeeworldsClient) onGameMsg(msg int, chunk chunk.Chunk, u *packer.Unpacker) { if msg == msgGameReadyToEnter { fmt.Println("got ready to enter") client.sendEnterGame() + } else if msg == msgGameSvMotd { + motd := u.GetString() + if motd != "" { + client.OnMotd(motd) + } } else if msg == msgGameSvChat { mode := u.GetInt() clientId := u.GetInt() targetId := u.GetInt() message := u.GetString() - onChatMessage(mode, clientId, targetId, message) + client.OnChatMessage(mode, clientId, targetId, message) + } else if msg == msgGameSvClientInfo { + clientId := packer.UnpackInt(chunk.Data[1:]) + client.Players[clientId].info.Unpack(u) + + fmt.Printf("got client info id=%d name=%s\n", clientId, client.Players[clientId].info.Name) } else { fmt.Printf("unknown game message id=%d data=%x\n", msg, chunk.Data) } @@ -309,6 +334,8 @@ func main() { clientToken: [4]byte{0x01, 0x02, 0x03, 0x04}, serverToken: [4]byte{0xff, 0xff, 0xff, 0xff}, conn: conn, + Ack: 0, + Players: make([]Player, MaxClients), } go readNetwork(ch, client.conn)