Another logical fixes

This commit is contained in:
JustArchi
2018-12-09 21:26:22 +01:00
parent cff67a61ba
commit cd0962f14c
4 changed files with 103 additions and 61 deletions

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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));