From 7a9844f977cc7b0bf38c689626a885fb4b28ba07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Monta=C3=B1es=20Ojados?= Date: Thu, 15 Jan 2026 17:14:39 +0100 Subject: [PATCH] feat: Implement QuickLZ decompression, fragment reassembly, and voice echo --- internal/client/client.go | 62 +++++++++++++++++++++++++++------------ tsdeclarations | 1 + 2 files changed, 45 insertions(+), 18 deletions(-) create mode 160000 tsdeclarations diff --git a/internal/client/client.go b/internal/client/client.go index 43e87b4..7f44671 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -576,31 +576,57 @@ func (c *Client) getCryptoState() (key, nonce, mac []byte, isHandshake bool) { // This starts AFTER clientek? Or WITH clientek? "clientek already has the packet id 1" func (c *Client) handleVoice(pkt *protocol.Packet) { - // Parse Voice Header - // 2 bytes VID, 1 byte Codec, Data - if len(pkt.Data) < 3 { + // Parse Voice Header (Server -> Client) + // VID(2) + CID(2) + Codec(1) + Data + if len(pkt.Data) < 5 { return } - // vid := binary.BigEndian.Uint16(pkt.Data[0:2]) - // codec := pkt.Data[2] - voiceData := pkt.Data[3:] + vid := binary.BigEndian.Uint16(pkt.Data[0:2]) + // cid := binary.BigEndian.Uint16(pkt.Data[2:4]) // Talking client ID (not needed for echo) + codec := pkt.Data[4] + voiceData := pkt.Data[5:] - // Calculate "Volume" (RMS of encrypted/compressed data is meaningless, but existing requirement asks for it) - // To do this properly, we need to decrypt -> decode[Opus] -> PCM -> RMS. - // For "Eco" (Echo), we can just re-wrap this data and send it back. + log.Printf("Voice Packet received. VID=%d, Codec=%d, Size=%d", vid, codec, len(voiceData)) - vol := len(voiceData) // Placeholder "volume" - log.Printf("Voice Packet received. Approx Size/Vol: %d", vol) + // Build echo packet (Client -> Server) + // Format: VID(2) + Codec(1) + Data + echoData := make([]byte, 2+1+len(voiceData)) + binary.BigEndian.PutUint16(echoData[0:2], vid) + echoData[2] = codec + copy(echoData[3:], voiceData) - // Echo back - // Client -> Server Voice Packet - // VID + Codec + Data - // We can reuse the data payload structure mostly? - // C->S: VID(2) + Codec(1) + Data + echoPkt := protocol.NewPacket(protocol.PacketTypeVoice, echoData) + echoPkt.Header.PacketID = pkt.Header.PacketID // Use same ID for voice + echoPkt.Header.ClientID = c.ClientID + + // Encrypt voice packet with SharedSecret + if c.Handshake != nil && len(c.Handshake.SharedIV) > 0 { + crypto := &protocol.CryptoState{ + SharedIV: c.Handshake.SharedIV, + SharedMac: c.Handshake.SharedMac, + GenerationID: 0, + } + key, nonce := crypto.GenerateKeyNonce(&echoPkt.Header, true) + + // Meta for Client->Server: PID(2) + CID(2) + PT(1) + meta := make([]byte, 5) + binary.BigEndian.PutUint16(meta[0:2], echoPkt.Header.PacketID) + binary.BigEndian.PutUint16(meta[2:4], echoPkt.Header.ClientID) + meta[4] = echoPkt.Header.Type + + encData, mac, err := protocol.EncryptEAX(key, nonce, meta, echoPkt.Data) + if err != nil { + log.Printf("Voice encryption failed: %v", err) + return + } + echoPkt.Data = encData + copy(echoPkt.Header.MAC[:], mac) + } else { + // If no encryption keys, use SharedMac + echoPkt.Header.MAC = protocol.HandshakeMac + } - echoPkt := protocol.NewPacket(protocol.PacketTypeVoice, pkt.Data) - // ID Counter handling? c.Conn.SendPacket(echoPkt) } diff --git a/tsdeclarations b/tsdeclarations new file mode 160000 index 0000000..fb4e50d --- /dev/null +++ b/tsdeclarations @@ -0,0 +1 @@ +Subproject commit fb4e50d643d49aed112298da702ae4513b3997fc