From 9ba6827816daaff14becb18bd801519dc55d41c0 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Tue, 13 Dec 2022 12:01:40 +0100 Subject: [PATCH] experimental first iteration of persistent transient resources on startup --- .../Factories/CharacterDataFactory.cs | 40 +++++++++---------- .../Factories/FileReplacementFactory.cs | 22 ++++++++++ .../{FileDbManager.cs => FileCacheManager.cs} | 13 +++--- MareSynchronos/FileCache/FileState.cs | 11 +++++ .../Managers/TransientResourceManager.cs | 34 ++++++++++++++-- MareSynchronos/Models/FileReplacement.cs | 37 ++++++++++++++--- MareSynchronos/Plugin.cs | 18 +++++---- 7 files changed, 128 insertions(+), 47 deletions(-) create mode 100644 MareSynchronos/Factories/FileReplacementFactory.cs rename MareSynchronos/FileCache/{FileDbManager.cs => FileCacheManager.cs} (98%) create mode 100644 MareSynchronos/FileCache/FileState.cs diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index 172cd90..d2687ad 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -8,7 +8,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource; using MareSynchronos.API; -using MareSynchronos.FileCache; using MareSynchronos.Interop; using MareSynchronos.Managers; using MareSynchronos.Models; @@ -24,16 +23,16 @@ public class CharacterDataFactory { private readonly DalamudUtil _dalamudUtil; private readonly IpcManager _ipcManager; - private readonly TransientResourceManager transientResourceManager; - private readonly FileCacheManager fileDbManager; + private readonly TransientResourceManager _transientResourceManager; + private readonly FileReplacementFactory _fileReplacementFactory; - public CharacterDataFactory(DalamudUtil dalamudUtil, IpcManager ipcManager, TransientResourceManager transientResourceManager, FileCacheManager fileDbManager) + public CharacterDataFactory(DalamudUtil dalamudUtil, IpcManager ipcManager, TransientResourceManager transientResourceManager, FileReplacementFactory fileReplacementFactory) { Logger.Verbose("Creating " + nameof(CharacterDataFactory)); - this.fileDbManager = fileDbManager; _dalamudUtil = dalamudUtil; _ipcManager = ipcManager; - this.transientResourceManager = transientResourceManager; + _transientResourceManager = transientResourceManager; + _fileReplacementFactory = fileReplacementFactory; } private unsafe bool CheckForPointer(IntPtr playerPointer) @@ -281,7 +280,7 @@ public class CharacterDataFactory foreach (var item in previousData.FileReplacements[objectKind]) { - transientResourceManager.RemoveTransientResource(charaPointer, item); + _transientResourceManager.RemoveTransientResource(charaPointer, item); } if (objectKind == ObjectKind.Player) @@ -293,7 +292,7 @@ public class CharacterDataFactory { foreach (var item in previousData.FileReplacements[objectKind]) { - transientResourceManager.AddSemiTransientResource(objectKind, item); + _transientResourceManager.AddSemiTransientResource(objectKind, item); } previousData.FileReplacements[objectKind].Clear(); @@ -315,9 +314,9 @@ public class CharacterDataFactory private unsafe void ManageSemiTransientData(CharacterData previousData, ObjectKind objectKind, IntPtr charaPointer) { - transientResourceManager.PersistTransientResources(charaPointer, objectKind, CreateFileReplacement); + _transientResourceManager.PersistTransientResources(charaPointer, objectKind, CreateFileReplacement); - foreach (var item in transientResourceManager.GetSemiTransientResources(objectKind)) + foreach (var item in _transientResourceManager.GetSemiTransientResources(objectKind)) { if (!previousData.FileReplacements.ContainsKey(objectKind)) { @@ -331,7 +330,7 @@ public class CharacterDataFactory if (string.Equals(penumResolve, gamePath, StringComparison.Ordinal)) { Logger.Verbose("PenumResolve was same as GamePath, not adding " + item); - transientResourceManager.RemoveTransientResource(charaPointer, item); + _transientResourceManager.RemoveTransientResource(charaPointer, item); } else { @@ -354,10 +353,10 @@ public class CharacterDataFactory foreach (var item in previousData.FileReplacements[objectKind]) { - transientResourceManager.RemoveTransientResource(charaPointer, item); + _transientResourceManager.RemoveTransientResource(charaPointer, item); } - foreach (var item in transientResourceManager.GetTransientResources((IntPtr)weaponObject)) + foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)weaponObject)) { Logger.Verbose("Found transient weapon resource: " + item); AddReplacement(item, objectKind, previousData, 1, true); @@ -371,10 +370,10 @@ public class CharacterDataFactory foreach (var item in previousData.FileReplacements[objectKind]) { - transientResourceManager.RemoveTransientResource((IntPtr)offHandWeapon, item); + _transientResourceManager.RemoveTransientResource((IntPtr)offHandWeapon, item); } - foreach (var item in transientResourceManager.GetTransientResources((IntPtr)offHandWeapon)) + foreach (var item in _transientResourceManager.GetTransientResources((IntPtr)offHandWeapon)) { Logger.Verbose("Found transient offhand weapon resource: " + item); AddReplacement(item, objectKind, previousData, 1, true); @@ -402,7 +401,7 @@ public class CharacterDataFactory foreach (var item in previousData.FileReplacements[objectKind]) { - transientResourceManager.RemoveTransientResource(charaPointer, item); + _transientResourceManager.RemoveTransientResource(charaPointer, item); } } @@ -420,17 +419,14 @@ public class CharacterDataFactory private FileReplacement CreateFileReplacement(string path, bool doNotReverseResolve = false) { - var fileReplacement = new FileReplacement(fileDbManager); + var fileReplacement = _fileReplacementFactory.Create(); if (!doNotReverseResolve) { - fileReplacement.GamePaths = - _ipcManager.PenumbraReverseResolvePlayer(path).ToList(); - fileReplacement.SetResolvedPath(path); + fileReplacement.ReverseResolvePath(path); } else { - fileReplacement.GamePaths = new List { path }; - fileReplacement.SetResolvedPath(_ipcManager.PenumbraResolvePath(path)!); + fileReplacement.ResolvePath(path); } return fileReplacement; diff --git a/MareSynchronos/Factories/FileReplacementFactory.cs b/MareSynchronos/Factories/FileReplacementFactory.cs new file mode 100644 index 0000000..bf314d7 --- /dev/null +++ b/MareSynchronos/Factories/FileReplacementFactory.cs @@ -0,0 +1,22 @@ +using MareSynchronos.FileCache; +using MareSynchronos.Managers; +using MareSynchronos.Models; + +namespace MareSynchronos.Factories; + +public class FileReplacementFactory +{ + private readonly FileCacheManager fileCacheManager; + private readonly IpcManager ipcManager; + + public FileReplacementFactory(FileCacheManager fileCacheManager, IpcManager ipcManager) + { + this.fileCacheManager = fileCacheManager; + this.ipcManager = ipcManager; + } + + public FileReplacement Create() + { + return new FileReplacement(fileCacheManager, ipcManager); + } +} diff --git a/MareSynchronos/FileCache/FileDbManager.cs b/MareSynchronos/FileCache/FileCacheManager.cs similarity index 98% rename from MareSynchronos/FileCache/FileDbManager.cs rename to MareSynchronos/FileCache/FileCacheManager.cs index 6de2bf3..34550f3 100644 --- a/MareSynchronos/FileCache/FileDbManager.cs +++ b/MareSynchronos/FileCache/FileCacheManager.cs @@ -10,14 +10,6 @@ using System.Text; namespace MareSynchronos.FileCache; - -public enum FileState -{ - Valid, - RequireUpdate, - RequireDeletion -} - public class FileCacheManager : IDisposable { private const string PenumbraPrefix = "{penumbra}"; @@ -227,6 +219,11 @@ public class FileCacheManager : IDisposable return fileCache; } + public string ResolveFileReplacement(string gamePath) + { + return _ipcManager.PenumbraResolvePath(gamePath); + } + public void Dispose() { WriteOutFullCsv(); diff --git a/MareSynchronos/FileCache/FileState.cs b/MareSynchronos/FileCache/FileState.cs new file mode 100644 index 0000000..d686e8d --- /dev/null +++ b/MareSynchronos/FileCache/FileState.cs @@ -0,0 +1,11 @@ +using MareSynchronos.Models; + +namespace MareSynchronos.FileCache; + + +public enum FileState +{ + Valid, + RequireUpdate, + RequireDeletion +} diff --git a/MareSynchronos/Managers/TransientResourceManager.cs b/MareSynchronos/Managers/TransientResourceManager.cs index 620f620..e143dbd 100644 --- a/MareSynchronos/Managers/TransientResourceManager.cs +++ b/MareSynchronos/Managers/TransientResourceManager.cs @@ -1,12 +1,12 @@ using MareSynchronos.API; +using MareSynchronos.Factories; using MareSynchronos.Models; using MareSynchronos.Utils; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace MareSynchronos.Managers; @@ -16,20 +16,39 @@ public class TransientResourceManager : IDisposable { private readonly IpcManager manager; private readonly DalamudUtil dalamudUtil; + private readonly FileReplacementFactory fileReplacementFactory; + private readonly string configurationDirectory; public event TransientResourceLoadedEvent? TransientResourceLoaded; public IntPtr[] PlayerRelatedPointers = Array.Empty(); private readonly string[] FileTypesToHandle = new[] { "tmb", "pap", "avfx", "atex", "sklb", "eid", "phyb", "scd", "skp" }; + private string PersistentDataCache => Path.Combine(configurationDirectory, "PersistentTransientData.lst"); private ConcurrentDictionary> TransientResources { get; } = new(); private ConcurrentDictionary> SemiTransientResources { get; } = new(); - public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil) + public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil, FileReplacementFactory fileReplacementFactory, string configurationDirectory) { manager.PenumbraResourceLoadEvent += Manager_PenumbraResourceLoadEvent; this.manager = manager; this.dalamudUtil = dalamudUtil; + this.fileReplacementFactory = fileReplacementFactory; + this.configurationDirectory = configurationDirectory; dalamudUtil.FrameworkUpdate += DalamudUtil_FrameworkUpdate; dalamudUtil.ClassJobChanged += DalamudUtil_ClassJobChanged; + if (File.Exists(PersistentDataCache)) + { + var persistentEntities = File.ReadAllLines(PersistentDataCache); + SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet()); + foreach (var line in persistentEntities) + { + var fileReplacement = fileReplacementFactory.Create(); + fileReplacement.ResolvePath(line); + if (fileReplacement.HasFileReplacement) + { + SemiTransientResources[ObjectKind.Player].Add(fileReplacement); + } + } + } } private void DalamudUtil_ClassJobChanged() @@ -180,6 +199,10 @@ public class TransientResourceManager : IDisposable } } + if (objectKind == ObjectKind.Player && SemiTransientResources.TryGetValue(ObjectKind.Player, out var fileReplacements)) + { + File.WriteAllLines(PersistentDataCache, fileReplacements.SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase)); + } TransientResources[gameObject].Clear(); } @@ -189,6 +212,11 @@ public class TransientResourceManager : IDisposable manager.PenumbraResourceLoadEvent -= Manager_PenumbraResourceLoadEvent; dalamudUtil.ClassJobChanged -= DalamudUtil_ClassJobChanged; TransientResources.Clear(); + SemiTransientResources.Clear(); + if (SemiTransientResources.ContainsKey(ObjectKind.Player)) + { + File.WriteAllLines(PersistentDataCache, SemiTransientResources[ObjectKind.Player].SelectMany(p => p.GamePaths).Distinct(StringComparer.OrdinalIgnoreCase)); + } } internal void AddSemiTransientResource(ObjectKind objectKind, FileReplacement item) diff --git a/MareSynchronos/Models/FileReplacement.cs b/MareSynchronos/Models/FileReplacement.cs index 6747ad2..f4aa19a 100644 --- a/MareSynchronos/Models/FileReplacement.cs +++ b/MareSynchronos/Models/FileReplacement.cs @@ -5,16 +5,20 @@ using System.Threading.Tasks; using MareSynchronos.API; using System.Text.RegularExpressions; using MareSynchronos.FileCache; +using MareSynchronos.Managers; +using System; namespace MareSynchronos.Models; public class FileReplacement { private readonly FileCacheManager fileDbManager; + private readonly IpcManager ipcManager; - public FileReplacement(FileCacheManager fileDbManager) + public FileReplacement(FileCacheManager fileDbManager, IpcManager ipcManager) { this.fileDbManager = fileDbManager; + this.ipcManager = ipcManager; } public bool Computed => IsFileSwap || !HasFileReplacement || !string.IsNullOrEmpty(Hash); @@ -29,7 +33,7 @@ public class FileReplacement public string ResolvedPath { get; set; } = string.Empty; - public void SetResolvedPath(string path) + private void SetResolvedPath(string path) { ResolvedPath = path.ToLowerInvariant().Replace('\\', '/'); if (!HasFileReplacement || IsFileSwap) return; @@ -43,10 +47,18 @@ public class FileReplacement public bool Verify() { - var cache = fileDbManager.GetFileCacheByPath(ResolvedPath); - if (cache == null) return false; - Hash = cache.Hash; - return true; + if (!IsFileSwap) + { + var cache = fileDbManager.GetFileCacheByPath(ResolvedPath); + if (cache == null) return false; + Hash = cache.Hash; + return true; + } + + var resolvedPath = fileDbManager.ResolveFileReplacement(GamePaths.First()); + ResolvedPath = resolvedPath.ToLowerInvariant(); + + return IsFileSwap; } public FileReplacementDto ToFileReplacementDto() @@ -58,10 +70,23 @@ public class FileReplacement FileSwapPath = IsFileSwap ? ResolvedPath : string.Empty }; } + public override string ToString() { StringBuilder builder = new(); builder.AppendLine($"Modded: {HasFileReplacement} - {string.Join(",", GamePaths)} => {ResolvedPath}"); return builder.ToString(); } + + internal void ReverseResolvePath(string path) + { + GamePaths = ipcManager.PenumbraReverseResolvePlayer(path).ToList(); + SetResolvedPath(path); + } + + internal void ResolvePath(string path) + { + GamePaths = new List { path }; + SetResolvedPath(ipcManager.PenumbraResolvePath(path)); + } } diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index b1770bd..61eb097 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -36,10 +36,11 @@ public sealed class Plugin : IDalamudPlugin private OnlinePlayerManager? _characterCacheManager; private readonly DownloadUi _downloadUi; private readonly FileDialogManager _fileDialogManager; - private readonly FileCacheManager _fileDbManager; + private readonly FileCacheManager _fileCacheManager; private readonly CompactUi _compactUi; private readonly UiShared _uiSharedComponent; private readonly Dalamud.Localization _localization; + private readonly FileReplacementFactory _fileReplacementFactory; public Plugin(DalamudPluginInterface pluginInterface, CommandManager commandManager, @@ -62,9 +63,10 @@ public sealed class Plugin : IDalamudPlugin _ipcManager = new IpcManager(_pluginInterface, _dalamudUtil); _fileDialogManager = new FileDialogManager(); - _fileDbManager = new FileCacheManager(_ipcManager, _configuration, _pluginInterface.ConfigDirectory.FullName); - _apiController = new ApiController(_configuration, _dalamudUtil, _fileDbManager); - _periodicFileScanner = new PeriodicFileScanner(_ipcManager, _configuration, _fileDbManager, _apiController, _dalamudUtil); + _fileCacheManager = new FileCacheManager(_ipcManager, _configuration, _pluginInterface.ConfigDirectory.FullName); + _apiController = new ApiController(_configuration, _dalamudUtil, _fileCacheManager); + _periodicFileScanner = new PeriodicFileScanner(_ipcManager, _configuration, _fileCacheManager, _apiController, _dalamudUtil); + _fileReplacementFactory = new FileReplacementFactory(_fileCacheManager, _ipcManager); _uiSharedComponent = new UiShared(_ipcManager, _apiController, _periodicFileScanner, _fileDialogManager, _configuration, _dalamudUtil, _pluginInterface, _localization); @@ -118,7 +120,7 @@ public sealed class Plugin : IDalamudPlugin _compactUi?.Dispose(); _periodicFileScanner?.Dispose(); - _fileDbManager?.Dispose(); + _fileCacheManager?.Dispose(); _playerManager?.Dispose(); _characterCacheManager?.Dispose(); _ipcManager?.Dispose(); @@ -180,13 +182,13 @@ public sealed class Plugin : IDalamudPlugin try { - _transientResourceManager = new TransientResourceManager(_ipcManager, _dalamudUtil); + _transientResourceManager = new TransientResourceManager(_ipcManager, _dalamudUtil, _fileReplacementFactory, _pluginInterface.ConfigDirectory.FullName); var characterCacheFactory = - new CharacterDataFactory(_dalamudUtil, _ipcManager, _transientResourceManager, _fileDbManager); + new CharacterDataFactory(_dalamudUtil, _ipcManager, _transientResourceManager, _fileReplacementFactory); _playerManager = new PlayerManager(_apiController, _ipcManager, characterCacheFactory, _dalamudUtil, _transientResourceManager, _periodicFileScanner); _characterCacheManager = new OnlinePlayerManager(_apiController, - _dalamudUtil, _ipcManager, _playerManager, _fileDbManager); + _dalamudUtil, _ipcManager, _playerManager, _fileCacheManager); } catch (Exception ex) {