From 08815da10dd8600acce5b7f1c786be74515b7647 Mon Sep 17 00:00:00 2001 From: Chomp Date: Sat, 2 Aug 2025 11:22:27 +0100 Subject: [PATCH] Improved Fake PMC offer count fluctuations #527 Added fake PMC offer count index to `RagfairOfferHolder` and made use of it when checking if offer count is over desired count --- .../Extensions/RagfairOfferExtensions.cs | 15 ++++++ .../Generators/RagfairAssortGenerator.cs | 12 +---- .../Generators/RagfairOfferGenerator.cs | 34 ++++++------ .../Utils/RagfairOfferHolder.cs | 54 +++++++++++++++---- 4 files changed, 75 insertions(+), 40 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Extensions/RagfairOfferExtensions.cs b/Libraries/SPTarkov.Server.Core/Extensions/RagfairOfferExtensions.cs index ec506610..a5d9a6ac 100644 --- a/Libraries/SPTarkov.Server.Core/Extensions/RagfairOfferExtensions.cs +++ b/Libraries/SPTarkov.Server.Core/Extensions/RagfairOfferExtensions.cs @@ -45,4 +45,19 @@ public static class RagfairOfferExtensions return false; } + + /// + /// Was this offer created by a fake player + /// + /// + /// + public static bool IsFakePlayerOffer(this RagfairOffer offer) + { + if (offer.CreatedBy is not null) + { + return offer.CreatedBy == OfferCreator.FakePlayer; + } + + return false; + } } diff --git a/Libraries/SPTarkov.Server.Core/Generators/RagfairAssortGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RagfairAssortGenerator.cs index f72d3b99..fca37fe5 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RagfairAssortGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RagfairAssortGenerator.cs @@ -35,21 +35,11 @@ public class RagfairAssortGenerator( BaseClasses.BUILT_IN_INSERTS, ]; - /// - /// Get a list of lists that can be sold on the flea.
- /// Each sub list contains item + children (if any) - ///
- /// List with children lists of items - public IEnumerable> GetAssortItems() - { - return GenerateRagfairAssortItems(); - } - /// /// Generate a list of lists (item + children) the flea can sell /// /// List of lists (item + children) - protected IEnumerable> GenerateRagfairAssortItems() + public IEnumerable> GenerateRagfairAssortItems() { IEnumerable> results = []; diff --git a/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs index 9bfd76ca..dd778a05 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RagfairOfferGenerator.cs @@ -74,26 +74,24 @@ public class RagfairOfferGenerator( /// RagfairOffer protected RagfairOffer CreateOffer(CreateFleaOfferDetails details) { - var offerRequirements = details - .BarterScheme.Select(barter => + var offerRequirements = details.BarterScheme.Select(barter => + { + var offerRequirement = new OfferRequirement { - var offerRequirement = new OfferRequirement - { - TemplateId = barter.Template, - Count = Math.Round(barter.Count.Value, 2), - OnlyFunctional = barter.OnlyFunctional ?? false, - }; + TemplateId = barter.Template, + Count = Math.Round(barter.Count.Value, 2), + OnlyFunctional = barter.OnlyFunctional ?? false, + }; - // Dogtags define level and side - if (barter.Level != null) - { - offerRequirement.Level = barter.Level; - offerRequirement.Side = barter.Side; - } + // Dogtags define level and side + if (barter.Level != null) + { + offerRequirement.Level = barter.Level; + offerRequirement.Side = barter.Side; + } - return offerRequirement; - }) - .ToList(); + return offerRequirement; + }); // Clone to avoid modifying original array var itemsClone = cloner.Clone(details.Items); @@ -295,7 +293,7 @@ public class RagfairOfferGenerator( var stopwatch = Stopwatch.StartNew(); // get assort items from param if they exist, otherwise grab freshly generated assorts - var assortItemsToProcess = replacingExpiredOffers ? expiredOffers ?? [] : ragfairAssortGenerator.GetAssortItems(); + var assortItemsToProcess = replacingExpiredOffers ? expiredOffers ?? [] : ragfairAssortGenerator.GenerateRagfairAssortItems(); stopwatch.Stop(); if (logger.IsLogEnabled(LogLevel.Debug) && stopwatch.ElapsedMilliseconds > 0) { diff --git a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs index c969a980..56c8f22c 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/RagfairOfferHolder.cs @@ -38,6 +38,11 @@ public class RagfairOfferHolder( /// private readonly ConcurrentDictionary> _offersByTrader = new(); + /// + /// Fake player offer ids keyed by itemTPl + /// + private readonly ConcurrentDictionary> _fakePlayerOffers = new(); + private readonly Lock _expiredOfferIdsLock = new(); private readonly Lock _ragfairOperationLock = new(); @@ -136,20 +141,16 @@ public class RagfairOfferHolder( offer.Id = new MongoId(); } - var itemTpl = offer.Items?.FirstOrDefault()?.Template ?? new MongoId(null); - - var sellerId = offer.User.Id; - var sellerIsTrader = offer.IsTraderOffer(); - var itemSoldTemplate = _itemHelper.GetItem(itemTpl); + var itemTpl = offer.Items?.FirstOrDefault()?.Template ?? new MongoId(); if ( !itemTpl.IsEmpty() // Has tpl - && !(sellerIsTrader || offer.IsPlayerOffer()) - && _offersByTemplate.TryGetValue(itemTpl, out var offers) - && offers?.Count >= _ragfairServerHelper.GetOfferCountByBaseType(itemSoldTemplate.Value.Parent) + && offer.IsFakePlayerOffer() + && _fakePlayerOffers.TryGetValue(itemTpl, out var offers) + && offers?.Count >= _ragfairServerHelper.GetOfferCountByBaseType(_itemHelper.GetItem(itemTpl).Value.Parent) ) { // If it is an NPC PMC offer AND we have already reached the maximum amount of possible offers - // for this template, just don't add in more + // for this template, don't add more return; } @@ -158,9 +159,14 @@ public class RagfairOfferHolder( _logger.Warning($"Offer: {offer.Id} already exists"); } - if (sellerIsTrader) + if (offer.IsTraderOffer()) { - AddOfferByTrader(sellerId, offer.Id); + AddOfferByTrader(offer.User.Id, offer.Id); + } + + if (offer.IsFakePlayerOffer()) + { + AddFakePlayerOffer(itemTpl, offer.Id); } AddOfferByTemplates(itemTpl, offer.Id); @@ -206,6 +212,11 @@ public class RagfairOfferHolder( { offers.Remove(offer.Id); } + + if (offer.IsFakePlayerOffer() && _fakePlayerOffers.TryGetValue(offer.Items.FirstOrDefault().Template, out var fakePlayerOfferIds)) + { + fakePlayerOfferIds.Remove(offer.Id); + } } /// @@ -286,6 +297,27 @@ public class RagfairOfferHolder( return false; } + protected bool AddFakePlayerOffer(MongoId itemTpl, MongoId offerId) + { + // Look for hashset for trader first + if (_fakePlayerOffers.TryGetValue(itemTpl, out var fakePlayerOfferIds)) + { + fakePlayerOfferIds.Add(offerId); + + return true; + } + + // Add new KvP of trader and offer id in new hashset + if (_fakePlayerOffers.TryAdd(itemTpl, [offerId])) + { + return true; + } + + _logger.Error($"Unable to add offer: {offerId} to _fakePlayerOffers"); + + return false; + } + /// /// Add a stale offers id to _expiredOfferIds collection for later processing ///