diff --git a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs index 6fba39c5d..a81495001 100644 --- a/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs +++ b/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/RemoteCommunication.cs @@ -20,6 +20,7 @@ // limitations under the License. using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -60,12 +61,12 @@ internal sealed class RemoteCommunication : IAsyncDisposable, IDisposable { private const byte MinimumSteamGuardEnabledDays = 15; // As imposed by Steam limits private const byte MinPersonaStateTTL = 5; // Minimum amount of minutes we must wait before requesting persona state update - private static readonly ImmutableHashSet AcceptedMatchableTypes = ImmutableHashSet.Create( + private static readonly FrozenSet AcceptedMatchableTypes = new HashSet(4) { Asset.EType.Emoticon, Asset.EType.FoilTradingCard, Asset.EType.ProfileBackground, Asset.EType.TradingCard - ); + }.ToFrozenSet(); private readonly Bot Bot; private readonly Timer? HeartBeatTimer; diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs index f038f2dc4..012f736af 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs @@ -21,8 +21,8 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Linq; @@ -39,7 +39,7 @@ using SteamKit2; namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper; internal sealed class GlobalCache : SerializableFile { - internal static readonly ArchiCacheable> KnownDepotIDs = new(ResolveKnownDepotIDs, TimeSpan.FromDays(7)); + internal static readonly ArchiCacheable> KnownDepotIDs = new(ResolveKnownDepotIDs, TimeSpan.FromDays(7)); private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, $"{nameof(SteamTokenDumper)}.cache"); @@ -306,7 +306,7 @@ internal sealed class GlobalCache : SerializableFile { return (depotKey.Length == 64) && Utilities.IsValidHexadecimalText(depotKey); } - private static async Task<(bool Success, ImmutableHashSet? Result)> ResolveKnownDepotIDs(CancellationToken cancellationToken = default) { + private static async Task<(bool Success, FrozenSet? Result)> ResolveKnownDepotIDs(CancellationToken cancellationToken = default) { if (ASF.WebBrowser == null) { throw new InvalidOperationException(nameof(ASF.WebBrowser)); } @@ -343,7 +343,7 @@ internal sealed class GlobalCache : SerializableFile { result.Add(depotID); } - return (result.Count > 0, result.ToImmutableHashSet()); + return (result.Count > 0, result.ToFrozenSet()); } catch (Exception e) { ASF.ArchiLogger.LogGenericWarningException(e); diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs index 9e4da397f..635a194f5 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs @@ -21,8 +21,8 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel; using System.Composition; using System.Globalization; @@ -384,7 +384,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotFinishedRetrievingTotalAppAccessTokens, appIDsToRefresh.Count)); bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotRetrievingTotalDepots, appIDsToRefresh.Count)); - (_, ImmutableHashSet? knownDepotIDs) = await GlobalCache.KnownDepotIDs.GetValue(ECacheFallback.SuccessPreviously).ConfigureAwait(false); + (_, FrozenSet? knownDepotIDs) = await GlobalCache.KnownDepotIDs.GetValue(ECacheFallback.SuccessPreviously).ConfigureAwait(false); using (HashSet.Enumerator enumerator = appIDsToRefresh.GetEnumerator()) { while (true) { diff --git a/ArchiSteamFarm/Core/ASF.cs b/ArchiSteamFarm/Core/ASF.cs index 0d0507411..30c428ac5 100644 --- a/ArchiSteamFarm/Core/ASF.cs +++ b/ArchiSteamFarm/Core/ASF.cs @@ -21,8 +21,8 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -75,9 +75,9 @@ public static class ASF { internal static ICrossProcessSemaphore? LoginRateLimitingSemaphore { get; private set; } internal static ICrossProcessSemaphore? LoginSemaphore { get; private set; } internal static ICrossProcessSemaphore? RateLimitingSemaphore { get; private set; } - internal static ImmutableDictionary? WebLimitingSemaphores { get; private set; } + internal static FrozenDictionary? WebLimitingSemaphores { get; private set; } - private static readonly ImmutableHashSet AssembliesNeededBeforeUpdate = ImmutableHashSet.Create(StringComparer.Ordinal, "System.IO.Pipes"); + private static readonly FrozenSet AssembliesNeededBeforeUpdate = new HashSet(1, StringComparer.Ordinal) { "System.IO.Pipes" }.ToFrozenSet(StringComparer.Ordinal); private static readonly SemaphoreSlim UpdateSemaphore = new(1, 1); private static Timer? AutoUpdatesTimer; @@ -441,7 +441,7 @@ public static class ASF { { ArchiWebHandler.SteamHelpURL, (await PluginsCore.GetCrossProcessSemaphore($"{nameof(ArchiWebHandler)}-{nameof(ArchiWebHandler.SteamHelpURL)}").ConfigureAwait(false), new SemaphoreSlim(WebBrowser.MaxConnections, WebBrowser.MaxConnections)) }, { ArchiWebHandler.SteamStoreURL, (await PluginsCore.GetCrossProcessSemaphore($"{nameof(ArchiWebHandler)}-{nameof(ArchiWebHandler.SteamStoreURL)}").ConfigureAwait(false), new SemaphoreSlim(WebBrowser.MaxConnections, WebBrowser.MaxConnections)) }, { WebAPI.DefaultBaseAddress, (await PluginsCore.GetCrossProcessSemaphore($"{nameof(ArchiWebHandler)}-{nameof(WebAPI)}").ConfigureAwait(false), new SemaphoreSlim(WebBrowser.MaxConnections, WebBrowser.MaxConnections)) } - }.ToImmutableDictionary(); + }.ToFrozenDictionary(); } private static void LoadAllAssemblies() { diff --git a/ArchiSteamFarm/Core/Utilities.cs b/ArchiSteamFarm/Core/Utilities.cs index dbc70dd4e..8693c4559 100644 --- a/ArchiSteamFarm/Core/Utilities.cs +++ b/ArchiSteamFarm/Core/Utilities.cs @@ -21,8 +21,8 @@ using System; using System.Collections; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.Globalization; using System.IdentityModel.Tokens.Jwt; using System.IO; @@ -48,7 +48,7 @@ public static class Utilities { private const byte TimeoutForLongRunningTasksInSeconds = 60; // normally we'd just use words like "steam" and "farm", but the library we're currently using is a bit iffy about banned words, so we need to also add combinations such as "steamfarm" - private static readonly ImmutableHashSet ForbiddenPasswordPhrases = ImmutableHashSet.Create(StringComparer.InvariantCultureIgnoreCase, "archisteamfarm", "archi", "steam", "farm", "archisteam", "archifarm", "steamfarm", "asf", "asffarm", "password"); + private static readonly FrozenSet ForbiddenPasswordPhrases = new HashSet(10, StringComparer.InvariantCultureIgnoreCase) { "archisteamfarm", "archi", "steam", "farm", "archisteam", "archifarm", "steamfarm", "asf", "asffarm", "password" }.ToFrozenSet(StringComparer.InvariantCultureIgnoreCase); private static readonly JwtSecurityTokenHandler JwtSecurityTokenHandler = new(); diff --git a/ArchiSteamFarm/Helpers/ArchiCryptoHelper.cs b/ArchiSteamFarm/Helpers/ArchiCryptoHelper.cs index 889fa26cf..dc0a49b70 100644 --- a/ArchiSteamFarm/Helpers/ArchiCryptoHelper.cs +++ b/ArchiSteamFarm/Helpers/ArchiCryptoHelper.cs @@ -20,8 +20,8 @@ // limitations under the License. using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel; using System.Globalization; using System.IO; @@ -45,7 +45,7 @@ public static class ArchiCryptoHelper { internal static bool HasDefaultCryptKey { get; private set; } = true; - private static readonly ImmutableHashSet ForbiddenCryptKeyPhrases = ImmutableHashSet.Create(StringComparer.InvariantCultureIgnoreCase, "crypt", "key", "cryptkey"); + private static readonly FrozenSet ForbiddenCryptKeyPhrases = new HashSet(3, StringComparer.InvariantCultureIgnoreCase) { "crypt", "key", "cryptkey" }.ToFrozenSet(StringComparer.InvariantCultureIgnoreCase); private static IEnumerable SteamParentalCharacters => Enumerable.Range('0', 10).Select(static character => (byte) character); diff --git a/ArchiSteamFarm/Plugins/PluginsCore.cs b/ArchiSteamFarm/Plugins/PluginsCore.cs index 3a716984e..d9092447f 100644 --- a/ArchiSteamFarm/Plugins/PluginsCore.cs +++ b/ArchiSteamFarm/Plugins/PluginsCore.cs @@ -20,8 +20,8 @@ // limitations under the License. using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel; using System.Composition; using System.Composition.Convention; @@ -52,7 +52,7 @@ public static class PluginsCore { internal static bool HasCustomPluginsLoaded => ActivePlugins?.Any(static plugin => plugin is not OfficialPlugin officialPlugin || !officialPlugin.HasSameVersion()) == true; [ImportMany] - internal static ImmutableHashSet? ActivePlugins { get; private set; } + internal static FrozenSet? ActivePlugins { get; private set; } [PublicAPI] public static async Task GetCrossProcessSemaphore(string objectName) { @@ -230,7 +230,7 @@ public static class PluginsCore { } } - ActivePlugins = activePlugins.ToImmutableHashSet(); + ActivePlugins = activePlugins.ToFrozenSet(); if (HasCustomPluginsLoaded) { ASF.ArchiLogger.LogGenericInfo(Strings.PluginsWarning); diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index b9ae3dd25..f5cc498da 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -20,8 +20,8 @@ // limitations under the License. using System; +using System.Collections.Frozen; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.IO; @@ -58,7 +58,7 @@ internal static class Program { private static readonly Dictionary RegisteredPosixSignals = new(); private static readonly TaskCompletionSource ShutdownResetEvent = new(); - private static readonly ImmutableHashSet SupportedPosixSignals = ImmutableHashSet.Create(PosixSignal.SIGINT, PosixSignal.SIGTERM); + private static readonly FrozenSet SupportedPosixSignals = new HashSet(2) { PosixSignal.SIGINT, PosixSignal.SIGTERM }.ToFrozenSet(); private static bool IgnoreUnsupportedEnvironment; private static bool InputCryptkeyManually; diff --git a/ArchiSteamFarm/Steam/Bot.cs b/ArchiSteamFarm/Steam/Bot.cs index d582eece4..7a9dddf31 100644 --- a/ArchiSteamFarm/Steam/Bot.cs +++ b/ArchiSteamFarm/Steam/Bot.cs @@ -22,6 +22,7 @@ using System; using System.Collections; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.Specialized; @@ -206,7 +207,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { [JsonIgnore] [PublicAPI] - public ImmutableDictionary OwnedPackageIDs { get; private set; } = ImmutableDictionary.Empty; + public FrozenDictionary OwnedPackageIDs { get; private set; } = FrozenDictionary.Empty; [JsonProperty] [PublicAPI] @@ -1355,7 +1356,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { } internal async Task<(Dictionary? UnusedKeys, Dictionary? UsedKeys)> GetUsedAndUnusedKeys() { - string[] files = { GetFilePath(EFileType.KeysToRedeemUnused), GetFilePath(EFileType.KeysToRedeemUsed) }; + string[] files = [GetFilePath(EFileType.KeysToRedeemUnused), GetFilePath(EFileType.KeysToRedeemUsed)]; IList?> results = await Utilities.InParallel(files.Select(GetKeysFromFile)).ConfigureAwait(false); @@ -2740,7 +2741,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { Trading.OnDisconnected(); FirstTradeSent = false; - OwnedPackageIDs = ImmutableDictionary.Empty; + OwnedPackageIDs = FrozenDictionary.Empty; EResult lastLogOnResult = LastLogOnResult; @@ -3057,7 +3058,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable { } } - OwnedPackageIDs = ownedPackageIDs.ToImmutableDictionary(); + OwnedPackageIDs = ownedPackageIDs.ToFrozenDictionary(); if (packageAccessTokens.Count > 0) { ASF.GlobalDatabase.RefreshPackageAccessTokens(packageAccessTokens); diff --git a/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs b/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs index 88d979eef..770960a20 100644 --- a/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs +++ b/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -54,12 +55,12 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable { private const byte HoursToIgnore = 1; // How many hours we ignore unreleased appIDs and don't bother checking them again [PublicAPI] - public static readonly ImmutableHashSet SalesBlacklist = ImmutableHashSet.Create(267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900, 762800, 876740, 991980, 1195670, 1343890, 1465680, 1658760, 1797760, 2021850, 2243720, 2459330, 2640280); + public static readonly FrozenSet SalesBlacklist = new HashSet(20) { 267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900, 762800, 876740, 991980, 1195670, 1343890, 1465680, 1658760, 1797760, 2021850, 2243720, 2459330, 2640280 }.ToFrozenSet(); private static readonly ConcurrentDictionary GloballyIgnoredAppIDs = new(); // Reserved for unreleased games // Games that were confirmed to show false status on general badges page - private static readonly ImmutableHashSet UntrustedAppIDs = ImmutableHashSet.Create(440, 570, 730); + private static readonly FrozenSet UntrustedAppIDs = new HashSet(3) { 440, 570, 730 }.ToFrozenSet(); [JsonProperty(nameof(CurrentGamesFarming))] [PublicAPI] @@ -1210,7 +1211,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable { private async Task IsAnythingToFarmRisky() { Task?> boosterCreatorEntriesTask = Bot.ArchiWebHandler.GetBoosterCreatorEntries(); - ImmutableHashSet? boosterElibility = await Bot.ArchiWebHandler.GetBoosterEligibility().ConfigureAwait(false); + HashSet? boosterElibility = await Bot.ArchiWebHandler.GetBoosterEligibility().ConfigureAwait(false); if (boosterElibility == null) { Bot.ArchiLogger.LogGenericWarning(Strings.WarningCouldNotCheckBadges); @@ -1399,11 +1400,11 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable { HashSet? marketableAppIDs = await Bot.GetMarketableAppIDs().ConfigureAwait(false); if (marketableAppIDs?.Count > 0) { - ImmutableHashSet immutableMarketableAppIDs = marketableAppIDs.ToImmutableHashSet(); + HashSet marketableAppIDsCopy = marketableAppIDs; orderedGamesToFarm = farmingOrder switch { - BotConfig.EFarmingOrder.MarketableAscending => orderedGamesToFarm.ThenBy(game => immutableMarketableAppIDs.Contains(game.AppID)), - BotConfig.EFarmingOrder.MarketableDescending => orderedGamesToFarm.ThenByDescending(game => immutableMarketableAppIDs.Contains(game.AppID)), + BotConfig.EFarmingOrder.MarketableAscending => orderedGamesToFarm.ThenBy(game => marketableAppIDsCopy.Contains(game.AppID)), + BotConfig.EFarmingOrder.MarketableDescending => orderedGamesToFarm.ThenByDescending(game => marketableAppIDsCopy.Contains(game.AppID)), _ => throw new InvalidOperationException(nameof(farmingOrder)) }; } @@ -1456,14 +1457,12 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable { redeemDates[game.AppID] = redeemDate; } - ImmutableDictionary immutableRedeemDates = redeemDates.ToImmutableDictionary(); - orderedGamesToFarm = farmingOrder switch { // ReSharper disable once AccessToModifiedClosure - you're wrong - BotConfig.EFarmingOrder.RedeemDateTimesAscending => orderedGamesToFarm.ThenBy(game => immutableRedeemDates[game.AppID]), + BotConfig.EFarmingOrder.RedeemDateTimesAscending => orderedGamesToFarm.ThenBy(game => redeemDates[game.AppID]), // ReSharper disable once AccessToModifiedClosure - you're wrong - BotConfig.EFarmingOrder.RedeemDateTimesDescending => orderedGamesToFarm.ThenByDescending(game => immutableRedeemDates[game.AppID]), + BotConfig.EFarmingOrder.RedeemDateTimesDescending => orderedGamesToFarm.ThenByDescending(game => redeemDates[game.AppID]), _ => throw new InvalidOperationException(nameof(farmingOrder)) }; diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index b5b36bd63..1baa55e98 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -188,7 +188,7 @@ public sealed class ArchiWebHandler : IDisposable { } [PublicAPI] - public async Task?> GetBoosterEligibility() { + public async Task?> GetBoosterEligibility() { Uri request = new(SteamCommunityURL, "/my/ajaxgetboostereligibility"); using HtmlDocumentResponse? response = await UrlGetToHtmlDocumentWithSession(request, checkSessionPreemptively: false).ConfigureAwait(false); @@ -227,7 +227,7 @@ public sealed class ArchiWebHandler : IDisposable { result.Add(appID); } - return result.ToImmutableHashSet(); + return result; } [PublicAPI] diff --git a/ArchiSteamFarm/Storage/GlobalConfig.cs b/ArchiSteamFarm/Storage/GlobalConfig.cs index 1e287b4b2..59b9440bd 100644 --- a/ArchiSteamFarm/Storage/GlobalConfig.cs +++ b/ArchiSteamFarm/Storage/GlobalConfig.cs @@ -20,6 +20,7 @@ // limitations under the License. using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; @@ -138,7 +139,7 @@ public sealed class GlobalConfig { [PublicAPI] public static readonly Guid? DefaultLicenseID; - private static readonly ImmutableHashSet ForbiddenIPCPasswordPhrases = ImmutableHashSet.Create(StringComparer.InvariantCultureIgnoreCase, "ipc", "api", "gui", "asf-ui", "asf-gui"); + private static readonly FrozenSet ForbiddenIPCPasswordPhrases = new HashSet(5, StringComparer.InvariantCultureIgnoreCase) { "ipc", "api", "gui", "asf-ui", "asf-gui" }.ToFrozenSet(StringComparer.InvariantCultureIgnoreCase); [JsonIgnore] [PublicAPI]