| @@ -16,8 +16,8 @@ namespace Discord.Net.Queue | |||||
| { | { | ||||
| var buckets = new[] | var buckets = new[] | ||||
| { | { | ||||
| new GatewayBucket(GatewayBucketType.Unbucketed, "<unbucketed>", 120, 60), | |||||
| new GatewayBucket(GatewayBucketType.Identify, "<identify>", 1, 5) | |||||
| new GatewayBucket(GatewayBucketType.Unbucketed, "<gateway-unbucketed>", 120, 60), | |||||
| new GatewayBucket(GatewayBucketType.Identify, "<gateway-identify>", 1, 5) | |||||
| }; | }; | ||||
| var builder = ImmutableDictionary.CreateBuilder<GatewayBucketType, GatewayBucket>(); | var builder = ImmutableDictionary.CreateBuilder<GatewayBucketType, GatewayBucket>(); | ||||
| @@ -332,6 +332,16 @@ namespace Discord.Net.Queue | |||||
| #if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
| Debug.WriteLine($"[{id}] Gateway Bucket ({GatewayBucket.Get(request.Options.BucketId).WindowSeconds * 1000} ms)"); | Debug.WriteLine($"[{id}] Gateway Bucket ({GatewayBucket.Get(request.Options.BucketId).WindowSeconds * 1000} ms)"); | ||||
| #endif | #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) | if (resetTick == null) | ||||
| @@ -80,7 +80,7 @@ namespace Discord.WebSocket | |||||
| internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) | internal BaseSocketClient(DiscordSocketConfig config, DiscordRestApiClient client) | ||||
| : base(config, client) => BaseConfig = config; | : base(config, client) => BaseConfig = config; | ||||
| private static DiscordSocketApiClient CreateApiClient(DiscordSocketConfig 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, | rateLimitPrecision: config.RateLimitPrecision, | ||||
| useSystemClock: config.UseSystemClock); | useSystemClock: config.UseSystemClock); | ||||
| @@ -6,6 +6,7 @@ using System.IO; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using System.Threading; | using System.Threading; | ||||
| using Discord.Net.Queue; | |||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| { | { | ||||
| @@ -83,9 +84,17 @@ namespace Discord.WebSocket | |||||
| RegisterEvents(_shards[i], i == 0); | 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) | 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); | rateLimitPrecision: config.RateLimitPrecision); | ||||
| internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
| @@ -37,7 +37,17 @@ namespace Discord.API | |||||
| public ConnectionState ConnectionState { get; private set; } | public ConnectionState ConnectionState { get; private set; } | ||||
| internal RequestQueue WebSocketRequestQueue { get; } | |||||
| public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, | 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, | string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, | ||||
| RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, | RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, | ||||
| bool useSystemClock = true) | bool useSystemClock = true) | ||||
| @@ -48,6 +58,7 @@ namespace Discord.API | |||||
| _isExplicitUrl = true; | _isExplicitUrl = true; | ||||
| WebSocketClient = webSocketProvider(); | WebSocketClient = webSocketProvider(); | ||||
| //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) | //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) | ||||
| WebSocketRequestQueue = websocketRequestQueue ?? new RequestQueue(); | |||||
| WebSocketClient.BinaryMessage += async (data, index, count) => | WebSocketClient.BinaryMessage += async (data, index, count) => | ||||
| { | { | ||||
| @@ -208,7 +219,7 @@ namespace Discord.API | |||||
| options.IsGatewayBucket = true; | options.IsGatewayBucket = true; | ||||
| options.BucketId = GatewayBucket.Get(opCode == GatewayOpCode.Identify ? GatewayBucketType.Identify : GatewayBucketType.Unbucketed).Id; | 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); | await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -118,7 +118,16 @@ namespace Discord.WebSocket | |||||
| /// </summary> | /// </summary> | ||||
| /// <param name="config">The configuration to be used with the client.</param> | /// <param name="config">The configuration to be used with the client.</param> | ||||
| #pragma warning disable IDISP004 | #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) { } | internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock, DiscordSocketClient parentClient) : this(config, CreateApiClient(config), groupLock, parentClient) { } | ||||
| #pragma warning restore IDISP004 | #pragma warning restore IDISP004 | ||||
| private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock, DiscordSocketClient parentClient) | private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock, DiscordSocketClient parentClient) | ||||
| @@ -178,7 +187,7 @@ namespace Discord.WebSocket | |||||
| _largeGuilds = new ConcurrentQueue<ulong>(); | _largeGuilds = new ConcurrentQueue<ulong>(); | ||||
| } | } | ||||
| private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | 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); | rateLimitPrecision: config.RateLimitPrecision); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| internal override void Dispose(bool disposing) | internal override void Dispose(bool disposing) | ||||
| @@ -254,10 +263,7 @@ namespace Discord.WebSocket | |||||
| finally | finally | ||||
| { | { | ||||
| if (_connectionGroupLock != null) | if (_connectionGroupLock != null) | ||||
| { | |||||
| await Task.Delay(5000).ConfigureAwait(false); | |||||
| _connectionGroupLock.Release(); | _connectionGroupLock.Release(); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| private async Task OnDisconnectingAsync(Exception ex) | private async Task OnDisconnectingAsync(Exception ex) | ||||
| @@ -1,3 +1,4 @@ | |||||
| using Discord.Net.Queue; | |||||
| using Discord.Net.Udp; | using Discord.Net.Udp; | ||||
| using Discord.Net.WebSockets; | using Discord.Net.WebSockets; | ||||
| using Discord.Rest; | using Discord.Rest; | ||||
| @@ -124,6 +125,8 @@ namespace Discord.WebSocket | |||||
| /// </summary> | /// </summary> | ||||
| public bool GuildSubscriptions { get; set; } = true; | public bool GuildSubscriptions { get; set; } = true; | ||||
| internal RequestQueue _websocketRequestQueue; | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a default configuration. | /// Initializes a default configuration. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -131,6 +134,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| WebSocketProvider = DefaultWebSocketProvider.Instance; | WebSocketProvider = DefaultWebSocketProvider.Instance; | ||||
| UdpSocketProvider = DefaultUdpSocketProvider.Instance; | UdpSocketProvider = DefaultUdpSocketProvider.Instance; | ||||
| _websocketRequestQueue = new RequestQueue(); | |||||
| } | } | ||||
| internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; | internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; | ||||