using SPTarkov.Common.Annotations; using SPTarkov.Server.Core.Generators; using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Launcher; using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Routers; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Services; using SPTarkov.Server.Core.Utils; using SPTarkov.Server.Core.Utils.Cloners; namespace SPTarkov.Server.Core.Controllers; [Injectable] public class ProfileController( ISptLogger _logger, HashUtil _hashUtil, ICloner _cloner, TimeUtil _timeUtil, SaveServer _saveServer, DatabaseService _databaseService, ItemHelper _itemHelper, ProfileFixerService _profileFixerService, LocalisationService _localisationService, CreateProfileService _createProfileService, SeasonalEventService _seasonalEventService, PlayerScavGenerator _playerScavGenerator, EventOutputHolder _eventOutputHolder, TraderHelper _traderHelper, DialogueHelper _dialogueHelper, QuestHelper _questHelper, QuestRewardHelper _questRewardHelper, ProfileHelper _profileHelper ) { /// /// Handle /launcher/profiles /// /// public virtual List GetMiniProfiles() { return _saveServer.GetProfiles().Select(kvp => GetMiniProfile(kvp.Key)).ToList(); } /// /// Handle launcher/profile/info /// /// Session/Player id /// public virtual MiniProfile GetMiniProfile(string sessionID) { var profile = _saveServer.GetProfile(sessionID); if (profile?.CharacterData == null) { throw new Exception($"Unable to find character data for id: {sessionID}. Profile may be corrupt"); } var pmc = profile.CharacterData.PmcData; var maxLvl = _profileHelper.GetMaxLevel(); // Player hasn't completed profile creation process, send defaults var currlvl = pmc?.Info?.Level.GetValueOrDefault(1); var xpToNextLevel = _profileHelper.GetExperience((currlvl ?? 1) + 1); if (pmc?.Info?.Level == null) { return new MiniProfile { Username = profile.ProfileInfo?.Username ?? "", Nickname = "unknown", HasPassword = profile.ProfileInfo.Password != "", Side = "unknown", CurrentLevel = 0, CurrentExperience = 0, PreviousExperience = 0, NextLevel = xpToNextLevel, MaxLevel = maxLvl, Edition = profile.ProfileInfo?.Edition ?? "", ProfileId = profile.ProfileInfo?.ProfileId ?? "", SptData = _profileHelper.GetDefaultSptDataObject() }; } return new MiniProfile { Username = profile.ProfileInfo.Username, Nickname = pmc.Info.Nickname, HasPassword = profile.ProfileInfo.Password != "", Side = pmc.Info.Side, CurrentLevel = pmc.Info.Level, CurrentExperience = pmc.Info.Experience ?? 0, PreviousExperience = currlvl == 0 ? 0 : _profileHelper.GetExperience(currlvl.Value), NextLevel = xpToNextLevel, MaxLevel = maxLvl, Edition = profile.ProfileInfo?.Edition ?? "", ProfileId = profile.ProfileInfo?.ProfileId ?? "", SptData = profile.SptData }; } /// /// Handle client/game/profile/list /// /// Session/Player id /// Return a full profile, scav and pmc profiles + meta data public virtual List GetCompleteProfile(string sessionID) { return _profileHelper.GetCompleteProfile(sessionID); } /// /// Handle client/game/profile/create /// /// Create profile request /// Player id /// Player id public virtual string CreateProfile(ProfileCreateRequestData request, string sessionID) { return _createProfileService.CreateProfile(sessionID, request); } /// /// Generate a player scav object /// PMC profile MUST exist first before player-scav can be generated /// /// Player id /// PmcData public virtual PmcData GeneratePlayerScav(string sessionID) { return _playerScavGenerator.Generate(sessionID); } /// /// Handle client/game/profile/nickname/validate /// /// Validate nickname request /// Session/Player id /// public virtual string ValidateNickname(ValidateNicknameRequestData request, string sessionID) { if (request.Nickname.Length < 3) { return "tooshort"; } if (_profileHelper.IsNicknameTaken(request, sessionID)) { return "taken"; } return "OK"; } /// /// Handle client/game/profile/nickname/change event /// Client allows player to adjust their profile name /// /// Change nickname request /// Player id /// public virtual string ChangeNickname(ProfileChangeNicknameRequestData request, string sessionID) { var output = ValidateNickname( new ValidateNicknameRequestData { Nickname = request.Nickname }, sessionID ); if (output == "OK") { var pmcData = _profileHelper.GetPmcProfile(sessionID); pmcData.Info.Nickname = request.Nickname; pmcData.Info.LowerNickname = request.Nickname.ToLower(); } return output; } /// /// Handle client/game/profile/voice/change event /// /// Change voice request /// Player id public virtual void ChangeVoice(ProfileChangeVoiceRequestData request, string sessionID) { var pmcData = _profileHelper.GetPmcProfile(sessionID); pmcData.Info.Voice = request.Voice; } /// /// Handle client/game/profile/search /// /// Search profiles request /// Player id /// Found profiles public virtual List SearchProfiles(SearchProfilesRequestData request, string sessionID) { var result = new List(); // Find any profiles with a nickname containing the entered name var allProfiles = _saveServer.GetProfiles().Values; foreach (var profile in allProfiles) { var pmcProfile = profile?.CharacterData?.PmcData; if (!pmcProfile?.Info?.LowerNickname?.Contains(request.Nickname.ToLower()) ?? false) { continue; } result.Add(_profileHelper.GetChatRoomMemberFromPmcProfile(pmcProfile)); } return result; } /// /// Handle client/profile/status /// /// Session/Player id /// public virtual GetProfileStatusResponseData GetProfileStatus(string sessionId) { var account = _saveServer.GetProfile(sessionId).ProfileInfo; var response = new GetProfileStatusResponseData { MaxPveCountExceeded = false, Profiles = [ new ProfileStatusData { ProfileId = account.ScavengerId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 }, new ProfileStatusData { ProfileId = account.ProfileId, ProfileToken = null, Status = "Free", Sid = "", Ip = "", Port = 0 } ] }; return response; } /// /// Handle client/profile/view /// /// Session/Player id /// Get other profile request /// GetOtherProfileResponse public virtual GetOtherProfileResponse GetOtherProfile(string sessionId, GetOtherProfileRequest request) { // Find the profile by the account ID, fall back to the current player if we can't find the account var profileToView = _profileHelper.GetFullProfileByAccountId(request.AccountId); if (profileToView?.CharacterData?.PmcData is null || profileToView.CharacterData.ScavData is null) { _logger.Warning($"Unable to get profile: {request.AccountId} to show, falling back to own profile"); profileToView = _profileHelper.GetFullProfile(sessionId); } var profileToViewPmc = profileToView.CharacterData.PmcData; var profileToViewScav = profileToView.CharacterData.ScavData; // Get the keys needed to find profiles hideout-related items var hideoutKeys = new HashSet(); hideoutKeys.UnionWith(profileToViewPmc.Inventory.HideoutAreaStashes.Keys); hideoutKeys.Add(profileToViewPmc.Inventory.HideoutCustomizationStashId); // Find hideout items e.g. posters var hideoutRootItems = profileToViewPmc.Inventory.Items.Where(x => hideoutKeys.Contains(x.Id)); var itemsToReturn = new List(); foreach (var rootItems in hideoutRootItems) { // Check each root items for children and add var itemWithChildren = _itemHelper.FindAndReturnChildrenAsItems(profileToViewPmc.Inventory.Items, rootItems.Id); itemsToReturn.AddRange(itemWithChildren); } return new GetOtherProfileResponse { Id = profileToViewPmc.Id, Aid = profileToViewPmc.Aid, Info = { Nickname = profileToViewPmc.Info.Nickname, Side = profileToViewPmc.Info.Side, Experience = profileToViewPmc.Info.Experience, MemberCategory = profileToViewPmc.Info.MemberCategory as int?, BannedState = profileToViewPmc.Info.BannedState, BannedUntil = profileToViewPmc.Info.BannedUntil, RegistrationDate = profileToViewPmc.Info.RegistrationDate }, Customization = { Head = profileToViewPmc.Customization.Head, Body = profileToViewPmc.Customization.Body, Feet = profileToViewPmc.Customization.Feet, Hands = profileToViewPmc.Customization.Hands, Dogtag = profileToViewPmc.Customization.DogTag }, Skills = profileToViewPmc.Skills, Equipment = { Id = profileToViewPmc.Inventory.Equipment, Items = profileToViewPmc.Inventory.Items }, Achievements = profileToViewPmc.Achievements, FavoriteItems = _profileHelper.GetOtherProfileFavorites(profileToViewPmc), PmcStats = { Eft = { TotalInGameTime = profileToViewPmc.Stats.Eft.TotalInGameTime, OverAllCounters = profileToViewPmc.Stats.Eft.OverallCounters } }, ScavStats = { Eft = { TotalInGameTime = profileToViewScav.Stats.Eft.TotalInGameTime, OverAllCounters = profileToViewScav.Stats.Eft.OverallCounters } }, Hideout = profileToViewPmc.Hideout, CustomizationStash = profileToViewPmc.Inventory.HideoutCustomizationStashId, HideoutAreaStashes = profileToViewPmc.Inventory.HideoutAreaStashes, Items = itemsToReturn }; } /// /// Handle client/profile/settings /// /// Session/Player id /// Get profile settings request /// public virtual bool SetChosenProfileIcon(string sessionId, GetProfileSettingsRequest request) { var profileToUpdate = _profileHelper.GetPmcProfile(sessionId); if (profileToUpdate == null) { return false; } if (request.MemberCategory != null) { profileToUpdate.Info.SelectedMemberCategory = request.MemberCategory as MemberCategory?; } if (request.SquadInviteRestriction != null) { profileToUpdate.Info.SquadInviteRestriction = request.SquadInviteRestriction; } return true; } }