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
///