Improvements to the way voice waits for events
This commit is contained in:
parent
7b17afc8e9
commit
6a029e0c80
|
@ -92,10 +92,16 @@ func (c *Connection) Speaking(s Speaking) error {
|
||||||
|
|
||||||
// StopSpeaking stops speaking.
|
// StopSpeaking stops speaking.
|
||||||
// https://discordapp.com/developers/docs/topics/voice-connections#voice-data-interpolation
|
// https://discordapp.com/developers/docs/topics/voice-connections#voice-data-interpolation
|
||||||
func (c *Connection) StopSpeaking() {
|
func (c *Connection) StopSpeaking() error {
|
||||||
|
if c.OpusSend == nil {
|
||||||
|
return ErrCannotSend
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
c.OpusSend <- []byte{0xF8, 0xFF, 0xFE}
|
c.OpusSend <- []byte{0xF8, 0xFF, 0xFE}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPCode 7
|
// OPCode 7
|
||||||
|
|
|
@ -66,6 +66,11 @@ type Connection struct {
|
||||||
ready ReadyEvent
|
ready ReadyEvent
|
||||||
sessionDescription SessionDescriptionEvent
|
sessionDescription SessionDescriptionEvent
|
||||||
|
|
||||||
|
// Operation Channels
|
||||||
|
helloChan chan bool
|
||||||
|
readyChan chan bool
|
||||||
|
sessionDescChan chan bool
|
||||||
|
|
||||||
// Filled by methods, internal use
|
// Filled by methods, internal use
|
||||||
paceDeath chan error
|
paceDeath chan error
|
||||||
waitGroup *sync.WaitGroup
|
waitGroup *sync.WaitGroup
|
||||||
|
@ -82,11 +87,17 @@ func newConnection() *Connection {
|
||||||
|
|
||||||
ErrorLog: defaultErrorHandler,
|
ErrorLog: defaultErrorHandler,
|
||||||
AfterClose: func(error) {},
|
AfterClose: func(error) {},
|
||||||
|
|
||||||
|
helloChan: make(chan bool),
|
||||||
|
readyChan: make(chan bool),
|
||||||
|
sessionDescChan: make(chan bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open .
|
// Open .
|
||||||
func (c *Connection) Open() error {
|
func (c *Connection) Open() error {
|
||||||
|
// Having this acquire a lock might cause a problem if the `onVoiceStateUpdate`
|
||||||
|
// does not set a session id in time :/
|
||||||
c.mut.Lock()
|
c.mut.Lock()
|
||||||
defer c.mut.Unlock()
|
defer c.mut.Unlock()
|
||||||
|
|
||||||
|
@ -96,19 +107,11 @@ func (c *Connection) Open() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the SessionID.
|
// I doubt this would happen from my testing, but you never know.
|
||||||
// TODO: Find better way to wait.
|
|
||||||
for i := 0; i < 20; i++ {
|
|
||||||
if c.SessionID != "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.SessionID == "" {
|
if c.SessionID == "" {
|
||||||
return ErrNoSessionID
|
return ErrNoSessionID
|
||||||
}
|
}
|
||||||
|
WSDebug("Connection has a session id")
|
||||||
|
|
||||||
// https://discordapp.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection
|
// https://discordapp.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection
|
||||||
endpoint := "wss://" + strings.TrimSuffix(c.Endpoint, ":80") + "/?v=" + Version
|
endpoint := "wss://" + strings.TrimSuffix(c.Endpoint, ":80") + "/?v=" + Version
|
||||||
|
@ -175,16 +178,8 @@ func (c *Connection) start() error {
|
||||||
go c.handleWS()
|
go c.handleWS()
|
||||||
|
|
||||||
// Wait for hello.
|
// Wait for hello.
|
||||||
// TODO: Find better way to wait
|
|
||||||
WSDebug("Waiting for Hello..")
|
WSDebug("Waiting for Hello..")
|
||||||
for {
|
<-c.helloChan
|
||||||
if c.hello.HeartbeatInterval == 0 {
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
WSDebug("Received Hello")
|
WSDebug("Received Hello")
|
||||||
|
|
||||||
// Start the pacemaker with the heartrate received from Hello, after
|
// Start the pacemaker with the heartrate received from Hello, after
|
||||||
|
|
18
voice/op.go
18
voice/op.go
|
@ -65,15 +65,7 @@ func HandleOP(c *Connection, op *OP) error {
|
||||||
return errors.Wrap(err, "Failed to parse READY event")
|
return errors.Wrap(err, "Failed to parse READY event")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.udpOpen(); err != nil {
|
c.readyChan <- true
|
||||||
return errors.Wrap(err, "Failed to open UDP connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.OpusSend == nil {
|
|
||||||
c.OpusSend = make(chan []byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
go c.opusSendLoop()
|
|
||||||
|
|
||||||
// Gives information about the encryption mode and secret key for sending voice packets
|
// Gives information about the encryption mode and secret key for sending voice packets
|
||||||
case SessionDescriptionOP:
|
case SessionDescriptionOP:
|
||||||
|
@ -81,10 +73,11 @@ func HandleOP(c *Connection, op *OP) error {
|
||||||
c.ErrorLog(errors.Wrap(err, "Failed to parse SESSION_DESCRIPTION"))
|
c.ErrorLog(errors.Wrap(err, "Failed to parse SESSION_DESCRIPTION"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.sessionDescChan <- true
|
||||||
|
|
||||||
// Someone started or stopped speaking.
|
// Someone started or stopped speaking.
|
||||||
case SpeakingOP:
|
case SpeakingOP:
|
||||||
// ?
|
// ?
|
||||||
return nil
|
|
||||||
|
|
||||||
// Heartbeat response from the server
|
// Heartbeat response from the server
|
||||||
case HeartbeatAckOP:
|
case HeartbeatAckOP:
|
||||||
|
@ -94,9 +87,10 @@ func HandleOP(c *Connection, op *OP) error {
|
||||||
case HelloOP:
|
case HelloOP:
|
||||||
// Decode the data.
|
// Decode the data.
|
||||||
if err := c.Driver.Unmarshal(op.Data, &c.hello); err != nil {
|
if err := c.Driver.Unmarshal(op.Data, &c.hello); err != nil {
|
||||||
c.ErrorLog(errors.Wrap(err, "Failed to parse SESSION_DESCRIPTION"))
|
c.ErrorLog(errors.Wrap(err, "Failed to parse HELLO"))
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
c.helloChan <- true
|
||||||
|
|
||||||
// Server is saying the connection was resumed, no data here.
|
// Server is saying the connection was resumed, no data here.
|
||||||
case ResumedOP:
|
case ResumedOP:
|
||||||
|
|
|
@ -83,10 +83,10 @@ func (c *Connection) udpOpen() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Wait until OP4 is received
|
// Wait for session description.
|
||||||
// side note: you cannot just do a blocking loop as I've done before
|
WSDebug("Waiting for Session Description..")
|
||||||
// as this method is currently called inside of the event loop
|
<-c.sessionDescChan
|
||||||
// so for as long as it blocks no other events can be received
|
WSDebug("Received Session Description")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ package voice
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/diamondburned/arikawa/discord"
|
"github.com/diamondburned/arikawa/discord"
|
||||||
"github.com/diamondburned/arikawa/gateway"
|
"github.com/diamondburned/arikawa/gateway"
|
||||||
|
@ -30,14 +29,17 @@ var (
|
||||||
// similarly to log.Println().
|
// similarly to log.Println().
|
||||||
WSDebug = func(v ...interface{}) {}
|
WSDebug = func(v ...interface{}) {}
|
||||||
|
|
||||||
// ErrMissingForIdentify .
|
// ErrMissingForIdentify is an error when we are missing information to identify.
|
||||||
ErrMissingForIdentify = errors.New("missing GuildID, UserID, SessionID, or Token for identify")
|
ErrMissingForIdentify = errors.New("missing GuildID, UserID, SessionID, or Token for identify")
|
||||||
|
|
||||||
// ErrMissingForResume .
|
// ErrMissingForResume is an error when we are missing information to resume.
|
||||||
ErrMissingForResume = errors.New("missing GuildID, SessionID, or Token for resuming")
|
ErrMissingForResume = errors.New("missing GuildID, SessionID, or Token for resuming")
|
||||||
|
|
||||||
|
// ErrCannotSend is an error when audio is sent to a closed channel.
|
||||||
|
ErrCannotSend = errors.New("cannot send audio to closed channel")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Voice .
|
// Voice represents a Voice Repository used for managing voice connections.
|
||||||
type Voice struct {
|
type Voice struct {
|
||||||
mut sync.RWMutex
|
mut sync.RWMutex
|
||||||
|
|
||||||
|
@ -50,7 +52,7 @@ type Voice struct {
|
||||||
ErrorLog func(err error)
|
ErrorLog func(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVoice creates a new Voice repository.
|
// NewVoice creates a new Voice Repository.
|
||||||
func NewVoice(s *state.State) *Voice {
|
func NewVoice(s *state.State) *Voice {
|
||||||
v := &Voice{
|
v := &Voice{
|
||||||
state: s,
|
state: s,
|
||||||
|
@ -85,7 +87,7 @@ func (v *Voice) RemoveConnection(guildID discord.Snowflake) {
|
||||||
delete(v.connections, guildID)
|
delete(v.connections, guildID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinChannel .
|
// JoinChannel joins the specified channel in the specified guild.
|
||||||
func (v *Voice) JoinChannel(gID, cID discord.Snowflake, muted, deafened bool) (*Connection, error) {
|
func (v *Voice) JoinChannel(gID, cID discord.Snowflake, muted, deafened bool) (*Connection, error) {
|
||||||
// Get the stored voice connection for the given guild.
|
// Get the stored voice connection for the given guild.
|
||||||
conn, ok := v.GetConnection(gID)
|
conn, ok := v.GetConnection(gID)
|
||||||
|
@ -126,41 +128,23 @@ func (v *Voice) JoinChannel(gID, cID discord.Snowflake, muted, deafened bool) (*
|
||||||
return nil, errors.Wrap(err, "Failed to send Voice State Update event")
|
return nil, errors.Wrap(err, "Failed to send Voice State Update event")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until we are connected.
|
// Wait for ready event.
|
||||||
WSDebug("Waiting for READY.")
|
WSDebug("Waiting for READY.")
|
||||||
|
<-conn.readyChan
|
||||||
// TODO: Find better way to wait for ready event.
|
|
||||||
|
|
||||||
// Check if the connection is opened.
|
|
||||||
for i := 0; i < 50; i++ {
|
|
||||||
if conn.WS != nil && conn.WS.Conn != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn.WS == nil || conn.WS.Conn == nil {
|
|
||||||
return nil, errors.Wrap(err, "Failed to wait for connection to open")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 50; i++ {
|
|
||||||
if conn.ready.IP != "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn.ready.IP == "" {
|
|
||||||
return nil, errors.New("failed to wait for ready event")
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn.udpConn == nil {
|
|
||||||
return nil, errors.New("udp connection is not open")
|
|
||||||
}
|
|
||||||
|
|
||||||
WSDebug("Received READY.")
|
WSDebug("Received READY.")
|
||||||
|
|
||||||
|
// Open the UDP connection.
|
||||||
|
if err := conn.udpOpen(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Failed to open UDP connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the OpusSend channel is set
|
||||||
|
if conn.OpusSend == nil {
|
||||||
|
conn.OpusSend = make(chan []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the opus send loop.
|
||||||
|
go conn.opusSendLoop()
|
||||||
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue