From c288df428264a9498a746c85816265b51055f07d Mon Sep 17 00:00:00 2001 From: Chomp Date: Thu, 12 Jun 2025 15:03:17 +0100 Subject: [PATCH] Refactored `GetSortedMoneyItemsInInventory` Return empty array if no money stacks found Filter out 'Locked' money stacks from being returned Prioritised stash money with the lowest stack size Optimised `GetSortedMoneyItemsInInventory` with use of 'in stash' cache --- .../Services/PaymentService.cs | 133 +++++++++++++----- 1 file changed, 96 insertions(+), 37 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/PaymentService.cs b/Libraries/SPTarkov.Server.Core/Services/PaymentService.cs index 6e1d605b..e2fb9554 100644 --- a/Libraries/SPTarkov.Server.Core/Services/PaymentService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/PaymentService.cs @@ -19,7 +19,6 @@ public class PaymentService( ISptLogger _logger, HashUtil _hashUtil, HttpResponseUtil _httpResponseUtil, - DatabaseService _databaseService, HandbookHelper _handbookHelper, TraderHelper _traderHelper, ItemHelper _itemHelper, @@ -361,7 +360,9 @@ public class PaymentService( } /// - /// Get all money stacks in inventory and prioritise items in stash + /// Get all money stacks in inventory and prioritise items in stash + /// Ignore locked stacks + /// Prioritise the lowest sized stack /// /// Player profile /// Currency to find @@ -370,69 +371,109 @@ public class PaymentService( // TODO - ensure money in containers inside secure container are LAST protected List GetSortedMoneyItemsInInventory(PmcData pmcData, string currencyTpl, string playerStashId) { + // Get money stacks player has var moneyItemsInInventory = _itemHelper.FindBarterItems("tpl", pmcData.Inventory.Items, currencyTpl); if (moneyItemsInInventory.Count == 0) { _logger.Debug($"No {currencyTpl} money items found in inventory"); + + return moneyItemsInInventory; + } + + // Create a cache inventory items with a bool of being in stash or not + var itemsInStashCache = GetItemInStashCache(pmcData.Inventory.Items, playerStashId); + + // Filter out 'Locked' money stacks as they cannot be used + var noLocked = moneyItemsInInventory.Where(moneyItem => moneyItem.Upd.PinLockState != PinLockState.Locked); + if (noLocked.Any()) + { + // We found unlocked money + moneyItemsInInventory = noLocked.ToList(); } // Prioritise items in stash to top of array - moneyItemsInInventory.Sort((a, b) => PrioritiseStashSort(a, b, pmcData.Inventory.Items, playerStashId)); + moneyItemsInInventory.Sort((a, b) => PrioritiseStashSort(a, b, pmcData.Inventory.Items, itemsInStashCache)); return moneyItemsInInventory; } + /// + /// Create a dictionary of all items from player inventory that are in the players stash + /// + /// Inventory items to check + /// Id of players stash + /// Dictionary + protected IReadOnlyDictionary GetItemInStashCache(List items, string playerStashId) + { + var itemsInStashCache = new Dictionary(); + foreach (var inventoryItem in items) + { + itemsInStashCache.TryAdd(inventoryItem.Id, IsInStash(inventoryItem.Id, items, playerStashId)); + } + + return itemsInStashCache; + } + /// /// Prioritise player stash first over player inventory. /// Post-raid healing would often take money out of the players pockets/secure container. /// /// First money stack item /// Second money stack item - /// Players inventory items - /// Players stash ID - /// Sort order, -1 if in a, 1 if in b, 0 if they match - protected int PrioritiseStashSort(Item a, Item b, List inventoryItems, string playerStashId) + /// Players inventory items + /// Cache of item IDs and if they're in stash + /// Sort order, -1 if A has priority, 1 if B has priority, 0 if they match + protected int PrioritiseStashSort( + Item a, + Item b, + List inventoryItems, + IReadOnlyDictionary itemInStashCache) { - // a in root of stash, prioritise - if (a.ParentId == playerStashId && b.ParentId != playerStashId) + itemInStashCache.TryGetValue(a.Id, out var aInStash); + itemInStashCache.TryGetValue(b.Id, out var bInStash); + + // A in root of stash, B not, prioritise A + if (aInStash && !bInStash) { return -1; } - // b in root stash, prioritise - if (a.ParentId != playerStashId && b.ParentId == playerStashId) + // B in root stash, A not, prioritise B + if (!aInStash && bInStash) { return 1; } - // both in containers + // Both in root stash, prioritise the smallest sized + if (aInStash && bInStash) + { + return GetPriorityBySmallestStackSize(a, b); + } + + // Both in containers if (a.SlotId == "main" && b.SlotId == "main") { - // Both items are in containers - var aInStash = IsInStash(a.ParentId, inventoryItems, playerStashId); - var bInStash = IsInStash(b.ParentId, inventoryItems, playerStashId); - - // a in stash in container, prioritise + // A container in stash, B not, prioritise A if (aInStash && !bInStash) { return -1; } - // b in stash in container, prioritise + // B container in stash, A not, prioritise B if (!aInStash && bInStash) { return 1; } - // Both in stash in containers + // Both containers in stash if (aInStash && bInStash) { - // Containers where taking money from would inconvinence player + // Containers where taking money from would inconvenience player var deprioritisedContainers = _inventoryConfig.DeprioritisedMoneyContainers; var aImmediateParent = inventoryItems.FirstOrDefault(item => item.Id == a.ParentId); var bImmediateParent = inventoryItems.FirstOrDefault(item => item.Id == b.ParentId); - // A is not a deprioritised container, B is + // A is not a deprioritized container, B is if ( !deprioritisedContainers.Contains(aImmediateParent.Template) && deprioritisedContainers.Contains(bImmediateParent.Template) @@ -441,7 +482,7 @@ public class PaymentService( return -1; } - // B is not a deprioritised container, A is + // B is not a deprioritized container, A is if ( deprioritisedContainers.Contains(aImmediateParent.Template) && !deprioritisedContainers.Contains(bImmediateParent.Template) @@ -452,10 +493,25 @@ public class PaymentService( } } - // they match + // They match / we don't know return 0; } + /// + /// Get priority of items based on their stack size + /// Smallest stack size has priority + /// + /// Item A + /// Item B + /// -1 = a, 1 = b, 0 = same + protected static int GetPriorityBySmallestStackSize(Item a, Item b) + { + var aStackSize = a.Upd?.StackObjectsCount ?? 1; + var bStackSize = b.Upd?.StackObjectsCount ?? 1; + + return aStackSize.CompareTo(bStackSize); + } + /// /// Recursively check items parents to see if it is inside the players inventory, not stash /// @@ -466,22 +522,25 @@ public class PaymentService( protected bool IsInStash(string itemId, List inventoryItems, string playerStashId) { var itemParent = inventoryItems.FirstOrDefault(item => item.Id == itemId); - - if (itemParent is not null) + if (itemParent is null) { - if (itemParent.SlotId == "hideout") - { - return true; - } - - if (itemParent.Id == playerStashId) - { - return true; - } - - return IsInStash(itemParent.ParentId, inventoryItems, playerStashId); + // Needs parent id be in stash + return false; } - return false; + // is root item and its parent is the player stash + if (itemParent.Id == playerStashId) + { + return true; + } + + // is child item and its parent is a root item + if (itemParent.SlotId == "hideout") + { + return true; + } + + // Recursive call for parentId + return IsInStash(itemParent.ParentId, inventoryItems, playerStashId); } }