From bfd616e7c36398ea6f5addd1e40ffdb69d14f8bd Mon Sep 17 00:00:00 2001 From: Chomp Date: Sat, 26 Jul 2025 12:53:56 +0100 Subject: [PATCH] Converted rewards data into dictionary - reduced need for reflection Moved clone outside of `UpdateQuestsForGameEdition` --- .../RepeatableQuestRewardGenerator.cs | 53 +++++++++---------- .../Helpers/QuestHelper.cs | 15 +++--- .../Helpers/QuestRewardHelper.cs | 19 +++---- .../Models/Eft/Common/Tables/Quest.cs | 29 +--------- .../Eft/Common/Tables/RepeatableQuests.cs | 2 +- .../Services/ProfileFixerService.cs | 20 +++---- .../HideoutCraftQuestIdGenerator.cs | 19 +++---- 7 files changed, 63 insertions(+), 94 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/RepeatableQuestRewardGenerator.cs b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/RepeatableQuestRewardGenerator.cs index 9616c775..205598d8 100644 --- a/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/RepeatableQuestRewardGenerator.cs +++ b/Libraries/SPTarkov.Server.Core/Generators/RepeatableQuestGeneration/RepeatableQuestRewardGenerator.cs @@ -58,7 +58,7 @@ public class RepeatableQuestRewardGenerator( /// Base Quest config /// Optional: list of tpls to NOT use when picking a reward /// QuestRewards - public QuestRewards? GenerateReward( + public Dictionary>? GenerateReward( int pmcLevel, double difficulty, MongoId traderId, @@ -78,11 +78,11 @@ public class RepeatableQuestRewardGenerator( var itemRewardBudget = rewardParams.RewardRoubles; // Possible improvement -> draw trader-specific items e.g. with _itemHelper.isOfBaseclass(val._id, ItemHelper.BASECLASS.FoodDrink) - QuestRewards rewards = new() + var rewards = new Dictionary> { - Started = [], - Success = [], - Fail = [], + { "Success", [] }, + { "Started", [] }, + { "Fail", [] }, }; // Start reward index to keep track @@ -91,29 +91,29 @@ public class RepeatableQuestRewardGenerator( // Add xp reward if (rewardParams.RewardXP > 0) { - rewards.Success.Add( - new Reward - { - Id = new MongoId(), - Unknown = false, - GameMode = [], - AvailableInGameEditions = [], - Index = rewardIndex, - Value = rewardParams.RewardXP, - Type = RewardType.Experience, - } - ); + rewards["Success"] + .Add( + new Reward + { + Id = new MongoId(), + Unknown = false, + GameMode = [], + AvailableInGameEditions = [], + Index = rewardIndex, + Value = rewardParams.RewardXP, + Type = RewardType.Experience, + } + ); rewardIndex++; } // Add money reward - rewards.Success.Add(GetMoneyReward(traderId, rewardParams.RewardRoubles, rewardIndex)); + rewards["Success"].Add(GetMoneyReward(traderId, rewardParams.RewardRoubles, rewardIndex)); rewardIndex++; // Add GP coin reward - rewards.Success.Add( - GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex) - ); + rewards["Success"] + .Add(GenerateItemReward(Money.GP, rewardParams.GpCoinRewardCount, rewardIndex)); rewardIndex++; // Add preset weapon to reward if checks pass @@ -135,7 +135,7 @@ public class RepeatableQuestRewardGenerator( var chosenWeapon = GetRandomWeaponPresetWithinBudget(itemRewardBudget, rewardIndex); if (chosenWeapon is not null) { - rewards.Success.Add(chosenWeapon.Value.Key); + rewards["Success"].Add(chosenWeapon.Value.Key); // Subtract price of preset from item budget so we don't give player too much stuff itemRewardBudget -= chosenWeapon.Value.Value; @@ -180,9 +180,8 @@ public class RepeatableQuestRewardGenerator( // Add item rewards foreach (var itemReward in itemsToReward) { - rewards.Success.Add( - GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex) - ); + rewards["Success"] + .Add(GenerateItemReward(itemReward.Key.Id, itemReward.Value, rewardIndex)); rewardIndex++; } } @@ -201,7 +200,7 @@ public class RepeatableQuestRewardGenerator( Type = RewardType.TraderStanding, Index = rewardIndex, }; - rewards.Success.Add(reward); + rewards["Success"].Add(reward); rewardIndex++; if (logger.IsLogEnabled(LogLevel.Debug)) @@ -227,7 +226,7 @@ public class RepeatableQuestRewardGenerator( Type = RewardType.Skill, Index = rewardIndex, }; - rewards.Success.Add(reward); + rewards["Success"].Add(reward); if (logger.IsLogEnabled(LogLevel.Debug)) { diff --git a/Libraries/SPTarkov.Server.Core/Helpers/QuestHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/QuestHelper.cs index 9f15c748..483b5910 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/QuestHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/QuestHelper.cs @@ -1,6 +1,5 @@ using System.Collections.Frozen; using System.Globalization; -using SPTarkov.Common.Extensions; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Models.Common; @@ -1403,7 +1402,10 @@ public class QuestHelper( } } - return UpdateQuestsForGameEdition(questsToShowPlayer, profile.Info.GameVersion); + return UpdateQuestsForGameEdition( + cloner.Clone(questsToShowPlayer), + profile.Info.GameVersion + ); } /// @@ -1414,11 +1416,10 @@ public class QuestHelper( /// Collection of Quest objects with the rewards filtered correctly for the game version protected List UpdateQuestsForGameEdition(List quests, string gameVersion) { - var modifiedQuests = cloner.Clone(quests); - foreach (var quest in modifiedQuests) + foreach (var quest in quests) { // Remove any reward that doesn't pass the game edition check - var propsAsDict = quest.Rewards.GetAllPropsAsDict(); + var propsAsDict = quest.Rewards; foreach (var rewardType in propsAsDict) { if (rewardType.Value is null) @@ -1426,13 +1427,13 @@ public class QuestHelper( continue; } - propsAsDict[rewardType.Key] = ((List)propsAsDict[rewardType.Key]) + propsAsDict[rewardType.Key] = propsAsDict[rewardType.Key] .Where(reward => rewardHelper.RewardIsForGameEdition(reward, gameVersion)) .ToList(); } } - return modifiedQuests; + return quests; } /// diff --git a/Libraries/SPTarkov.Server.Core/Helpers/QuestRewardHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/QuestRewardHelper.cs index d36bb492..29be0a2f 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/QuestRewardHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/QuestRewardHelper.cs @@ -1,4 +1,3 @@ -using SPTarkov.Common.Extensions; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Models.Common; @@ -89,7 +88,7 @@ public class QuestRewardHelper( } // e.g. 'Success' or 'AvailableForFinish' - var rewards = questDetails.Rewards.GetByJsonProp>(state.ToString()); + var rewards = questDetails.Rewards[state.ToString()]; return rewardHelper.ApplyRewards( rewards, CustomisationSource.UNLOCKED_IN_GAME, @@ -172,18 +171,20 @@ public class QuestRewardHelper( public Quest ApplyMoneyBoost(Quest quest, double bonusPercent, QuestStatusEnum questStatus) { var clonedQuest = cloner.Clone(quest); - if (clonedQuest?.Rewards?.Success == null) + if (clonedQuest?.Rewards?["Success"] == null) { return clonedQuest; } // Grab just the money rewards from quest reward pool - var moneyRewards = clonedQuest.Rewards.Success.Where(reward => - reward.Type == RewardType.Item - && reward.Items != null - && reward.Items.Count > 0 - && paymentHelper.IsMoneyTpl(reward.Items.FirstOrDefault().Template) - ); + var moneyRewards = clonedQuest + .Rewards["Success"] + .Where(reward => + reward.Type == RewardType.Item + && reward.Items != null + && reward.Items.Count > 0 + && paymentHelper.IsMoneyTpl(reward.Items.FirstOrDefault().Template) + ); foreach (var moneyReward in moneyRewards) { diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Quest.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Quest.cs index 20b603e5..3d573534 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Quest.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/Quest.cs @@ -85,7 +85,7 @@ public record Quest public string? TemplateId { get; set; } [JsonPropertyName("rewards")] - public QuestRewards? Rewards { get; set; } + public Dictionary>? Rewards { get; set; } /// /// Becomes 'AppearStatus' inside client @@ -489,30 +489,3 @@ public record VisibilityCondition [JsonPropertyName("conditionType")] public required string ConditionType { get; set; } } - -public record QuestRewards -{ - [JsonExtensionData] - public Dictionary? ExtensionData { get; set; } - - [JsonPropertyName("AvailableForStart")] - public List? AvailableForStart { get; set; } - - [JsonPropertyName("AvailableForFinish")] - public List? AvailableForFinish { get; set; } - - [JsonPropertyName("Started")] - public List? Started { get; set; } - - [JsonPropertyName("Success")] - public List? Success { get; set; } - - [JsonPropertyName("Fail")] - public List? Fail { get; set; } - - [JsonPropertyName("FailRestartable")] - public List? FailRestartable { get; set; } - - [JsonPropertyName("Expired")] - public List? Expired { get; set; } -} diff --git a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/RepeatableQuests.cs b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/RepeatableQuests.cs index 2e06d406..aaf6eed9 100644 --- a/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/RepeatableQuests.cs +++ b/Libraries/SPTarkov.Server.Core/Models/Eft/Common/Tables/RepeatableQuests.cs @@ -240,7 +240,7 @@ public record SampleQuests public bool? CanShowNotificationsInGame { get; set; } [JsonPropertyName("rewards")] - public QuestRewards? Rewards { get; set; } + public Dictionary>? Rewards { get; set; } [JsonPropertyName("conditions")] public QuestConditionTypes? Conditions { get; set; } diff --git a/Libraries/SPTarkov.Server.Core/Services/ProfileFixerService.cs b/Libraries/SPTarkov.Server.Core/Services/ProfileFixerService.cs index 3da3e142..19d07c72 100644 --- a/Libraries/SPTarkov.Server.Core/Services/ProfileFixerService.cs +++ b/Libraries/SPTarkov.Server.Core/Services/ProfileFixerService.cs @@ -362,9 +362,9 @@ public class ProfileFixerService( // For started or successful quests, check for unlocks in the `Started` rewards if (profileQuest.Status is QuestStatusEnum.Started or QuestStatusEnum.Success) { - var productionRewards = quest.Rewards.Started?.Where(reward => - reward.Type == RewardType.ProductionScheme - ); + var productionRewards = quest + .Rewards["Started"] + ?.Where(reward => reward.Type == RewardType.ProductionScheme); if (productionRewards is not null) { @@ -378,9 +378,9 @@ public class ProfileFixerService( // For successful quests, check for unlocks in the `Success` rewards if (profileQuest.Status is QuestStatusEnum.Success) { - var productionRewards = quest.Rewards.Success?.Where(reward => - reward.Type == RewardType.ProductionScheme - ); + var productionRewards = quest + .Rewards["Success"] + ?.Where(reward => reward.Type == RewardType.ProductionScheme); if (productionRewards is not null) { @@ -793,16 +793,16 @@ public class ProfileFixerService( continue; } - if (activeQuest.Rewards?.Success is null) + if (activeQuest.Rewards?["Success"] is null) { continue; } // Get Item rewards only foreach ( - var successReward in activeQuest.Rewards.Success.Where(reward => - reward.Type == RewardType.Item - ) + var successReward in activeQuest + .Rewards["Success"] + .Where(reward => reward.Type == RewardType.Item) ) foreach (var item in successReward.Items) { diff --git a/Tools/HideoutCraftQuestIdGenerator/HideoutCraftQuestIdGenerator.cs b/Tools/HideoutCraftQuestIdGenerator/HideoutCraftQuestIdGenerator.cs index e4fddedb..47b03994 100644 --- a/Tools/HideoutCraftQuestIdGenerator/HideoutCraftQuestIdGenerator.cs +++ b/Tools/HideoutCraftQuestIdGenerator/HideoutCraftQuestIdGenerator.cs @@ -231,19 +231,14 @@ public class HideoutCraftQuestIdGenerator( return true; } - private HashSet CombineRewards(QuestRewards? questRewards) + /// + /// Merge all rewards together into one collection + /// + /// Rewards to merge + /// IEnumerable + protected IEnumerable CombineRewards(Dictionary>? questRewards) { - var result = new HashSet(); - questRewards.Started?.ForEach(x => result.Add(x)); - questRewards.Success?.ForEach(x => result.Add(x)); - questRewards.AvailableForFinish?.ForEach(x => result.Add(x)); - questRewards.Expired?.ForEach(x => result.Add(x)); - questRewards.AvailableForStart?.ForEach(x => result.Add(x)); - questRewards.Fail?.ForEach(x => result.Add(x)); - questRewards.FailRestartable?.ForEach(x => result.Add(x)); - questRewards.Started?.ForEach(x => result.Add(x)); - - return result; + return questRewards?.SelectMany(rewardKvP => rewardKvP.Value) ?? []; } }