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

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