diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 6a9e2007b..6b6d3f655 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -2415,11 +2415,11 @@ namespace ArchiSteamFarm { return; } - foreach (KeyValuePair notification in callback.Notifications) { - switch (notification.Key) { + foreach ((ArchiHandler.UserNotificationsCallback.EUserNotification notification, uint count) in callback.Notifications) { + switch (notification) { case ArchiHandler.UserNotificationsCallback.EUserNotification.Items: - bool newItems = notification.Value > ItemsCount; - ItemsCount = notification.Value; + bool newItems = count > ItemsCount; + ItemsCount = count; if (newItems) { ArchiLogger.LogGenericTrace(nameof(ArchiHandler.UserNotificationsCallback.EUserNotification.Items)); @@ -2432,8 +2432,8 @@ namespace ArchiSteamFarm { break; case ArchiHandler.UserNotificationsCallback.EUserNotification.Trading: - bool newTrades = notification.Value > TradesCount; - TradesCount = notification.Value; + bool newTrades = count > TradesCount; + TradesCount = count; if (newTrades) { ArchiLogger.LogGenericTrace(nameof(ArchiHandler.UserNotificationsCallback.EUserNotification.Trading)); @@ -2442,8 +2442,8 @@ namespace ArchiSteamFarm { break; case ArchiHandler.UserNotificationsCallback.EUserNotification.Gifts: - bool newGifts = notification.Value > GiftsCount; - GiftsCount = notification.Value; + bool newGifts = count > GiftsCount; + GiftsCount = count; if (newGifts && BotConfig.AcceptGifts) { ArchiLogger.LogGenericTrace(nameof(ArchiHandler.UserNotificationsCallback.EUserNotification.Gifts)); diff --git a/ArchiSteamFarm/Commands.cs b/ArchiSteamFarm/Commands.cs index 95f41790b..b7b6cfe5f 100644 --- a/ArchiSteamFarm/Commands.cs +++ b/ArchiSteamFarm/Commands.cs @@ -1541,8 +1541,8 @@ namespace ArchiSteamFarm { lock (CachedGamesOwned) { if (CachedGamesOwned.Count == 0) { - foreach (KeyValuePair ownedGame in ownedGames) { - CachedGamesOwned[ownedGame.Key] = ownedGame.Value; + foreach ((uint appID, string gameName) in ownedGames) { + CachedGamesOwned[appID] = gameName; } CachedGamesOwned.TrimExcess(); @@ -1554,9 +1554,9 @@ namespace ArchiSteamFarm { HashSet ownedGameIDs = new HashSet(); if (query.Equals("*")) { - foreach (KeyValuePair ownedGame in ownedGames) { - ownedGameIDs.Add(ownedGame.Key); - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, ownedGame.Key, ownedGame.Value))); + foreach ((uint appID, string gameName) in ownedGames) { + ownedGameIDs.Add(appID); + response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, appID, gameName))); } } else { string[] games = query.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -1586,9 +1586,9 @@ namespace ArchiSteamFarm { } // This is a string, so check our entire library - foreach (KeyValuePair ownedGame in ownedGames.Where(ownedGame => ownedGame.Value.IndexOf(game, StringComparison.OrdinalIgnoreCase) >= 0)) { - ownedGameIDs.Add(ownedGame.Key); - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, ownedGame.Key, ownedGame.Value))); + foreach ((uint appID, string gameName) in ownedGames.Where(ownedGame => ownedGame.Value.IndexOf(game, StringComparison.OrdinalIgnoreCase) >= 0)) { + ownedGameIDs.Add(appID); + response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, appID, gameName))); } } } @@ -2113,8 +2113,8 @@ namespace ArchiSteamFarm { continue; } - foreach (KeyValuePair item in otherResult.Items.Where(item => !items.ContainsKey(item.Key))) { - items[item.Key] = item.Value; + foreach ((uint packageID, string packageName) in otherResult.Items.Where(item => !items.ContainsKey(item.Key))) { + items[packageID] = packageName; } } diff --git a/ArchiSteamFarm/GlobalDatabase.cs b/ArchiSteamFarm/GlobalDatabase.cs index 1d2eadd76..2e112612a 100644 --- a/ArchiSteamFarm/GlobalDatabase.cs +++ b/ArchiSteamFarm/GlobalDatabase.cs @@ -135,8 +135,8 @@ namespace ArchiSteamFarm { return; } - foreach (KeyValuePair AppIDs)> packageData in packagesData) { - PackagesData[packageData.Key] = packageData.Value; + foreach ((uint packageID, (uint ChangeNumber, HashSet AppIDs) package) in packagesData) { + PackagesData[packageID] = package; } await Save().ConfigureAwait(false); diff --git a/ArchiSteamFarm/RuntimeCompatibility.cs b/ArchiSteamFarm/RuntimeCompatibility.cs index 753d0802f..57863eeb7 100644 --- a/ArchiSteamFarm/RuntimeCompatibility.cs +++ b/ArchiSteamFarm/RuntimeCompatibility.cs @@ -115,6 +115,11 @@ namespace ArchiSteamFarm { } #if NETFRAMEWORK + internal static void Deconstruct(this KeyValuePair kv, out T1 key, out T2 value) { + key = kv.Key; + value = kv.Value; + } + internal static async Task ReceiveAsync([NotNull] this WebSocket webSocket, [NotNull] byte[] buffer, CancellationToken cancellationToken) => await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken).ConfigureAwait(false); internal static async Task SendAsync([NotNull] this WebSocket webSocket, [NotNull] byte[] buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => await webSocket.SendAsync(new ArraySegment(buffer), messageType, endOfMessage, cancellationToken).ConfigureAwait(false); diff --git a/ArchiSteamFarm/Statistics.cs b/ArchiSteamFarm/Statistics.cs index dbe203dfb..cd2334e75 100644 --- a/ArchiSteamFarm/Statistics.cs +++ b/ArchiSteamFarm/Statistics.cs @@ -343,44 +343,44 @@ namespace ArchiSteamFarm { Dictionary classIDsToGive = new Dictionary(); Dictionary classIDsToReceive = new Dictionary(); - foreach (KeyValuePair<(uint AppID, Steam.Asset.EType Type), Dictionary> ourInventoryStateSet in fullState.Where(set => listedUser.MatchableTypes.Contains(set.Key.Type) && set.Value.Values.Any(count => count > 1))) { - if (!tradableState.TryGetValue(ourInventoryStateSet.Key, out Dictionary ourTradableItems) || (ourTradableItems.Count == 0)) { + foreach (((uint AppID, Steam.Asset.EType Type) set, Dictionary ourFullItems) in fullState.Where(set => listedUser.MatchableTypes.Contains(set.Key.Type) && set.Value.Values.Any(count => count > 1))) { + if (!tradableState.TryGetValue(set, out Dictionary ourTradableItems) || (ourTradableItems.Count == 0)) { continue; } - if (!theirTradableState.TryGetValue(ourInventoryStateSet.Key, out Dictionary theirItems) || (theirItems.Count == 0)) { + if (!theirTradableState.TryGetValue(set, out Dictionary theirItems) || (theirItems.Count == 0)) { continue; } // Those 2 collections are on user-basis since we can't be sure that the trade passes through (and therefore we need to keep original state in case of failure) - Dictionary ourFullSet = new Dictionary(ourInventoryStateSet.Value); + Dictionary ourFullSet = new Dictionary(ourFullItems); Dictionary ourTradableSet = new Dictionary(ourTradableItems); // We also have to take into account changes that happened in previoius trades with this user, so this block will adapt to that - if (inventoryStateChanges.TryGetValue(ourInventoryStateSet.Key, out Dictionary pastChanges) && (pastChanges.Count > 0)) { - foreach (KeyValuePair pastChange in pastChanges) { - if (!ourFullSet.TryGetValue(pastChange.Key, out uint fullAmount) || (fullAmount == 0) || (fullAmount < pastChange.Value)) { + if (inventoryStateChanges.TryGetValue(set, out Dictionary pastChanges) && (pastChanges.Count > 0)) { + foreach ((ulong classID, uint amount) in pastChanges) { + if (!ourFullSet.TryGetValue(classID, out uint fullAmount) || (fullAmount == 0) || (fullAmount < amount)) { Bot.ArchiLogger.LogNullError(nameof(fullAmount)); return false; } - if (fullAmount > pastChange.Value) { - ourFullSet[pastChange.Key] = fullAmount - pastChange.Value; + if (fullAmount > amount) { + ourFullSet[classID] = fullAmount - amount; } else { - ourFullSet.Remove(pastChange.Key); + ourFullSet.Remove(classID); } - if (!ourTradableSet.TryGetValue(pastChange.Key, out uint tradableAmount) || (tradableAmount == 0) || (tradableAmount < pastChange.Value)) { + if (!ourTradableSet.TryGetValue(classID, out uint tradableAmount) || (tradableAmount == 0) || (tradableAmount < amount)) { Bot.ArchiLogger.LogNullError(nameof(tradableAmount)); return false; } - if (fullAmount > pastChange.Value) { - ourTradableSet[pastChange.Key] = fullAmount - pastChange.Value; + if (fullAmount > amount) { + ourTradableSet[classID] = fullAmount - amount; } else { - ourTradableSet.Remove(pastChange.Key); + ourTradableSet.Remove(classID); } } @@ -394,56 +394,56 @@ namespace ArchiSteamFarm { do { match = false; - foreach (KeyValuePair ourItem in ourFullSet.Where(item => item.Value > 1).OrderByDescending(item => item.Value)) { - if (!ourTradableSet.TryGetValue(ourItem.Key, out uint tradableAmount) || (tradableAmount == 0)) { + foreach ((ulong ourItem, uint ourAmount) in ourFullSet.Where(item => item.Value > 1).OrderByDescending(item => item.Value)) { + if (!ourTradableSet.TryGetValue(ourItem, out uint tradableAmount) || (tradableAmount == 0)) { continue; } - foreach (KeyValuePair theirItem in theirItems.OrderBy(item => ourFullSet.TryGetValue(item.Key, out uint ourAmount) ? ourAmount : 0)) { - if (ourFullSet.TryGetValue(theirItem.Key, out uint ourAmountOfTheirItem) && (ourItem.Value <= ourAmountOfTheirItem + 1)) { + foreach ((ulong theirItem, _) in theirItems.OrderBy(item => ourFullSet.TryGetValue(item.Key, out uint ourAmountOfTheirItem) ? ourAmountOfTheirItem : 0)) { + if (ourFullSet.TryGetValue(theirItem, out uint ourAmountOfTheirItem) && (ourAmount <= ourAmountOfTheirItem + 1)) { continue; } // Skip this set from the remaining of this round - skippedSetsThisTrade.Add(ourInventoryStateSet.Key); + skippedSetsThisTrade.Add(set); // Update our state based on given items - classIDsToGive[ourItem.Key] = classIDsToGive.TryGetValue(ourItem.Key, out uint givenAmount) ? givenAmount + 1 : 1; - ourFullSet[ourItem.Key] = ourItem.Value - 1; // We don't need to remove anything here because we can guarantee that ourItem.Value is at least 2 + classIDsToGive[ourItem] = classIDsToGive.TryGetValue(ourItem, out uint givenAmount) ? givenAmount + 1 : 1; + ourFullSet[ourItem] = ourAmount - 1; // We don't need to remove anything here because we can guarantee that ourItem.Value is at least 2 - if (inventoryStateChanges.TryGetValue(ourInventoryStateSet.Key, out Dictionary currentChanges)) { - if (currentChanges.TryGetValue(ourItem.Key, out uint amount)) { - currentChanges[ourItem.Key] = amount + 1; + if (inventoryStateChanges.TryGetValue(set, out Dictionary currentChanges)) { + if (currentChanges.TryGetValue(ourItem, out uint amount)) { + currentChanges[ourItem] = amount + 1; } else { - currentChanges[ourItem.Key] = 1; + currentChanges[ourItem] = 1; } } else { - inventoryStateChanges[ourInventoryStateSet.Key] = new Dictionary { - { ourItem.Key, 1 } + inventoryStateChanges[set] = new Dictionary { + { ourItem, 1 } }; } // Update our state based on received items - classIDsToReceive[theirItem.Key] = classIDsToReceive.TryGetValue(theirItem.Key, out uint receivedAmount) ? receivedAmount + 1 : 1; - ourFullSet[theirItem.Key] = ourAmountOfTheirItem + 1; + classIDsToReceive[theirItem] = classIDsToReceive.TryGetValue(theirItem, out uint receivedAmount) ? receivedAmount + 1 : 1; + ourFullSet[theirItem] = ourAmountOfTheirItem + 1; if (tradableAmount > 1) { - ourTradableSet[ourItem.Key] = tradableAmount - 1; + ourTradableSet[ourItem] = tradableAmount - 1; } else { - ourTradableSet.Remove(ourItem.Key); + ourTradableSet.Remove(ourItem); } // Update their state based on taken items - if (!theirItems.TryGetValue(theirItem.Key, out uint theirAmount) || (theirAmount == 0)) { + if (!theirItems.TryGetValue(theirItem, out uint theirAmount) || (theirAmount == 0)) { Bot.ArchiLogger.LogNullError(nameof(theirAmount)); return false; } if (theirAmount > 1) { - theirItems[theirItem.Key] = theirAmount - 1; + theirItems[theirItem] = theirAmount - 1; } else { - theirItems.Remove(theirItem.Key); + theirItems.Remove(theirItem); } itemsInTrade += 2; diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs index 6897211e2..e98526efa 100644 --- a/ArchiSteamFarm/Trading.cs +++ b/ArchiSteamFarm/Trading.cs @@ -149,14 +149,14 @@ namespace ArchiSteamFarm { return false; } - foreach (KeyValuePair<(uint AppID, Steam.Asset.EType Type), Dictionary> tradableSet in tradableState) { - if (!fullState.TryGetValue(tradableSet.Key, out Dictionary fullSet) || (fullSet == null) || (fullSet.Count == 0)) { + foreach (((uint AppID, Steam.Asset.EType Type) set, Dictionary state) in tradableState) { + if (!fullState.TryGetValue(set, out Dictionary fullSet) || (fullSet == null) || (fullSet.Count == 0)) { ASF.ArchiLogger.LogNullError(nameof(fullSet)); return false; } - if (!IsEmptyForMatching(fullSet, tradableSet.Value)) { + if (!IsEmptyForMatching(fullSet, state)) { return false; } } @@ -172,18 +172,18 @@ namespace ArchiSteamFarm { return false; } - foreach (KeyValuePair tradableItem in tradableSet) { - switch (tradableItem.Value) { + foreach ((ulong classID, uint amount) in tradableSet) { + switch (amount) { case 0: // No tradable items, this should never happen, dictionary should not have this key to begin with - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(tradableItem.Value), tradableItem.Value)); + ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(amount), amount)); return false; case 1: // Single tradable item, can be matchable or not depending on the rest of the inventory - if (!fullSet.TryGetValue(tradableItem.Key, out uint fullAmount) || (fullAmount == 0) || (fullAmount < tradableItem.Value)) { + if (!fullSet.TryGetValue(classID, out uint fullAmount) || (fullAmount == 0) || (fullAmount < amount)) { ASF.ArchiLogger.LogNullError(nameof(fullAmount)); return false; @@ -237,17 +237,17 @@ namespace ArchiSteamFarm { } // Ensure that amount of items to give is at least amount of items to receive (per game and per type) - foreach (KeyValuePair> itemsPerGame in itemsToGivePerGame) { - if (!itemsToReceivePerGame.TryGetValue(itemsPerGame.Key, out Dictionary otherItemsPerType)) { + foreach ((uint appID, Dictionary itemsPerGame) in itemsToGivePerGame) { + if (!itemsToReceivePerGame.TryGetValue(appID, out Dictionary otherItemsPerType)) { return false; } - foreach (KeyValuePair itemsPerType in itemsPerGame.Value) { - if (!otherItemsPerType.TryGetValue(itemsPerType.Key, out uint otherAmount)) { + foreach ((Steam.Asset.EType type, uint amount) in itemsPerGame) { + if (!otherItemsPerType.TryGetValue(type, out uint otherAmount)) { return false; } - if (itemsPerType.Value > otherAmount) { + if (amount > otherAmount) { return false; } } @@ -352,9 +352,8 @@ namespace ArchiSteamFarm { Dictionary<(uint AppID, Steam.Asset.EType Type), List> finalSets = GetInventorySets(inventory); // Once we have both states, we can check overall fairness - foreach (KeyValuePair<(uint AppID, Steam.Asset.EType Type), List> finalSet in finalSets) { - List beforeAmounts = initialSets[finalSet.Key]; - List afterAmounts = finalSet.Value; + foreach (((uint AppID, Steam.Asset.EType Type) set, List afterAmounts) in finalSets) { + List beforeAmounts = initialSets[set]; // If amount of unique items in the set decreases, this is always a bad trade (e.g. 1 1 -> 0 2) if (afterAmounts.Count < beforeAmounts.Count) {