mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-05 08:30:18 +00:00
Another logical fixes
This commit is contained in:
@@ -105,14 +105,6 @@ namespace ArchiSteamFarm {
|
||||
internal static async Task SendAsync(this WebSocket webSocket, byte[] buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) => await webSocket.SendAsync(new ArraySegment<byte>(buffer), messageType, endOfMessage, cancellationToken).ConfigureAwait(false);
|
||||
internal static string[] Split(this string text, char separator, StringSplitOptions options = StringSplitOptions.None) => text.Split(new[] { separator }, options);
|
||||
internal static void TrimExcess<T1, T2>(this Dictionary<T1, T2> _) { } // no-op
|
||||
|
||||
internal static void TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) {
|
||||
if (dictionary.ContainsKey(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary.Add(key, value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,12 +302,10 @@ namespace ArchiSteamFarm {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Those 3 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)
|
||||
HashSet<(uint AppID, Steam.Asset.EType Type)> skippedSetsThisUser = new HashSet<(uint AppID, Steam.Asset.EType Type)>();
|
||||
Dictionary<ulong, uint> ourFullSet = new Dictionary<ulong, uint>();
|
||||
Dictionary<ulong, uint> ourTradableSet = new Dictionary<ulong, uint>();
|
||||
|
||||
Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary<ulong, uint>> theirTradableState = Trading.GetInventoryState(theirInventory);
|
||||
Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary<ulong, uint>> inventoryStateChanges = new Dictionary<(uint AppID, Steam.Asset.EType Type), Dictionary<ulong, uint>>();
|
||||
|
||||
for (byte i = 0; i < Trading.MaxTradesPerAccount; i++) {
|
||||
byte itemsInTrade = 0;
|
||||
@@ -317,16 +315,48 @@ namespace ArchiSteamFarm {
|
||||
Dictionary<ulong, uint> classIDsToReceive = new Dictionary<ulong, uint>();
|
||||
|
||||
foreach (KeyValuePair<(uint AppID, Steam.Asset.EType Type), Dictionary<ulong, uint>> 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<ulong, uint> ourTradableItems)) {
|
||||
if (!tradableState.TryGetValue(ourInventoryStateSet.Key, out Dictionary<ulong, uint> ourTradableItems) || (ourTradableItems.Count == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!theirTradableState.TryGetValue(ourInventoryStateSet.Key, out Dictionary<ulong, uint> theirItems)) {
|
||||
if (!theirTradableState.TryGetValue(ourInventoryStateSet.Key, out Dictionary<ulong, uint> theirItems) || (theirItems.Count == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ourFullSet.AddRange(ourInventoryStateSet.Value);
|
||||
ourTradableSet.AddRange(ourTradableItems);
|
||||
// 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<ulong, uint> ourFullSet = new Dictionary<ulong, uint>(ourInventoryStateSet.Value);
|
||||
Dictionary<ulong, uint> ourTradableSet = new Dictionary<ulong, uint>(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<ulong, uint> pastChanges) && (pastChanges.Count > 0)) {
|
||||
foreach (KeyValuePair<ulong, uint> pastChange in pastChanges) {
|
||||
if (!ourFullSet.TryGetValue(pastChange.Key, out uint fullAmount) || (fullAmount == 0) || (fullAmount < pastChange.Value)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(fullAmount));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fullAmount > pastChange.Value) {
|
||||
ourFullSet[pastChange.Key] = fullAmount - pastChange.Value;
|
||||
} else {
|
||||
ourFullSet.Remove(pastChange.Key);
|
||||
}
|
||||
|
||||
if (!ourTradableSet.TryGetValue(pastChange.Key, out uint tradableAmount) || (tradableAmount == 0) || (tradableAmount < pastChange.Value)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(tradableAmount));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fullAmount > pastChange.Value) {
|
||||
ourTradableSet[pastChange.Key] = fullAmount - pastChange.Value;
|
||||
} else {
|
||||
ourTradableSet.Remove(pastChange.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (Trading.IsEmptyForMatching(ourFullSet, ourTradableSet)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool match;
|
||||
|
||||
@@ -334,7 +364,7 @@ namespace ArchiSteamFarm {
|
||||
match = false;
|
||||
|
||||
foreach (KeyValuePair<ulong, uint> ourItem in ourFullSet.Where(item => item.Value > 1).OrderByDescending(item => item.Value)) {
|
||||
if (!ourTradableSet.TryGetValue(ourItem.Key, out uint tradableAmount)) {
|
||||
if (!ourTradableSet.TryGetValue(ourItem.Key, out uint tradableAmount) || (tradableAmount == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -348,25 +378,42 @@ namespace ArchiSteamFarm {
|
||||
|
||||
// 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;
|
||||
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
|
||||
|
||||
if (inventoryStateChanges.TryGetValue(ourInventoryStateSet.Key, out Dictionary<ulong, uint> currentChanges)) {
|
||||
if (currentChanges.TryGetValue(ourItem.Key, out uint amount)) {
|
||||
currentChanges[ourItem.Key] = amount + 1;
|
||||
} else {
|
||||
currentChanges[ourItem.Key] = 1;
|
||||
}
|
||||
} else {
|
||||
inventoryStateChanges[ourInventoryStateSet.Key] = new Dictionary<ulong, uint> {
|
||||
{ ourItem.Key, 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;
|
||||
|
||||
// Update their state based on taken items
|
||||
if (theirItems.TryGetValue(theirItem.Key, out uint theirAmount) && (theirAmount > 1)) {
|
||||
theirItems[theirItem.Key] = theirAmount - 1;
|
||||
} else {
|
||||
theirItems.Remove(theirItem.Key);
|
||||
}
|
||||
|
||||
if (tradableAmount > 1) {
|
||||
ourTradableSet[ourItem.Key] = tradableAmount - 1;
|
||||
} else {
|
||||
ourTradableSet.Remove(ourItem.Key);
|
||||
}
|
||||
|
||||
// Update their state based on taken items
|
||||
if (!theirItems.TryGetValue(theirItem.Key, out uint theirAmount) || (theirAmount == 0)) {
|
||||
Bot.ArchiLogger.LogNullError(nameof(theirAmount));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (theirAmount > 1) {
|
||||
theirItems[theirItem.Key] = theirAmount - 1;
|
||||
} else {
|
||||
theirItems.Remove(theirItem.Key);
|
||||
}
|
||||
|
||||
itemsInTrade += 2;
|
||||
|
||||
match = true;
|
||||
|
||||
@@ -144,35 +144,49 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<(uint AppID, Steam.Asset.EType Type), Dictionary<ulong, uint>> tradableSet in tradableState) {
|
||||
foreach (KeyValuePair<ulong, uint> tradableItem in tradableSet.Value) {
|
||||
switch (tradableItem.Value) {
|
||||
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));
|
||||
if (!fullState.TryGetValue(tradableSet.Key, out Dictionary<ulong, uint> fullSet) || (fullSet == null) || (fullSet.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(fullSet));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsEmptyForMatching(fullSet, tradableSet.Value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find any matchable combinations, so this inventory is empty
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsEmptyForMatching(IReadOnlyDictionary<ulong, uint> fullSet, IReadOnlyDictionary<ulong, uint> tradableSet) {
|
||||
if ((fullSet == null) || (tradableSet == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(fullSet) + " || " + nameof(tradableSet));
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<ulong, uint> tradableItem in tradableSet) {
|
||||
switch (tradableItem.Value) {
|
||||
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));
|
||||
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)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(fullAmount));
|
||||
return false;
|
||||
case 1:
|
||||
// Single tradable item, can be matchable or not depending on the rest of the inventory
|
||||
if (!fullState.TryGetValue(tradableSet.Key, out Dictionary<ulong, uint> fullItem) || (fullItem == null) || (fullItem.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(fullItem));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fullItem.TryGetValue(tradableItem.Key, out uint fullAmount) || (fullAmount == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(fullAmount));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fullAmount > 1) {
|
||||
// If we have a single tradable item but more than 1 in total, this is matchable
|
||||
return false;
|
||||
}
|
||||
|
||||
// A single exclusive tradable item is not matchable, continue
|
||||
continue;
|
||||
default:
|
||||
// Any other combination of tradable items is always matchable
|
||||
if (fullAmount > 1) {
|
||||
// If we have a single tradable item but more than 1 in total, this is matchable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A single exclusive tradable item is not matchable, continue
|
||||
continue;
|
||||
default:
|
||||
// Any other combination of tradable items is always matchable
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,17 +36,6 @@ namespace ArchiSteamFarm {
|
||||
// Normally we wouldn't need to use this singleton, but we want to ensure decent randomness across entire program's lifetime
|
||||
private static readonly Random Random = new Random();
|
||||
|
||||
internal static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IEnumerable<KeyValuePair<TKey, TValue>> other) {
|
||||
if ((dictionary == null) || (other == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(dictionary) + " || " + nameof(other));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> item in other) {
|
||||
dictionary.TryAdd(item.Key, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetArgsAsText(string[] args, byte argsToSkip, string delimiter) {
|
||||
if ((args == null) || (args.Length <= argsToSkip) || string.IsNullOrEmpty(delimiter)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(args) + " || " + nameof(argsToSkip) + " || " + nameof(delimiter));
|
||||
|
||||
Reference in New Issue
Block a user