package transport import ( "fmt" "net" "sync" "time" "go-ts/pkg/protocol" ) // TS3Conn handles the UDP connection to the TeamSpeak server. type TS3Conn struct { conn *net.UDPConn readQueue chan *protocol.Packet closeChan chan struct{} wg sync.WaitGroup writeMu sync.Mutex } // NewTS3Conn creates a new connection to the specified address. func NewTS3Conn(address string) (*TS3Conn, error) { raddr, err := net.ResolveUDPAddr("udp", address) if err != nil { return nil, fmt.Errorf("failed to resolve address: %w", err) } conn, err := net.DialUDP("udp", nil, raddr) if err != nil { return nil, fmt.Errorf("failed to dial UDP: %w", err) } ts3c := &TS3Conn{ conn: conn, readQueue: make(chan *protocol.Packet, 100), closeChan: make(chan struct{}), } ts3c.startReader() return ts3c, nil } func (c *TS3Conn) startReader() { c.wg.Add(1) go func() { defer c.wg.Done() buf := make([]byte, 2048) // Max packet size should be around 500, but use larger buffer to be safe for { select { case <-c.closeChan: return default: c.conn.SetReadDeadline(time.Now().Add(1 * time.Second)) n, _, err := c.conn.ReadFromUDP(buf) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { continue } // If closed, return select { case <-c.closeChan: return default: fmt.Printf("Error reading valid UDP packet: %v\n", err) continue } } // Parse packet pkt, err := protocol.Decode(buf[:n], false) // Server -> Client if err != nil { fmt.Printf("Failed to decode packet: %v\n", err) continue } select { case c.readQueue <- pkt: default: fmt.Println("Read queue full, dropping packet") } } } }() } // SendPacket sends a packet to the server. func (c *TS3Conn) SendPacket(pkt *protocol.Packet) error { c.writeMu.Lock() defer c.writeMu.Unlock() // Client -> Server bytes, err := pkt.Encode(true) if err != nil { return err } _, err = c.conn.Write(bytes) return err } // ReadPacket returns the next received packet channel. func (c *TS3Conn) PacketChan() <-chan *protocol.Packet { return c.readQueue } // RemoteAddr returns the remote network address. func (c *TS3Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } // Close closes the connection. func (c *TS3Conn) Close() error { close(c.closeChan) c.conn.Close() c.wg.Wait() return nil }