From 42e79c981bbd4e01d09042f34b0c713a8ed8f878 Mon Sep 17 00:00:00 2001 From: Chomp Date: Sat, 28 Jun 2025 12:38:34 +0100 Subject: [PATCH] Converted `FindAndReturnChildrenAsItems` into extension method --- .../Controllers/InsuranceController.cs | 5 +- .../Controllers/ProfileController.cs | 4 +- .../Controllers/QuestController.cs | 3 +- .../Controllers/RagfairController.cs | 12 ++--- .../Controllers/TradeController.cs | 3 +- .../Extensions/ItemExtensions.cs | 47 ++++++++++++++++++ .../Generators/LocationLootGenerator.cs | 3 +- .../Helpers/BotGeneratorHelper.cs | 6 +-- .../Helpers/InRaidHelper.cs | 6 +-- .../Helpers/InventoryHelper.cs | 15 ++---- .../Helpers/ItemHelper.cs | 49 +------------------ .../Helpers/ProfileHelper.cs | 5 +- .../Helpers/TradeHelper.cs | 10 +--- .../Services/CircleOfCultistService.cs | 3 +- .../Services/FenceService.cs | 26 +++------- .../Services/RagfairTaxService.cs | 6 +-- 16 files changed, 82 insertions(+), 121 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs b/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs index c892a0a9..8025a706 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/InsuranceController.cs @@ -407,13 +407,12 @@ public class InsuranceController( if (itemRoll ?? false) { // Check to see if this item is a parent in the parentAttachmentsMap. If so, do a look-up for *all* of - // its children and mark them for deletion as well. Additionally remove the parent (and its children) + // its children and mark them for deletion as well. Also remove parent (and its children) // from the parentAttachmentsMap so that it's children are not rolled for later in the process. if (parentAttachmentsMap.ContainsKey(insuredItem.Id)) { // This call will also return the parent item itself, queueing it for deletion as well. - var itemAndChildren = _itemHelper.FindAndReturnChildrenAsItems( - insured.Items, + var itemAndChildren = insured.Items.FindAndReturnChildrenAsItems( insuredItem.Id ); foreach (var item in itemAndChildren) diff --git a/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs b/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs index e5cfa1aa..508de31f 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/ProfileController.cs @@ -1,4 +1,5 @@ using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Generators; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Common; @@ -310,8 +311,7 @@ public class ProfileController( foreach (var rootItems in hideoutRootItems) { // Check each root items for children and add - var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems( - profileToViewPmc.Inventory.Items, + var itemWithChildren = profileToViewPmc.Inventory.Items.FindAndReturnChildrenAsItems( rootItems.Id ); itemsToReturn.AddRange(itemWithChildren); diff --git a/Libraries/SPTarkov.Server.Core/Controllers/QuestController.cs b/Libraries/SPTarkov.Server.Core/Controllers/QuestController.cs index b6e22c32..8d017cbe 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/QuestController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/QuestController.cs @@ -335,8 +335,7 @@ public class QuestController( // element `location` properties of the parent so they are sequential, while retaining order if (removedItem.Location?.GetType() == typeof(int)) { - var childItems = _itemHelper.FindAndReturnChildrenAsItems( - pmcData.Inventory.Items, + var childItems = pmcData.Inventory.Items.FindAndReturnChildrenAsItems( removedItem.ParentId ); childItems.RemoveAt(0); // Remove the parent diff --git a/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs b/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs index bfcf12bc..08073d0e 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/RagfairController.cs @@ -649,8 +649,7 @@ public class RagfairController // multi-offers are all the same item, // Get first item and its children and use as template - var inventoryItems = _itemHelper.FindAndReturnChildrenAsItems( - pmcData.Inventory.Items, + var inventoryItems = pmcData.Inventory.Items.FindAndReturnChildrenAsItems( firstOfferItemId // Choose first item as they're all the same item ); @@ -766,9 +765,8 @@ public class RagfairController // multi-offers are all the same item, // Get first item and its children and use as template - var firstInventoryItemAndChildren = _itemHelper.FindAndReturnChildrenAsItems( - pmcData.Inventory.Items, - offerRequest.Items[0] + var firstInventoryItemAndChildren = pmcData.Inventory.Items.FindAndReturnChildrenAsItems( + offerRequest.Items.FirstOrDefault() ); // Find items to be listed on flea (+ children) from player inventory @@ -1147,9 +1145,7 @@ public class RagfairController rootItem.FixItemStackCount(); - itemsToReturn.Add( - _itemHelper.FindAndReturnChildrenAsItems(pmcData.Inventory.Items, itemId) - ); + itemsToReturn.Add(pmcData.Inventory.Items.FindAndReturnChildrenAsItems(itemId)); } if (itemsToReturn?.Count == 0) diff --git a/Libraries/SPTarkov.Server.Core/Controllers/TradeController.cs b/Libraries/SPTarkov.Server.Core/Controllers/TradeController.cs index 6d305816..95f34706 100644 --- a/Libraries/SPTarkov.Server.Core/Controllers/TradeController.cs +++ b/Libraries/SPTarkov.Server.Core/Controllers/TradeController.cs @@ -1,4 +1,5 @@ using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; @@ -367,7 +368,7 @@ public class TradeController( TraderBase traderDetails ) { - var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(items, parentItemId); + var itemWithChildren = items.FindAndReturnChildrenAsItems(parentItemId); var totalPrice = 0; foreach (var itemToSell in itemWithChildren) diff --git a/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs b/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs index 3e8a0610..18c42444 100644 --- a/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs +++ b/Libraries/SPTarkov.Server.Core/Extensions/ItemExtensions.cs @@ -276,5 +276,52 @@ namespace SPTarkov.Server.Core.Extensions // Ensure item has 'StackObjectsCount' property item.Upd.StackObjectsCount ??= 1; } + + /// + /// A variant of FindAndReturnChildren where the output is list of item objects instead of their ids. + /// + /// List of items (item + possible children) + /// Parent item's id + /// OPTIONAL - Include only mod items, exclude items stored inside root item + /// list of Item objects + public static List FindAndReturnChildrenAsItems( + this IEnumerable items, + string baseItemId, + bool modsOnly = false + ) + { + // Use dictionary to make key lookup faster, convert to list before being returned + OrderedDictionary result = []; + foreach (var childItem in items) + { + // Include itself + if (string.Equals(childItem.Id, baseItemId, StringComparison.Ordinal)) + { + // Root item MUST be at 0 index for things like flea market offers + result.Insert(0, childItem.Id, childItem); + continue; + } + + // Is stored in parent and disallowed + if (modsOnly && childItem.Location is not null) + { + continue; + } + + // Items parentId matches root item AND returned items doesn't contain current child + if ( + !result.ContainsKey(childItem.Id) + && string.Equals(childItem.ParentId, baseItemId, StringComparison.Ordinal) + ) + { + foreach (var item in FindAndReturnChildrenAsItems(items, childItem.Id)) + { + result.Add(item.Id, item); + } + } + } + + return result.Values.ToList(); + } } } diff --git a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs index 4f2058fd..1a358a6b 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/LocationLootGenerator.cs @@ -1,5 +1,6 @@ using System.Text.Json.Serialization; using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; @@ -1145,7 +1146,7 @@ public class LocationLootGenerator( { // Also used by armors to get child mods // Get item + children and add into array we return - var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(items, chosenItem.Id); + var itemWithChildren = items.FindAndReturnChildrenAsItems(chosenItem.Id); // Ensure all IDs are unique itemWithChildren = _itemHelper.ReplaceIDs(_cloner.Clone(itemWithChildren)); diff --git a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs index a3a114f2..15d30e47 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/BotGeneratorHelper.cs @@ -1,6 +1,7 @@ using System.Collections.Frozen; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Constants; +using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Spt.Bots; @@ -800,10 +801,7 @@ public class BotGeneratorHelper( { // Check item in container for children, store for later insertion into `containerItemsToCheck` // (used later when figuring out how much space weapon takes up) - var itemWithChildItems = _itemHelper.FindAndReturnChildrenAsItems( - itemsWithoutLocation, - rootItem.Id - ); + var itemWithChildItems = itemsWithoutLocation.FindAndReturnChildrenAsItems(rootItem.Id); // Item had children, replace existing data with item + its children result.Add(rootItem); diff --git a/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs index 1bbdc3a7..e78562bf 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/InRaidHelper.cs @@ -75,14 +75,12 @@ public class InRaidHelper( ); // Get all items that have a parent of `serverProfile.Inventory.equipment` (All items player had on them at end of raid) - var postRaidInventoryItems = _itemHelper.FindAndReturnChildrenAsItems( - postRaidProfile.Inventory.Items, + var postRaidInventoryItems = postRaidProfile.Inventory.Items.FindAndReturnChildrenAsItems( postRaidProfile.Inventory.Equipment ); // Get all items that have a parent of `serverProfile.Inventory.questRaidItems` (Quest items player had on them at end of raid) - var postRaidQuestItems = _itemHelper.FindAndReturnChildrenAsItems( - postRaidProfile.Inventory.Items, + var postRaidQuestItems = postRaidProfile.Inventory.Items.FindAndReturnChildrenAsItems( postRaidProfile.Inventory.QuestRaidItems ); diff --git a/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs index f9b8cf2f..65686a6f 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/InventoryHelper.cs @@ -508,10 +508,7 @@ public class InventoryHelper( } // Get children of item, they get deleted too - var itemAndChildrenToRemove = _itemHelper.FindAndReturnChildrenAsItems( - profile.Inventory.Items, - itemId - ); + var itemAndChildrenToRemove = profile.Inventory.Items.FindAndReturnChildrenAsItems(itemId); if (!itemAndChildrenToRemove.Any()) { if (_logger.IsLogEnabled(LogLevel.Debug)) @@ -591,11 +588,10 @@ public class InventoryHelper( if (messageWithReward is not null) { // Find item + any possible children and remove them from mails items array - var itemWithChildern = _itemHelper.FindAndReturnChildrenAsItems( - messageWithReward.Items.Data, + var itemWithChildren = messageWithReward.Items.Data.FindAndReturnChildrenAsItems( removeRequest.Item ); - foreach (var itemToDelete in itemWithChildern) + foreach (var itemToDelete in itemWithChildren) { // Get index of item to remove from reward array + remove it var indexOfItemToRemove = messageWithReward.Items.Data.IndexOf(itemToDelete); @@ -649,10 +645,7 @@ public class InventoryHelper( } // Goal is to keep removing items until we can remove part of an items stack - var itemsToReduce = _itemHelper.FindAndReturnChildrenAsItems( - pmcData.Inventory.Items, - itemId - ); + var itemsToReduce = pmcData.Inventory.Items.FindAndReturnChildrenAsItems(itemId); var remainingCount = countToRemove; foreach (var itemToReduce in itemsToReduce) { diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs index f3eb037e..fb837020 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/ItemHelper.cs @@ -712,53 +712,6 @@ public class ItemHelper( return Math.Sqrt(durability ?? 0); } - /// - /// A variant of FindAndReturnChildren where the output is list of item objects instead of their ids. - /// - /// List of items (item + possible children) - /// Parent item's id - /// OPTIONAL - Include only mod items, exclude items stored inside root item - /// list of Item objects - public List FindAndReturnChildrenAsItems( - IEnumerable items, - string baseItemId, - bool modsOnly = false - ) - { - // Use dictionary to make key lookup faster, convert to list before being returned - OrderedDictionary result = []; - foreach (var childItem in items) - { - // Include itself - if (string.Equals(childItem.Id, baseItemId, StringComparison.Ordinal)) - { - // Root item MUST be at 0 index for things like flea market offers - result.Insert(0, childItem.Id, childItem); - continue; - } - - // Is stored in parent and disallowed - if (modsOnly && childItem.Location is not null) - { - continue; - } - - // Items parentId matches root item AND returned items doesn't contain current child - if ( - !result.ContainsKey(childItem.Id) - && string.Equals(childItem.ParentId, baseItemId, StringComparison.Ordinal) - ) - { - foreach (var item in FindAndReturnChildrenAsItems(items, childItem.Id)) - { - result.Add(item.Id, item); - } - } - } - - return result.Values.ToList(); - } - /// /// Find children of the item in a given assort (weapons parts for example, need recursive loop function) /// @@ -1402,7 +1355,7 @@ public class ItemHelper( var forcedLeft = 0; var forcedRight = 0; - var children = FindAndReturnChildrenAsItems(items, rootItemId); + var children = items.FindAndReturnChildrenAsItems(rootItemId); foreach (var ci in children) { var itemTemplate = GetItem(ci.Template).Value; diff --git a/Libraries/SPTarkov.Server.Core/Helpers/ProfileHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/ProfileHelper.cs index 5a060e47..ef54876e 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/ProfileHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/ProfileHelper.cs @@ -678,10 +678,7 @@ public class ProfileHelper( foreach (var itemId in profile.Inventory?.FavoriteItems ?? []) { // When viewing another users profile, the client expects a full item with children, so get that - var itemAndChildren = _itemHelper.FindAndReturnChildrenAsItems( - profile.Inventory.Items, - itemId - ); + var itemAndChildren = profile.Inventory.Items.FindAndReturnChildrenAsItems(itemId); if (itemAndChildren?.Count > 0) { // To get the client to actually see the items, we set the main item's parent to null, so it's treated as a root item diff --git a/Libraries/SPTarkov.Server.Core/Helpers/TradeHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/TradeHelper.cs index 6af675c1..bae8b9a2 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/TradeHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/TradeHelper.cs @@ -152,10 +152,7 @@ public class TradeHelper( return; } - offerItems = _itemHelper.FindAndReturnChildrenAsItems( - fenceItems, - buyRequestData.ItemId - ); + offerItems = fenceItems.FindAndReturnChildrenAsItems(buyRequestData.ItemId); } else { @@ -223,10 +220,7 @@ public class TradeHelper( .Items; // Get item + children for purchase - var relevantItems = _itemHelper.FindAndReturnChildrenAsItems( - traderItems, - buyRequestData.ItemId - ); + var relevantItems = traderItems.FindAndReturnChildrenAsItems(buyRequestData.ItemId); if (relevantItems.Count == 0) { _logger.Error( diff --git a/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs b/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs index 117dc3b0..16962987 100644 --- a/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/CircleOfCultistService.cs @@ -332,8 +332,7 @@ public class CircleOfCultistService( List sacrificedItems = []; foreach (var rootItem in inventoryRootItemsInCultistGrid) { - var rootItemWithChildren = _itemHelper.FindAndReturnChildrenAsItems( - pmcData.Inventory.Items, + var rootItemWithChildren = pmcData.Inventory.Items.FindAndReturnChildrenAsItems( rootItem.Id ); sacrificedItems.AddRange(rootItemWithChildren); diff --git a/Libraries/SPTarkov.Server.Core/Services/FenceService.cs b/Libraries/SPTarkov.Server.Core/Services/FenceService.cs index 3256ad89..8b316280 100644 --- a/Libraries/SPTarkov.Server.Core/Services/FenceService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/FenceService.cs @@ -145,9 +145,7 @@ public class FenceService( { // HUGE THANKS TO LACYWAY AND LEAVES FOR PROVIDING THIS SOLUTION FOR SPT TO IMPLEMENT!! // Copy the item and its children - var clonedItems = _cloner.Clone( - itemHelper.FindAndReturnChildrenAsItems(items, mainItem.Id) - ); + var clonedItems = _cloner.Clone(items.FindAndReturnChildrenAsItems(mainItem.Id)); // I BLAME LACY FOR THIS ISSUE, I SPENT HOURS FIXING IT /s // i think on node the one with hideout usually came first var root = clonedItems.FirstOrDefault(x => x.SlotId == "hideout"); @@ -423,8 +421,7 @@ public class FenceService( // Check if same type of item exists + its on list of item types to always stack if (existingRootItem != null && ItemInPreventDupeCategoryList(newRootItem.Template)) { - var existingFullItemTree = itemHelper.FindAndReturnChildrenAsItems( - existingFenceAssorts.Items, + var existingFullItemTree = existingFenceAssorts.Items.FindAndReturnChildrenAsItems( existingRootItem.Id ); if ( @@ -580,10 +577,7 @@ public class FenceService( } // Remove item + child mods (if any) - var itemWithChildren = itemHelper.FindAndReturnChildrenAsItems( - assort.Items, - rootItemToAdjust.Id - ); + var itemWithChildren = assort.Items.FindAndReturnChildrenAsItems(rootItemToAdjust.Id); foreach (var itemToDelete in itemWithChildren) // Delete item from assort items array { @@ -824,10 +818,7 @@ public class FenceService( .ToList(); var desiredAssortItemAndChildrenClone = _cloner.Clone( - itemHelper.FindAndReturnChildrenAsItems( - childItemsAndSingleRoot, - chosenBaseAssortRoot.Id - ) + childItemsAndSingleRoot.FindAndReturnChildrenAsItems(chosenBaseAssortRoot.Id) ); var itemDbDetails = itemHelper.GetItem(chosenBaseAssortRoot.Template).Value; @@ -1121,10 +1112,7 @@ public class FenceService( var rootItemDb = itemHelper.GetItem(randomPresetRoot.Template).Value; var presetWithChildrenClone = _cloner.Clone( - itemHelper.FindAndReturnChildrenAsItems( - baseFenceAssort.Items, - randomPresetRoot.Id - ) + baseFenceAssort.Items.FindAndReturnChildrenAsItems(randomPresetRoot.Id) ); RandomiseItemUpdProperties(rootItemDb, presetWithChildrenClone[0]); @@ -1204,7 +1192,7 @@ public class FenceService( var rootItemDb = itemHelper.GetItem(randomPresetRoot.Template).Value; var presetWithChildrenClone = _cloner.Clone( - itemHelper.FindAndReturnChildrenAsItems(baseFenceAssort.Items, randomPresetRoot.Id) + baseFenceAssort.Items.FindAndReturnChildrenAsItems(randomPresetRoot.Id) ); // Need to add mods to armors so they don't show as red in the trade screen @@ -1799,7 +1787,7 @@ public class FenceService( protected void DeleteOffer(string assortId, List assorts) { // Assort could have child items, remove those too - var itemWithChildrenToRemove = itemHelper.FindAndReturnChildrenAsItems(assorts, assortId); + var itemWithChildrenToRemove = assorts.FindAndReturnChildrenAsItems(assortId); foreach (var itemToRemove in itemWithChildrenToRemove) { var indexToRemove = assorts.FindIndex(item => item.Id == itemToRemove.Id); diff --git a/Libraries/SPTarkov.Server.Core/Services/RagfairTaxService.cs b/Libraries/SPTarkov.Server.Core/Services/RagfairTaxService.cs index c97260e0..eaba6f9a 100644 --- a/Libraries/SPTarkov.Server.Core/Services/RagfairTaxService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/RagfairTaxService.cs @@ -1,4 +1,5 @@ using SPTarkov.DI.Annotations; +using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; @@ -168,10 +169,7 @@ public class RagfairTaxService( if (isRootItem) { // Since we get a flat list of all child items, we only want to recurse from parent item - var itemChildren = _itemHelper.FindAndReturnChildrenAsItems( - pmcData.Inventory.Items, - item.Id - ); + var itemChildren = pmcData.Inventory.Items.FindAndReturnChildrenAsItems(item.Id); if (itemChildren.Count > 1) { var itemChildrenClone = _cloner.Clone(itemChildren); // Clone is expensive, only run if necessary