From a6b1633abe928a95530c4d667ba5fb44ce717c26 Mon Sep 17 00:00:00 2001 From: Paulo Date: Sun, 17 May 2020 23:18:49 -0300 Subject: [PATCH] Share WebSocketRequestQueue between clients --- src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs | 4 ++-- .../Net/Queue/RequestQueueBucket.cs | 10 ++++++++++ src/Discord.Net.WebSocket/BaseSocketClient.cs | 2 +- .../DiscordShardedClient.cs | 11 ++++++++++- .../DiscordSocketApiClient.cs | 13 ++++++++++++- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 16 +++++++++++----- src/Discord.Net.WebSocket/DiscordSocketConfig.cs | 4 ++++ 7 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs b/src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs index 1c2133fd4..a1b2b9a7a 100644 --- a/src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/GatewayBucket.cs @@ -16,8 +16,8 @@ namespace Discord.Net.Queue { var buckets = new[] { - new GatewayBucket(GatewayBucketType.Unbucketed, "", 120, 60), - new GatewayBucket(GatewayBucketType.Identify, "", 1, 5) + new GatewayBucket(GatewayBucketType.Unbucketed, "", 120, 60), + new GatewayBucket(GatewayBucketType.Identify, "", 1, 5) }; var builder = ImmutableDictionary.CreateBuilder(); diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index eaa22f024..758ba6783 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -332,6 +332,16 @@ namespace Discord.Net.Queue #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Gateway Bucket ({GatewayBucket.Get(request.Options.BucketId).WindowSeconds * 1000} ms)"); #endif + if (!hasQueuedReset) + { + _resetTick = resetTick; + LastAttemptAt = resetTick.Value; +#if DEBUG_LIMITS + Debug.WriteLine($"[{id}] Reset in {(int)Math.Ceiling((resetTick - DateTimeOffset.UtcNow).Value.TotalMilliseconds)} ms"); +#endif + var _ = QueueReset(id, (int)Math.Ceiling((_resetTick.Value - DateTimeOffset.UtcNow).TotalMilliseconds)); + } + return; } if (resetTick == null) diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index 548bb75bf..5d0c9b886 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -80,7 +80,7 @@ namespace Discord.WebSocket internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) : base(config, client) => BaseConfig = config; private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, + => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config._websocketRequestQueue, rateLimitPrecision: config.RateLimitPrecision, useSystemClock: config.UseSystemClock); diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 8359ca048..4eb539d0c 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Threading; +using Discord.Net.Queue; namespace Discord.WebSocket { @@ -83,9 +84,17 @@ namespace Discord.WebSocket RegisterEvents(_shards[i], i == 0); } } + + ApiClient.WebSocketRequestQueue.RateLimitTriggered += async (id, info) => + { + if (info == null) + await _restLogger.VerboseAsync($"Preemptive Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); + else + await _restLogger.WarningAsync($"Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); + }; } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config._websocketRequestQueue, rateLimitPrecision: config.RateLimitPrecision); internal override async Task OnLoginAsync(TokenType tokenType, string token) diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 0e076b664..1bf59994e 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -37,7 +37,17 @@ namespace Discord.API public ConnectionState ConnectionState { get; private set; } + internal RequestQueue WebSocketRequestQueue { get; } + public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, + string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, + RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, + bool useSystemClock = true) + : this(restClientProvider, webSocketProvider, userAgent, null, url, defaultRetryMode, serializer, rateLimitPrecision, useSystemClock) + { + } + + internal DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, RequestQueue websocketRequestQueue, string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, bool useSystemClock = true) @@ -48,6 +58,7 @@ namespace Discord.API _isExplicitUrl = true; WebSocketClient = webSocketProvider(); //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) + WebSocketRequestQueue = websocketRequestQueue ?? new RequestQueue(); WebSocketClient.BinaryMessage += async (data, index, count) => { @@ -208,7 +219,7 @@ namespace Discord.API options.IsGatewayBucket = true; options.BucketId = GatewayBucket.Get(opCode == GatewayOpCode.Identify ? GatewayBucketType.Identify : GatewayBucketType.Unbucketed).Id; - await RequestQueue.SendAsync(new WebSocketRequest(WebSocketClient, bytes, true, options)).ConfigureAwait(false); + await WebSocketRequestQueue.SendAsync(new WebSocketRequest(WebSocketClient, bytes, true, options)).ConfigureAwait(false); await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index be7432bc3..61d15cf19 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -118,7 +118,16 @@ namespace Discord.WebSocket /// /// The configuration to be used with the client. #pragma warning disable IDISP004 - public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null) { } + public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null) + { + ApiClient.WebSocketRequestQueue.RateLimitTriggered += async (id, info) => + { + if (info == null) + await _restLogger.VerboseAsync($"Preemptive Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); + else + await _restLogger.WarningAsync($"Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); + }; + } internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock, DiscordSocketClient parentClient) : this(config, CreateApiClient(config), groupLock, parentClient) { } #pragma warning restore IDISP004 private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock, DiscordSocketClient parentClient) @@ -178,7 +187,7 @@ namespace Discord.WebSocket _largeGuilds = new ConcurrentQueue(); } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost, + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config._websocketRequestQueue, config.GatewayHost, rateLimitPrecision: config.RateLimitPrecision); /// internal override void Dispose(bool disposing) @@ -254,10 +263,7 @@ namespace Discord.WebSocket finally { if (_connectionGroupLock != null) - { - await Task.Delay(5000).ConfigureAwait(false); _connectionGroupLock.Release(); - } } } private async Task OnDisconnectingAsync(Exception ex) diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 98ab0ef9b..91b597bbf 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -1,3 +1,4 @@ +using Discord.Net.Queue; using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; @@ -124,6 +125,8 @@ namespace Discord.WebSocket /// public bool GuildSubscriptions { get; set; } = true; + internal RequestQueue _websocketRequestQueue; + /// /// Initializes a default configuration. /// @@ -131,6 +134,7 @@ namespace Discord.WebSocket { WebSocketProvider = DefaultWebSocketProvider.Instance; UdpSocketProvider = DefaultUdpSocketProvider.Instance; + _websocketRequestQueue = new RequestQueue(); } internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig;