diff --git a/Libraries/Core/Services/RagfairOfferService.cs b/Libraries/Core/Services/RagfairOfferService.cs index de5180c0..60236eb4 100644 --- a/Libraries/Core/Services/RagfairOfferService.cs +++ b/Libraries/Core/Services/RagfairOfferService.cs @@ -1,15 +1,20 @@ +using Core.Helpers; using SptCommon.Annotations; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.Ragfair; using Core.Models.Utils; +using Core.Utils; namespace Core.Services; [Injectable(InjectionType.Singleton)] public class RagfairOfferService( ISptLogger _logger, + TimeUtil _timeUtil, DatabaseService _databaseService, + RagfairOfferHelper _ragfairOfferHelper , RagfairOfferService _ragfairOfferService, + RagfairOfferHolder _ragfairOfferHolder, LocalisationService _localisationService) { /// @@ -131,7 +136,10 @@ public class RagfairOfferService( public void ExpireStaleOffers() { - Console.WriteLine($"actually implement me plz: owo: ExpireStaleOffers"); + var time = _timeUtil.GetTimeStamp(); + foreach (var staleOffer in _ragfairOfferHolder.GetStaleOffers(time)) { + ProcessStaleOffer(staleOffer); + } } /// diff --git a/Libraries/Core/Utils/RagfairOfferHolder.cs b/Libraries/Core/Utils/RagfairOfferHolder.cs new file mode 100644 index 00000000..ba80580c --- /dev/null +++ b/Libraries/Core/Utils/RagfairOfferHolder.cs @@ -0,0 +1,165 @@ +using Core.Helpers; +using Core.Models.Eft.Ragfair; +using SptCommon.Annotations; + +namespace Core.Utils; + +[Injectable(InjectionType.Singleton)] +public class RagfairOfferHolder( + int maxOffersPerTemplate, + RagfairServerHelper ragfairServerHelper, + ProfileHelper profileHelper) +{ + protected Dictionary _offersById; + protected Dictionary> _offersByTemplate; + protected Dictionary> _offersByTrader; + + public RagfairOffer? GetOfferById(string id) + { + if (_offersById.ContainsKey(id)) + { + return _offersById[id]; + } + + return null; + } + + public List GetOffersByTemplate(string templateId) + { + if (_offersByTemplate.ContainsKey(templateId)) + { + return _offersByTemplate[templateId].Values.ToList(); + } + + return null; + } + + public List GetOffersByTrader(string traderId) + { + if (_offersByTrader.ContainsKey(traderId)) + { + return _offersByTrader[traderId].Values.ToList(); + } + + return null; + } + + public List GetOffers() + { + if (_offersById.Count > 0) + { + return _offersById.Values.ToList(); + } + + return []; + } + + public void AddOffers(List offers) + { + foreach (var offer in offers) AddOffer(offer); + } + + 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 + ) + { + return; + } + + _offersById.Add(offerId, offer); + AddOfferByTrader(trader, offer); + AddOfferByTemplates(itemTpl, offer); + } + + /** + * Purge offer from offer cache + * @param offer Offer to remove + */ + public void RemoveOffer(RagfairOffer offer) + { + if (_offersById.ContainsKey(offer.Id)) + { + _offersById.Remove(offer.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); + } + } + } + + public void RemoveOffers(List offers) + { + foreach (var offer in offers) RemoveOffer(offer); + } + + public void RemoveAllOffersByTrader(string traderId) + { + if (_offersByTrader.ContainsKey(traderId)) + { + RemoveOffers(_offersByTrader[traderId].Values.ToList()); + } + } + + /** + * Get an array of stale offers that are still shown to player + * @returns RagfairOffer array + */ + public List GetStaleOffers(long time) + { + return GetOffers().Where(o => IsStale(o, time)).ToList(); + } + + protected void AddOfferByTemplates(string template, RagfairOffer offer) + { + 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)) + { + _offersByTrader[trader].Add(offer.Id, offer); + } + else + { + var valueMapped = new Dictionary(); + valueMapped.Add(offer.Id, offer); + _offersByTrader.Add(trader, valueMapped); + } + } + + protected bool IsStale(RagfairOffer offer, long time) + { + return offer.EndTime < time || (offer.Items[0].Upd?.StackObjectsCount ?? 0) < 1; + } +}