diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs index 416ac6759..53d3341eb 100644 --- a/src/Discord.Net/DiscordClient.cs +++ b/src/Discord.Net/DiscordClient.cs @@ -95,7 +95,7 @@ namespace Discord _api = new DiscordAPIClient(_config.LogLevel); _dataSocket = new DataWebSocket(this); _dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); }; - _dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await Connect(_token); /*await _dataSocket.Reconnect(_cancelToken);*/ }; + _dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await Reconnect(_token); }; if (_config.EnableVoice) { _voiceSocket = new VoiceWebSocket(this); @@ -553,7 +553,8 @@ namespace Discord /// Connects to the Discord server with the provided token. public async Task Connect(string token) { - await Disconnect().ConfigureAwait(false); + if (_state != (int)DiscordClientState.Disconnected) + await Disconnect().ConfigureAwait(false); if (_config.LogLevel >= LogMessageSeverity.Verbose) RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token."); @@ -564,7 +565,8 @@ namespace Discord /// Returns a token for future connections. public async Task Connect(string email, string password) { - await Disconnect().ConfigureAwait(false); + if (_state != (int)DiscordClientState.Disconnected) + await Disconnect().ConfigureAwait(false); var response = await _api.Login(email, password).ConfigureAwait(false); if (_config.LogLevel >= LogMessageSeverity.Verbose) @@ -572,11 +574,15 @@ namespace Discord return await ConnectInternal(response.Token).ConfigureAwait(false); } - private async Task ConnectInternal(string token) + private Task Reconnect(string token) { - if (_state != (int)DiscordClientState.Disconnected) - throw new InvalidOperationException("Client is already connected or connecting to the server."); + if (_config.LogLevel >= LogMessageSeverity.Verbose) + RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token."); + return ConnectInternal(token); + } + private async Task ConnectInternal(string token) + { try { _disconnectedEvent.Reset(); @@ -618,14 +624,14 @@ namespace Discord } protected void CompleteConnect() { - _state = (int)WebSocketState.Connected; + _state = (int)DiscordClientState.Connected; _connectedEvent.Set(); RaiseConnected(); } /// Disconnects from the Discord server, canceling any pending requests. public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); - protected Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false) + protected Task DisconnectInternal(Exception ex = null, bool isUnexpected = true, bool skipAwait = false) { int oldState; bool hasWriterLock; @@ -644,7 +650,7 @@ namespace Discord if (hasWriterLock) { _wasDisconnectUnexpected = isUnexpected; - _disconnectReason = ExceptionDispatchInfo.Capture(ex); + _disconnectReason = ex != null ? ExceptionDispatchInfo.Capture(ex) : null; _cancelTokenSource.Cancel(); } @@ -662,12 +668,12 @@ namespace Discord else task = _cancelToken.Wait(); - try - { - await task.ConfigureAwait(false); - } + try { await task.ConfigureAwait(false); } catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); } + //When the first task ends, make sure the rest do too + await DisconnectInternal(skipAwait: true); + bool wasUnexpected = _wasDisconnectUnexpected; _wasDisconnectUnexpected = false; @@ -680,8 +686,11 @@ namespace Discord if (_config.EnableVoice) await _voiceSocket.Disconnect().ConfigureAwait(false); - Message ignored; - while (_pendingMessages.TryDequeue(out ignored)) { } + if (_config.UseMessageQueue) + { + Message ignored; + while (_pendingMessages.TryDequeue(out ignored)) { } + } _channels.Clear(); _members.Clear(); diff --git a/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs b/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs index 38b64f8f5..86c7e80ce 100644 --- a/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs +++ b/src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs @@ -70,6 +70,29 @@ namespace Discord.Net.WebSockets await Connect(host, cancelToken); } + public async Task Reconnect(CancellationToken cancelToken) + { + try + { + await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); + while (!cancelToken.IsCancellationRequested) + { + try + { + await Connect(_host, cancelToken).ConfigureAwait(false); + break; + } + catch (OperationCanceledException) { throw; } + catch (Exception ex) + { + RaiseOnLog(LogMessageSeverity.Error, $"DataSocket reconnect failed: {ex.GetBaseException().Message}"); + //Net is down? We can keep trying to reconnect until the user runs Disconnect() + await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false); + } + } + } + catch (OperationCanceledException) { } + } protected override Task[] Run() { diff --git a/src/Discord.Net/Net/WebSockets/WebSocket.cs b/src/Discord.Net/Net/WebSockets/WebSocket.cs index 4644c396a..59acefdf5 100644 --- a/src/Discord.Net/Net/WebSockets/WebSocket.cs +++ b/src/Discord.Net/Net/WebSockets/WebSocket.cs @@ -69,29 +69,6 @@ namespace Discord.Net.WebSockets }; } - public async Task Reconnect(CancellationToken cancelToken) - { - try - { - await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); - while (!cancelToken.IsCancellationRequested) - { - try - { - await Connect(_host, cancelToken).ConfigureAwait(false); - break; - } - catch (OperationCanceledException) { throw; } - catch (Exception ex) - { - RaiseOnLog(LogMessageSeverity.Error, $"DataSocket reconnect failed: {ex.GetBaseException().Message}"); - //Net is down? We can keep trying to reconnect until the user runs Disconnect() - await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false); - } - } - } - catch (OperationCanceledException) { } - } protected virtual async Task Connect(string host, CancellationToken cancelToken) { if (_state != (int)WebSocketState.Disconnected) @@ -127,7 +104,7 @@ namespace Discord.Net.WebSockets => Connect(_host, _cancelToken);*/ public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); - protected Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false) + protected Task DisconnectInternal(Exception ex = null, bool isUnexpected = true, bool skipAwait = false) { int oldState; bool hasWriterLock; @@ -146,7 +123,7 @@ namespace Discord.Net.WebSockets if (hasWriterLock) { _wasDisconnectUnexpected = isUnexpected; - _disconnectReason = ExceptionDispatchInfo.Capture(ex); + _disconnectReason = ex != null ? ExceptionDispatchInfo.Capture(ex) : null; _cancelTokenSource.Cancel(); } @@ -158,13 +135,17 @@ namespace Discord.Net.WebSockets protected virtual async Task RunTasks() { - Task task = Task.WhenAll(Run()); + Task[] tasks = Run(); + Task firstTask = Task.WhenAny(tasks); + Task allTasks = Task.WhenAll(tasks); - try - { - await task.ConfigureAwait(false); - } - catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); } + try { await firstTask.ConfigureAwait(false); } + catch (Exception ex) { await DisconnectInternal(ex: ex, skipAwait: true).ConfigureAwait(false); } + + //When the first task ends, make sure the rest do too + await DisconnectInternal(skipAwait: true); + try { await allTasks.ConfigureAwait(false); } + catch { } bool wasUnexpected = _wasDisconnectUnexpected; _wasDisconnectUnexpected = false; @@ -207,7 +188,7 @@ namespace Discord.Net.WebSockets { while (!cancelToken.IsCancellationRequested) { - if (_heartbeatInterval > 0) + if (_state == (int)WebSocketState.Connected) { QueueMessage(GetKeepAlive()); await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false);