From edc11a63ee513b29f3bb6388b0b4a328b94d9e3c Mon Sep 17 00:00:00 2001 From: CWX Date: Fri, 10 Jan 2025 22:16:02 +0000 Subject: [PATCH 1/4] Complete transfer of ProfileHelper --- Core/Helpers/ProfileHelper.cs | 377 +++++++++++++++--- Core/Models/Eft/Common/Tables/BotBase.cs | 4 +- .../Eft/Profile/SearchFriendResponse.cs | 11 +- 3 files changed, 339 insertions(+), 53 deletions(-) diff --git a/Core/Helpers/ProfileHelper.cs b/Core/Helpers/ProfileHelper.cs index 409d330a..9d8d2d3a 100644 --- a/Core/Helpers/ProfileHelper.cs +++ b/Core/Helpers/ProfileHelper.cs @@ -3,12 +3,52 @@ using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; using Core.Models.Eft.Profile; using Core.Models.Enums; +using Core.Models.Spt.Config; +using Core.Servers; +using Core.Services; +using Core.Utils; +using Core.Utils.Cloners; namespace Core.Helpers; [Injectable] public class ProfileHelper { + protected Models.Utils.ILogger _logger; + + protected ICloner _cloner; + protected SaveServer _saveServer; + protected DatabaseService _databaseService; + protected Watermark _watermark; + protected ItemHelper _itemHelper; + protected TimeUtil _timeUtil; + protected LocalisationService _localisationService; + protected InventoryConfig _inventoryConfig; + protected HashUtil _hashUtil; + + public ProfileHelper( + ICloner cloner, + SaveServer saveServer, + DatabaseService databaseService, + Watermark watermark, + ItemHelper itemHelper, + TimeUtil timeUtil, + LocalisationService localisationService, + InventoryConfig inventoryConfig, + HashUtil hashUtil + ) + { + _cloner = cloner; + _saveServer = saveServer; + _databaseService = databaseService; + _watermark = watermark; + _itemHelper = itemHelper; + _timeUtil = timeUtil; + _localisationService = localisationService; + _inventoryConfig = inventoryConfig; + _hashUtil = hashUtil; + } + /// /// Remove/reset a completed quest condtion from players profile quest data /// @@ -16,7 +56,14 @@ public class ProfileHelper /// Quest with condition to remove public void RemoveQuestConditionFromProfile(PmcData pmcData, Dictionary questConditionId) { - throw new NotImplementedException(); + foreach (var questId in questConditionId) + { + var conditionId = questId.Value; + var profileQuest = pmcData.Quests.FirstOrDefault(q => q.QId == conditionId); + + if (profileQuest != null) // Remove condition + profileQuest.CompletedConditions.Remove(conditionId); + } } /// @@ -25,7 +72,7 @@ public class ProfileHelper /// Dictionary of profiles public Dictionary GetProfiles() { - throw new NotImplementedException(); + return _saveServer.GetProfiles(); } /// @@ -35,7 +82,21 @@ public class ProfileHelper /// Array of PmcData objects public List GetCompleteProfile(string sessionId) { - throw new NotImplementedException(); + var output = new List(); + + if (IsWiped(sessionId)) + return output; + + var FullProfileClone = _cloner.Clone(GetFullProfile(sessionId)); + + // Sanitize any data the client can not receive + SanitizeProfileForClient(FullProfileClone); + + // PMC must be at array index 0, scav at 1 + output.Add(FullProfileClone.CharacterData.PmcData); + output.Add(FullProfileClone.CharacterData.ScavData); + + return output; } /// @@ -44,7 +105,12 @@ public class ProfileHelper /// A clone of the full player profile protected void SanitizeProfileForClient(SptProfile clonedProfile) { - throw new NotImplementedException(); + // Remove `loyaltyLevel` from `TradersInfo`, as otherwise it causes the client to not + // properly calculate the player's `loyaltyLevel` + foreach (var trader in clonedProfile.CharacterData.PmcData.TradersInfo.Values) + { + trader.LoyaltyLevel = null; + } } /// @@ -55,17 +121,22 @@ public class ProfileHelper /// True if already in use public bool IsNicknameTaken(ValidateNicknameRequestData nicknameRequest, string sessionID) { - throw new NotImplementedException(); + var allProfiles = _saveServer.GetProfiles().Values; + + // Find a profile that doesn't have same session id but has same name + return allProfiles.Any(p => + ProfileHasInfoProperty(p) && !StringsMatch(p.ProfileInfo.ProfileId, sessionID) && // SessionIds dont match + StringsMatch(p.CharacterData.PmcData.Info.LowerNickname.ToLower(), nicknameRequest.Nickname.ToLower())); // Nicknames do } protected bool ProfileHasInfoProperty(SptProfile profile) { - throw new NotImplementedException(); + return profile?.CharacterData?.PmcData?.Info != null; } protected bool StringsMatch(string stringA, string stringB) { - throw new NotImplementedException(); + return stringA == stringB; } /// @@ -75,7 +146,11 @@ public class ProfileHelper /// Experience to add to PMC character public void AddExperienceToPmc(string sessionID, int experienceToAdd) { - throw new NotImplementedException(); + var pmcData = GetPmcProfile(sessionID); + if (pmcData != null) + pmcData.Info.Experience += experienceToAdd; + else + _logger.Error($"Profile {sessionID} does not exist"); } /// @@ -85,7 +160,7 @@ public class ProfileHelper /// PmcData public PmcData? GetProfileByPmcId(string pmcId) { - throw new NotImplementedException(); + return _saveServer.GetProfiles().Values.First(p => p.CharacterData?.PmcData?.Id == pmcId).CharacterData.PmcData; } /// @@ -93,9 +168,21 @@ public class ProfileHelper /// /// Level to get xp for /// Number of xp points for level - public int GetExperience(int level) + public double? GetExperience(int level) { - throw new NotImplementedException(); + var playerLevel = level; + var expTable = _databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable; + double? exp = null; + + if (playerLevel >= expTable.Length) // make sure to not go out of bounds + playerLevel = expTable.Length - 1; + + foreach (var expLevel in expTable) + { + exp += expLevel.Experience; + } + + return exp; } /// @@ -104,7 +191,7 @@ public class ProfileHelper /// Max level public int GetMaxLevel() { - throw new NotImplementedException(); + return _databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable.Length - 1; } /// @@ -113,7 +200,16 @@ public class ProfileHelper /// Spt public Spt GetDefaultSptDataObject() { - throw new NotImplementedException(); + return new() + { + Version = _watermark.GetVersionTag(true), + Mods = new(), + ReceivedGifts = new(), + BlacklistedItemTemplates = new(), + FreeRepeatableRefreshUsedCount = new(), + Migrations = new(), + CultistRewards = new() + }; } /// @@ -123,7 +219,7 @@ public class ProfileHelper /// SptProfile object public SptProfile? GetFullProfile(string sessionID) { - throw new NotImplementedException(); + return _saveServer.ProfileExists(sessionID) ? _saveServer.GetProfile(sessionID) : null; } /// @@ -133,7 +229,11 @@ public class ProfileHelper /// public SptProfile? GetFullProfileByAccountId(string accountID) { - throw new NotImplementedException(); + var check = int.TryParse(accountID, out var aid); + if (!check) + _logger.Error($"Account {accountID} does not exist"); + + return _saveServer.GetProfiles().FirstOrDefault(p => p.Value?.ProfileInfo?.Aid == aid).Value; } /// @@ -143,7 +243,11 @@ public class ProfileHelper /// public SearchFriendResponse? GetChatRoomMemberFromSessionId(string sessionID) { - throw new NotImplementedException(); + var pmcProfile = GetFullProfile(sessionID)?.CharacterData?.PmcData; + if (pmcProfile == null) + return null; + + return GetChatRoomMemberFromPmcProfile(pmcProfile); } /// @@ -151,9 +255,21 @@ public class ProfileHelper /// /// The PMC profile data to format into a ChatRoomMember structure /// - public SearchFriendResponse GetChatRoomMemberFromPmcProfile(PmcData pmcProfile) + public SearchFriendResponse? GetChatRoomMemberFromPmcProfile(PmcData pmcProfile) { - throw new NotImplementedException(); + return new() + { + Id = pmcProfile.Id, + Aid = pmcProfile.Aid, + Info = new() + { + Nickname = pmcProfile.Info.Nickname, + Side = pmcProfile.Info.Side, + Level = pmcProfile.Info.Level, + MemberCategory = pmcProfile.Info.MemberCategory, + SelectedMemberCategory = pmcProfile.Info.SelectedMemberCategory + } + }; } /// @@ -163,7 +279,9 @@ public class ProfileHelper /// PmcData object public PmcData? GetPmcProfile(string sessionID) { - throw new NotImplementedException(); + var fullProfile = GetFullProfile(sessionID); + + return fullProfile?.CharacterData?.PmcData; } /// @@ -171,9 +289,10 @@ public class ProfileHelper /// /// Id to validate /// True is a player + /// UNUSED? public bool IsPlayer(string userId) { - throw new NotImplementedException(); + return _saveServer.ProfileExists(userId); } /// @@ -181,9 +300,9 @@ public class ProfileHelper /// /// Profiles id /// IPmcData object - public PmcData GetScavProfile(string sessionID) + public PmcData? GetScavProfile(string sessionID) { - throw new NotImplementedException(); + return _saveServer.GetProfile(sessionID)?.CharacterData?.ScavData; } /// @@ -192,7 +311,26 @@ public class ProfileHelper /// Default profile Stats object public Stats GetDefaultCounters() { - throw new NotImplementedException(); + return new() + { + Eft = new() + { + CarriedQuestItems = new(), + DamageHistory = new() { LethalDamagePart = "Head", LethalDamage = null, BodyParts = new()}, + DroppedItems = new(), + ExperienceBonusMult = 0, + FoundInRaidItems = new(), + LastPlayerState = null, + LastSessionDate = 0, + OverallCounters = new(), + SessionCounters = new(), + SessionExperienceMult = 0, + SurvivorClass = "Unknown", + TotalInGameTime = 0, + TotalSessionExperience = 0, + Victims = new() + } + }; } /// @@ -200,9 +338,10 @@ public class ProfileHelper /// /// Profile id /// True if profile is to be wiped of data/progress + /// TODO: logic doesnt feel right to have IsWiped being nullable protected bool IsWiped(string sessionID) { - throw new NotImplementedException(); + return _saveServer.GetProfile(sessionID)?.ProfileInfo?.IsWiped ?? false; } /// @@ -212,7 +351,18 @@ public class ProfileHelper /// profile without secure container public PmcData RemoveSecureContainer(PmcData profile) { - throw new NotImplementedException(); + var items = profile.Inventory.Items; + var secureContainer = items.First(i => i.SlotId == "SecuredContainer"); + if (secureContainer != null) + { + // Find and remove container + children + var childItemsInSecureContainer = _itemHelper.FindAndReturnChildrenByItems(items, secureContainer.Id); + + // Remove child items + secure container + profile.Inventory.Items = items.Where(i => !childItemsInSecureContainer.Contains(i.Id)).ToList(); + } + + return profile; } /// @@ -224,7 +374,24 @@ public class ProfileHelper /// Limit of how many of this gift a player can have public void FlagGiftReceivedInProfile(string playerId, string giftId, int maxCount) { - throw new NotImplementedException(); + var profileToUpdate = GetFullProfile(playerId); + profileToUpdate.SptData.ReceivedGifts ??= new(); + + var giftData = profileToUpdate.SptData.ReceivedGifts.FirstOrDefault(g => g.GiftId == giftId); + if (giftData != null) + { + // Increment counter + giftData.Current++; + return; + } + + // Player has never received gift, make a new object + profileToUpdate.SptData.ReceivedGifts.Add(new() + { + GiftId = giftId, + TimestampLastAccepted = _timeUtil.GetTimeStamp(), + Current = 1 + }); } /// @@ -236,28 +403,56 @@ public class ProfileHelper /// True if player has recieved gift previously public bool PlayerHasRecievedMaxNumberOfGift(string playerId, string giftId, int maxGiftCount) { - throw new NotImplementedException(); + var profile = GetFullProfile(playerId); + if (profile == null) + { + _logger.Debug($"Unable to gift {giftId}, Profile: {playerId} does not exist"); + return false; + } + + if (profile.SptData.ReceivedGifts == null) + return false; + + var giftDataFromProfile = profile.SptData.ReceivedGifts.FirstOrDefault(g => g.GiftId == giftId); + if (giftDataFromProfile == null) + return false; + + return giftDataFromProfile.Current >= maxGiftCount; } /// - /// Find Stat in profile counters and increment by one + /// Find Stat in profile counters and increment by one. /// /// Counters to search for key /// Key + /// Was Includes in Node so might not be exact? public void IncrementStatCounter(CounterKeyValue[] counters, string keyToIncrement) { - throw new NotImplementedException(); + var stat = counters.FirstOrDefault(c => c.Key.Contains(keyToIncrement)); + if (stat != null) + stat.Value++; } /// /// Check if player has a skill at elite level /// - /// Skill to check + /// Skill to check /// Profile to find skill in /// True if player has skill at elite level - public bool HasEliteSkillLevel(SkillTypes skillType, PmcData pmcProfile) + public bool HasEliteSkillLevel(SkillTypes skill, PmcData pmcProfile) { - throw new NotImplementedException(); + var profileSkills = pmcProfile.Skills.Common; + if (profileSkills == null) + return false; + + var profileSkill = profileSkills.Dictionary.FirstOrDefault(s => s.Value.Id == skill.ToString()).Value; + if (profileSkill == null) + { + _logger.Error(_localisationService.GetText("quest-no_skill_found", skill)); + return false; + } + + return profileSkill.Progress >= 5100; // 51 } /// @@ -267,9 +462,42 @@ public class ProfileHelper /// Skill to add points to /// Points to add /// Skills are multiplied by a value in globals, default is off to maintain compatibility with legacy code - public void AddSkillPointsToPlayer(PmcData pmcProfile, SkillTypes skill, int pointsToAdd, bool useSkillProgressRateMultipler = false) + public void AddSkillPointsToPlayer(PmcData pmcProfile, SkillTypes skill, double? pointsToAdd, bool useSkillProgressRateMultipler = false) { - throw new NotImplementedException(); + var pointsToAddToSkill = pointsToAdd; + + if (pointsToAddToSkill < 0D) + { + _logger.Warning(_localisationService.GetText("player-attempt_to_increment_skill_with_negative_value", skill)); + return; + } + + var profileSkills = pmcProfile?.Skills?.Common; + if (profileSkills == null) + { + _logger.Warning($"Unable to add {pointsToAddToSkill} points to {skill}, Profile has no skills"); + return; + } + + var profileSkill = profileSkills.Dictionary.FirstOrDefault(s => s.Value.Id == skill.ToString()).Value; + if (profileSkill == null) + { + _logger.Error(_localisationService.GetText("quest-no_skill_found", skill)); + return; + } + + if (useSkillProgressRateMultipler) + { + var skillProgressRate = _databaseService.GetGlobals().Configuration.SkillsSettings.SkillProgressRate; + pointsToAddToSkill *= skillProgressRate; + } + + if (_inventoryConfig.SkillGainMultipliers[skill.ToString()] != null) + pointsToAddToSkill *= _inventoryConfig.SkillGainMultipliers[skill.ToString()]; + + profileSkill.Progress += pointsToAddToSkill; + profileSkill.Progress = Math.Min(profileSkill?.Progress ?? 0D, 5100); // Prevent skill from ever going above level 51 (5100) + profileSkill.LastAccess = _timeUtil.GetTimeStamp(); } /// @@ -278,9 +506,13 @@ public class ProfileHelper /// Player profile /// Skill to look up and return value from /// Common skill object from desired profile - public Common GetSkillFromProfile(PmcData pmcData, SkillTypes skill) + public Common? GetSkillFromProfile(PmcData pmcData, SkillTypes skill) { - throw new NotImplementedException(); + var skillToReturn = pmcData?.Skills?.Common.List.FirstOrDefault(s => s.Id == skill.ToString()); + if (skillToReturn == null) + _logger.Warning($"Profile {pmcData.SessionId} does not have a skill named: {skill.ToString()}"); + + return skillToReturn; } /// @@ -290,7 +522,7 @@ public class ProfileHelper /// True if account is developer public bool IsDeveloperAccount(string sessionID) { - throw new NotImplementedException(); + return GetFullProfile(sessionID)?.ProfileInfo?.Edition?.ToLower().StartsWith("spt developer") == false; } /// @@ -300,7 +532,24 @@ public class ProfileHelper /// How many rows to give profile public void AddStashRowsBonusToProfile(string sessionId, int rowsToAdd) { - throw new NotImplementedException(); + var profile = GetPmcProfile(sessionId); + var existingBonus = profile?.Bonuses?.FirstOrDefault(b => b.Type == BonusType.StashRows); + if (existingBonus != null) + { + profile?.Bonuses?.Add(new() + { + Id = _hashUtil.Generate(), + Value = rowsToAdd, + Type = BonusType.StashRows, + IsPassive = true, + IsVisible = true, + IsProduction = false + }); + } + else + { + existingBonus.Value += rowsToAdd; + } } /// @@ -309,14 +558,20 @@ public class ProfileHelper /// Player profile /// Bonus to sum up /// Summed bonus value or 0 if no bonus found - public int GetBonusValueFromProfile(PmcData pmcProfile, BonusType desiredBonus) + public double GetBonusValueFromProfile(PmcData pmcProfile, BonusType desiredBonus) { - throw new NotImplementedException(); + var bonuses = pmcProfile?.Bonuses?.Where(b => b.Type == desiredBonus); + if (bonuses.Count() == 0) + return 0; + + // Sum all bonuses found above + return bonuses?.Sum(new Func(bonus => bonus?.Value ?? 0)) ?? 0; } public bool PlayerIsFleaBanned(PmcData pmcProfile) { - throw new NotImplementedException(); + var currentTimestamp = _timeUtil.GetTimeStamp(); + return pmcProfile?.Info?.Bans?.Any(b => b.BanType == BanType.RAGFAIR && currentTimestamp < b.DateTime) ?? false; } /// @@ -326,12 +581,14 @@ public class ProfileHelper /// Id of achievement to add public void AddAchievementToProfile(PmcData pmcProfile, string achievementId) { - throw new NotImplementedException(); + pmcProfile.Achievements[achievementId] = _timeUtil.GetTimeStamp(); } + protected readonly List gameEditions = ["edge_of_darkness", "unheard_edition"]; + public bool HasAccessToRepeatableFreeRefreshSystem(PmcData pmcProfile) { - throw new NotImplementedException(); + return gameEditions.Contains(pmcProfile.Info.GameVersion); } /// @@ -341,7 +598,19 @@ public class ProfileHelper /// New tpl to set profiles Pockets to public void ReplaceProfilePocketTpl(PmcData pmcProfile, string newPocketTpl) { - throw new NotImplementedException(); + // Find all pockets in profile, may be multiple as they could have equipment stand + // (1 pocket for each upgrade level of equipment stand) + var pockets = pmcProfile.Inventory.Items.Where(i => i.SlotId == "Pockets"); + if (pockets.Count() == 0) + { + _logger.Error($"Unable to replace profile: {pmcProfile.Id} pocket tpl with: {newPocketTpl} as Pocket item could not be found."); + return; + } + + foreach (var pocket in pockets) + { + pocket.Id = newPocketTpl; + } } /// @@ -351,7 +620,7 @@ public class ProfileHelper /// List of item objects public List GetQuestItemsInProfile(PmcData profile) { - throw new NotImplementedException(); + return profile?.Inventory?.Items.Where(i => i.ParentId == profile.Inventory.QuestRaidItems).ToList(); } /// @@ -361,6 +630,22 @@ public class ProfileHelper /// A list of Item objects representing the favorited data public List GetOtherProfileFavorites(PmcData profile) { - throw new NotImplementedException(); + var fullFavorites = new List(); + + foreach (var itemId in profile.Inventory.FavoriteItems ?? new List()) + { + // When viewing another users profile, the client expects a full item with children, so get that + var itemAndChildren = _itemHelper.FindAndReturnChildrenAsItems(profile.Inventory.Items, itemId); + if (itemAndChildren != null && itemAndChildren.Count > 0) + { + // To get the client to actually see the items, we set the main item's parent to null, so it's treated as a root item + var clonedItems = _cloner.Clone(itemAndChildren); + clonedItems.First().ParentId = null; + + fullFavorites.AddRange(clonedItems); + } + } + + return fullFavorites; } } diff --git a/Core/Models/Eft/Common/Tables/BotBase.cs b/Core/Models/Eft/Common/Tables/BotBase.cs index 2ca32223..cc6dd3d2 100644 --- a/Core/Models/Eft/Common/Tables/BotBase.cs +++ b/Core/Models/Eft/Common/Tables/BotBase.cs @@ -71,7 +71,7 @@ public class BotBase /** Achievement id and timestamp */ [JsonPropertyName("Achievements")] [JsonConverter(typeof(ArrayToObjectFactoryConverter))] - public Dictionary? Achievements { get; set; } + public Dictionary? Achievements { get; set; } [JsonPropertyName("RepeatableQuests")] public List? RepeatableQuests { get; set; } @@ -332,7 +332,7 @@ public class BaseSkill public class Common : BaseSkill { public int? PointsEarnedDuringSession { get; set; } - public int? LastAccess { get; set; } + public long? LastAccess { get; set; } } public class Mastering : BaseSkill diff --git a/Core/Models/Eft/Profile/SearchFriendResponse.cs b/Core/Models/Eft/Profile/SearchFriendResponse.cs index a0d0d949..dcf9c1f9 100644 --- a/Core/Models/Eft/Profile/SearchFriendResponse.cs +++ b/Core/Models/Eft/Profile/SearchFriendResponse.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using Core.Models.Enums; namespace Core.Models.Eft.Profile; @@ -8,7 +9,7 @@ public class SearchFriendResponse public string? Id { get; set; } [JsonPropertyName("aid")] - public int? Aid { get; set; } + public double? Aid { get; set; } [JsonPropertyName("Info")] public FriendInfo? Info { get; set; } @@ -24,11 +25,11 @@ public class FriendInfo public string? Side { get; set; } [JsonPropertyName("Level")] - public int? Level { get; set; } + public double? Level { get; set; } [JsonPropertyName("MemberCategory")] - public int? MemberCategory { get; set; } + public MemberCategory? MemberCategory { get; set; } [JsonPropertyName("SelectedMemberCategory")] - public int? SelectedMemberCategory { get; set; } -} \ No newline at end of file + public MemberCategory? SelectedMemberCategory { get; set; } +} From b9316fee6352c1a9447fa95fc6c7d3a687ab0e7b Mon Sep 17 00:00:00 2001 From: CWX Date: Fri, 10 Jan 2025 22:18:49 +0000 Subject: [PATCH 2/4] Fix compile errors --- Core/Controllers/ProfileController.cs | 273 ++++++++++-------- Core/Helpers/ProfileHelper.cs | 4 +- Core/Models/Eft/Common/Globals.cs | 4 +- .../Eft/Profile/GetOtherProfileResponse.cs | 4 +- 4 files changed, 166 insertions(+), 119 deletions(-) diff --git a/Core/Controllers/ProfileController.cs b/Core/Controllers/ProfileController.cs index 544b03e6..df590862 100644 --- a/Core/Controllers/ProfileController.cs +++ b/Core/Controllers/ProfileController.cs @@ -31,12 +31,14 @@ public class ProfileController protected LocalisationService _localisationService; protected SeasonalEventService _seasonalEventService; - // TODO: MailSendService mailSendService: MailSendService - protected PlayerScavGenerator _playerScavGenerator; - // TODO: EventOutputHolder eventOutputHolder: EventOutputHolder - protected TraderHelper _traderHelper; - protected DialogueHelper _dialogueHelper; - protected QuestHelper _questHelper; + + // TODO: MailSendService mailSendService: MailSendService + protected PlayerScavGenerator _playerScavGenerator; + + // TODO: EventOutputHolder eventOutputHolder: EventOutputHolder + protected TraderHelper _traderHelper; + protected DialogueHelper _dialogueHelper; + protected QuestHelper _questHelper; protected ProfileHelper _profileHelper; public ProfileController( @@ -90,7 +92,8 @@ public class ProfileController public MiniProfile GetMiniProfile(string sessionID) { var profile = _saveServer.GetProfile(sessionID); - if (profile?.CharacterData == null) { + if (profile?.CharacterData == null) + { throw new Exception($"Unable to find character data for id: {sessionID}. Profile may be corrupt"); } @@ -98,36 +101,39 @@ public class ProfileController var maxlvl = _profileHelper.GetMaxLevel(); // Player hasn't completed profile creation process, send defaults - if (pmc?.Info?.Level == null) { - return new MiniProfile(){ + if (pmc?.Info?.Level == null) + { + return new MiniProfile() + { Username = profile.ProfileInfo?.UserName ?? "", Nickname = "unknown", - Side= "unknown", - CurrentLevel= 0, - CurrentExperience= 0, - PreviousExperience= 0, - NextLevel= 0, - MaxLevel= maxlvl, - Edition= profile.ProfileInfo?.Edition ?? "", - ProfileId= profile.ProfileInfo?.ProfileId ?? "", - SptData= _profileHelper.GetDefaultSptDataObject(), + Side = "unknown", + CurrentLevel = 0, + CurrentExperience = 0, + PreviousExperience = 0, + NextLevel = 0, + MaxLevel = maxlvl, + Edition = profile.ProfileInfo?.Edition ?? "", + ProfileId = profile.ProfileInfo?.ProfileId ?? "", + SptData = _profileHelper.GetDefaultSptDataObject(), }; } var currlvl = pmc.Info.Level; var nextlvl = _profileHelper.GetExperience((int)(currlvl + 1)); - return new MiniProfile(){ - Username= profile.ProfileInfo.UserName, - Nickname= pmc.Info.Nickname, - Side= pmc.Info.Side, - CurrentLevel= (int) (pmc.Info.Level), - CurrentExperience= (int) (pmc.Info.Experience ?? 0), - PreviousExperience= currlvl == 0 ? 0 : _profileHelper.GetExperience((int) currlvl), - NextLevel= nextlvl, - MaxLevel= maxlvl, - Edition= profile.ProfileInfo?.Edition ?? "", - ProfileId= profile.ProfileInfo?.ProfileId ?? "", - SptData= profile.SptData, + return new MiniProfile() + { + Username = profile.ProfileInfo.UserName, + Nickname = pmc.Info.Nickname, + Side = pmc.Info.Side, + CurrentLevel = (int)(pmc.Info.Level), + CurrentExperience = (int)(pmc.Info.Experience ?? 0), + PreviousExperience = currlvl == 0 ? 0 : _profileHelper.GetExperience((int)currlvl), + NextLevel = nextlvl, + MaxLevel = maxlvl, + Edition = profile.ProfileInfo?.Edition ?? "", + ProfileId = profile.ProfileInfo?.ProfileId ?? "", + SptData = profile.SptData, }; } @@ -175,7 +181,8 @@ public class ProfileController UpdateInventoryEquipmentId(pmcData); - if (pmcData.UnlockedInfo == null) { + if (pmcData.UnlockedInfo == null) + { pmcData.UnlockedInfo = new UnlockedInfo { UnlockedProductionRecipe = [] }; } @@ -191,31 +198,34 @@ public class ProfileController ); // Create profile - var profileDetails = new SptProfile { - ProfileInfo= account, - CharacterData= new Characters { PmcData = pmcData, ScavData = new()}, - Suits= profileTemplate.Suits, - UserBuildData= profileTemplate.UserBuilds, - DialogueRecords= profileTemplate.Dialogues, - SptData= _profileHelper.GetDefaultSptDataObject(), - VitalityData= new(), - InraidData= new (), - InsuranceList= [], - TraderPurchases= new(), - PlayerAchievements= new(), - FriendProfileIds= [], + var profileDetails = new SptProfile + { + ProfileInfo = account, + CharacterData = new Characters { PmcData = pmcData, ScavData = new() }, + Suits = profileTemplate.Suits, + UserBuildData = profileTemplate.UserBuilds, + DialogueRecords = profileTemplate.Dialogues, + SptData = _profileHelper.GetDefaultSptDataObject(), + VitalityData = new(), + InraidData = new(), + InsuranceList = [], + TraderPurchases = new(), + PlayerAchievements = new(), + FriendProfileIds = [], }; _profileFixerService.CheckForAndFixPmcProfileIssues(profileDetails.CharacterData.PmcData); _saveServer.AddProfile(profileDetails); - if (profileTemplate.Trader.SetQuestsAvailableForStart ?? false) { + if (profileTemplate.Trader.SetQuestsAvailableForStart ?? false) + { _questHelper.AddAllQuestsToProfile(profileDetails.CharacterData.PmcData, [QuestStatusEnum.AvailableForStart]); } // Profile is flagged as wanting quests set to ready to hand in and collect rewards - if (profileTemplate.Trader.SetQuestsAvailableForFinish ?? false) { + if (profileTemplate.Trader.SetQuestsAvailableForFinish ?? false) + { _questHelper.AddAllQuestsToProfile(profileDetails.CharacterData.PmcData, [ QuestStatusEnum.AvailableForStart, QuestStatusEnum.Started, @@ -253,13 +263,16 @@ public class ProfileController var oldEquipmentId = pmcData.Inventory.Equipment; pmcData.Inventory.Equipment = _hashUtil.Generate(); - foreach (var item in pmcData.Inventory.Items) { - if (item.ParentId == oldEquipmentId) { + foreach (var item in pmcData.Inventory.Items) + { + if (item.ParentId == oldEquipmentId) + { item.ParentId = pmcData.Inventory.Equipment; continue; } - if (item.Id == oldEquipmentId) { + if (item.Id == oldEquipmentId) + { item.Id = pmcData.Inventory.Equipment; } } @@ -272,30 +285,38 @@ public class ProfileController */ protected void AddMissingInternalContainersToProfile(PmcData pmcData) { - if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.HideoutCustomizationStashId)) { - pmcData.Inventory.Items.Add(new (){ + if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.HideoutCustomizationStashId)) + { + pmcData.Inventory.Items.Add(new() + { Id = pmcData.Inventory.HideoutCustomizationStashId, - Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION, + Template = ItemTpl.HIDEOUTAREACONTAINER_CUSTOMIZATION, }); } - if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.SortingTable)) { - pmcData.Inventory.Items.Add(new (){ - Id = pmcData.Inventory.SortingTable, + if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.SortingTable)) + { + pmcData.Inventory.Items.Add(new() + { + Id = pmcData.Inventory.SortingTable, Template = ItemTpl.SORTINGTABLE_SORTING_TABLE, }); } - if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.QuestStashItems)) { - pmcData.Inventory.Items.Add(new (){ - Id = pmcData.Inventory.QuestStashItems, + if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.QuestStashItems)) + { + pmcData.Inventory.Items.Add(new() + { + Id = pmcData.Inventory.QuestStashItems, Template = ItemTpl.STASH_QUESTOFFLINE, }); } - - if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.QuestRaidItems)) { - pmcData.Inventory.Items.Add(new (){ - Id = pmcData.Inventory.QuestRaidItems, + + if (!pmcData.Inventory.Items.Any((item) => item.Id == pmcData.Inventory.QuestRaidItems)) + { + pmcData.Inventory.Items.Add(new() + { + Id = pmcData.Inventory.QuestRaidItems, Template = ItemTpl.STASH_QUESTRAID, }); } @@ -307,9 +328,12 @@ public class ProfileController */ protected void DeleteProfileBySessionId(string sessionID) { - if (_saveServer.GetProfiles().ContainsKey(sessionID)) { + if (_saveServer.GetProfiles().ContainsKey(sessionID)) + { _saveServer.DeleteProfileById(sessionID); - } else { + } + else + { _logger.Warning( _localisationService.GetText("profile-unable_to_find_profile_by_id_cannot_delete", sessionID) ); @@ -329,7 +353,8 @@ public class ProfileController ItemEventRouterResponse response ) { - foreach (var quest in profileDetails.CharacterData.PmcData.Quests) { + foreach (var quest in profileDetails.CharacterData.PmcData.Quests) + { var questFromDb = _questHelper.GetQuestFromDb(quest.QId, profileDetails.CharacterData.PmcData); // Get messageId of text to send to player as text message in game @@ -365,7 +390,8 @@ public class ProfileController */ protected void ResetAllTradersInProfile(string sessionId) { - foreach (var traderId in _databaseService.GetTraders().Keys) { + foreach (var traderId in _databaseService.GetTraders().Keys) + { _traderHelper.ResetTrader(sessionId, traderId); } } @@ -386,11 +412,13 @@ public class ProfileController */ public string ValidateNickname(ValidateNicknameRequestData info, string sessionID) { - if (info.Nickname.Length < 3) { + if (info.Nickname.Length < 3) + { return "tooshort"; } - if (_profileHelper.IsNicknameTaken(info, sessionID)) { + if (_profileHelper.IsNicknameTaken(info, sessionID)) + { return "taken"; } @@ -403,9 +431,10 @@ public class ProfileController */ public string ChangeNickname(ProfileChangeNicknameRequestData info, string sessionID) { - var output = ValidateNickname(new ValidateNicknameRequestData(){Nickname = info.Nickname}, sessionID); + var output = ValidateNickname(new ValidateNicknameRequestData() { Nickname = info.Nickname }, sessionID); - if (output == "OK") { + if (output == "OK") + { var pmcData = _profileHelper.GetPmcProfile(sessionID); pmcData.Info.Nickname = info.Nickname; @@ -427,17 +456,20 @@ public class ProfileController /** * Handle client/game/profile/search */ - public List GetFriends(SearchFriendRequestData info, string sessionID) { + public List GetFriends(SearchFriendRequestData info, string sessionID) + { // TODO: We should probably rename this method in the next client update var result = new List(); // Find any profiles with a nickname containing the entered name var allProfiles = _saveServer.GetProfiles().Values; - foreach (var profile in allProfiles) { + foreach (var profile in allProfiles) + { var pmcProfile = profile?.CharacterData?.PmcData; - if (!pmcProfile?.Info?.LowerNickname?.Contains(info.Nickname.ToLower()) ?? false) { + if (!pmcProfile?.Info?.LowerNickname?.Contains(info.Nickname.ToLower()) ?? false) + { continue; } @@ -453,11 +485,13 @@ public class ProfileController public GetProfileStatusResponseData GetProfileStatus(string sessionId) { var account = _saveServer.GetProfile(sessionId).ProfileInfo; - var response = new GetProfileStatusResponseData() { + var response = new GetProfileStatusResponseData() + { MaxPveCountExceeded = false, - Profiles = [ - new (){ ProfileId = account.ScavengerId, ProfileToken = null, Status = "Free",Sid = "", Ip = "", Port = 0 }, - new (){ProfileId = account.ProfileId, ProfileToken = null, Status = "Free",Sid = "", Ip = "", Port = 0 }, + Profiles = + [ + new() { ProfileId = account.ScavengerId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 }, + new() { ProfileId = account.ProfileId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 }, ] }; @@ -471,48 +505,58 @@ public class ProfileController { // Find the profile by the account ID, fall back to the current player if we can't find the account var profile = _profileHelper.GetFullProfileByAccountId(request.AccountId); - if (profile?.CharacterData?.PmcData == null || profile?.CharacterData?.ScavData == null) { + if (profile?.CharacterData?.PmcData == null || profile?.CharacterData?.ScavData == null) + { profile = _profileHelper.GetFullProfile(sessionId); } + var playerPmc = profile.CharacterData.PmcData; var playerScav = profile.CharacterData.ScavData; - return new GetOtherProfileResponse(){ - Id= playerPmc.Id, - Aid= playerPmc.Aid as int?, - Info= { - Nickname= playerPmc.Info.Nickname, - Side= playerPmc.Info.Side, - Experience= playerPmc.Info.Experience as int?, - MemberCategory= playerPmc.Info.MemberCategory as int?, - BannedState= playerPmc.Info.BannedState, - BannedUntil= playerPmc.Info.BannedUntil, - RegistrationDate= playerPmc.Info.RegistrationDate, + return new GetOtherProfileResponse() + { + Id = playerPmc.Id, + Aid = playerPmc.Aid as int?, + Info = + { + Nickname = playerPmc.Info.Nickname, + Side = playerPmc.Info.Side, + Experience = playerPmc.Info.Experience as int?, + MemberCategory = playerPmc.Info.MemberCategory as int?, + BannedState = playerPmc.Info.BannedState, + BannedUntil = playerPmc.Info.BannedUntil, + RegistrationDate = playerPmc.Info.RegistrationDate, }, - Customization= { - Head= playerPmc.Customization.Head, - Body= playerPmc.Customization.Body, - Feet= playerPmc.Customization.Feet, - Hands= playerPmc.Customization.Hands, - Dogtag= playerPmc.Customization.DogTag, + Customization = + { + Head = playerPmc.Customization.Head, + Body = playerPmc.Customization.Body, + Feet = playerPmc.Customization.Feet, + Hands = playerPmc.Customization.Hands, + Dogtag = playerPmc.Customization.DogTag, }, - Skills= playerPmc.Skills, - Equipment= { - Id= playerPmc.Inventory.Equipment, - Items= playerPmc.Inventory.Items, + Skills = playerPmc.Skills, + Equipment = + { + Id = playerPmc.Inventory.Equipment, + Items = playerPmc.Inventory.Items, }, - Achievements= playerPmc.Achievements, - FavoriteItems= _profileHelper.GetOtherProfileFavorites(playerPmc), - PmcStats= { - Eft= { - TotalInGameTime= playerPmc.Stats.Eft.TotalInGameTime as int?, - OverAllCounters= playerPmc.Stats.Eft.OverallCounters, + Achievements = playerPmc.Achievements, + FavoriteItems = _profileHelper.GetOtherProfileFavorites(playerPmc), + PmcStats = + { + Eft = + { + TotalInGameTime = playerPmc.Stats.Eft.TotalInGameTime as int?, + OverAllCounters = playerPmc.Stats.Eft.OverallCounters, }, }, - ScavStats= { - Eft= { - TotalInGameTime= playerScav.Stats.Eft.TotalInGameTime as int?, - OverAllCounters= playerScav.Stats.Eft.OverallCounters, + ScavStats = + { + Eft = + { + TotalInGameTime = playerScav.Stats.Eft.TotalInGameTime as int?, + OverAllCounters = playerScav.Stats.Eft.OverallCounters, } } }; @@ -521,18 +565,21 @@ public class ProfileController /** * Handle client/profile/settings */ - public bool SetChosenProfileIcon(string sessionId, GetProfileSettingsRequest request ) + public bool SetChosenProfileIcon(string sessionId, GetProfileSettingsRequest request) { var profileToUpdate = _profileHelper.GetPmcProfile(sessionId); - if (profileToUpdate == null) { + if (profileToUpdate == null) + { return false; } - if (request.MemberCategory != null) { + if (request.MemberCategory != null) + { profileToUpdate.Info.SelectedMemberCategory = request.MemberCategory as MemberCategory?; } - if (request.SquadInviteRestriction != null) { + if (request.SquadInviteRestriction != null) + { profileToUpdate.Info.SquadInviteRestriction = request.SquadInviteRestriction; } diff --git a/Core/Helpers/ProfileHelper.cs b/Core/Helpers/ProfileHelper.cs index 9d8d2d3a..9eaf62ca 100644 --- a/Core/Helpers/ProfileHelper.cs +++ b/Core/Helpers/ProfileHelper.cs @@ -168,11 +168,11 @@ public class ProfileHelper /// /// Level to get xp for /// Number of xp points for level - public double? GetExperience(int level) + public int? GetExperience(int level) { var playerLevel = level; var expTable = _databaseService.GetGlobals().Configuration.Exp.Level.ExperienceTable; - double? exp = null; + int? exp = null; if (playerLevel >= expTable.Length) // make sure to not go out of bounds playerLevel = expTable.Length - 1; diff --git a/Core/Models/Eft/Common/Globals.cs b/Core/Models/Eft/Common/Globals.cs index b4cb155a..f1c7609c 100644 --- a/Core/Models/Eft/Common/Globals.cs +++ b/Core/Models/Eft/Common/Globals.cs @@ -1255,7 +1255,7 @@ public class Level public class ExpTable { [JsonPropertyName("exp")] - public double? Experience { get; set; } + public int? Experience { get; set; } } public class LootAttempt @@ -4347,4 +4347,4 @@ public class QuestSettings [JsonPropertyName("GlobalRewardRepModifierQuestPvE")] public double? GlobalRewardRepModifierQuestPvE { get; set; } -} \ No newline at end of file +} diff --git a/Core/Models/Eft/Profile/GetOtherProfileResponse.cs b/Core/Models/Eft/Profile/GetOtherProfileResponse.cs index b7921c89..e7f07c08 100644 --- a/Core/Models/Eft/Profile/GetOtherProfileResponse.cs +++ b/Core/Models/Eft/Profile/GetOtherProfileResponse.cs @@ -24,7 +24,7 @@ public class GetOtherProfileResponse public OtherProfileEquipment? Equipment { get; set; } [JsonPropertyName("achievements")] - public Dictionary? Achievements { get; set; } + public Dictionary? Achievements { get; set; } [JsonPropertyName("favoriteItems")] public List? FavoriteItems { get; set; } @@ -100,4 +100,4 @@ public class OtherProfileSubStats [JsonPropertyName("overAllCounters")] public OverallCounters? OverAllCounters { get; set; } -} \ No newline at end of file +} From 7bb98b69918b89a3dc2b15b6ad89b0cee0949538 Mon Sep 17 00:00:00 2001 From: CWX Date: Fri, 10 Jan 2025 22:33:46 +0000 Subject: [PATCH 3/4] Fix reflection for GameVersions --- Core/Controllers/LauncherController.cs | 2 +- Core/Helpers/ItemHelper.cs | 2 ++ Core/Helpers/ProfileHelper.cs | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Core/Controllers/LauncherController.cs b/Core/Controllers/LauncherController.cs index 684d6860..a3d874cf 100644 --- a/Core/Controllers/LauncherController.cs +++ b/Core/Controllers/LauncherController.cs @@ -82,7 +82,7 @@ public class LauncherController { var result = new Dictionary(); var dbProfiles = _databaseService.GetProfiles(); - foreach (var templatesProperty in typeof(ProfileTemplates).GetProperties()) + foreach (var templatesProperty in typeof(ProfileTemplates).GetProperties().Where(p => p.CanWrite == true)) { var propertyValue = templatesProperty.GetValue(dbProfiles); if (propertyValue == null) { diff --git a/Core/Helpers/ItemHelper.cs b/Core/Helpers/ItemHelper.cs index 1848696d..73072a97 100644 --- a/Core/Helpers/ItemHelper.cs +++ b/Core/Helpers/ItemHelper.cs @@ -1,9 +1,11 @@ using System.Text.Json.Serialization; +using Core.Annotations; using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; namespace Core.Helpers; +[Injectable] public class ItemHelper { /** diff --git a/Core/Helpers/ProfileHelper.cs b/Core/Helpers/ProfileHelper.cs index 9eaf62ca..945ddbde 100644 --- a/Core/Helpers/ProfileHelper.cs +++ b/Core/Helpers/ProfileHelper.cs @@ -34,8 +34,8 @@ public class ProfileHelper ItemHelper itemHelper, TimeUtil timeUtil, LocalisationService localisationService, - InventoryConfig inventoryConfig, - HashUtil hashUtil + HashUtil hashUtil, + ConfigServer configServer ) { _cloner = cloner; @@ -45,8 +45,8 @@ public class ProfileHelper _itemHelper = itemHelper; _timeUtil = timeUtil; _localisationService = localisationService; - _inventoryConfig = inventoryConfig; _hashUtil = hashUtil; + _inventoryConfig = configServer.GetConfig(ConfigTypes.INVENTORY); } /// From 97c5fb257ee0048ca1a2c697fb3e233bf1dce0bd Mon Sep 17 00:00:00 2001 From: CWX Date: Fri, 10 Jan 2025 23:01:22 +0000 Subject: [PATCH 4/4] Fix launcher breaking on login/register --- Core/Callbacks/SaveCallbacks.cs | 39 ++++++++++++++++++++------ Core/Controllers/LauncherController.cs | 4 +-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Core/Callbacks/SaveCallbacks.cs b/Core/Callbacks/SaveCallbacks.cs index 176ed83b..28d881b0 100644 --- a/Core/Callbacks/SaveCallbacks.cs +++ b/Core/Callbacks/SaveCallbacks.cs @@ -1,28 +1,51 @@ -using Core.DI; +using Core.Annotations; +using Core.DI; +using Core.Models.Enums; using Core.Models.Spt.Config; +using Core.Servers; +using Core.Services; namespace Core.Callbacks; +[Injectable] public class SaveCallbacks : OnLoad, OnUpdate { - private CoreConfig _coreConfig; + protected SaveServer _saveServer; + protected CoreConfig _coreConfig; + protected BackupService _backupService; - public SaveCallbacks() + public SaveCallbacks( + SaveServer saveServer, + ConfigServer configServer, + BackupService backupService + ) { + _saveServer = saveServer; + _coreConfig = configServer.GetConfig(ConfigTypes.CORE); + _backupService = backupService; } - public async Task OnLoad() + public Task OnLoad() { - throw new NotImplementedException(); + _backupService.InitAsync(); + _saveServer.Load(); + + return Task.CompletedTask; } public async Task OnUpdate(long SecondsSinceLastRun) { - throw new NotImplementedException(); + if (SecondsSinceLastRun > _coreConfig.ProfileSaveIntervalInSeconds) + { + _saveServer.Save(); + return true; + } + + return false; } public string GetRoute() { - throw new NotImplementedException(); + return "spt-save"; } -} \ No newline at end of file +} diff --git a/Core/Controllers/LauncherController.cs b/Core/Controllers/LauncherController.cs index a3d874cf..2c44cc39 100644 --- a/Core/Controllers/LauncherController.cs +++ b/Core/Controllers/LauncherController.cs @@ -102,7 +102,7 @@ public class LauncherController return _saveServer.GetProfiles().TryGetValue(sessionId, out var profile) ? profile.ProfileInfo : null; } - public string Login(LoginRequestData info) + public string? Login(LoginRequestData info) { foreach (var sessionID in _saveServer.GetProfiles()) { var account = _saveServer.GetProfile(sessionID.Key).ProfileInfo; @@ -111,7 +111,7 @@ public class LauncherController } } - return ""; + return null; } public string Register(RegisterData info)