diff --git a/MareSynchronos/Managers/CharacterCacheManager.cs b/MareSynchronos/Managers/CharacterCacheManager.cs index 71cf1b9..817673a 100644 --- a/MareSynchronos/Managers/CharacterCacheManager.cs +++ b/MareSynchronos/Managers/CharacterCacheManager.cs @@ -66,6 +66,8 @@ public class CharacterCacheManager : IDisposable public void Initialize() { + _onlineCachedPlayers.Clear(); + _apiController.CharacterReceived += ApiControllerOnCharacterReceived; _apiController.PairedClientOnline += ApiControllerOnPairedClientOnline; _apiController.PairedClientOffline += ApiControllerOnPairedClientOffline; @@ -150,6 +152,7 @@ public class CharacterCacheManager : IDisposable private void ApiControllerOnPairedClientOffline(object? sender, EventArgs e) { Logger.Debug("Player offline: " + sender!); + RestoreCharacter(_onlineCachedPlayers.SingleOrDefault(f => f.PlayerNameHash == (string)sender!)); _onlineCachedPlayers.RemoveAll(p => p.PlayerNameHash == ((string)sender!)); } @@ -237,9 +240,10 @@ public class CharacterCacheManager : IDisposable PluginLog.Error(ex, "error"); } } - private void RestoreCharacter(CachedPlayer character) + + private void RestoreCharacter(CachedPlayer? character) { - if (string.IsNullOrEmpty(character.PlayerName)) return; + if (character == null || string.IsNullOrEmpty(character.PlayerName)) return; Logger.Debug("Restoring state for " + character.PlayerName); _ipcManager.PenumbraRemoveTemporaryCollection(character.PlayerName); diff --git a/MareSynchronos/Managers/FileCacheManager.cs b/MareSynchronos/Managers/FileCacheManager.cs index f3f3b3d..fd0685a 100644 --- a/MareSynchronos/Managers/FileCacheManager.cs +++ b/MareSynchronos/Managers/FileCacheManager.cs @@ -15,12 +15,10 @@ namespace MareSynchronos.Managers { private readonly IpcManager _ipcManager; private readonly Configuration _pluginConfiguration; + private FileSystemWatcher? _cacheDirWatcher; + private FileSystemWatcher? _penumbraDirWatcher; private CancellationTokenSource? _scanCancellationTokenSource; private Task? _scanTask; - private FileSystemWatcher? _penumbraDirWatcher; - private FileSystemWatcher? _cacheDirWatcher; - public long FileCacheSize { get; set; } - public FileCacheManager(IpcManager ipcManager, Configuration pluginConfiguration) { Logger.Debug("Creating " + nameof(FileCacheManager)); @@ -34,10 +32,108 @@ namespace MareSynchronos.Managers && _pluginConfiguration.ClientSecret.ContainsKey(_pluginConfiguration.ApiUri) && !string.IsNullOrEmpty(_ipcManager.PenumbraModDirectory())) { + StartWatchers(); StartInitialScan(); } } + public long CurrentFileProgress { get; private set; } + public long FileCacheSize { get; set; } + public bool IsScanRunning => !_scanTask?.IsCompleted ?? false; + + public long TotalFiles { get; private set; } + + public string WatchedCacheDirectory => (_cacheDirWatcher?.EnableRaisingEvents ?? false) ? _cacheDirWatcher!.Path : "Not watched"; + + public string WatchedPenumbraDirectory => (_penumbraDirWatcher?.EnableRaisingEvents ?? false) ? _penumbraDirWatcher!.Path : "Not watched"; + + public FileCache Create(string file) + { + FileInfo fileInfo = new(file); + while (IsFileLocked(fileInfo)) + { + Thread.Sleep(100); + Logger.Debug("Waiting for file release " + fileInfo.FullName); + } + var sha1Hash = Crypto.GetFileHash(fileInfo.FullName); + return new FileCache() + { + Filepath = fileInfo.FullName.ToLower(), + Hash = sha1Hash, + LastModifiedDate = fileInfo.LastWriteTimeUtc.Ticks.ToString(), + }; + } + + public void Dispose() + { + Logger.Debug("Disposing " + nameof(FileCacheManager)); + + _cacheDirWatcher?.Dispose(); + _penumbraDirWatcher?.Dispose(); + _scanCancellationTokenSource?.Cancel(); + } + + public void StartInitialScan() + { + _scanCancellationTokenSource = new CancellationTokenSource(); + _scanTask = Task.Run(() => StartFileScan(_scanCancellationTokenSource.Token)); + } + + public void StartWatchers() + { + Logger.Debug("Starting File System Watchers"); + _penumbraDirWatcher?.Dispose(); + _cacheDirWatcher?.Dispose(); + + _penumbraDirWatcher = new FileSystemWatcher(_ipcManager.PenumbraModDirectory()!) + { + IncludeSubdirectories = true, + InternalBufferSize = 65536 + }; + _penumbraDirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; + //_penumbraDirWatcher.Created += OnCreated; + _penumbraDirWatcher.Deleted += OnDeleted; + _penumbraDirWatcher.Changed += OnModified; + _penumbraDirWatcher.Filters.Add("*.mtrl"); + _penumbraDirWatcher.Filters.Add("*.mdl"); + _penumbraDirWatcher.Filters.Add("*.tex"); + _penumbraDirWatcher.Error += (sender, args) => PluginLog.Error(args.GetException(), "Error in Penumbra Dir Watcher"); + _penumbraDirWatcher.EnableRaisingEvents = true; + + _cacheDirWatcher = new FileSystemWatcher(_pluginConfiguration.CacheFolder) + { + EnableRaisingEvents = true, + IncludeSubdirectories = true, + InternalBufferSize = 65536 + }; + _cacheDirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; + //_cacheDirWatcher.Created += OnCreated; + _cacheDirWatcher.Deleted += OnDeleted; + _cacheDirWatcher.Changed += OnModified; + _cacheDirWatcher.Filters.Add("*.mtrl"); + _cacheDirWatcher.Filters.Add("*.mdl"); + _cacheDirWatcher.Filters.Add("*.tex"); + _cacheDirWatcher.Error += + (sender, args) => PluginLog.Error(args.GetException(), "Error in Cache Dir Watcher"); + _cacheDirWatcher.EnableRaisingEvents = true; + + Task.Run(RecalculateFileCacheSize); + } + + private bool IsFileLocked(FileInfo file) + { + try + { + using var fs = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); + } + catch + { + return true; + } + + return false; + } + private void OnCreated(object sender, FileSystemEventArgs e) { var fi = new FileInfo(e.FullPath); @@ -48,14 +144,13 @@ namespace MareSynchronos.Managers Logger.Debug("File created: " + e.FullPath); try { - var createdFileCache = Create(fi.FullName); + var createdFileCache = Create(fi.FullName.ToLower()); db.Add(createdFileCache); } catch (FileLoadException) { Logger.Debug("File was still being written to."); } - } else { @@ -116,15 +211,21 @@ namespace MareSynchronos.Managers private void OnModified(object sender, FileSystemEventArgs e) { - Logger.Debug("OnModified: " + e.FullPath); var fi = new FileInfo(e.FullPath); - if (fi.Extension.ToLower() is not ".mdl" or ".tex" or ".mtrl") return; Logger.Debug("File changed: " + e.FullPath); using var db = new FileCacheContext(); var modifiedFile = Create(fi.FullName); - var fileInDb = db.FileCaches.SingleOrDefault(f => f.Filepath == fi.FullName.ToLower() || modifiedFile.Hash == f.Hash); - if (fileInDb == null) return; - db.Remove(fileInDb); + var fileInDb = db.FileCaches.SingleOrDefault(f => f.Filepath == fi.FullName.ToLower()); + if (fileInDb != null) + db.Remove(fileInDb); + else + { + var files = db.FileCaches.Where(f => f.Hash == modifiedFile.Hash); + foreach (var file in files) + { + if (!File.Exists(file.Filepath)) db.Remove(file.Filepath); + } + } db.Add(modifiedFile); db.SaveChanges(); @@ -133,26 +234,13 @@ namespace MareSynchronos.Managers Task.Run(RecalculateFileCacheSize); } } - - public long CurrentFileProgress { get; private set; } - public bool IsScanRunning => !_scanTask?.IsCompleted ?? false; - public long TotalFiles { get; private set; } - public string WatchedPenumbraDirectory => (!_penumbraDirWatcher?.EnableRaisingEvents ?? false) ? "Not watched" : _penumbraDirWatcher!.Path; - public string WatchedCacheDirectory => (!_cacheDirWatcher?.EnableRaisingEvents ?? false) ? "Not watched" : _cacheDirWatcher!.Path; - - public void Dispose() + private void RecalculateFileCacheSize() { - Logger.Debug("Disposing " + nameof(FileCacheManager)); - - _cacheDirWatcher?.Dispose(); - _penumbraDirWatcher?.Dispose(); - _scanCancellationTokenSource?.Cancel(); - } - - public void StartInitialScan() - { - _scanCancellationTokenSource = new CancellationTokenSource(); - _scanTask = Task.Run(() => StartFileScan(_scanCancellationTokenSource.Token)); + FileCacheSize = 0; + foreach (var file in Directory.EnumerateFiles(_pluginConfiguration.CacheFolder)) + { + FileCacheSize += new FileInfo(file).Length; + } } private async Task StartFileScan(CancellationToken ct) @@ -248,87 +336,6 @@ namespace MareSynchronos.Managers _pluginConfiguration.InitialScanComplete = true; _pluginConfiguration.Save(); } - - StartWatchers(); - } - - public void StartWatchers() - { - Logger.Debug("Starting File System Watchers"); - _penumbraDirWatcher?.Dispose(); - _cacheDirWatcher?.Dispose(); - - _penumbraDirWatcher = new FileSystemWatcher(_ipcManager.PenumbraModDirectory()!) - { - EnableRaisingEvents = true, - IncludeSubdirectories = true, - InternalBufferSize = 65536 - }; - _penumbraDirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; - _penumbraDirWatcher.Created += OnCreated; - _penumbraDirWatcher.Deleted += OnDeleted; - _penumbraDirWatcher.Changed += OnModified; - _penumbraDirWatcher.Filters.Add("*.mtrl"); - _penumbraDirWatcher.Filters.Add("*.mdl"); - _penumbraDirWatcher.Filters.Add("*.tex"); - _penumbraDirWatcher.Error += (sender, args) => PluginLog.Error(args.GetException(), "Error in Penumbra Dir Watcher"); - - _cacheDirWatcher = new FileSystemWatcher(_pluginConfiguration.CacheFolder) - { - EnableRaisingEvents = true, - IncludeSubdirectories = true, - InternalBufferSize = 65536 - }; - _cacheDirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; - _cacheDirWatcher.Created += OnCreated; - _cacheDirWatcher.Deleted += OnDeleted; - _cacheDirWatcher.Changed += OnModified; - _cacheDirWatcher.Filters.Add("*.mtrl"); - _cacheDirWatcher.Filters.Add("*.mdl"); - _cacheDirWatcher.Filters.Add("*.tex"); - _cacheDirWatcher.Error += - (sender, args) => PluginLog.Error(args.GetException(), "Error in Cache Dir Watcher"); - - Task.Run(RecalculateFileCacheSize); - } - - private void RecalculateFileCacheSize() - { - FileCacheSize = 0; - foreach (var file in Directory.EnumerateFiles(_pluginConfiguration.CacheFolder)) - { - FileCacheSize += new FileInfo(file).Length; - } - } - - private bool IsFileLocked(FileInfo file) - { - try - { - using var fs = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); - } - catch - { - return true; - } - - return false; - } - - public FileCache Create(string file) - { - FileInfo fileInfo = new(file); - if (IsFileLocked(fileInfo)) - { - throw new FileLoadException(); - } - var sha1Hash = Crypto.GetFileHash(fileInfo.FullName); - return new FileCache() - { - Filepath = fileInfo.FullName, - Hash = sha1Hash, - LastModifiedDate = fileInfo.LastWriteTimeUtc.Ticks.ToString(), - }; } } } diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 419e3ca..53143fa 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -28,12 +28,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - @@ -76,7 +71,7 @@ - Always + Never diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 0fb6a40..4749f3d 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -86,6 +86,7 @@ namespace MareSynchronos public void Dispose() { Logger.Debug("Disposing " + Name); + _apiController?.Dispose(); _commandManager.RemoveHandler(CommandName); _clientState.Login -= ClientState_Login; @@ -99,7 +100,6 @@ namespace MareSynchronos _ipcManager?.Dispose(); _characterManager?.Dispose(); _characterCacheManager.Dispose(); - _apiController?.Dispose(); _playerWatcher.Disable(); _playerWatcher.Dispose(); }