From d298e1762bc66a123318fa59627bfcb43d994a8f Mon Sep 17 00:00:00 2001 From: CWX Date: Tue, 21 Jan 2025 01:22:54 +0000 Subject: [PATCH] finish off eventoutputholder --- Libraries/Core/Helpers/HideoutHelper.cs | 8 +- .../Core/Models/Eft/Common/Tables/BotBase.cs | 14 +- .../Eft/ItemEvent/ItemEventRouterBase.cs | 2 +- Libraries/Core/Routers/EventOutputHolder.cs | 183 +++++++++++++++--- 4 files changed, 159 insertions(+), 48 deletions(-) diff --git a/Libraries/Core/Helpers/HideoutHelper.cs b/Libraries/Core/Helpers/HideoutHelper.cs index 6f0ec933..e20703af 100644 --- a/Libraries/Core/Helpers/HideoutHelper.cs +++ b/Libraries/Core/Helpers/HideoutHelper.cs @@ -49,7 +49,7 @@ public class HideoutHelper( /// /// /// - public bool IsProductionType(Productive productive) + public bool IsProductionType(Production Production) { throw new NotImplementedException(); } @@ -189,7 +189,7 @@ public class HideoutHelper( throw new NotImplementedException(); } - protected void FlagCultistCircleCraftAsComplete(Productive production) + protected void FlagCultistCircleCraftAsComplete(Production Production) { throw new NotImplementedException(); } @@ -342,7 +342,7 @@ public class HideoutHelper( protected void UpdateBitcoinFarm( PmcData playerProfile, - Productive btcProduction, + Production btcProduction, int btcFarmCGs, bool isGeneratorOn) { @@ -431,7 +431,7 @@ public class HideoutHelper( throw new NotImplementedException(); } - public bool IsProduction(Productive productive) + public bool IsProduction(Production Production) { throw new NotImplementedException(); } diff --git a/Libraries/Core/Models/Eft/Common/Tables/BotBase.cs b/Libraries/Core/Models/Eft/Common/Tables/BotBase.cs index 152180ca..5828618e 100644 --- a/Libraries/Core/Models/Eft/Common/Tables/BotBase.cs +++ b/Libraries/Core/Models/Eft/Common/Tables/BotBase.cs @@ -545,7 +545,7 @@ public record HideoutImprovement public long? ImproveCompleteTimestamp { get; set; } } -public record Productive +public record Production // use this instead of productive and scavcase { public List? Products { get; set; } @@ -592,17 +592,7 @@ public record Productive // Craft is cultist circle sacrifice [JsonPropertyName("sptIsCultistCircle")] public bool? SptIsCultistCircle { get; set; } -} - -public record Production : Productive -{ - public string? RecipeId { get; set; } - public int? SkipTime { get; set; } - public int? ProductionTime { get; set; } -} - -public record ScavCase : Productive -{ + public string? RecipeId { get; set; } } diff --git a/Libraries/Core/Models/Eft/ItemEvent/ItemEventRouterBase.cs b/Libraries/Core/Models/Eft/ItemEvent/ItemEventRouterBase.cs index c313f44b..b8ff7d7b 100644 --- a/Libraries/Core/Models/Eft/ItemEvent/ItemEventRouterBase.cs +++ b/Libraries/Core/Models/Eft/ItemEvent/ItemEventRouterBase.cs @@ -54,7 +54,7 @@ public record ProfileChange public ItemChanges? Items { get; set; } [JsonPropertyName("production")] - public Dictionary? Production { get; set; } + public Dictionary? Production { get; set; } /** Hideout area improvement id */ [JsonPropertyName("improvements")] diff --git a/Libraries/Core/Routers/EventOutputHolder.cs b/Libraries/Core/Routers/EventOutputHolder.cs index 6a433c66..469ddc02 100644 --- a/Libraries/Core/Routers/EventOutputHolder.cs +++ b/Libraries/Core/Routers/EventOutputHolder.cs @@ -1,7 +1,9 @@ using SptCommon.Annotations; using Core.Helpers; +using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.ItemEvent; +using Core.Models.Utils; using Core.Utils; using Core.Utils.Cloners; @@ -10,18 +12,22 @@ namespace Core.Routers; [Injectable] public class EventOutputHolder { + protected ISptLogger _logger; protected ProfileHelper _profileHelper; protected TimeUtil _timeUtil; protected ICloner _cloner; protected Dictionary _outputStore = new(); + protected Dictionary> _clientActiveSessionStorage = new(); public EventOutputHolder( + ISptLogger logger, ProfileHelper profileHelper, TimeUtil timeUtil, ICloner cloner ) { + _logger = logger; _profileHelper = profileHelper; _timeUtil = timeUtil; _cloner = cloner; @@ -46,37 +52,149 @@ public class EventOutputHolder { var pmcProfile = _profileHelper.GetPmcProfile(sessionId); - _outputStore.Add(sessionId, new ItemEventRouterResponse - { - ProfileChanges = new Dictionary() + _outputStore.Add( + sessionId, + new ItemEventRouterResponse { + ProfileChanges = new Dictionary() { - sessionId, new ProfileChange { - Id = sessionId, - Experience = pmcProfile.Info.Experience, - Quests = [], - RagFairOffers = [], - WeaponBuilds = [], - EquipmentBuilds = [], - Items = new ItemChanges(){ NewItems = [], ChangedItems = [], DeletedItems = []}, - Production = new Dictionary(), - Improvements = new Dictionary(), - Skills = new Skills{ Common = [], Mastering = [], Points = 0}, - Health = _cloner.Clone(pmcProfile.Health), - TraderRelations = new Dictionary(), - RecipeUnlocked = {}, - QuestsStatus = [] + sessionId, new ProfileChange + { + Id = sessionId, + Experience = pmcProfile.Info.Experience, + Quests = [], + RagFairOffers = [], + WeaponBuilds = [], + EquipmentBuilds = [], + Items = new ItemChanges { NewItems = [], ChangedItems = [], DeletedItems = [] }, + Production = new Dictionary(), + Improvements = new Dictionary(), + Skills = new Skills { Common = [], Mastering = [], Points = 0 }, + Health = _cloner.Clone(pmcProfile.Health), + TraderRelations = new Dictionary(), + RecipeUnlocked = { }, + QuestsStatus = [] + } } - } - }, - Warnings = {} - }); + }, + Warnings = { } + } + ); } public void UpdateOutputProperties(string sessionId) { - throw new NotImplementedException(); + PmcData pmcData = _profileHelper.GetPmcProfile(sessionId); + ProfileChange profileChanges = _outputStore[sessionId].ProfileChanges[sessionId]; + + profileChanges.Experience = pmcData.Info.Experience; + profileChanges.Health = _cloner.Clone(pmcData.Health); + profileChanges.Skills.Common = _cloner.Clone(pmcData.Skills.Common); // Always send skills for Item event route response + profileChanges.Skills.Mastering = _cloner.Clone(pmcData.Skills.Mastering); + + // Clone productions to ensure we preseve the profile jsons data + profileChanges.Production = GetProductionsFromProfileAndFlagComplete( + _cloner.Clone(pmcData.Hideout.Production), + sessionId + ); + profileChanges.Improvements = _cloner.Clone(GetImprovementsFromProfileAndFlagComplete(pmcData)); + profileChanges.TraderRelations = ConstructTraderRelations(pmcData.TradersInfo); + + ResetMoneyTransferLimit(pmcData.MoneyTransferLimitData); + profileChanges.MoneyTransferLimitData = pmcData.MoneyTransferLimitData; + + // Fixes container craft from water collector not resetting after collection + removed completed normal crafts + CleanUpCompleteCraftsInProfile(pmcData.Hideout.Production); + } + + private void CleanUpCompleteCraftsInProfile(Dictionary? productions) + { + foreach (var production in productions) + { + if ((production.Value.SptIsComplete ?? false) && (production.Value.SptIsContinuous ?? false)) + { + // Water collector / Bitcoin etc + production.Value.SptIsComplete = false; + production.Value.Progress = 0; + production.Value.StartTimestamp = _timeUtil.GetTimeStamp().ToString(); + } + else if (!production.Value.InProgress ?? false) + { + // Normal completed craft, delete + productions.Remove(production.Key); + } + } + } + + private Dictionary? GetImprovementsFromProfileAndFlagComplete(PmcData pmcData) + { + foreach (var improvementKey in pmcData.Hideout.Improvements) + { + var improvement = pmcData.Hideout.Improvements[improvementKey.Key]; + + // Skip completed + if (improvement.Completed ?? false) + { + continue; + } + + if (improvement.ImproveCompleteTimestamp < _timeUtil.GetTimeStamp()) + { + improvement.Completed = true; + } + } + + return pmcData.Hideout.Improvements; + } + + private Dictionary? GetProductionsFromProfileAndFlagComplete(Dictionary? productions, string sessionId) + { + foreach (var production in productions) + { + if (production.Value is null) + { + // Could be cancelled production, skip item to save processing + continue; + } + + // Complete and is Continuous e.g. water collector + if ((production.Value.SptIsComplete ?? false) && (production.Value.SptIsContinuous ?? false)) + { + continue; + } + + // Skip completed + if (!production.Value.InProgress ?? false) + { + continue; + } + + // Client informed of craft, remove from data returned + Dictionary? storageForSessionId = null; + if (!_clientActiveSessionStorage.TryGetValue(sessionId, out storageForSessionId)) + { + _clientActiveSessionStorage.Add(sessionId, new Dictionary()); + storageForSessionId = _clientActiveSessionStorage[sessionId]; + } + + // Ensure we don't inform client of production again + if (storageForSessionId[production.Key]) + { + productions.Remove(production.Key); + + continue; + } + + // Flag started craft as having been seen by client so it won't happen subsequent times + if (production.Value.Progress > 0 && !storageForSessionId[production.Key]) + { + storageForSessionId[production.Key] = true; + } + } + + // Return undefined if there's no crafts to send to client to match live behaviour + return productions.Keys.Count > 0 ? productions : null; } private void ResetMoneyTransferLimit(MoneyTransferLimits limit) @@ -90,13 +208,16 @@ public class EventOutputHolder private Dictionary ConstructTraderRelations(Dictionary traderData) { - return traderData.ToDictionary(trader => trader.Key, trader => new TraderData() - { - SalesSum = trader.Value.SalesSum, - Disabled = trader.Value.Disabled, - Loyalty = trader.Value.LoyaltyLevel, - Standing = trader.Value.Standing, - Unlocked = trader.Value.Unlocked, - }); + return traderData.ToDictionary( + trader => trader.Key, + trader => new TraderData() + { + SalesSum = trader.Value.SalesSum, + Disabled = trader.Value.Disabled, + Loyalty = trader.Value.LoyaltyLevel, + Standing = trader.Value.Standing, + Unlocked = trader.Value.Unlocked, + } + ); } }