- 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 | |||
| { | |||
| 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() | |||
| { | |||
| 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[] | |||
| { | |||
| 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>(); | |||
| @@ -50,6 +34,9 @@ namespace Discord.Net.Queue | |||
| DefsById = builder2.ToImmutable(); | |||
| } | |||
| public static GatewayBucket Get(GatewayBucketType type) => DefsByType[type]; | |||
| public static GatewayBucket Get(string id) => DefsById[id]; | |||
| public GatewayBucketType Type { get; } | |||
| public string Id { get; } | |||
| public int WindowCount { get; set; } | |||
| @@ -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, config.GatewayLimits, | |||
| => new DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config, | |||
| rateLimitPrecision: config.RateLimitPrecision, | |||
| useSystemClock: config.UseSystemClock); | |||
| @@ -6,7 +6,6 @@ using System.IO; | |||
| using System.Linq; | |||
| using System.Threading.Tasks; | |||
| using System.Threading; | |||
| using Discord.Net.Queue; | |||
| namespace Discord.WebSocket | |||
| { | |||
| @@ -67,7 +66,6 @@ namespace Discord.WebSocket | |||
| config.DisplayInitialLog = false; | |||
| _baseConfig = config; | |||
| _connectionGroupLock = new SemaphoreSlim(1, 1); | |||
| GatewayBucket.SetLimits(GatewayLimits.GetOrCreate(config.GatewayLimits)); | |||
| if (config.TotalShards == null) | |||
| _automaticShards = true; | |||
| @@ -81,15 +79,15 @@ namespace Discord.WebSocket | |||
| _shardIdsToIndex.Add(_shardIds[i], i); | |||
| var newConfig = config.Clone(); | |||
| 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); | |||
| RegisterEvents(_shards[i], i == 0); | |||
| } | |||
| } | |||
| } | |||
| 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); | |||
| internal override async Task OnLoginAsync(TokenType tokenType, string token) | |||
| @@ -101,7 +99,7 @@ namespace Discord.WebSocket | |||
| _totalShards = _shardIds.Length; | |||
| _shards = new DiscordSocketClient[_shardIds.Length]; | |||
| int maxConcurrency = botGateway.SessionStartLimit.MaxConcurrency; | |||
| _baseConfig.GatewayLimits.IdentifyMaxConcurrency = maxConcurrency; | |||
| _baseConfig.IdentifyMaxConcurrency = maxConcurrency; | |||
| for (int i = 0; i < _shardIds.Length; i++) | |||
| { | |||
| _shardIdsToIndex.Add(_shardIds[i], i); | |||
| @@ -109,7 +107,7 @@ namespace Discord.WebSocket | |||
| newConfig.ShardId = _shardIds[i]; | |||
| newConfig.TotalShards = _totalShards; | |||
| if (maxConcurrency != 1) | |||
| newConfig.GatewayLimits.IdentifySemaphoreName += $"_{i / maxConcurrency}"; | |||
| newConfig.IdentifySemaphoreName += $"_{i / maxConcurrency}"; | |||
| _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); | |||
| RegisterEvents(_shards[i], i == 0); | |||
| } | |||
| @@ -38,11 +38,11 @@ namespace Discord.API | |||
| 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, | |||
| RateLimitPrecision rateLimitPrecision = RateLimitPrecision.Second, | |||
| 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; | |||
| if (url != null) | |||
| @@ -119,10 +119,7 @@ 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) | |||
| { | |||
| 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) { } | |||
| #pragma warning restore IDISP004 | |||
| private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock, DiscordSocketClient parentClient) | |||
| @@ -182,7 +179,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.GatewayLimits, config.GatewayHost, | |||
| => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config, config.GatewayHost, | |||
| rateLimitPrecision: config.RateLimitPrecision); | |||
| /// <inheritdoc /> | |||
| internal override void Dispose(bool disposing) | |||
| @@ -1,7 +1,7 @@ | |||
| using Discord.Net.Queue; | |||
| using Discord.Net.Udp; | |||
| using Discord.Net.WebSockets; | |||
| using Discord.Rest; | |||
| using System; | |||
| namespace Discord.WebSocket | |||
| { | |||
| @@ -126,14 +126,32 @@ namespace Discord.WebSocket | |||
| public bool GuildSubscriptions { get; set; } = true; | |||
| /// <summary> | |||
| /// Gets or sets the gateway limits. | |||
| /// Gets or sets the name of the master <see cref="System.Threading.Semaphore"/> | |||
| /// used by identify. | |||
| /// </summary> | |||
| /// <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> | |||
| 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> | |||
| /// Initializes a default configuration. | |||
| @@ -144,11 +162,6 @@ namespace Discord.WebSocket | |||
| UdpSocketProvider = DefaultUdpSocketProvider.Instance; | |||
| } | |||
| internal DiscordSocketConfig Clone() | |||
| { | |||
| var clone = MemberwiseClone() as DiscordSocketConfig; | |||
| clone.GatewayLimits = GatewayLimits.Clone(); | |||
| return clone; | |||
| } | |||
| internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; | |||
| } | |||
| } | |||