From 87dd6f6e0ff0378e4bfa3d5f79a01103a7548e11 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 27 Jan 2025 20:57:47 +0000 Subject: [PATCH] multithreaded ragfair and botgen --- Libraries/Core/Controllers/BotController.cs | 30 ++-- .../Core/Generators/RagfairOfferGenerator.cs | 16 +- .../Services/BotGenerationCacheService.cs | 19 ++- Libraries/Core/Utils/RagfairOfferHolder.cs | 142 +++++++++++------- 4 files changed, 139 insertions(+), 68 deletions(-) diff --git a/Libraries/Core/Controllers/BotController.cs b/Libraries/Core/Controllers/BotController.cs index 220fb5a7..6af9717a 100644 --- a/Libraries/Core/Controllers/BotController.cs +++ b/Libraries/Core/Controllers/BotController.cs @@ -149,21 +149,31 @@ public class BotController( _pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance ); + var tasks = new List(); // Map conditions to promises for bot generation foreach (var condition in request.Conditions ?? []) { - var botGenerationDetails = GetBotGenerationDetailsForWave( - condition, - pmcProfile, - allPmcsHaveSameNameAsPlayer, - raidSettings, - _botConfig.PresetBatch!.GetValueOrDefault(condition.Role, 15), - _botHelper.IsBotPmc(condition.Role) - ); + tasks.Add( + Task.Factory.StartNew( + () => + { + var botGenerationDetails = GetBotGenerationDetailsForWave( + condition, + pmcProfile, + allPmcsHaveSameNameAsPlayer, + raidSettings, + _botConfig.PresetBatch!.GetValueOrDefault(condition.Role, 15), + _botHelper.IsBotPmc(condition.Role) + ); - // Generate bots for the current condition - GenerateWithBotDetails(condition, botGenerationDetails, sessionId); + // Generate bots for the current condition + GenerateWithBotDetails(condition, botGenerationDetails, sessionId); + } + ) + ); } + + Task.WaitAll(tasks.ToArray()); return []; } diff --git a/Libraries/Core/Generators/RagfairOfferGenerator.cs b/Libraries/Core/Generators/RagfairOfferGenerator.cs index 894ae356..c695f903 100644 --- a/Libraries/Core/Generators/RagfairOfferGenerator.cs +++ b/Libraries/Core/Generators/RagfairOfferGenerator.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Runtime.InteropServices.JavaScript; using Core.Helpers; using SptCommon.Annotations; @@ -327,15 +328,26 @@ public class RagfairOfferGenerator( { var replacingExpiredOffers = (expiredOffers?.Count ?? 0) > 0; + var stopwatch = Stopwatch.StartNew(); // get assort items from param if they exist, otherwise grab freshly generated assorts var assortItemsToProcess = replacingExpiredOffers ? expiredOffers ?? [] : ragfairAssortGenerator.GetAssortItems(); - + stopwatch.Stop(); + logger.Info($"Took {stopwatch.ElapsedMilliseconds}ms to GetRagfairAssorts"); + stopwatch.Restart(); + var tasks = new List(); foreach (var assortItem in assortItemsToProcess) { - CreateOffersFromAssort(assortItem, replacingExpiredOffers, ragfairConfig.Dynamic); + tasks.Add( + Task.Factory.StartNew( + () => { CreateOffersFromAssort(assortItem, replacingExpiredOffers, ragfairConfig.Dynamic); } + ) + ); } + Task.WaitAll(tasks.ToArray()); + stopwatch.Stop(); + logger.Info($"Took {stopwatch.ElapsedMilliseconds}ms to CreateOffersFromAssort"); } diff --git a/Libraries/Core/Services/BotGenerationCacheService.cs b/Libraries/Core/Services/BotGenerationCacheService.cs index c5fa329f..9f1b8a67 100644 --- a/Libraries/Core/Services/BotGenerationCacheService.cs +++ b/Libraries/Core/Services/BotGenerationCacheService.cs @@ -11,9 +11,9 @@ public class BotGenerationCacheService( LocalisationService _localisationService ) { - protected Dictionary> _storedBots = new Dictionary>(); + protected Dictionary> _storedBots = new(); protected Queue _activeBotsInRaid = []; - protected Lock _lock = new Lock(); + protected object _lock = new(); /** @@ -108,16 +108,25 @@ public class BotGenerationCacheService( */ public bool CacheHasBotWithKey(string key, int size = 0) { - return _storedBots.ContainsKey(key) && _storedBots[key].Count > size; + lock (_lock) + { + return _storedBots.ContainsKey(key) && _storedBots[key].Count > size; + } } public int GetCachedBotCount(string key) { - return _storedBots.TryGetValue(key, out var bot) ? bot.Count : 0; + lock (_lock) + { + return _storedBots.TryGetValue(key, out var bot) ? bot.Count : 0; + } } public string CreateCacheKey(string? role, string? difficulty) { - return $"{role?.ToLower()}{difficulty?.ToLower()}"; + lock (_lock) + { + return $"{role?.ToLower()}{difficulty?.ToLower()}"; + } } } diff --git a/Libraries/Core/Utils/RagfairOfferHolder.cs b/Libraries/Core/Utils/RagfairOfferHolder.cs index 5a17adc0..64b46469 100644 --- a/Libraries/Core/Utils/RagfairOfferHolder.cs +++ b/Libraries/Core/Utils/RagfairOfferHolder.cs @@ -10,29 +10,42 @@ namespace Core.Utils; public class RagfairOfferHolder( RagfairServerHelper ragfairServerHelper, ProfileHelper profileHelper, + HashUtil hashUtil, ConfigServer configServer) { protected Dictionary _offersById = new(); + protected object _offersByIdLock = new(); protected Dictionary> _offersByTemplate = new(); + protected object _offersByTemplateLock = new(); protected Dictionary> _offersByTrader = new(); + protected object _offersByTraderLock = new(); protected int _maxOffersPerTemplate = (int) configServer.GetConfig().Dynamic.OfferItemCount.Max; public RagfairOffer? GetOfferById(string id) { - return _offersById.GetValueOrDefault(id); + lock (_offersByIdLock) + { + return _offersById.GetValueOrDefault(id); + } } public List? GetOffersByTemplate(string templateId) { - return _offersByTemplate.TryGetValue(templateId, out var value) ? value.Values.ToList() : null; + lock (_offersByTemplateLock) + { + return _offersByTemplate.TryGetValue(templateId, out var value) ? value.Values.ToList() : null; + } } public List GetOffersByTrader(string traderId) { - if (_offersByTrader.ContainsKey(traderId)) + lock (_offersByTraderLock) { - return _offersByTrader[traderId].Values.ToList(); + if (_offersByTrader.ContainsKey(traderId)) + { + return _offersByTrader[traderId].Values.ToList(); + } } return null; @@ -40,9 +53,12 @@ public class RagfairOfferHolder( public List GetOffers() { - if (_offersById.Count > 0) + lock (_offersByIdLock) { - return _offersById.Values.ToList(); + if (_offersById.Count > 0) + { + return _offersById.Values.ToList(); + } } return []; @@ -55,22 +71,28 @@ public class RagfairOfferHolder( public void AddOffer(RagfairOffer offer) { - var trader = offer.User.Id; - var offerId = offer.Id; - var itemTpl = offer.Items.FirstOrDefault().Template; - // If its an NPC PMC offer AND we have already reached the maximum amount of possible offers - // for this template, just dont add in more - if ( - !(ragfairServerHelper.IsTrader(trader) || profileHelper.IsPlayer(trader)) && - (GetOffersByTemplate(itemTpl)?.Count ?? 0) >= _maxOffersPerTemplate - ) + lock (_offersByIdLock) { - return; + var trader = offer.User.Id; + // keep generating IDs until we get a new one + while (_offersById.ContainsKey(offer.Id)) + offer.Id = hashUtil.Generate(); + + var offerId = offer.Id; + var itemTpl = offer.Items.FirstOrDefault().Template; + // If its an NPC PMC offer AND we have already reached the maximum amount of possible offers + // for this template, just dont add in more + if (!(ragfairServerHelper.IsTrader(trader) || profileHelper.IsPlayer(trader)) && + (GetOffersByTemplate(itemTpl)?.Count ?? 0) >= _maxOffersPerTemplate + ) + { + return; + } + + _offersById.Add(offerId, offer); + AddOfferByTrader(trader, offer); + AddOfferByTemplates(itemTpl, offer); } - - _offersById.Add(offerId, offer); - AddOfferByTrader(trader, offer); - AddOfferByTemplates(itemTpl, offer); } /** @@ -79,25 +101,34 @@ public class RagfairOfferHolder( */ public void RemoveOffer(RagfairOffer offer) { - if (_offersById.ContainsKey(offer.Id)) + lock (_offersByIdLock) { - _offersById.Remove(offer.Id); - if (_offersByTrader.ContainsKey(offer.User.Id)) + if (_offersById.ContainsKey(offer.Id)) { - _offersByTrader[offer.User.Id].Remove(offer.Id); - // This was causing a memory leak, we need to make sure that we remove - // the user ID from the cached offers after they dont have anything else - // on the flea placed. We regenerate the ID for the NPC users, making it - // continuously grow otherwise - if (_offersByTrader[offer.User.Id].Count == 0) + _offersById.Remove(offer.Id); + lock (_offersByTraderLock) { - _offersByTrader.Remove(offer.User.Id); + if (_offersByTrader.ContainsKey(offer.User.Id)) + { + _offersByTrader[offer.User.Id].Remove(offer.Id); + // This was causing a memory leak, we need to make sure that we remove + // the user ID from the cached offers after they dont have anything else + // on the flea placed. We regenerate the ID for the NPC users, making it + // continuously grow otherwise + if (_offersByTrader[offer.User.Id].Count == 0) + { + _offersByTrader.Remove(offer.User.Id); + } + } } - } - if (_offersByTemplate.ContainsKey(offer.Items.FirstOrDefault().Template)) - { - _offersByTemplate[offer.Items[0].Template].Remove(offer.Id); + lock (_offersByTemplateLock) + { + if (_offersByTemplate.ContainsKey(offer.Items.FirstOrDefault().Template)) + { + _offersByTemplate[offer.Items[0].Template].Remove(offer.Id); + } + } } } } @@ -109,9 +140,12 @@ public class RagfairOfferHolder( public void RemoveAllOffersByTrader(string traderId) { - if (_offersByTrader.ContainsKey(traderId)) + lock (_offersByTraderLock) { - RemoveOffers(_offersByTrader[traderId].Values.ToList()); + if (_offersByTrader.ContainsKey(traderId)) + { + RemoveOffers(_offersByTrader[traderId].Values.ToList()); + } } } @@ -126,28 +160,34 @@ public class RagfairOfferHolder( protected void AddOfferByTemplates(string template, RagfairOffer offer) { - if (_offersByTemplate.ContainsKey(template)) + lock (_offersByTemplateLock) { - _offersByTemplate[template].Add(offer.Id, offer); - } - else - { - var valueMapped = new Dictionary(); - valueMapped.Add(offer.Id, offer); - _offersByTemplate.Add(template, valueMapped); + if (_offersByTemplate.ContainsKey(template)) + { + _offersByTemplate[template].Add(offer.Id, offer); + } + else + { + var valueMapped = new Dictionary(); + valueMapped.Add(offer.Id, offer); + _offersByTemplate.Add(template, valueMapped); + } } } protected void AddOfferByTrader(string trader, RagfairOffer offer) { - if (_offersByTrader.ContainsKey(trader)) + lock (_offersByTraderLock) { - _offersByTrader[trader].Add(offer.Id, offer); - } - else - { - var valueMapped = new Dictionary { { offer.Id, offer } }; - _offersByTrader.Add(trader, valueMapped); + if (_offersByTrader.ContainsKey(trader)) + { + _offersByTrader[trader].Add(offer.Id, offer); + } + else + { + var valueMapped = new Dictionary { { offer.Id, offer } }; + _offersByTrader.Add(trader, valueMapped); + } } }