diff --git a/src/Discord.Net/DiscordSocketClient.cs b/src/Discord.Net/DiscordSocketClient.cs index d3ea71ffd..966c2aef8 100644 --- a/src/Discord.Net/DiscordSocketClient.cs +++ b/src/Discord.Net/DiscordSocketClient.cs @@ -35,6 +35,7 @@ namespace Discord private int _unavailableGuilds; private long _lastGuildAvailableTime; private int _nextAudioId; + private bool _canReconnect; /// Gets the shard if of this client. public int ShardId { get; } @@ -114,7 +115,7 @@ namespace Discord protected override async Task OnLogoutAsync() { if (ConnectionState != ConnectionState.Disconnected) - await DisconnectInternalAsync(null).ConfigureAwait(false); + await DisconnectInternalAsync(null, false).ConfigureAwait(false); _voiceRegions = ImmutableDictionary.Create(); } @@ -125,7 +126,7 @@ namespace Discord await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await ConnectInternalAsync().ConfigureAwait(false); + await ConnectInternalAsync(false).ConfigureAwait(false); } finally { _connectionLock.Release(); } @@ -136,17 +137,17 @@ namespace Discord await _guildDownloadTask.ConfigureAwait(false); } } - private async Task ConnectInternalAsync() + private async Task ConnectInternalAsync(bool isReconnecting) { if (LoginState != LoginState.LoggedIn) throw new InvalidOperationException("You must log in before connecting."); - if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) + if (!isReconnecting && _reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) _reconnectCancelToken.Cancel(); var state = ConnectionState; if (state == ConnectionState.Connecting || state == ConnectionState.Connected) - await DisconnectInternalAsync(null).ConfigureAwait(false); + await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); ConnectionState = ConnectionState.Connecting; await _gatewayLogger.InfoAsync("Connecting").ConfigureAwait(false); @@ -164,12 +165,13 @@ namespace Discord await ApiClient.SendIdentifyAsync().ConfigureAwait(false); await _connectTask.Task.ConfigureAwait(false); + _canReconnect = true; ConnectionState = ConnectionState.Connected; await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); } catch (Exception) { - await DisconnectInternalAsync(null).ConfigureAwait(false); + await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); throw; } } @@ -180,7 +182,7 @@ namespace Discord await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync(null).ConfigureAwait(false); + await DisconnectInternalAsync(null, false).ConfigureAwait(false); } finally { _connectionLock.Release(); } } @@ -190,14 +192,18 @@ namespace Discord await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync(ex).ConfigureAwait(false); + await DisconnectInternalAsync(ex, false).ConfigureAwait(false); } finally { _connectionLock.Release(); } } - private async Task DisconnectInternalAsync(Exception ex) + private async Task DisconnectInternalAsync(Exception ex, bool isReconnecting) { - if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) - _reconnectCancelToken.Cancel(); + if (!isReconnecting) + { + _canReconnect = false; + if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) + _reconnectCancelToken.Cancel(); + } ulong guildId; @@ -242,7 +248,8 @@ namespace Discord await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await DisconnectInternalAsync(null).ConfigureAwait(false); + if (!_canReconnect || _reconnectTask != null) return; + await DisconnectInternalAsync(null, true).ConfigureAwait(false); _reconnectCancelToken = new CancellationTokenSource(); _reconnectTask = ReconnectInternalAsync(_reconnectCancelToken.Token); } @@ -253,34 +260,37 @@ namespace Discord try { int nextReconnectDelay = 1000; - while (!cancelToken.IsCancellationRequested) + while (true) { + await Task.Delay(nextReconnectDelay, cancelToken).ConfigureAwait(false); + nextReconnectDelay *= 2; + if (nextReconnectDelay > 30000) + nextReconnectDelay = 30000; + + await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await Task.Delay(nextReconnectDelay, cancelToken).ConfigureAwait(false); - nextReconnectDelay *= 2; - if (nextReconnectDelay > 30000) - nextReconnectDelay = 30000; - - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - if (cancelToken.IsCancellationRequested) return; - await ConnectInternalAsync().ConfigureAwait(false); - } - finally { _connectionLock.Release(); } + if (cancelToken.IsCancellationRequested) return; + await ConnectInternalAsync(true).ConfigureAwait(false); + _reconnectTask = null; return; } catch (Exception ex) { await _gatewayLogger.WarningAsync("Reconnect failed", ex).ConfigureAwait(false); } + finally { _connectionLock.Release(); } } } - catch (OperationCanceledException) { } - finally + catch (OperationCanceledException) { - _reconnectTask = null; + await _connectionLock.WaitAsync().ConfigureAwait(false); + try + { + await _gatewayLogger.DebugAsync("Reconnect cancelled").ConfigureAwait(false); + _reconnectTask = null; + } + finally { _connectionLock.Release(); } } }