- Remove GatewayLimits - Transfer a few properties to DiscordSocketConfig - Remove unnecessary usingspull/1537/head
@@ -1,29 +0,0 @@ | |||||
namespace Discord.Rest | |||||
{ | |||||
/// <summary> | |||||
/// Represents the limits for a gateway request. | |||||
/// </summary> | |||||
public struct GatewayLimit | |||||
{ | |||||
/// <summary> | |||||
/// Gets or sets the maximum amount of this type of request in a time frame, that is set by <see cref="Seconds"/>. | |||||
/// </summary> | |||||
/// <returns> | |||||
/// Returns the maximum amount of this type of request in a time frame to not trigger the rate limit. | |||||
/// </returns> | |||||
public int Count { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the amount of seconds until the rate limiter resets the remaining requests back to <see cref="Count"/>. | |||||
/// </summary> | |||||
/// <returns> | |||||
/// Returns the amount of seconds that define the time frame to reset. | |||||
/// </returns> | |||||
public int Seconds { get; set; } | |||||
internal GatewayLimit(int count, int seconds) | |||||
{ | |||||
Count = count; | |||||
Seconds = seconds; | |||||
} | |||||
} | |||||
} |
@@ -1,100 +0,0 @@ | |||||
using System; | |||||
namespace Discord.Rest | |||||
{ | |||||
/// <summary> | |||||
/// Contains the rate limits for the gateway. | |||||
/// </summary> | |||||
public class GatewayLimits | |||||
{ | |||||
/// <summary> | |||||
/// Creates a new <see cref="GatewayLimits"/> with the default values. | |||||
/// </summary> | |||||
public static GatewayLimits Default => new GatewayLimits(); | |||||
/// <summary> | |||||
/// Gets or sets the global limits for the gateway rate limiter. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// This property includes all the other limits, like Identify, | |||||
/// and it is per websocket. | |||||
/// </remarks> | |||||
public GatewayLimit Global { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the limits of Identify requests. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// This limit is included into <see cref="Global"/> but it is | |||||
/// also per account. | |||||
/// </remarks> | |||||
public GatewayLimit Identify { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the limits of Presence Update requests. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Presence updates include activity (playing, watching, etc) | |||||
/// and status (online, idle, etc) | |||||
/// </remarks> | |||||
public GatewayLimit PresenceUpdate { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the name of the master <see cref="System.Threading.Semaphore"/> | |||||
/// used by identify. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// It is used to define what slave <see cref="System.Threading.Semaphore"/> | |||||
/// is free to run for concurrent identify requests. | |||||
/// </remarks> | |||||
public string IdentifyMasterSemaphoreName { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the name of the slave <see cref="System.Threading.Semaphore"/> | |||||
/// used by identify. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// If the maximum concurrency is higher than one and you are using the sharded client, | |||||
/// it will be dinamilly renamed to fit the necessary needs. | |||||
/// </remarks> | |||||
public string IdentifySemaphoreName { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the maximum identify concurrency. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// This limit is provided by Discord. | |||||
/// </remarks> | |||||
public int IdentifyMaxConcurrency { get; set; } | |||||
/// <summary> | |||||
/// Initializes a new <see cref="GatewayLimits"/> with the default values. | |||||
/// </summary> | |||||
public GatewayLimits() | |||||
{ | |||||
Global = new GatewayLimit(120, 60); | |||||
Identify = new GatewayLimit(1, 5); | |||||
PresenceUpdate = new GatewayLimit(5, 60); | |||||
IdentifyMasterSemaphoreName = Guid.NewGuid().ToString(); | |||||
IdentifySemaphoreName = Guid.NewGuid().ToString(); | |||||
IdentifyMaxConcurrency = 1; | |||||
} | |||||
internal GatewayLimits(GatewayLimits limits) | |||||
{ | |||||
Global = new GatewayLimit(limits.Global.Count, limits.Global.Seconds); | |||||
Identify = new GatewayLimit(limits.Identify.Count, limits.Identify.Seconds); | |||||
PresenceUpdate = new GatewayLimit(limits.PresenceUpdate.Count, limits.PresenceUpdate.Seconds); | |||||
IdentifyMasterSemaphoreName = limits.IdentifyMasterSemaphoreName; | |||||
IdentifySemaphoreName = limits.IdentifySemaphoreName; | |||||
IdentifyMaxConcurrency = limits.IdentifyMaxConcurrency; | |||||
} | |||||
internal static GatewayLimits GetOrCreate(GatewayLimits limits) | |||||
=> limits ?? Default; | |||||
public GatewayLimits Clone() | |||||
=> new GatewayLimits(this); | |||||
} | |||||
} |
@@ -11,32 +11,16 @@ namespace Discord.Net.Queue | |||||
} | } | ||||
internal struct GatewayBucket | internal struct GatewayBucket | ||||
{ | { | ||||
private static ImmutableDictionary<GatewayBucketType, GatewayBucket> DefsByType; | |||||
private static ImmutableDictionary<string, GatewayBucket> DefsById; | |||||
private static readonly ImmutableDictionary<GatewayBucketType, GatewayBucket> DefsByType; | |||||
private static readonly ImmutableDictionary<string, GatewayBucket> DefsById; | |||||
static GatewayBucket() | static GatewayBucket() | ||||
{ | { | ||||
SetLimits(GatewayLimits.GetOrCreate(null)); | |||||
} | |||||
public static GatewayBucket Get(GatewayBucketType type) => DefsByType[type]; | |||||
public static GatewayBucket Get(string id) => DefsById[id]; | |||||
public static void SetLimits(GatewayLimits limits) | |||||
{ | |||||
limits = GatewayLimits.GetOrCreate(limits); | |||||
Preconditions.GreaterThan(limits.Global.Count, 0, nameof(limits.Global.Count), "Global count must be greater than zero."); | |||||
Preconditions.GreaterThan(limits.Global.Seconds, 0, nameof(limits.Global.Seconds), "Global seconds must be greater than zero."); | |||||
Preconditions.GreaterThan(limits.Identify.Count, 0, nameof(limits.Identify.Count), "Identify count must be greater than zero."); | |||||
Preconditions.GreaterThan(limits.Identify.Seconds, 0, nameof(limits.Identify.Seconds), "Identify seconds must be greater than zero."); | |||||
Preconditions.GreaterThan(limits.PresenceUpdate.Count, 0, nameof(limits.PresenceUpdate.Count), "PresenceUpdate count must be greater than zero."); | |||||
Preconditions.GreaterThan(limits.PresenceUpdate.Seconds, 0, nameof(limits.PresenceUpdate.Seconds), "PresenceUpdate seconds must be greater than zero."); | |||||
var buckets = new[] | var buckets = new[] | ||||
{ | { | ||||
new GatewayBucket(GatewayBucketType.Unbucketed, "<gateway-unbucketed>", limits.Global.Count, limits.Global.Seconds), | |||||
new GatewayBucket(GatewayBucketType.Identify, "<gateway-identify>", limits.Identify.Count, limits.Identify.Seconds), | |||||
new GatewayBucket(GatewayBucketType.PresenceUpdate, "<gateway-presenceupdate>", limits.Identify.Count, limits.Identify.Seconds), | |||||
new GatewayBucket(GatewayBucketType.Unbucketed, "<gateway-unbucketed>", 120, 60), | |||||
new GatewayBucket(GatewayBucketType.Identify, "<gateway-identify>", 1, 5), | |||||
new GatewayBucket(GatewayBucketType.PresenceUpdate, "<gateway-presenceupdate>", 5, 60), | |||||
}; | }; | ||||
var builder = ImmutableDictionary.CreateBuilder<GatewayBucketType, GatewayBucket>(); | var builder = ImmutableDictionary.CreateBuilder<GatewayBucketType, GatewayBucket>(); | ||||
@@ -50,6 +34,9 @@ namespace Discord.Net.Queue | |||||
DefsById = builder2.ToImmutable(); | DefsById = builder2.ToImmutable(); | ||||
} | } | ||||
public static GatewayBucket Get(GatewayBucketType type) => DefsByType[type]; | |||||
public static GatewayBucket Get(string id) => DefsById[id]; | |||||
public GatewayBucketType Type { get; } | public GatewayBucketType Type { get; } | ||||
public string Id { get; } | public string Id { get; } | ||||
public int WindowCount { get; set; } | public int WindowCount { get; set; } | ||||
@@ -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, config.GatewayLimits, | |||||
=> new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config, | |||||
rateLimitPrecision: config.RateLimitPrecision, | rateLimitPrecision: config.RateLimitPrecision, | ||||
useSystemClock: config.UseSystemClock); | useSystemClock: config.UseSystemClock); | ||||
@@ -6,7 +6,6 @@ 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 | ||||
{ | { | ||||
@@ -67,7 +66,6 @@ namespace Discord.WebSocket | |||||
config.DisplayInitialLog = false; | config.DisplayInitialLog = false; | ||||
_baseConfig = config; | _baseConfig = config; | ||||
_connectionGroupLock = new SemaphoreSlim(1, 1); | _connectionGroupLock = new SemaphoreSlim(1, 1); | ||||
GatewayBucket.SetLimits(GatewayLimits.GetOrCreate(config.GatewayLimits)); | |||||
if (config.TotalShards == null) | if (config.TotalShards == null) | ||||
_automaticShards = true; | _automaticShards = true; | ||||
@@ -81,15 +79,15 @@ namespace Discord.WebSocket | |||||
_shardIdsToIndex.Add(_shardIds[i], i); | _shardIdsToIndex.Add(_shardIds[i], i); | ||||
var newConfig = config.Clone(); | var newConfig = config.Clone(); | ||||
newConfig.ShardId = _shardIds[i]; | newConfig.ShardId = _shardIds[i]; | ||||
if (config.GatewayLimits.IdentifyMaxConcurrency != 1) | |||||
newConfig.GatewayLimits.IdentifySemaphoreName += $"_{i / config.GatewayLimits.IdentifyMaxConcurrency}"; | |||||
if (config.IdentifyMaxConcurrency != 1) | |||||
newConfig.IdentifySemaphoreName += $"_{i / config.IdentifyMaxConcurrency}"; | |||||
_shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); | _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); | ||||
RegisterEvents(_shards[i], i == 0); | RegisterEvents(_shards[i], i == 0); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) | ||||
=> new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayLimits, | |||||
=> new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config, | |||||
rateLimitPrecision: config.RateLimitPrecision); | rateLimitPrecision: config.RateLimitPrecision); | ||||
internal override async Task OnLoginAsync(TokenType tokenType, string token) | internal override async Task OnLoginAsync(TokenType tokenType, string token) | ||||
@@ -101,7 +99,7 @@ namespace Discord.WebSocket | |||||
_totalShards = _shardIds.Length; | _totalShards = _shardIds.Length; | ||||
_shards = new DiscordSocketClient[_shardIds.Length]; | _shards = new DiscordSocketClient[_shardIds.Length]; | ||||
int maxConcurrency = botGateway.SessionStartLimit.MaxConcurrency; | int maxConcurrency = botGateway.SessionStartLimit.MaxConcurrency; | ||||
_baseConfig.GatewayLimits.IdentifyMaxConcurrency = maxConcurrency; | |||||
_baseConfig.IdentifyMaxConcurrency = maxConcurrency; | |||||
for (int i = 0; i < _shardIds.Length; i++) | for (int i = 0; i < _shardIds.Length; i++) | ||||
{ | { | ||||
_shardIdsToIndex.Add(_shardIds[i], i); | _shardIdsToIndex.Add(_shardIds[i], i); | ||||
@@ -109,7 +107,7 @@ namespace Discord.WebSocket | |||||
newConfig.ShardId = _shardIds[i]; | newConfig.ShardId = _shardIds[i]; | ||||
newConfig.TotalShards = _totalShards; | newConfig.TotalShards = _totalShards; | ||||
if (maxConcurrency != 1) | if (maxConcurrency != 1) | ||||
newConfig.GatewayLimits.IdentifySemaphoreName += $"_{i / maxConcurrency}"; | |||||
newConfig.IdentifySemaphoreName += $"_{i / maxConcurrency}"; | |||||
_shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); | _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); | ||||
RegisterEvents(_shards[i], i == 0); | RegisterEvents(_shards[i], i == 0); | ||||
} | } | ||||
@@ -38,11 +38,11 @@ namespace Discord.API | |||||
public ConnectionState ConnectionState { get; private set; } | public ConnectionState ConnectionState { get; private set; } | ||||
public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, GatewayLimits limits, | |||||
public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, DiscordSocketConfig config, | |||||
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) | ||||
: base(restClientProvider, userAgent, new RequestQueue(limits.IdentifyMasterSemaphoreName, limits.IdentifySemaphoreName, limits.IdentifyMaxConcurrency), defaultRetryMode, serializer, rateLimitPrecision, useSystemClock) | |||||
: base(restClientProvider, userAgent, new RequestQueue(config.IdentifyMasterSemaphoreName, config.IdentifySemaphoreName, config.IdentifyMaxConcurrency), defaultRetryMode, serializer, rateLimitPrecision, useSystemClock) | |||||
{ | { | ||||
_gatewayUrl = url; | _gatewayUrl = url; | ||||
if (url != null) | if (url != null) | ||||
@@ -119,10 +119,7 @@ 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) | |||||
{ | |||||
GatewayBucket.SetLimits(GatewayLimits.GetOrCreate(config.GatewayLimits)); | |||||
} | |||||
public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null) { } | |||||
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) | ||||
@@ -182,7 +179,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.GatewayLimits, config.GatewayHost, | |||||
=> new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config, config.GatewayHost, | |||||
rateLimitPrecision: config.RateLimitPrecision); | rateLimitPrecision: config.RateLimitPrecision); | ||||
/// <inheritdoc /> | /// <inheritdoc /> | ||||
internal override void Dispose(bool disposing) | internal override void Dispose(bool disposing) | ||||
@@ -1,7 +1,7 @@ | |||||
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; | ||||
using System; | |||||
namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
{ | { | ||||
@@ -126,14 +126,32 @@ namespace Discord.WebSocket | |||||
public bool GuildSubscriptions { get; set; } = true; | public bool GuildSubscriptions { get; set; } = true; | ||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the gateway limits. | |||||
/// Gets or sets the name of the master <see cref="System.Threading.Semaphore"/> | |||||
/// used by identify. | |||||
/// </summary> | /// </summary> | ||||
/// <remarks> | /// <remarks> | ||||
/// <note type="warning"> | |||||
/// This property should only be changed for bots that have special limits provided by Discord. | |||||
/// </note> | |||||
/// It is used to define what slave <see cref="System.Threading.Semaphore"/> | |||||
/// is free to run for concurrent identify requests. | |||||
/// </remarks> | /// </remarks> | ||||
public GatewayLimits GatewayLimits { get; set; } = GatewayLimits.Default; | |||||
public string IdentifyMasterSemaphoreName { get; set; } = Guid.NewGuid().ToString(); | |||||
/// <summary> | |||||
/// Gets or sets the name of the slave <see cref="System.Threading.Semaphore"/> | |||||
/// used by identify. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// If the maximum concurrency is higher than one and you are using the sharded client, | |||||
/// it will be dinamilly renamed to fit the necessary needs. | |||||
/// </remarks> | |||||
public string IdentifySemaphoreName { get; set; } = Guid.NewGuid().ToString(); | |||||
/// <summary> | |||||
/// Gets or sets the maximum identify concurrency. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// This information is provided by Discord. | |||||
/// </remarks> | |||||
public int IdentifyMaxConcurrency { get; set; } = 1; | |||||
/// <summary> | /// <summary> | ||||
/// Initializes a default configuration. | /// Initializes a default configuration. | ||||
@@ -144,11 +162,6 @@ namespace Discord.WebSocket | |||||
UdpSocketProvider = DefaultUdpSocketProvider.Instance; | UdpSocketProvider = DefaultUdpSocketProvider.Instance; | ||||
} | } | ||||
internal DiscordSocketConfig Clone() | |||||
{ | |||||
var clone = MemberwiseClone() as DiscordSocketConfig; | |||||
clone.GatewayLimits = GatewayLimits.Clone(); | |||||
return clone; | |||||
} | |||||
internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; | |||||
} | } | ||||
} | } |