Add connect timeout, fix ChannelVoiceJoin "leaking" connections

This commit is contained in:
andrei 2016-03-08 02:36:00 -08:00
parent b48e8c992e
commit 5dc0b9f2a1
3 changed files with 40 additions and 16 deletions

View file

@ -54,7 +54,7 @@ type Session struct {
// Whether the UDP Connection is ready // Whether the UDP Connection is ready
UDPReady bool UDPReady bool
// Stores a mapping of channel id's to VoiceConnections // Stores a mapping of guild id's to VoiceConnections
VoiceConnections map[string]*VoiceConnection VoiceConnections map[string]*VoiceConnection
// Managed state object, updated internally with events when // Managed state object, updated internally with events when

View file

@ -12,6 +12,7 @@ package discordgo
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net" "net"
"runtime" "runtime"
@ -97,7 +98,6 @@ type voiceHandshakeOp struct {
// after VoiceConnectionChannelJoin is used and the data VOICE websocket events // after VoiceConnectionChannelJoin is used and the data VOICE websocket events
// are captured. // are captured.
func (v *VoiceConnection) Open() (err error) { func (v *VoiceConnection) Open() (err error) {
v.Lock() v.Lock()
defer v.Unlock() defer v.Unlock()
@ -131,8 +131,19 @@ func (v *VoiceConnection) Open() (err error) {
return return
} }
func (v *VoiceConnection) WaitUntilConnected() { func (v *VoiceConnection) WaitUntilConnected() error {
<-v.connected if v.Ready {
return nil
}
value, ok := <-v.connected
if (!value && !v.Ready) || !ok {
delete(v.session.VoiceConnections, v.GuildID)
return errors.New("Timed out connecting to voice")
}
return nil
} }
// wsListen listens on the voice websocket for messages and passes them // wsListen listens on the voice websocket for messages and passes them
@ -603,9 +614,11 @@ func (v *VoiceConnection) Close() {
v.Lock() v.Lock()
defer v.Unlock() defer v.Unlock()
if v.Ready { // Send a OP4 with a nil channel to disconnect
if v.sessionID != "" {
data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}} data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}}
v.session.wsConn.WriteJSON(data) v.session.wsConn.WriteJSON(data)
v.sessionID = ""
} }
v.Ready = false v.Ready = false
@ -632,15 +645,10 @@ func (v *VoiceConnection) Close() {
} }
} }
// Change channels // Request to change channels
func (v *VoiceConnection) ChangeChannel(channelID string) (err error) { func (v *VoiceConnection) ChangeChannel(channelID string) (err error) {
data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, true, true}} data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, &channelID, true, true}}
err = v.session.wsConn.WriteJSON(data) err = v.session.wsConn.WriteJSON(data)
if err == nil {
v.ChannelID = channelID
}
return err return err
} }

View file

@ -332,11 +332,17 @@ type voiceChannelJoinOp struct {
// this func please monitor the Session.Voice.Ready bool to determine when // this func please monitor the Session.Voice.Ready bool to determine when
// it is ready and able to send/receive audio, that should happen quickly. // it is ready and able to send/receive audio, that should happen quickly.
// //
// gID : Guild ID of the channel to join. // gID : Guild ID of the channel to join.
// cID : Channel ID of the channel to join. // cID : Channel ID of the channel to join.
// mute : If true, you will be set to muted upon joining. // mute : If true, you will be set to muted upon joining.
// deaf : If true, you will be set to deafened upon joining. // deaf : If true, you will be set to deafened upon joining.
func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *VoiceConnection, err error) { // timeout : If greater than zero, the timeout in milliseconds after which connecting will fail
func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool, timeout int) (voice *VoiceConnection, err error) {
// If a voice connection for the guild exists, return that
if _, exists := s.VoiceConnections[gID]; exists {
return s.VoiceConnections[gID], err
}
// Send the request to Discord that we want to join the voice channel // Send the request to Discord that we want to join the voice channel
data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}} data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}}
err = s.wsConn.WriteJSON(data) err = s.wsConn.WriteJSON(data)
@ -359,6 +365,16 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
voice.GuildID = gID voice.GuildID = gID
voice.ChannelID = cID voice.ChannelID = cID
// Queue the timeout in case we fail to connect
if timeout > 0 {
go func() {
time.Sleep(time.Millisecond * time.Duration(timeout))
if !voice.Ready {
voice.connected <- false
}
}()
}
return voice, err return voice, err
} }