@@ -16,8 +16,8 @@ namespace Discord.Net.Queue | |||
{ | |||
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>(); | |||
@@ -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) | |||
@@ -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); | |||
@@ -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) | |||
@@ -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); | |||
} | |||
@@ -118,7 +118,16 @@ namespace Discord.WebSocket | |||
/// </summary> | |||
/// <param name="config">The configuration to be used with the client.</param> | |||
#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<ulong>(); | |||
} | |||
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); | |||
/// <inheritdoc /> | |||
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) | |||
@@ -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 | |||
/// </summary> | |||
public bool GuildSubscriptions { get; set; } = true; | |||
internal RequestQueue _websocketRequestQueue; | |||
/// <summary> | |||
/// Initializes a default configuration. | |||
/// </summary> | |||
@@ -131,6 +134,7 @@ namespace Discord.WebSocket | |||
{ | |||
WebSocketProvider = DefaultWebSocketProvider.Instance; | |||
UdpSocketProvider = DefaultUdpSocketProvider.Instance; | |||
_websocketRequestQueue = new RequestQueue(); | |||
} | |||
internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; | |||