Slight cleanup and autoformat.

This commit is contained in:
Ottermandias 2025-10-22 21:56:16 +02:00
parent c8cf560fc1
commit cbedc878b9
5 changed files with 66 additions and 40 deletions

View file

@ -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;

View file

@ -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<ResidentResourceManager>();
_services.GetService<ResourceManagerService>(); // Initialize because not required anywhere else.
_modManager = _services.GetService<ModManager>();
_fileWatcher = _services.GetService<FileWatcher>();
_collectionManager = _services.GetService<CollectionManager>();
_tempCollections = _services.GetService<TempCollectionManager>();
_redrawService = _services.GetService<RedrawService>();

View file

@ -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<string> _queue;
private readonly CancellationTokenSource _cts = new();
private readonly Task _consumer;
private readonly ConcurrentDictionary<string, byte> _pending = new(StringComparer.OrdinalIgnoreCase);
private readonly ModImportManager _modImportManager;
private readonly MessageService _messageService;
private readonly Configuration _config;
private readonly FileSystemWatcher _fsw;
private readonly Channel<string> _queue;
private readonly CancellationTokenSource _cts = new();
private readonly Task _consumer;
private readonly ConcurrentDictionary<string, byte> _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<string>(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<bool> 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();
}
}

View file

@ -20,7 +20,6 @@ namespace Penumbra.Services;
public class InstallNotification(string message, Action<bool> installRequest) : IMessage
{
private readonly Action<bool> _installRequest = installRequest;
private bool _invoked = false;
public string Message { get; } = message;
@ -33,7 +32,7 @@ public class InstallNotification(string message, Action<bool> installRequest) :
{
if (ImUtf8.ButtonEx("Install"u8, "Install this mod."u8, disabled: _invoked))
{
_installRequest(true);
installRequest(true);
_invoked = true;
}
}

View file

@ -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();
}