From cbedc878b94ceda8cc91105d5b2456b76bda2fdb Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 22 Oct 2025 21:56:16 +0200 Subject: [PATCH] Slight cleanup and autoformat. --- Penumbra/Configuration.cs | 2 +- Penumbra/Penumbra.cs | 2 - Penumbra/Services/FileWatcher.cs | 91 +++++++++++++++++++---------- Penumbra/Services/MessageService.cs | 3 +- Penumbra/UI/Tabs/SettingsTab.cs | 8 +-- 5 files changed, 66 insertions(+), 40 deletions(-) diff --git a/Penumbra/Configuration.cs b/Penumbra/Configuration.cs index e337997b..2991230e 100644 --- a/Penumbra/Configuration.cs +++ b/Penumbra/Configuration.cs @@ -53,7 +53,7 @@ public class Configuration : IPluginConfiguration, ISavable, IService public string ModDirectory { get; set; } = string.Empty; public string ExportDirectory { get; set; } = string.Empty; - public string WatchDirectory { get; set; } = string.Empty; + public string WatchDirectory { get; set; } = string.Empty; public bool? UseCrashHandler { get; set; } = null; public bool OpenWindowAtStart { get; set; } = false; diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 8ed2c585..d433a0fb 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -43,7 +43,6 @@ public class Penumbra : IDalamudPlugin private readonly TempModManager _tempMods; private readonly TempCollectionManager _tempCollections; private readonly ModManager _modManager; - private readonly FileWatcher _fileWatcher; private readonly CollectionManager _collectionManager; private readonly Configuration _config; private readonly CharacterUtility _characterUtility; @@ -81,7 +80,6 @@ public class Penumbra : IDalamudPlugin _residentResources = _services.GetService(); _services.GetService(); // Initialize because not required anywhere else. _modManager = _services.GetService(); - _fileWatcher = _services.GetService(); _collectionManager = _services.GetService(); _tempCollections = _services.GetService(); _redrawService = _services.GetService(); diff --git a/Penumbra/Services/FileWatcher.cs b/Penumbra/Services/FileWatcher.cs index e7172f58..141825f5 100644 --- a/Penumbra/Services/FileWatcher.cs +++ b/Penumbra/Services/FileWatcher.cs @@ -1,40 +1,41 @@ using System.Threading.Channels; using OtterGui.Services; -using Penumbra.Services; using Penumbra.Mods.Manager; namespace Penumbra.Services; + public class FileWatcher : IDisposable, IService { - private readonly FileSystemWatcher _fsw; - private readonly Channel _queue; - private readonly CancellationTokenSource _cts = new(); - private readonly Task _consumer; - private readonly ConcurrentDictionary _pending = new(StringComparer.OrdinalIgnoreCase); - private readonly ModImportManager _modImportManager; - private readonly MessageService _messageService; - private readonly Configuration _config; + private readonly FileSystemWatcher _fsw; + private readonly Channel _queue; + private readonly CancellationTokenSource _cts = new(); + private readonly Task _consumer; + private readonly ConcurrentDictionary _pending = new(StringComparer.OrdinalIgnoreCase); + private readonly ModImportManager _modImportManager; + private readonly MessageService _messageService; + private readonly Configuration _config; public FileWatcher(ModImportManager modImportManager, MessageService messageService, Configuration config) { _modImportManager = modImportManager; - _messageService = messageService; - _config = config; + _messageService = messageService; + _config = config; - if (!_config.EnableDirectoryWatch) return; + if (!_config.EnableDirectoryWatch) + return; _queue = Channel.CreateBounded(new BoundedChannelOptions(256) { SingleReader = true, SingleWriter = false, - FullMode = BoundedChannelFullMode.DropOldest + FullMode = BoundedChannelFullMode.DropOldest, }); _fsw = new FileSystemWatcher(_config.WatchDirectory) { IncludeSubdirectories = false, - NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime, - InternalBufferSize = 32 * 1024 + NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime, + InternalBufferSize = 32 * 1024, }; // Only wake us for the exact patterns we care about @@ -56,13 +57,17 @@ public class FileWatcher : IDisposable, IService private void OnPath(object? sender, FileSystemEventArgs e) { // Cheap de-dupe: only queue once per filename until processed - if (!_config.EnableDirectoryWatch || !_pending.TryAdd(e.FullPath, 0)) return; + if (!_config.EnableDirectoryWatch || !_pending.TryAdd(e.FullPath, 0)) + return; + _ = _queue.Writer.TryWrite(e.FullPath); } private async Task ConsumerLoopAsync(CancellationToken token) { - if (!_config.EnableDirectoryWatch) return; + if (!_config.EnableDirectoryWatch) + return; + var reader = _queue.Reader; while (await reader.WaitToReadAsync(token).ConfigureAwait(false)) { @@ -72,7 +77,10 @@ public class FileWatcher : IDisposable, IService { await ProcessOneAsync(path, token).ConfigureAwait(false); } - catch (OperationCanceledException) { Penumbra.Log.Debug($"[FileWatcher] Canceled via Token."); } + catch (OperationCanceledException) + { + Penumbra.Log.Debug($"[FileWatcher] Canceled via Token."); + } catch (Exception ex) { Penumbra.Log.Debug($"[FileWatcher] Error during Processing: {ex}"); @@ -90,15 +98,19 @@ public class FileWatcher : IDisposable, IService // Downloads often finish via rename; file may be locked briefly. // Wait until it exists and is readable; also require two stable size checks. const int maxTries = 40; - long lastLen = -1; + long lastLen = -1; - for (int i = 0; i < maxTries && !token.IsCancellationRequested; i++) + for (var i = 0; i < maxTries && !token.IsCancellationRequested; i++) { - if (!File.Exists(path)) { await Task.Delay(100, token); continue; } + if (!File.Exists(path)) + { + await Task.Delay(100, token); + continue; + } try { - var fi = new FileInfo(path); + var fi = new FileInfo(path); var len = fi.Length; if (len > 0 && len == lastLen) { @@ -112,7 +124,9 @@ public class FileWatcher : IDisposable, IService var invoked = false; Action installRequest = args => { - if (invoked) return; + if (invoked) + return; + invoked = true; _modImportManager.AddUnpack(path); }; @@ -122,13 +136,19 @@ public class FileWatcher : IDisposable, IService installRequest); return; - } + } } lastLen = len; } - catch (IOException) { Penumbra.Log.Debug($"[FileWatcher] File is still being written to."); } - catch (UnauthorizedAccessException) { Penumbra.Log.Debug($"[FileWatcher] File is locked."); } + catch (IOException) + { + Penumbra.Log.Debug($"[FileWatcher] File is still being written to."); + } + catch (UnauthorizedAccessException) + { + Penumbra.Log.Debug($"[FileWatcher] File is locked."); + } await Task.Delay(150, token); } @@ -136,21 +156,32 @@ public class FileWatcher : IDisposable, IService public void UpdateDirectory(string newPath) { - if (!_config.EnableDirectoryWatch || _fsw is null || !Directory.Exists(newPath) || string.IsNullOrWhiteSpace(newPath)) return; + if (!_config.EnableDirectoryWatch || _fsw is null || !Directory.Exists(newPath) || string.IsNullOrWhiteSpace(newPath)) + return; _fsw.EnableRaisingEvents = false; - _fsw.Path = newPath; + _fsw.Path = newPath; _fsw.EnableRaisingEvents = true; } public void Dispose() { - if (!_config.EnableDirectoryWatch) return; + if (!_config.EnableDirectoryWatch) + return; + _fsw.EnableRaisingEvents = false; _cts.Cancel(); _fsw.Dispose(); _queue.Writer.TryComplete(); - try { _consumer.Wait(TimeSpan.FromSeconds(5)); } catch { /* swallow */ } + try + { + _consumer.Wait(TimeSpan.FromSeconds(5)); + } + catch + { + /* swallow */ + } + _cts.Dispose(); } } diff --git a/Penumbra/Services/MessageService.cs b/Penumbra/Services/MessageService.cs index 6c13fc38..3dc6a90c 100644 --- a/Penumbra/Services/MessageService.cs +++ b/Penumbra/Services/MessageService.cs @@ -20,7 +20,6 @@ namespace Penumbra.Services; public class InstallNotification(string message, Action installRequest) : IMessage { - private readonly Action _installRequest = installRequest; private bool _invoked = false; public string Message { get; } = message; @@ -33,7 +32,7 @@ public class InstallNotification(string message, Action installRequest) : { if (ImUtf8.ButtonEx("Install"u8, "Install this mod."u8, disabled: _invoked)) { - _installRequest(true); + installRequest(true); _invoked = true; } } diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index 217b6788..46f4d38f 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -53,7 +53,6 @@ public class SettingsTab : ITab, IUiService private readonly MigrationSectionDrawer _migrationDrawer; private readonly CollectionAutoSelector _autoSelector; private readonly CleanupService _cleanupService; - private readonly MessageService _messageService; private readonly AttributeHook _attributeHook; private readonly PcpService _pcpService; @@ -70,7 +69,7 @@ public class SettingsTab : ITab, IUiService CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, FileWatcher fileWatcher, HttpApi httpApi, DalamudSubstitutionProvider dalamudSubstitutionProvider, FileCompactor compactor, DalamudConfigService dalamudConfig, IDataManager gameData, PredefinedTagManager predefinedTagConfig, CrashHandlerService crashService, - MigrationSectionDrawer migrationDrawer, CollectionAutoSelector autoSelector, CleanupService cleanupService, MessageService messageService, + MigrationSectionDrawer migrationDrawer, CollectionAutoSelector autoSelector, CleanupService cleanupService, AttributeHook attributeHook, PcpService pcpService) { _pluginInterface = pluginInterface; @@ -97,7 +96,6 @@ public class SettingsTab : ITab, IUiService _migrationDrawer = migrationDrawer; _autoSelector = autoSelector; _cleanupService = cleanupService; - _messageService = messageService; _attributeHook = attributeHook; _pcpService = pcpService; } @@ -652,10 +650,10 @@ public class SettingsTab : ITab, IUiService DrawPcpFolder(); DrawDefaultModExportPath(); Checkbox("Enable Directory Watcher", - "Enables a File Watcher that automatically listens for Mod files that enter, causing Penumbra to open a Popup to import these mods.", + "Enables a File Watcher that automatically listens for Mod files that enter a specified directory, causing Penumbra to open a popup to import these mods.", _config.EnableDirectoryWatch, v => _config.EnableDirectoryWatch = v); Checkbox("Enable Fully Automatic Import", - "Uses the File Watcher in order to not just open a Popup, but fully automatically import new mods.", + "Uses the File Watcher in order to skip the query popup and automatically import any new mods.", _config.EnableAutomaticModImport, v => _config.EnableAutomaticModImport = v); DrawFileWatcherPath(); }