diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f6a943d --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,28 @@ +name: Go + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.22' + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... + + - name: Format + run: diff -u <(echo -n) <(gofmt -d ./) diff --git a/.gitignore b/.gitignore index be8b570..2739162 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ teeworlds_client +teeworlds + diff --git a/go.mod b/go.mod index aaad7a9..b4c6411 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/teeworlds-go/teeworlds go 1.22.3 + +require github.com/teeworlds-go/huffman v1.0.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3447da3 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/teeworlds-go/huffman v1.0.0 h1:XSNMNAJZb+njNrPACsxcDrrLDXTGjZZt35FqLAuHi4I= +github.com/teeworlds-go/huffman v1.0.0/go.mod h1:kjaXpL6C6xL7CM+tWPNYjdEgVZB2GumKhx7rCDdXArU= diff --git a/packet.go b/packet.go deleted file mode 100644 index 060362a..0000000 --- a/packet.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -const ( - packetFlagControl = 1 - packetFlagResend = 2 - packetFlagCompression = 4 - packetFlagConnless = 8 -) - -type PacketFlags struct { - connless bool - compression bool - resend bool - control bool -} - -type PacketHeader struct { - flags PacketFlags - ack int - numChunks int - token [4]byte -} - -func (header *PacketHeader) unpack(packet []byte) { - header.flags.unpack(packet) - header.ack = (int(packet[0]&0x3) << 8) | int(packet[1]) - header.numChunks = int(packet[2]) - copy(header.token[:], packet[3:7]) -} - -func (flags *PacketFlags) unpack(packetHeaderRaw []byte) { - flagBits := packetHeaderRaw[0] >> 2 - flags.control = (flagBits & packetFlagControl) != 0 - flags.resend = (flagBits & packetFlagResend) != 0 - flags.compression = (flagBits & packetFlagCompression) != 0 - flags.connless = (flagBits & packetFlagConnless) != 0 -} - -func (flags *PacketFlags) pack() []byte { - data := 0 - - if flags.control { - data |= packetFlagControl - } - if flags.resend { - data |= packetFlagResend - } - if flags.compression { - data |= packetFlagCompression - } - if flags.connless { - data |= packetFlagConnless - } - - return []byte{byte(data)} -} diff --git a/packet/packet.go b/packet/packet.go new file mode 100644 index 0000000..0abbe98 --- /dev/null +++ b/packet/packet.go @@ -0,0 +1,56 @@ +package packet + +const ( + packetFlagControl = 1 + packetFlagResend = 2 + packetFlagCompression = 4 + packetFlagConnless = 8 +) + +type PacketFlags struct { + Connless bool + Compression bool + Resend bool + Control bool +} + +type PacketHeader struct { + Flags PacketFlags + Ack int + NumChunks int + Token [4]byte +} + +func (header *PacketHeader) Unpack(packet []byte) { + header.Flags.Unpack(packet) + header.Ack = (int(packet[0]&0x3) << 8) | int(packet[1]) + header.NumChunks = int(packet[2]) + copy(header.Token[:], packet[3:7]) +} + +func (flags *PacketFlags) Unpack(packetHeaderRaw []byte) { + flagBits := packetHeaderRaw[0] >> 2 + flags.Control = (flagBits & packetFlagControl) != 0 + flags.Resend = (flagBits & packetFlagResend) != 0 + flags.Compression = (flagBits & packetFlagCompression) != 0 + flags.Connless = (flagBits & packetFlagConnless) != 0 +} + +func (flags *PacketFlags) Pack() []byte { + data := 0 + + if flags.Control { + data |= packetFlagControl + } + if flags.Resend { + data |= packetFlagResend + } + if flags.Compression { + data |= packetFlagCompression + } + if flags.Connless { + data |= packetFlagConnless + } + + return []byte{byte(data)} +} diff --git a/packet_test.go b/packet/packet_test.go similarity index 55% rename from packet_test.go rename to packet/packet_test.go index c6e50dc..98c4f6a 100644 --- a/packet_test.go +++ b/packet/packet_test.go @@ -1,4 +1,4 @@ -package main +package packet import ( "reflect" @@ -10,13 +10,13 @@ import ( func TestPackFlagsUnset(t *testing.T) { flags := PacketFlags{ - connless: false, - compression: false, - resend: false, - control: false, + Connless: false, + Compression: false, + Resend: false, + Control: false, } - got := flags.pack() + got := flags.Pack() want := []byte{0b0000} if !reflect.DeepEqual(got, want) { @@ -26,13 +26,13 @@ func TestPackFlagsUnset(t *testing.T) { func TestPackFlagsCompressionSet(t *testing.T) { flags := PacketFlags{ - connless: false, - compression: true, - resend: false, - control: false, + Connless: false, + Compression: true, + Resend: false, + Control: false, } - got := flags.pack() + got := flags.Pack() want := []byte{0b0100} if !reflect.DeepEqual(got, want) { @@ -42,13 +42,13 @@ func TestPackFlagsCompressionSet(t *testing.T) { func TestPackFlagsAllSet(t *testing.T) { flags := PacketFlags{ - connless: true, - compression: true, - resend: true, - control: true, + Connless: true, + Compression: true, + Resend: true, + Control: true, } - got := flags.pack() + got := flags.Pack() want := []byte{0b1111} if !reflect.DeepEqual(got, want) { @@ -61,13 +61,13 @@ func TestPackFlagsAllSet(t *testing.T) { func TestUnpackFlagsAllSet(t *testing.T) { got := PacketFlags{} want := PacketFlags{ - connless: true, - compression: true, - resend: true, - control: true, + Connless: true, + Compression: true, + Resend: true, + Control: true, } - got.unpack([]byte{0b00111100}) + got.Unpack([]byte{0b00111100}) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, wanted %v", got, want) @@ -77,13 +77,13 @@ func TestUnpackFlagsAllSet(t *testing.T) { func TestUnpackFlagsControlSet(t *testing.T) { got := PacketFlags{} want := PacketFlags{ - connless: false, - compression: false, - resend: false, - control: true, + Connless: false, + Compression: false, + Resend: false, + Control: true, } - got.unpack([]byte{0b00000100}) + got.Unpack([]byte{0b00000100}) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, wanted %v", got, want) @@ -93,13 +93,13 @@ func TestUnpackFlagsControlSet(t *testing.T) { func TestUnpackFlagsUnset(t *testing.T) { got := PacketFlags{} want := PacketFlags{ - connless: false, - compression: false, - resend: false, - control: false, + Connless: false, + Compression: false, + Resend: false, + Control: false, } - got.unpack([]byte{0b00000000}) + got.Unpack([]byte{0b00000000}) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, wanted %v", got, want) @@ -111,18 +111,18 @@ func TestUnpackFlagsUnset(t *testing.T) { func TestUnpackCloseWithReason(t *testing.T) { got := PacketHeader{} want := PacketHeader{ - flags: PacketFlags{ - connless: false, - compression: false, - resend: false, - control: true, + Flags: PacketFlags{ + Connless: false, + Compression: false, + Resend: false, + Control: true, }, - ack: 10, - numChunks: 0, - token: [4]byte{0xcf, 0x2e, 0xde, 0x1d}, + Ack: 10, + NumChunks: 0, + Token: [4]byte{0xcf, 0x2e, 0xde, 0x1d}, } - got.unpack(slices.Concat([]byte{0x04, 0x0a, 0x00, 0xcf, 0x2e, 0xde, 0x1d, 0x04}, []byte("shutdown"), []byte{0x00})) + got.Unpack(slices.Concat([]byte{0x04, 0x0a, 0x00, 0xcf, 0x2e, 0xde, 0x1d, 0x04}, []byte("shutdown"), []byte{0x00})) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, wanted %v", got, want) @@ -132,18 +132,18 @@ func TestUnpackCloseWithReason(t *testing.T) { func TestUnpackHeaderFlagsControlSet(t *testing.T) { got := PacketHeader{} want := PacketHeader{ - flags: PacketFlags{ - connless: false, - compression: false, - resend: false, - control: true, + Flags: PacketFlags{ + Connless: false, + Compression: false, + Resend: false, + Control: true, }, - ack: 0, - numChunks: 0, - token: [4]byte{0xff, 0xff, 0xff, 0xff}, + Ack: 0, + NumChunks: 0, + Token: [4]byte{0xff, 0xff, 0xff, 0xff}, } - got.unpack([]byte{0b00000100, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}) + got.Unpack([]byte{0b00000100, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, wanted %v", got, want) @@ -153,18 +153,18 @@ func TestUnpackHeaderFlagsControlSet(t *testing.T) { func TestUnpackHeaderFlagsAllSet(t *testing.T) { got := PacketHeader{} want := PacketHeader{ - flags: PacketFlags{ - connless: true, - compression: true, - resend: true, - control: true, + Flags: PacketFlags{ + Connless: true, + Compression: true, + Resend: true, + Control: true, }, - ack: 0, - numChunks: 0, - token: [4]byte{0xff, 0xff, 0xff, 0xff}, + Ack: 0, + NumChunks: 0, + Token: [4]byte{0xff, 0xff, 0xff, 0xff}, } - got.unpack([]byte{0b00111100, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}) + got.Unpack([]byte{0b00111100, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff}) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, wanted %v", got, want) diff --git a/teeworlds.go b/teeworlds.go index b6ea3ba..52813a2 100644 --- a/teeworlds.go +++ b/teeworlds.go @@ -8,6 +8,9 @@ import ( "os" "slices" "time" + + "github.com/teeworlds-go/huffman" + "github.com/teeworlds-go/teeworlds/packet" ) const ( @@ -160,7 +163,10 @@ func byteSliceToString(s []byte) string { } func (client *TeeworldsClient) onMessage(data []byte) { - if isCtrlMsg(data) { + header := packet.PacketHeader{} + header.Unpack(data) + + if header.Flags.Control { ctrlMsg := data[7] fmt.Printf("got ctrl msg %d\n", ctrlMsg) if ctrlMsg == msgCtrlToken { @@ -181,10 +187,19 @@ func (client *TeeworldsClient) onMessage(data []byte) { } else { fmt.Printf("unknown control message: %x\n", data) } + } else if header.Flags.Compression { + payload := data[8:] + fmt.Printf("got compressed data: %v\n", payload) + huff := huffman.Huffman{} + decompressed, err := huff.Decompress(payload) + if err != nil { + fmt.Printf("huffman error: %v\n", err) + return + } + fmt.Printf("got decompressed: %v\n", decompressed) } else if isMapChange(data) { fmt.Println("got map change") client.sendReady() - } else if isConReady(data) { fmt.Println("got ready") client.sendStartInfo()