diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index d929abc40..9b5f7f13d 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -20,8 +20,10 @@ // limitations under the License. using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -659,7 +661,7 @@ namespace ArchiSteamFarm { } try { - SortedDictionary gamesToRedeemInBackground = new SortedDictionary(); + OrderedDictionary gamesToRedeemInBackground = new OrderedDictionary(); using (StreamReader reader = new StreamReader(filePath)) { string line; @@ -1159,35 +1161,43 @@ namespace ArchiSteamFarm { } } - internal async Task ValidateAndAddGamesToRedeemInBackground(SortedDictionary gamesToRedeemInBackground) { + internal async Task ValidateAndAddGamesToRedeemInBackground(OrderedDictionary gamesToRedeemInBackground) { if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) { ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground)); return; } - HashSet invalidGames = new HashSet(); + HashSet invalidKeys = new HashSet(); - foreach (KeyValuePair game in gamesToRedeemInBackground) { + foreach (DictionaryEntry game in gamesToRedeemInBackground) { bool invalid = false; - if (!IsValidCdKey(game.Key)) { + string key = game.Key as string; + if (string.IsNullOrEmpty(key)) { invalid = true; - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, game.Key)); + ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key))); + } else if (!IsValidCdKey(key)) { + invalid = true; + ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); } - if (string.IsNullOrEmpty(game.Value)) { + string name = game.Value as string; + if (string.IsNullOrEmpty(name)) { invalid = true; - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, game.Value)); + ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name))); + } else if (string.IsNullOrEmpty(name)) { + invalid = true; + ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, name)); } if (invalid) { - invalidGames.Add(game.Key); + invalidKeys.Add(game.Key); } } - if (invalidGames.Count > 0) { - foreach (string invalidGame in invalidGames) { - gamesToRedeemInBackground.Remove(invalidGame); + if (invalidKeys.Count > 0) { + foreach (string invalidKey in invalidKeys) { + gamesToRedeemInBackground.Remove(invalidKey); } if (gamesToRedeemInBackground.Count == 0) { @@ -2323,9 +2333,9 @@ namespace ArchiSteamFarm { } while (IsConnectedAndLoggedOn && BotDatabase.HasGamesToRedeemInBackground) { - KeyValuePair game = BotDatabase.GetGameToRedeemInBackground(); - if (string.IsNullOrEmpty(game.Key) || string.IsNullOrEmpty(game.Value)) { - ArchiLogger.LogNullError(nameof(game.Key) + " || " + nameof(game.Value)); + (string Key, string Name) game = BotDatabase.GetGameToRedeemInBackground(); + if (string.IsNullOrEmpty(game.Key) || string.IsNullOrEmpty(game.Name)) { + ArchiLogger.LogNullError(nameof(game.Key) + " || " + nameof(game.Name)); break; } @@ -2379,10 +2389,10 @@ namespace ArchiSteamFarm { if (shouldKeep) { try { - await File.AppendAllTextAsync(KeysToRedeemAlreadyOwnedFilePath, game.Value + "\t" + game.Key + " (" + result.PurchaseResultDetail + ")" + Environment.NewLine).ConfigureAwait(false); + await File.AppendAllTextAsync(KeysToRedeemAlreadyOwnedFilePath, game.Name + "\t" + game.Key + " [" + result.PurchaseResultDetail + "]" + Environment.NewLine).ConfigureAwait(false); } catch (Exception e) { ArchiLogger.LogGenericException(e); - await BotDatabase.AddGameToRedeemInBackground(game.Key, game.Value).ConfigureAwait(false); // Failsafe + await BotDatabase.AddGameToRedeemInBackground(game.Key, game.Name).ConfigureAwait(false); // Failsafe break; } } diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index 42c782859..d8238ca26 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -20,9 +20,10 @@ // limitations under the License. using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; @@ -43,7 +44,7 @@ namespace ArchiSteamFarm { private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1); [JsonProperty(Required = Required.DisallowNull)] - private readonly SortedDictionary GamesToRedeemInBackground = new SortedDictionary(); + private readonly OrderedDictionary GamesToRedeemInBackground = new OrderedDictionary(); [JsonProperty(Required = Required.DisallowNull)] private readonly ConcurrentHashSet IdlingBlacklistedAppIDs = new ConcurrentHashSet(); @@ -86,17 +87,23 @@ namespace ArchiSteamFarm { } } - internal async Task AddGamesToRedeemInBackground(IReadOnlyDictionary games) { + internal async Task AddGamesToRedeemInBackground(OrderedDictionary games) { if ((games == null) || (games.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(games)); return; } - bool save; + bool save = false; lock (GamesToRedeemInBackground) { - // We use Count() and not Any() because we must ensure full loop pass - save = games.Count(game => GamesToRedeemInBackground.TryAdd(game.Key, game.Value)) > 0; + foreach (DictionaryEntry game in games) { + if (GamesToRedeemInBackground.Contains(game.Key)) { + continue; + } + + GamesToRedeemInBackground.Add(game.Key, game.Value); + save = true; + } } if (save) { @@ -110,15 +117,15 @@ namespace ArchiSteamFarm { return; } - bool save; - lock (GamesToRedeemInBackground) { - save = GamesToRedeemInBackground.TryAdd(key, game); + if (!GamesToRedeemInBackground.Contains(key)) { + return; + } + + GamesToRedeemInBackground.Remove(key); } - if (save) { - await Save().ConfigureAwait(false); - } + await Save().ConfigureAwait(false); } internal async Task AddIdlingBlacklistedAppIDs(IReadOnlyCollection appIDs) { @@ -156,10 +163,14 @@ namespace ArchiSteamFarm { internal IReadOnlyCollection GetBlacklistedFromTradesSteamIDs() => BlacklistedFromTradesSteamIDs; - internal KeyValuePair GetGameToRedeemInBackground() { + internal (string Key, string Name) GetGameToRedeemInBackground() { lock (GamesToRedeemInBackground) { - return GamesToRedeemInBackground.FirstOrDefault(); + foreach (DictionaryEntry game in GamesToRedeemInBackground) { + return (game.Key as string, game.Value as string); + } } + + return (null, null); } internal IReadOnlyCollection GetIdlingBlacklistedAppIDs() => IdlingBlacklistedAppIDs; @@ -258,15 +269,15 @@ namespace ArchiSteamFarm { return; } - bool save; - lock (GamesToRedeemInBackground) { - save = GamesToRedeemInBackground.Remove(key, out _); + if (!GamesToRedeemInBackground.Contains(key)) { + return; + } + + GamesToRedeemInBackground.Remove(key); } - if (save) { - await Save().ConfigureAwait(false); - } + await Save().ConfigureAwait(false); } internal async Task RemoveIdlingBlacklistedAppIDs(IReadOnlyCollection appIDs) { diff --git a/ArchiSteamFarm/IPC.cs b/ArchiSteamFarm/IPC.cs index 63d131bff..ae7546e5c 100644 --- a/ArchiSteamFarm/IPC.cs +++ b/ArchiSteamFarm/IPC.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -1046,7 +1047,7 @@ namespace ArchiSteamFarm { private sealed class GamesToRedeemInBackgroundRequest { #pragma warning disable 649 [JsonProperty(Required = Required.Always)] - internal readonly SortedDictionary GamesToRedeemInBackground; + internal readonly OrderedDictionary GamesToRedeemInBackground; #pragma warning restore 649 // Deserialized from JSON