From 819ed6931d2eed7c593fb2431d897ac2fecd41ce Mon Sep 17 00:00:00 2001 From: "Brandon McGinty (deepseek)" Date: Sun, 9 Aug 2026 00:30:27 -0400 Subject: [PATCH] Add -tcp flag to disable UDP and force audio through TCP tunnel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Config.DisableUDP field. When set: - DialWithDialer skips startUDP() entirely (no UDP socket, no UDP reader) - WriteAudio skips WriteAudioUDP and goes straight to TCP-tunneled audio Useful for debugging crypto/transport issues — rules out UDP-specific problems like NAT, firewall, or OCB2 counter mismatches. --- gumble/gumble/client.go | 28 ++++++++++++++++++---------- gumble/gumble/config.go | 3 +++ main.go | 2 ++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/gumble/gumble/client.go b/gumble/gumble/client.go index 2c9b9ee..ba3c65e 100644 --- a/gumble/gumble/client.go +++ b/gumble/gumble/client.go @@ -142,10 +142,14 @@ func DialWithDialer(dialer *net.Dialer, config *Config, tlsConfig *tls.Config) ( // Start UDP transport immediately so it's ready when CryptSetup // arrives during the sync handshake. - if err := client.startUDP(); err != nil { - log.Warn("UDP setup failed, audio will use TCP tunnel: %v", err) - } else if client.udpConn != nil { - log.Info("UDP socket opened to %s, waiting for CryptSetup", client.udpConn.RemoteAddr()) + if !client.Config.DisableUDP { + if err := client.startUDP(); err != nil { + log.Warn("UDP setup failed, audio will use TCP tunnel: %v", err) + } else if client.udpConn != nil { + log.Info("UDP socket opened to %s, waiting for CryptSetup", client.udpConn.RemoteAddr()) + } + } else { + log.Info("UDP disabled by config, audio will use TCP tunnel") } go client.pingRoutine() @@ -330,17 +334,21 @@ func (c *Client) EnableStereoEncoder() { var udpFallbackLogged bool func (c *Client) WriteAudio(format, target byte, sequence int64, final bool, data []byte, X, Y, Z *float32) error { - // Try UDP first - if sent, err := c.WriteAudioUDP(format, target, sequence, final, data, X, Y, Z); sent { - if err != nil { - log.Error("UDP send error: %v", err) + // Try UDP first (unless disabled) + if !c.Config.DisableUDP { + if sent, err := c.WriteAudioUDP(format, target, sequence, final, data, X, Y, Z); sent { + if err != nil { + log.Error("UDP send error: %v", err) + } + return err } - return err } // Fall back to TCP tunnel — log once per process if !udpFallbackLogged { udpFallbackLogged = true - if c.udpConn == nil { + if c.Config.DisableUDP { + log.Info("UDP disabled, audio using TCP tunnel") + } else if c.udpConn == nil { log.Info("no UDP socket, audio using TCP tunnel") } else if !c.cryptOut.initialized { log.Info("waiting for CryptSetup, audio using TCP tunnel") diff --git a/gumble/gumble/config.go b/gumble/gumble/config.go index 7b3699f..f0add06 100644 --- a/gumble/gumble/config.go +++ b/gumble/gumble/config.go @@ -25,6 +25,9 @@ type Config struct { // AudioDataBytes is the number of bytes that an audio frame can use. AudioDataBytes int + // DisableUDP forces all audio to use the TCP tunnel instead of UDP. + DisableUDP bool + // The event listeners used when client events are triggered. Listeners Listeners AudioListeners AudioListeners diff --git a/main.go b/main.go index 6ee2d41..15c54e1 100644 --- a/main.go +++ b/main.go @@ -119,6 +119,7 @@ func main() { autoTransmit := flag.Bool("auto-transmit", false, "start transmitting immediately on connect") toneTest := flag.Bool("tone-test", false, "send a 440 Hz test tone instead of microphone (bypasses soundcard)") toneTestOutput := flag.String("tone-out", "incoming.pcm", "file to save incoming audio to in tone-test mode") + tcpOnly := flag.Bool("tcp", false, "disable UDP, force audio through TCP tunnel") logLevel := flag.String("log", "warn", "log level: debug, info, warn, error") logFile := flag.String("logfile", "", "write logs to this file (in addition to stderr)") @@ -218,6 +219,7 @@ func main() { NoiseSuppressor: noise.NewSuppressor(), } b.Config.Buffers = *buffers + b.Config.DisableUDP = *tcpOnly b.Hotkeys = b.UserConfig.GetHotkeys() b.UserConfig.SaveConfig()