From 60271229f9e3ed3e7b14789f693e88259d8ed726 Mon Sep 17 00:00:00 2001 From: Chomp Date: Sun, 14 Sep 2025 15:07:20 +0100 Subject: [PATCH] Performance/locking/name improvements to `GeneratePool` --- .../Services/BotEquipmentModPoolService.cs | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs index f2c6ba23..5b493e90 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs @@ -44,7 +44,7 @@ public class BotEquipmentModPoolService( } /// - /// Get dictionary of mods for each item passed in + /// Create a dictionary of mods for each item passed in /// /// Items to find related mods and store in modPool /// Mod pool to choose from e.g. "weapon" for weaponModPool @@ -53,6 +53,7 @@ public class BotEquipmentModPoolService( string poolType ) { + // Null guard bad input if (inputItems is null || !inputItems.Any()) { logger.Error(localisationService.GetText("bot-unable_to_generate_item_pool_no_items", poolType)); @@ -61,43 +62,45 @@ public class BotEquipmentModPoolService( } var pool = new ConcurrentDictionary>>(); - foreach (var item in inputItems) + foreach (var dbItem in inputItems) { - if (item.Properties is null) + if (dbItem.Properties is null) { - logger.Error(localisationService.GetText("bot-item_missing_props_property", new { itemTpl = item.Id, name = item.Name })); + logger.Error( + localisationService.GetText("bot-item_missing_props_property", new { itemTpl = dbItem.Id, name = dbItem.Name }) + ); continue; } - // No slots - if (item.Properties?.Slots is null || !item.Properties.Slots.Any()) + // No slots for mods + if (dbItem.Properties.Slots is null || !dbItem.Properties.Slots.Any()) { continue; } - // Add base item (weapon/armor) to pool - pool.TryAdd(item.Id, new ConcurrentDictionary>()); + // Add base item (weapon/armor) to pool if it doesn't exist + var itemPool = pool.GetOrAdd(dbItem.Id, new ConcurrentDictionary>()); - // Iterate over each items mod slots e.g. mod_muzzle - foreach (var slot in item.Properties.Slots) + // Look for slots on item that hold mods + foreach (var slot in dbItem.Properties.Slots) { - // Get mods that fit into the current mod slot - var itemsThatFit = slot.Properties.Filters.FirstOrDefault().Filter; + // Get whitelist of mods that fit into mod slot + var itemsThatFit = slot?.Properties?.Filters?.FirstOrDefault()?.Filter ?? []; + if (!itemsThatFit.Any()) + { + continue; + } - // Get weapon/armor pool to add mod slots + mod tpls to - - var itemModPool = pool[item.Id]; + // Ensure Mod slot key + blank dict exist in pool + var modItemPool = GetSetModItemPool(itemPool, slot.Name); foreach (var itemToAddTpl in itemsThatFit) { - // Ensure Mod slot key + blank dict value exist - InitSetInDict(itemModPool, slot.Name); - - // Does tpl exist inside mod_slots hashset - if (!SetContainsTpl(itemModPool[slot.Name], itemToAddTpl)) + // Does tpl exist inside mods' slots' hashset + if (!SetContainsTpl(modItemPool, itemToAddTpl)) // Keyed by mod slot { - AddTplToSet(itemModPool[slot.Name], itemToAddTpl); + AddTplToSet(modItemPool, itemToAddTpl); } var subItemDetails = itemHelper.GetItem(itemToAddTpl).Value; @@ -105,8 +108,8 @@ public class BotEquipmentModPoolService( // Item has Slots + pool doesn't have value if (hasSubItemsToAdd && !pool.ContainsKey(subItemDetails.Id)) - // Recursive call { + // Recursive call GeneratePool([subItemDetails], poolType); } } @@ -116,6 +119,14 @@ public class BotEquipmentModPoolService( return pool; } + private HashSet GetSetModItemPool(ConcurrentDictionary> dictionary, string slotName) + { + lock (_lockObject) + { + return dictionary.GetOrAdd(slotName, []); + } + } + private bool SetContainsTpl(HashSet itemSet, MongoId tpl) { lock (_lockObject) @@ -132,14 +143,6 @@ public class BotEquipmentModPoolService( } } - private bool InitSetInDict(ConcurrentDictionary> dictionary, string slotName) - { - lock (_lockObject) - { - return dictionary.TryAdd(slotName, []); - } - } - /// /// Empty the mod pool ///