using System.Collections.Concurrent; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Utils; namespace SPTarkov.Server.Core.Services.Mod; [Injectable(InjectionType.Singleton)] public class ProfileDataService(ISptLogger logger, FileUtil fileUtil, JsonUtil jsonUtil) { protected const string ProfileDataFilepath = "user/profileData/"; private readonly ConcurrentDictionary _profileDataCache = new(); /// /// Check if a specfici mod file exists for a profile /// /// Profile to look up /// Name of json file to look up public bool ProfileDataExists(string profileId, string modKey) { return fileUtil.FileExists(Path.Combine(ProfileDataFilepath, profileId, $"{modKey}.json")); } public T? GetProfileData(string profileId, string modKey) { var profileDataKey = GetCacheKey(profileId, modKey); if (!_profileDataCache.TryGetValue(profileDataKey, out var value)) { if (ProfileDataExists(profileId, modKey)) { value = jsonUtil.Deserialize(fileUtil.ReadFile(Path.Combine(ProfileDataFilepath, profileId, $"{modKey}.json"))); if (value != null) { _profileDataCache[GetCacheKey(profileId, modKey)] = value; } } else { value = null; } } return (T?)value; } public void SaveProfileData(string profileId, string modKey, T profileData) { ArgumentNullException.ThrowIfNull(profileData); var data = jsonUtil.Serialize(profileData, profileData.GetType(), true) ?? throw new Exception("The profile data when serialized resulted in a null value"); _profileDataCache[GetCacheKey(profileId, modKey)] = profileData; fileUtil.WriteFile(Path.Combine(ProfileDataFilepath, profileId, $"{modKey}.json"), data); } /// /// Clear all data for a profile /// /// Id of profile to delete files for public void ClearProfileData(string profileId) { if (!fileUtil.DirectoryExists(Path.Combine(ProfileDataFilepath, profileId))) { return; } var profileFiles = fileUtil.GetFiles(Path.Combine(ProfileDataFilepath, profileId)); foreach (var filepPath in profileFiles) { fileUtil.DeleteFile(filepPath); } var keysInCacheToRemove = _profileDataCache.Keys.Where(key => key.StartsWith($"{profileId}:")).ToList(); // ToList so we can iterate over results without modifying collection foreach (var key in keysInCacheToRemove) { _profileDataCache.TryRemove(key, out _); } } /// /// Get the cache key in specific format /// protected string GetCacheKey(string profileId, string modKey) { return $"{profileId}:{modKey}"; } }