diff --git a/src/Discord.Net.Commands/DiscordBotClient.cs b/src/Discord.Net.Commands/DiscordBotClient.cs index e8ffacaa1..403b002e7 100644 --- a/src/Discord.Net.Commands/DiscordBotClient.cs +++ b/src/Discord.Net.Commands/DiscordBotClient.cs @@ -22,6 +22,7 @@ namespace Discord _commands = new List(); CommandChar = '~'; + UseCommandChar = true; RequireCommandCharInPublic = true; RequireCommandCharInPrivate = true; @@ -32,7 +33,7 @@ namespace Discord return; //Ignore messages from ourselves - if (e.Message.UserId == _myId) + if (e.Message.UserId == _myId) return; //Check for the command character diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 301037f88..f01df398e 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -49,8 +49,7 @@ namespace Discord private bool _isDebugMode; #if !DNXCORE50 - public Server CurrentVoiceServer => _currentVoiceToken != null ? _servers[_currentVoiceServerId] : null; - private string _currentVoiceServerId, _currentVoiceToken; + public Server CurrentVoiceServer => GetServer(_voiceWebSocket.CurrentVoiceServerId); #endif //Constructor @@ -142,13 +141,12 @@ namespace Discord //Reconnect if we didn't cause the disconnect if (e.WasUnexpected) { + await Task.Delay(_config.ReconnectDelay); while (!_disconnectToken.IsCancellationRequested) { try { - await Task.Delay(_config.ReconnectDelay); await _voiceWebSocket.ReconnectAsync(); - await _voiceWebSocket.Login(_currentVoiceServerId, _myId, _sessionId, _currentVoiceToken); break; } catch (Exception ex) @@ -425,11 +423,10 @@ namespace Discord try { RaiseVoiceServerUpdated(server, data.Endpoint); } catch { } #if !DNXCORE50 - if (_config.EnableVoice && data.ServerId == _currentVoiceServerId) + if (_config.EnableVoice) { - _currentVoiceToken = data.Token; + _voiceWebSocket.SetSessionData(data.ServerId, _myId, _sessionId, data.Token); await _voiceWebSocket.ConnectAsync("wss://" + data.Endpoint.Split(':')[0]); - await _voiceWebSocket.Login(_currentVoiceServerId, _myId, _sessionId, data.Token); } #endif } @@ -627,19 +624,19 @@ namespace Discord if (channel == null) throw new ArgumentNullException(nameof(channel)); await LeaveVoiceServer(); - _currentVoiceServerId = channel.ServerId; + //_currentVoiceServerId = channel.ServerId; _webSocket.JoinVoice(channel); + await _voiceWebSocket.BeginConnect(); } public async Task LeaveVoiceServer() { + CheckReady(); if (!_config.EnableVoice) throw new InvalidOperationException("Voice is not enabled for this client."); await _voiceWebSocket.DisconnectAsync(); - if (_currentVoiceServerId != null) - _webSocket.LeaveVoice(); - _currentVoiceServerId = null; - _currentVoiceToken = null; + //if (_voiceWebSocket.CurrentVoiceServerId != null) + _webSocket.LeaveVoice(); } /// Sends a PCM frame to the voice server. diff --git a/src/Discord.Net/DiscordClientConfig.cs b/src/Discord.Net/DiscordClientConfig.cs index 6e92d0e7b..bf6ff8715 100644 --- a/src/Discord.Net/DiscordClientConfig.cs +++ b/src/Discord.Net/DiscordClientConfig.cs @@ -3,8 +3,7 @@ public class DiscordClientConfig { #if !DNXCORE50 - /// Enables the voice websocket and UDP client (Experimental!). - /// This option requires the opus .dll or .so be in the local lib/ folder. + /// Enables the voice websocket and UDP client (Experimental!). This option requires the opus .dll or .so be in the local lib/ folder. public bool EnableVoice { get; set; } = false; #endif /// Enables the verbose DebugMessage event handler. May hinder performance but should help debug any issues. @@ -24,9 +23,8 @@ public bool UseMessageQueue { get; set; } = false; /// Gets or sets the time (in milliseconds) to wait when the message queue is empty before checking again. public int MessageQueueInterval { get; set; } = 100; - /// Gets or sets the max buffer length (in milliseconds) for outgoing voice packets. - /// This value is the target maximum but is not guaranteed. The buffer will often go a bit above this value. - public int VoiceBufferLength { get; set; } = 1000; + /// Gets or sets the max buffer length (in milliseconds) for outgoing voice packets. This value is the target maximum but is not guaranteed, the buffer will often go slightly above this value. + public int VoiceBufferLength { get; set; } = 3000; public DiscordClientConfig() { } } diff --git a/src/Discord.Net/DiscordDataSocket.cs b/src/Discord.Net/DiscordDataSocket.cs index 59700e737..b84c09a33 100644 --- a/src/Discord.Net/DiscordDataSocket.cs +++ b/src/Discord.Net/DiscordDataSocket.cs @@ -20,6 +20,11 @@ namespace Discord _connectWaitOnLogin2 = new ManualResetEventSlim(false); } + public override Task ConnectAsync(string url) + { + BeginConnect(); + return base.ConnectAsync(url); + } public async Task Login(string token) { var cancelToken = _disconnectToken.Token; diff --git a/src/Discord.Net/DiscordVoiceSocket.cs b/src/Discord.Net/DiscordVoiceSocket.cs index ecf8f4f0c..cd205c1c3 100644 --- a/src/Discord.Net/DiscordVoiceSocket.cs +++ b/src/Discord.Net/DiscordVoiceSocket.cs @@ -1,4 +1,4 @@ -//#define USE_THREAD +#define USE_THREAD #if !DNXCORE50 using Discord.API.Models; using Discord.Opus; @@ -35,10 +35,14 @@ namespace Discord private ushort _sequence; private string _mode; private byte[] _encodingBuffer; + private string _serverId, _userId, _sessionId, _token; + #if USE_THREAD private Thread _sendThread; #endif + public string CurrentVoiceServerId => _serverId; + public DiscordVoiceSocket(DiscordClient client, int timeout, int interval, int audioBufferLength, bool isDebug) : base(client, timeout, interval, isDebug) { @@ -54,25 +58,54 @@ namespace Discord protected override void OnConnect() { _udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); -#if !DNX451 && !MONO +#if !DNX451 _udp.AllowNatTraversal(true); #endif _isReady = false; _isClearing = false; - } + + var cancelToken = _disconnectToken.Token; + Task.Factory.StartNew(async () => + { + _connectWaitOnLogin.Reset(); + + VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login(); + msg.Payload.ServerId = _serverId; + msg.Payload.SessionId = _sessionId; + msg.Payload.Token = _token; + msg.Payload.UserId = _userId; + await SendMessage(msg, cancelToken); + + try + { + if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) + return; + } + catch (OperationCanceledException) + { + return; + } + + SetConnected(); + }); + } protected override void OnDisconnect() { _udp = null; + _serverId = null; + _userId = null; + _sessionId = null; + _token = null; #if USE_THREAD _sendThread.Join(); _sendThread = null; #endif - } + } protected override Task[] CreateTasks() { #if USE_THREAD - _sendThread = new Thread(new ThreadStart(() => SendAsync(_disconnectToken))); + _sendThread = new Thread(new ThreadStart(() => SendVoiceAsync(_disconnectToken))); _sendThread.Start(); #endif return new Task[] @@ -85,19 +118,20 @@ namespace Discord }.Concat(base.CreateTasks()).ToArray(); } - public async Task Login(string serverId, string userId, string sessionId, string token) + public void SetSessionData(string serverId, string userId, string sessionId, string token) { - var cancelToken = _disconnectToken.Token; - - _connectWaitOnLogin.Reset(); + _serverId = serverId; + _userId = userId; + _sessionId = sessionId; + _token = token; + } - VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login(); - msg.Payload.ServerId = serverId; - msg.Payload.SessionId = sessionId; - msg.Payload.Token = token; - msg.Payload.UserId = userId; - await SendMessage(msg, cancelToken); + public new async Task BeginConnect() + { + base.BeginConnect(); + var cancelToken = _disconnectToken.Token; + await Task.Yield(); try { if (!_connectWaitOnLogin.Wait(_timeout, cancelToken)) //Waiting on JoinServer message @@ -110,8 +144,6 @@ namespace Discord else _disconnectReason.Throw(); } - - SetConnected(); } private async Task ReceiveVoiceAsync() @@ -143,6 +175,7 @@ namespace Discord var cancelSource = _disconnectToken; var cancelToken = cancelSource.Token; await Task.Yield(); +#endif byte[] packet; try @@ -189,7 +222,7 @@ namespace Discord rtpPacket[7] = (byte)((timestamp >> 0) & 0xFF); Buffer.BlockCopy(packet, 0, rtpPacket, 12, packet.Length); #if USE_THREAD - _udp.Send(rtpPacket, packet.Count + 12); + _udp.Send(rtpPacket, packet.Length + 12); #else await _udp.SendAsync(rtpPacket, packet.Length + 12); #endif @@ -222,8 +255,7 @@ namespace Discord catch (ObjectDisposedException) { } catch (Exception ex) { DisconnectInternal(ex); } } -#endif - //Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken + //Closes the UDP socket when _disconnectToken is triggered, since UDPClient doesn't allow passing a canceltoken private async Task WatcherAsync() { var cancelToken = _disconnectToken.Token; diff --git a/src/Discord.Net/DiscordWebSocket.cs b/src/Discord.Net/DiscordWebSocket.cs index 3a8f28a29..b8d4bca62 100644 --- a/src/Discord.Net/DiscordWebSocket.cs +++ b/src/Discord.Net/DiscordWebSocket.cs @@ -40,12 +40,15 @@ namespace Discord _sendQueue = new ConcurrentQueue(); } - public async Task ConnectAsync(string url) + protected void BeginConnect() { - await DisconnectAsync(); - _disconnectToken = new CancellationTokenSource(); _disconnectReason = null; + } + public virtual async Task ConnectAsync(string url) + { + await DisconnectAsync(); + var cancelToken = _disconnectToken.Token; _webSocket = new ClientWebSocket();