| @@ -28,6 +28,7 @@ namespace Discord.WebSocket | |||||
| private readonly JsonSerializer _serializer; | private readonly JsonSerializer _serializer; | ||||
| private readonly SemaphoreSlim _connectionGroupLock; | private readonly SemaphoreSlim _connectionGroupLock; | ||||
| private readonly DiscordSocketClient _parentClient; | private readonly DiscordSocketClient _parentClient; | ||||
| private readonly ConcurrentQueue<long> _heartbeatTimes; | |||||
| private string _sessionId; | private string _sessionId; | ||||
| private int _lastSeq; | private int _lastSeq; | ||||
| @@ -35,9 +36,8 @@ namespace Discord.WebSocket | |||||
| private TaskCompletionSource<bool> _connectTask; | private TaskCompletionSource<bool> _connectTask; | ||||
| private CancellationTokenSource _cancelToken, _reconnectCancelToken; | private CancellationTokenSource _cancelToken, _reconnectCancelToken; | ||||
| private Task _heartbeatTask, _guildDownloadTask, _reconnectTask; | private Task _heartbeatTask, _guildDownloadTask, _reconnectTask; | ||||
| private long _heartbeatTime; | |||||
| private int _unavailableGuilds; | private int _unavailableGuilds; | ||||
| private long _lastGuildAvailableTime; | |||||
| private long _lastGuildAvailableTime, _lastMessageTime; | |||||
| private int _nextAudioId; | private int _nextAudioId; | ||||
| private bool _canReconnect; | private bool _canReconnect; | ||||
| private DateTimeOffset? _statusSince; | private DateTimeOffset? _statusSince; | ||||
| @@ -93,6 +93,7 @@ namespace Discord.WebSocket | |||||
| ConnectionTimeout = config.ConnectionTimeout; | ConnectionTimeout = config.ConnectionTimeout; | ||||
| State = new ClientState(0, 0); | State = new ClientState(0, 0); | ||||
| _downloadUsersFor = new ConcurrentHashSet<ulong>(); | _downloadUsersFor = new ConcurrentHashSet<ulong>(); | ||||
| _heartbeatTimes = new ConcurrentQueue<long>(); | |||||
| _nextAudioId = 1; | _nextAudioId = 1; | ||||
| _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); | _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); | ||||
| @@ -292,6 +293,10 @@ namespace Discord.WebSocket | |||||
| await heartbeatTask.ConfigureAwait(false); | await heartbeatTask.ConfigureAwait(false); | ||||
| _heartbeatTask = null; | _heartbeatTask = null; | ||||
| long times; | |||||
| while (_heartbeatTimes.TryDequeue(out times)) { } | |||||
| _lastMessageTime = 0; | |||||
| await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); | ||||
| var guildDownloadTask = _guildDownloadTask; | var guildDownloadTask = _guildDownloadTask; | ||||
| if (guildDownloadTask != null) | if (guildDownloadTask != null) | ||||
| @@ -538,6 +543,8 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| if (seq != null) | if (seq != null) | ||||
| _lastSeq = seq.Value; | _lastSeq = seq.Value; | ||||
| _lastMessageTime = Environment.TickCount; | |||||
| try | try | ||||
| { | { | ||||
| switch (opCode) | switch (opCode) | ||||
| @@ -547,7 +554,6 @@ namespace Discord.WebSocket | |||||
| await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false); | ||||
| var data = (payload as JToken).ToObject<HelloEvent>(_serializer); | var data = (payload as JToken).ToObject<HelloEvent>(_serializer); | ||||
| _heartbeatTime = 0; | |||||
| _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token, _gatewayLogger); | _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token, _gatewayLogger); | ||||
| } | } | ||||
| break; | break; | ||||
| @@ -562,12 +568,10 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| await _gatewayLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false); | ||||
| var heartbeatTime = _heartbeatTime; | |||||
| if (heartbeatTime != 0) | |||||
| long time; | |||||
| if (_heartbeatTimes.TryDequeue(out time)) | |||||
| { | { | ||||
| int latency = (int)(Environment.TickCount - _heartbeatTime); | |||||
| _heartbeatTime = 0; | |||||
| int latency = (int)(Environment.TickCount - time); | |||||
| int before = Latency; | int before = Latency; | ||||
| Latency = latency; | Latency = latency; | ||||
| @@ -1693,7 +1697,10 @@ namespace Discord.WebSocket | |||||
| await logger.DebugAsync("Heartbeat Started").ConfigureAwait(false); | await logger.DebugAsync("Heartbeat Started").ConfigureAwait(false); | ||||
| while (!cancelToken.IsCancellationRequested) | while (!cancelToken.IsCancellationRequested) | ||||
| { | { | ||||
| if (_heartbeatTime != 0) //Server never responded to our last heartbeat | |||||
| var now = Environment.TickCount; | |||||
| //Did server respond to our last heartbeat, or are we still receiving messages (long load?) | |||||
| if (_heartbeatTimes.Count != 0 && (now - _lastMessageTime) > intervalMillis) | |||||
| { | { | ||||
| if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) | if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) | ||||
| { | { | ||||
| @@ -1702,7 +1709,7 @@ namespace Discord.WebSocket | |||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| _heartbeatTime = Environment.TickCount; | |||||
| _heartbeatTimes.Enqueue(now); | |||||
| try | try | ||||
| { | { | ||||