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