Moved ragfairOfferHolder to use ConcurrentCollections where possible and reduced nesting where possible
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Concurrent;
|
||||
using SPTarkov.Server.Core.Helpers;
|
||||
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
|
||||
using SPTarkov.Server.Core.Models.Eft.Ragfair;
|
||||
@@ -6,25 +7,24 @@ using SPTarkov.Server.Core.Models.Utils;
|
||||
using SPTarkov.Server.Core.Servers;
|
||||
using SPTarkov.Server.Core.Services;
|
||||
using SPTarkov.Common.Annotations;
|
||||
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
|
||||
|
||||
namespace SPTarkov.Server.Core.Utils;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class RagfairOfferHolder(
|
||||
ISptLogger<RagfairOfferHolder> logger,
|
||||
RagfairServerHelper ragfairServerHelper,
|
||||
ProfileHelper profileHelper,
|
||||
HashUtil hashUtil,
|
||||
LocalisationService localisationService,
|
||||
ConfigServer configServer)
|
||||
ISptLogger<RagfairOfferHolder> _logger,
|
||||
RagfairServerHelper _ragfairServerHelper,
|
||||
ProfileHelper _profileHelper,
|
||||
HashUtil _hashUtil,
|
||||
LocalisationService _localisationService,
|
||||
ConfigServer _configServer
|
||||
)
|
||||
{
|
||||
protected int _maxOffersPerTemplate = configServer.GetConfig<RagfairConfig>().Dynamic.OfferItemCount.Max;
|
||||
protected Dictionary<string, RagfairOffer> _offersById = new();
|
||||
protected readonly Lock _offersByIdLock = new();
|
||||
protected Dictionary<string, HashSet<string>> _offersByTemplate = new(); // key = tplId, value = list of offerIds
|
||||
protected readonly Lock _offersByTemplateLock = new();
|
||||
protected Dictionary<string, HashSet<string>> _offersByTrader = new(); // key = traderId, value = list of offerIds
|
||||
protected readonly Lock _offersByTraderLock = new();
|
||||
protected int _maxOffersPerTemplate = _configServer.GetConfig<RagfairConfig>().Dynamic.OfferItemCount.Max;
|
||||
protected ConcurrentDictionary<string, RagfairOffer> _offersById = new();
|
||||
protected ConcurrentDictionary<string, HashSet<string>> _offersByTemplate = new(); // key = tplId, value = list of offerIds
|
||||
protected ConcurrentDictionary<string, HashSet<string>> _offersByTrader = new(); // key = traderId, value = list of offerIds
|
||||
|
||||
protected HashSet<string> _expiredOfferIds = [];
|
||||
protected readonly Lock _expiredOfferIdsLock = new();
|
||||
@@ -36,10 +36,7 @@ public class RagfairOfferHolder(
|
||||
/// <returns>RagfairOffer</returns>
|
||||
public RagfairOffer? GetOfferById(string id)
|
||||
{
|
||||
lock (_offersByIdLock)
|
||||
{
|
||||
return _offersById.GetValueOrDefault(id);
|
||||
}
|
||||
return _offersById.GetValueOrDefault(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,21 +46,18 @@ public class RagfairOfferHolder(
|
||||
/// <returns>RagfairOffer list</returns>
|
||||
public List<RagfairOffer>? GetOffersByTemplate(string templateId)
|
||||
{
|
||||
lock (_offersByTemplateLock)
|
||||
// Get the offerIds we want to return
|
||||
if (!_offersByTemplate.TryGetValue(templateId, out var offerIds))
|
||||
{
|
||||
// Get the offerIds we want to return
|
||||
if (!_offersByTemplate.TryGetValue(templateId, out var offerIds))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = _offersById
|
||||
.Where(x => offerIds.Contains(x.Key))
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = _offersById
|
||||
.Where(x => offerIds.Contains(x.Key))
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,17 +67,15 @@ public class RagfairOfferHolder(
|
||||
/// <returns>RagfairOffer list</returns>
|
||||
public List<RagfairOffer>? GetOffersByTrader(string traderId)
|
||||
{
|
||||
lock (_offersByTraderLock)
|
||||
if (!_offersByTrader.TryGetValue(traderId, out var offerIds))
|
||||
{
|
||||
if (!_offersByTrader.TryGetValue(traderId, out var offerIds))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return offerIds.Select(offerId => _offersById.GetValueOrDefault(offerId))
|
||||
.Where(offer => offer != null)
|
||||
.ToList();
|
||||
return null;
|
||||
}
|
||||
|
||||
return offerIds
|
||||
.Select(offerId => _offersById.GetValueOrDefault(offerId))
|
||||
.Where(offer => offer != null)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,12 +84,9 @@ public class RagfairOfferHolder(
|
||||
/// <returns>RagfairOffer list</returns>
|
||||
public List<RagfairOffer> GetOffers()
|
||||
{
|
||||
lock (_offersByIdLock)
|
||||
if (_offersById.Count > 0)
|
||||
{
|
||||
if (_offersById.Count > 0)
|
||||
{
|
||||
return _offersById.Values.ToList();
|
||||
}
|
||||
return _offersById.Values.ToList();
|
||||
}
|
||||
|
||||
return [];
|
||||
@@ -121,37 +110,38 @@ public class RagfairOfferHolder(
|
||||
/// <param name="offer">Offer to add</param>
|
||||
public void AddOffer(RagfairOffer offer)
|
||||
{
|
||||
lock (_offersByIdLock)
|
||||
var sellerId = offer.User.Id;
|
||||
// Keep generating IDs until we get a unique one
|
||||
while (_offersById.ContainsKey(offer.Id))
|
||||
{
|
||||
var sellerId = offer.User.Id;
|
||||
// Keep generating IDs until we get a unique one
|
||||
while (_offersById.ContainsKey(offer.Id))
|
||||
{
|
||||
offer.Id = hashUtil.Generate();
|
||||
}
|
||||
|
||||
var itemTpl = offer.Items?.FirstOrDefault()?.Template;
|
||||
// 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
|
||||
var sellerIsTrader = ragfairServerHelper.IsTrader(sellerId);
|
||||
if (itemTpl != null
|
||||
&& !(sellerIsTrader || profileHelper.IsPlayer(sellerId))
|
||||
&& _offersByTemplate.TryGetValue(itemTpl, out var offers)
|
||||
&& offers?.Count >= _maxOffersPerTemplate
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_offersById.Add(offer.Id, offer);
|
||||
|
||||
if (sellerIsTrader)
|
||||
{
|
||||
AddOfferByTrader(sellerId, offer.Id);
|
||||
}
|
||||
|
||||
AddOfferByTemplates(itemTpl, offer.Id);
|
||||
offer.Id = _hashUtil.Generate();
|
||||
}
|
||||
|
||||
var itemTpl = offer.Items?.FirstOrDefault()?.Template;
|
||||
// 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
|
||||
var sellerIsTrader = _ragfairServerHelper.IsTrader(sellerId);
|
||||
if (
|
||||
itemTpl != null
|
||||
&& !(sellerIsTrader || _profileHelper.IsPlayer(sellerId))
|
||||
&& _offersByTemplate.TryGetValue(itemTpl, out var offers)
|
||||
&& offers?.Count >= _maxOffersPerTemplate
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_offersById.TryAdd(offer.Id, offer))
|
||||
{
|
||||
_logger.Warning($"Unable to add offer: {offer.Id}");
|
||||
}
|
||||
|
||||
if (sellerIsTrader)
|
||||
{
|
||||
AddOfferByTrader(sellerId, offer.Id);
|
||||
}
|
||||
|
||||
AddOfferByTemplates(itemTpl, offer.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,43 +151,37 @@ public class RagfairOfferHolder(
|
||||
/// <param name="checkTraderOffers">OPTIONAL - Should trader offers be checked for offer id</param>
|
||||
public void RemoveOffer(string offerId, bool checkTraderOffers = true)
|
||||
{
|
||||
lock (_offersByIdLock)
|
||||
if (!_offersById.TryGetValue(offerId, out var offer))
|
||||
{
|
||||
if (!_offersById.TryGetValue(offerId, out var offer))
|
||||
{
|
||||
logger.Warning(localisationService.GetText("ragfair-unable_to_remove_offer_doesnt_exist", offerId));
|
||||
return;
|
||||
}
|
||||
_logger.Warning(_localisationService.GetText("ragfair-unable_to_remove_offer_doesnt_exist", offerId));
|
||||
return;
|
||||
}
|
||||
|
||||
_offersById.Remove(offer.Id);
|
||||
if (!_offersById.TryRemove(offer.Id, out _))
|
||||
{
|
||||
_logger.Warning($"Unable to remove offer: {offer.Id}");
|
||||
}
|
||||
|
||||
if (checkTraderOffers)
|
||||
if (checkTraderOffers && _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)
|
||||
{
|
||||
lock (_offersByTraderLock)
|
||||
if (!_offersByTrader.TryRemove(offer.User.Id, out _))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
_logger.Warning($"Unable to remove Trader offer: {offer.Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lock (_offersByTemplateLock)
|
||||
{
|
||||
var firstItem = offer.Items.FirstOrDefault();
|
||||
if (_offersByTemplate.TryGetValue(firstItem.Template, out var offers))
|
||||
{
|
||||
offers.Remove(offer.Id);
|
||||
}
|
||||
}
|
||||
var firstItem = offer.Items.FirstOrDefault();
|
||||
if (_offersByTemplate.TryGetValue(firstItem.Template, out var offers))
|
||||
{
|
||||
offers.Remove(offer.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,18 +191,18 @@ public class RagfairOfferHolder(
|
||||
/// <param name="traderId">Trader id to remove offers from</param>
|
||||
public void RemoveAllOffersByTrader(string traderId)
|
||||
{
|
||||
lock (_offersByTraderLock)
|
||||
if (_offersByTrader.TryGetValue(traderId, out var offerIdsToRemove))
|
||||
{
|
||||
if (_offersByTrader.TryGetValue(traderId, out var offerIdsToRemove))
|
||||
foreach (var offerId in offerIdsToRemove)
|
||||
{
|
||||
foreach (var offerId in offerIdsToRemove)
|
||||
if (!_offersById.TryRemove(offerId, out _))
|
||||
{
|
||||
_offersById.Remove(offerId);
|
||||
_logger.Warning($"Unable to remove offer: {offerId}");
|
||||
}
|
||||
|
||||
// Clear out linking table
|
||||
_offersByTrader[traderId].Clear();
|
||||
}
|
||||
|
||||
// Clear out linking table
|
||||
_offersByTrader[traderId].Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,16 +213,15 @@ public class RagfairOfferHolder(
|
||||
/// <param name="offerId">Offer to store against tpl</param>
|
||||
protected void AddOfferByTemplates(string template, string offerId)
|
||||
{
|
||||
lock (_offersByTemplateLock)
|
||||
if (_offersByTemplate.ContainsKey(template))
|
||||
{
|
||||
if (_offersByTemplate.ContainsKey(template))
|
||||
{
|
||||
_offersByTemplate[template].Add(offerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_offersByTemplate.Add(template, [offerId]);
|
||||
}
|
||||
_offersByTemplate[template].Add(offerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_offersByTemplate.TryAdd(template, [offerId]))
|
||||
{
|
||||
_logger.Warning($"Unable to add offer: {offerId} to offersByTemplate");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,16 +232,15 @@ public class RagfairOfferHolder(
|
||||
/// <param name="offerId">Offer to store against</param>
|
||||
protected void AddOfferByTrader(string trader, string offerId)
|
||||
{
|
||||
lock (_offersByTraderLock)
|
||||
if (_offersByTrader.ContainsKey(trader))
|
||||
{
|
||||
if (_offersByTrader.ContainsKey(trader))
|
||||
{
|
||||
_offersByTrader[trader].Add(offerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_offersByTrader.Add(trader, [offerId]);
|
||||
}
|
||||
_offersByTrader[trader].Add(offerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_offersByTrader.TryAdd(trader, [offerId]))
|
||||
{
|
||||
_logger.Error($"Unable to add offer: {offerId} to offersByTrader");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +268,10 @@ public class RagfairOfferHolder(
|
||||
{
|
||||
lock (_expiredOfferIdsLock)
|
||||
{
|
||||
_expiredOfferIds.Add(staleOfferId);
|
||||
if (!_expiredOfferIds.Add(staleOfferId))
|
||||
{
|
||||
_logger.Warning($"Unable to add offer: {staleOfferId} to expired offers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,13 +302,13 @@ public class RagfairOfferHolder(
|
||||
var offer = GetOfferById(expiredOfferId);
|
||||
if (offer is null)
|
||||
{
|
||||
logger.Warning($"offerId: {expiredOfferId} was not found !!");
|
||||
_logger.Warning($"offerId: {expiredOfferId} was not found !!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offer?.Items?.Count == 0)
|
||||
{
|
||||
logger.Error($"Unable to process expired offer: {expiredOfferId}, it has no items");
|
||||
|
||||
_logger.Error($"Unable to process expired offer: {expiredOfferId}, it has no items");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -355,7 +340,7 @@ public class RagfairOfferHolder(
|
||||
{
|
||||
foreach (var offer in GetOffers())
|
||||
{
|
||||
if (_expiredOfferIds.Contains(offer.Id) || ragfairServerHelper.IsTrader(offer.User.Id))
|
||||
if (_expiredOfferIds.Contains(offer.Id) || _ragfairServerHelper.IsTrader(offer.User.Id))
|
||||
{
|
||||
// Already flagged or trader offer (handled separately), skip
|
||||
continue;
|
||||
@@ -363,7 +348,10 @@ public class RagfairOfferHolder(
|
||||
|
||||
if (IsStale(offer, timestamp))
|
||||
{
|
||||
_expiredOfferIds.Add(offer.Id);
|
||||
if (!_expiredOfferIds.Add(offer.Id))
|
||||
{
|
||||
_logger.Warning($"Unable to add offer: {offer.Id} to expired offers");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user