From 96e12d80e41788c3a99153d99f681935820e44d5 Mon Sep 17 00:00:00 2001 From: Chomp Date: Wed, 18 Jun 2025 15:36:50 +0100 Subject: [PATCH] Refactored caching inside `BotEquipmentModPoolService` Made use of primary constructor Made `GetModsForWeaponSlot` access dictionary via`TryGet` Made `GetCompatibleModsForWeaponSlot` access dictionary via`TryGet` --- .../Services/BotEquipmentModPoolService.cs | 117 ++++++++---------- 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs index e11357ce..8a29cd49 100644 --- a/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/BotEquipmentModPoolService.cs @@ -8,58 +8,47 @@ using SPTarkov.Server.Core.Models.Utils; namespace SPTarkov.Server.Core.Services; [Injectable(InjectionType.Singleton)] -public class BotEquipmentModPoolService +public class BotEquipmentModPoolService( + ISptLogger logger, + ItemHelper itemHelper, + DatabaseService databaseService, + LocalisationService localisationService) { private readonly Lock _lockObject = new(); - - protected DatabaseService _databaseService; - protected ConcurrentDictionary>> _gearModPool; - protected ItemHelper _itemHelper; - protected LocalisationService _localisationService; - protected ISptLogger _logger; - protected ConcurrentDictionary>> _weaponModPool; - protected bool _weaponPoolGenerated; - protected bool _armorPoolGenerated; - - public BotEquipmentModPoolService( - ISptLogger logger, - ItemHelper itemHelper, - DatabaseService databaseService, - LocalisationService localisationService - ) + private ConcurrentDictionary>>? _gearModPool; + protected ConcurrentDictionary>> GearModPool { - _logger = logger; - _itemHelper = itemHelper; - _databaseService = databaseService; - _localisationService = localisationService; + get { return _gearModPool ??= GenerateGearPool(); } + } - _weaponModPool = new ConcurrentDictionary>>(); - _gearModPool = new ConcurrentDictionary>>(); + private ConcurrentDictionary>>? _weaponModPool; + protected ConcurrentDictionary>> WeaponModPool + { + get { return _weaponModPool ??= GenerateWeaponPool(); } } /// - /// Store dictionary of mods for each item passed in + /// Get dictionary of mods for each item passed in /// - /// Items to find related mods and store in modPool + /// Items to find related mods and store in modPool /// Mod pool to choose from e.g. "weapon" for weaponModPool - protected void GeneratePool(IEnumerable? items, string poolType) + protected ConcurrentDictionary>> GeneratePool(IEnumerable? inputItems, string poolType) { - if (items is null) + if (inputItems is null || !inputItems.Any()) { - _logger.Error(_localisationService.GetText("bot-unable_to_generate_item_pool_no_items", poolType)); + logger.Error(localisationService.GetText("bot-unable_to_generate_item_pool_no_items", poolType)); - return; + return []; } - // Get weapon or gear pool - var pool = poolType == "weapon" ? _weaponModPool : _gearModPool; - foreach (var item in items) + var pool = new ConcurrentDictionary>>(); + foreach (var item in inputItems) { if (item.Properties is null) { - _logger.Error( - _localisationService.GetText( + logger.Error( + localisationService.GetText( "bot-item_missing_props_property", new { @@ -102,7 +91,7 @@ public class BotEquipmentModPoolService AddTplToSet(itemModPool[slot.Name], itemToAddTpl); } - var subItemDetails = _itemHelper.GetItem(itemToAddTpl).Value; + var subItemDetails = itemHelper.GetItem(itemToAddTpl).Value; var hasSubItemsToAdd = (subItemDetails?.Properties?.Slots?.Count ?? 0) > 0; if (hasSubItemsToAdd && !pool.ContainsKey(subItemDetails.Id)) // Recursive call @@ -112,6 +101,8 @@ public class BotEquipmentModPoolService } } } + + return pool; } private bool SetContainsTpl(HashSet itemSet, string tpl) @@ -141,9 +132,9 @@ public class BotEquipmentModPoolService /// /// Empty the mod pool /// - public void ResetPool() + public void ResetWeaponPool() { - _weaponModPool.Clear(); + WeaponModPool.Clear(); } /// @@ -154,13 +145,16 @@ public class BotEquipmentModPoolService /// Hashset of tpls that fit the slot public HashSet GetCompatibleModsForWeaponSlot(string itemTpl, string slotName) { - if (!_weaponPoolGenerated) - // Get every weapon in db and generate mod pool + if(WeaponModPool.TryGetValue(itemTpl, out var value)) { - GenerateWeaponPool(); + if (value.TryGetValue(slotName, out var tplsForSlotHashSet)) + { + return tplsForSlotHashSet; + } } + logger.Warning($"Slot: {slotName} not found for item: {itemTpl} in cache"); - return _weaponModPool[itemTpl][slotName]; + return []; } /// @@ -170,12 +164,7 @@ public class BotEquipmentModPoolService /// Dictionary of mods (keys are mod slot names) with array of compatible mod tpls as value public ConcurrentDictionary> GetModsForGearSlot(string itemTpl) { - if (!_armorPoolGenerated) - { - GenerateGearPool(); - } - - return _gearModPool.TryGetValue(itemTpl, out var value) + return GearModPool.TryGetValue(itemTpl, out var value) ? value : []; } @@ -187,12 +176,9 @@ public class BotEquipmentModPoolService /// Dictionary of mods (keys are mod slot names) with array of compatible mod tpls as value public ConcurrentDictionary> GetModsForWeaponSlot(string itemTpl) { - if (!_weaponPoolGenerated) - { - GenerateWeaponPool(); - } - - return _weaponModPool[itemTpl]; + return WeaponModPool.TryGetValue(itemTpl, out var value) + ? value + : []; } /// @@ -205,14 +191,14 @@ public class BotEquipmentModPoolService var result = new Dictionary>(); // Get item from db - var itemDb = _itemHelper.GetItem(itemTpl).Value; + var itemDb = itemHelper.GetItem(itemTpl).Value; if (itemDb.Properties.Slots is not null) // Loop over slots flagged as 'required' { foreach (var slot in itemDb.Properties.Slots.Where(slot => slot.Required.GetValueOrDefault(false))) { // Create dict entry for mod slot - result.Add(slot.Name, []); + result.TryAdd(slot.Name, []); // Add compatible tpls to dicts hashset foreach (var compatibleItemTpl in slot.Props.Filters.FirstOrDefault().Filter) @@ -228,25 +214,24 @@ public class BotEquipmentModPoolService /// /// Create weapon mod pool and set generated flag to true /// - protected void GenerateWeaponPool() + protected ConcurrentDictionary>> GenerateWeaponPool() { - var weapons = _databaseService.GetItems() - .Values.Where(item => string.Equals(item.Type, "Item", StringComparison.OrdinalIgnoreCase) && _itemHelper.IsOfBaseclass(item.Id, BaseClasses.WEAPON) + var weapons = databaseService.GetItems() + .Values.Where(item => string.Equals(item.Type, "Item", StringComparison.OrdinalIgnoreCase) && + itemHelper.IsOfBaseclass(item.Id, BaseClasses.WEAPON) ); - GeneratePool(weapons, "weapon"); - // Flag pool as being complete - _weaponPoolGenerated = true; + return GeneratePool(weapons, "weapon"); } /// /// Create gear mod pool and set generated flag to true /// - protected void GenerateGearPool() + protected ConcurrentDictionary>> GenerateGearPool() { - var gear = _databaseService.GetItems() + var gear = databaseService.GetItems() .Values.Where(item => string.Equals(item.Type, "Item", StringComparison.OrdinalIgnoreCase) && - _itemHelper.IsOfBaseclasses( + itemHelper.IsOfBaseclasses( item.Id, [ BaseClasses.ARMORED_EQUIPMENT, @@ -256,9 +241,7 @@ public class BotEquipmentModPoolService ] ) ); - GeneratePool(gear, "gear"); - // Flag pool as being complete - _armorPoolGenerated = true; + return GeneratePool(gear, "gear"); } }