diff --git a/messages7/con_ready.go b/messages7/con_ready.go new file mode 100644 index 0000000..2acc457 --- /dev/null +++ b/messages7/con_ready.go @@ -0,0 +1,42 @@ +package messages7 + +import ( + "github.com/teeworlds-go/teeworlds/chunk7" + "github.com/teeworlds-go/teeworlds/network7" + "github.com/teeworlds-go/teeworlds/packer" +) + +type ConReady struct { + ChunkHeader *chunk7.ChunkHeader +} + +func (msg ConReady) MsgId() int { + return network7.MsgSysConReady +} + +func (msg ConReady) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (msg ConReady) System() bool { + return true +} + +func (msg ConReady) Vital() bool { + return true +} + +func (msg ConReady) Pack() []byte { + return []byte{} +} + +func (msg *ConReady) Unpack(u *packer.Unpacker) { +} + +func (msg *ConReady) Header() *chunk7.ChunkHeader { + return msg.ChunkHeader +} + +func (msg *ConReady) SetHeader(header *chunk7.ChunkHeader) { + msg.ChunkHeader = header +} diff --git a/messages7/map_change.go b/messages7/map_change.go new file mode 100644 index 0000000..f0416e7 --- /dev/null +++ b/messages7/map_change.go @@ -0,0 +1,64 @@ +package messages7 + +import ( + "slices" + + "github.com/teeworlds-go/teeworlds/chunk7" + "github.com/teeworlds-go/teeworlds/network7" + "github.com/teeworlds-go/teeworlds/packer" +) + +type MapChange struct { + ChunkHeader *chunk7.ChunkHeader + + Name string + Crc int + Size int + NumResponseChunksPerRequest int + ChunkSize int + Sha256 [32]byte +} + +func (msg MapChange) MsgId() int { + return network7.MsgSysMapChange +} + +func (msg MapChange) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (msg MapChange) System() bool { + return true +} + +func (msg MapChange) Vital() bool { + return true +} + +func (msg MapChange) Pack() []byte { + return slices.Concat( + packer.PackStr(msg.Name), + packer.PackInt(msg.Crc), + packer.PackInt(msg.Size), + packer.PackInt(msg.NumResponseChunksPerRequest), + packer.PackInt(msg.ChunkSize), + msg.Sha256[:], + ) +} + +func (msg *MapChange) Unpack(u *packer.Unpacker) { + msg.Name = u.GetString() + msg.Crc = u.GetInt() + msg.Size = u.GetInt() + msg.NumResponseChunksPerRequest = u.GetInt() + msg.ChunkSize = u.GetInt() + msg.Sha256 = [32]byte(u.Rest()) +} + +func (msg *MapChange) Header() *chunk7.ChunkHeader { + return msg.ChunkHeader +} + +func (msg *MapChange) SetHeader(header *chunk7.ChunkHeader) { + msg.ChunkHeader = header +} diff --git a/messages7/sv_client_info.go b/messages7/sv_client_info.go index b5dde22..152057a 100644 --- a/messages7/sv_client_info.go +++ b/messages7/sv_client_info.go @@ -1,6 +1,8 @@ package messages7 import ( + "slices" + "github.com/teeworlds-go/teeworlds/chunk7" "github.com/teeworlds-go/teeworlds/network7" "github.com/teeworlds-go/teeworlds/packer" @@ -51,6 +53,35 @@ func (info *SvClientInfo) Vital() bool { return true } +func (msg SvClientInfo) Pack() []byte { + return slices.Concat( + packer.PackInt(msg.ClientId), + packer.PackBool(msg.Local), + packer.PackInt(msg.Team), + packer.PackStr(msg.Name), + packer.PackStr(msg.Clan), + packer.PackInt(msg.Country), + packer.PackStr(msg.Body), + packer.PackStr(msg.Marking), + packer.PackStr(msg.Decoration), + packer.PackStr(msg.Hands), + packer.PackStr(msg.Feet), + packer.PackStr(msg.Eyes), + packer.PackBool(msg.CustomColorBody), + packer.PackBool(msg.CustomColorMarking), + packer.PackBool(msg.CustomColorDecoration), + packer.PackBool(msg.CustomColorHands), + packer.PackBool(msg.CustomColorFeet), + packer.PackBool(msg.CustomColorEyes), + packer.PackInt(msg.ColorBody), + packer.PackInt(msg.ColorMarking), + packer.PackInt(msg.ColorHands), + packer.PackInt(msg.ColorFeet), + packer.PackInt(msg.ColorEyes), + packer.PackBool(msg.Silent), + ) +} + func (info *SvClientInfo) Unpack(u *packer.Unpacker) { info.ClientId = u.GetInt() info.Local = u.GetInt() != 0 diff --git a/messages7/sv_motd.go b/messages7/sv_motd.go new file mode 100644 index 0000000..51773ac --- /dev/null +++ b/messages7/sv_motd.go @@ -0,0 +1,45 @@ +package messages7 + +import ( + "github.com/teeworlds-go/teeworlds/chunk7" + "github.com/teeworlds-go/teeworlds/network7" + "github.com/teeworlds-go/teeworlds/packer" +) + +type SvMotd struct { + ChunkHeader *chunk7.ChunkHeader + + Message string +} + +func (msg SvMotd) MsgId() int { + return network7.MsgGameSvMotd +} + +func (msg SvMotd) MsgType() network7.MsgType { + return network7.TypeNet +} + +func (msg SvMotd) System() bool { + return false +} + +func (msg SvMotd) Vital() bool { + return true +} + +func (msg SvMotd) Pack() []byte { + return packer.PackStr(msg.Message) +} + +func (msg *SvMotd) Unpack(u *packer.Unpacker) { + msg.Message = u.GetString() +} + +func (msg *SvMotd) Header() *chunk7.ChunkHeader { + return msg.ChunkHeader +} + +func (msg *SvMotd) SetHeader(header *chunk7.ChunkHeader) { + msg.ChunkHeader = header +} diff --git a/protocol7/packet.go b/protocol7/packet.go index fd0dbb0..0d7e0d7 100644 --- a/protocol7/packet.go +++ b/protocol7/packet.go @@ -80,6 +80,118 @@ func PackChunk(msg messages7.NetMessage, connection *Connection) []byte { return data } +func (packet *Packet) unpackSystem(msgId int, chunk chunk7.Chunk, u *packer.Unpacker) bool { + if msgId == network7.MsgSysMapChange { + msg := &messages7.MapChange{ChunkHeader: &chunk.Header} + msg.Unpack(u) + packet.Messages = append(packet.Messages, msg) + } else if msgId == network7.MsgSysConReady { + msg := &messages7.ConReady{ChunkHeader: &chunk.Header} + msg.Unpack(u) + packet.Messages = append(packet.Messages, msg) + } else { + return false + } + // packet.Messages[len(packet.Messages)-1].SetHeader(&chunk.Header) + return true +} + +func (packet *Packet) unpackGame(msgId int, chunk chunk7.Chunk, u *packer.Unpacker) bool { + if msgId == network7.MsgGameReadyToEnter { + msg := &messages7.Ready{ChunkHeader: &chunk.Header} + msg.Unpack(u) + packet.Messages = append(packet.Messages, msg) + } else if msgId == network7.MsgGameSvMotd { + msg := &messages7.SvMotd{ChunkHeader: &chunk.Header} + msg.Unpack(u) + packet.Messages = append(packet.Messages, msg) + } else if msgId == network7.MsgGameSvChat { + msg := &messages7.SvChat{ChunkHeader: &chunk.Header} + msg.Unpack(u) + packet.Messages = append(packet.Messages, msg) + } else if msgId == network7.MsgGameSvClientInfo { + msg := &messages7.SvClientInfo{ChunkHeader: &chunk.Header} + msg.Unpack(u) + packet.Messages = append(packet.Messages, msg) + } else { + return false + } + return true +} + +func (packet *Packet) unpackChunk(chunk chunk7.Chunk) bool { + u := packer.Unpacker{} + u.Reset(chunk.Data) + + msg := u.GetInt() + + sys := msg&1 != 0 + msg >>= 1 + + if sys { + return packet.unpackSystem(msg, chunk, &u) + } + return packet.unpackGame(msg, chunk, &u) +} + +func (packet *Packet) unpackPayload(payload []byte) { + chunks := chunk7.UnpackChunks(payload) + + for _, c := range chunks { + if packet.unpackChunk(c) == false { + unknown := &messages7.Unknown{ + Data: slices.Concat(c.Header.Pack(), c.Data), + Type: network7.TypeNet, + } + packet.Messages = append(packet.Messages, unknown) + } + } +} + +// Only returns an error if there is invalid huffman compression applied +// Unknown messages will be unpacked as messages7.Unknown +// There is no validation no messages will be dropped +func (packet *Packet) Unpack(data []byte) error { + packet.Header.Unpack(data[:7]) + payload := data[7:] + + if packet.Header.Flags.Control { + unpacker := packer.Unpacker{} + unpacker.Reset(payload) + ctrlMsg := unpacker.GetInt() + if ctrlMsg == network7.MsgCtrlToken { + msg := &messages7.CtrlToken{} + msg.Unpack(&unpacker) + packet.Messages = append(packet.Messages, msg) + } else if ctrlMsg == network7.MsgCtrlAccept { + packet.Messages = append(packet.Messages, &messages7.CtrlAccept{}) + } else if ctrlMsg == network7.MsgCtrlClose { + msg := &messages7.CtrlClose{} + msg.Unpack(&unpacker) + packet.Messages = append(packet.Messages, msg) + } else { + unknown := &messages7.Unknown{ + Data: payload, + Type: network7.TypeControl, + } + packet.Messages = append(packet.Messages, unknown) + } + return nil + } + + if packet.Header.Flags.Compression { + huff := huffman.Huffman{} + var err error + payload, err = huff.Decompress(payload) + if err != nil { + return err + } + } + + packet.unpackPayload(payload) + return nil +} + func (packet *Packet) Pack(connection *Connection) []byte { payload := []byte{} control := false