Warning cleansing process

This commit is contained in:
cake
2025-11-15 00:46:14 +01:00
parent acc8593996
commit e4a28c70eb
5 changed files with 80 additions and 63 deletions

View File

@@ -85,7 +85,7 @@ public sealed partial class FileCompactor : IDisposable
int workerId = i; int workerId = i;
_workers.Add(Task.Factory.StartNew( _workers.Add(Task.Factory.StartNew(
() => ProcessQueueWorkerAsync(_compactionCts.Token, workerId), () => ProcessQueueWorkerAsync(workerId, _compactionCts.Token),
_compactionCts.Token, _compactionCts.Token,
TaskCreationOptions.LongRunning, TaskCreationOptions.LongRunning,
TaskScheduler.Default).Unwrap()); TaskScheduler.Default).Unwrap());
@@ -120,7 +120,8 @@ public sealed partial class FileCompactor : IDisposable
var folder = _lightlessConfigService.Current.CacheFolder; var folder = _lightlessConfigService.Current.CacheFolder;
if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder)) if (string.IsNullOrWhiteSpace(folder) || !Directory.Exists(folder))
{ {
_logger.LogWarning("Filecompacator couldnt find your Cache folder: {folder}", folder); if (_logger.IsEnabled(LogLevel.Warning))
_logger.LogWarning("Filecompacator couldnt find your Cache folder: {folder}", folder);
Progress = "0/0"; Progress = "0/0";
return; return;
} }
@@ -281,12 +282,15 @@ public sealed partial class FileCompactor : IDisposable
return (flowControl: false, value: blocks * 512L); return (flowControl: false, value: blocks * 512L);
} }
_logger.LogDebug("Btrfs size probe failed for {linux} (exit {code}). stdout='{so}' stderr='{se}'. Falling back to Length.", linuxPath, res.code, outTrim, (res.se ?? "").Trim()); if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug("Btrfs size probe failed for {linux} (exit {code}). stdout='{so}' stderr='{se}'. Falling back to Length.", linuxPath, res.code, outTrim, (res.se ?? "").Trim());
return (flowControl: false, value: fileInfo.Length); return (flowControl: false, value: fileInfo.Length);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogDebug(ex, "Failed Btrfs size probe for {file}, using Length", fileInfo.FullName); if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug(ex, "Failed Btrfs size probe for {file}, using Length", fileInfo.FullName);
return (flowControl: true, value: fileInfo.Length); return (flowControl: true, value: fileInfo.Length);
} }
} }
@@ -307,7 +311,8 @@ public sealed partial class FileCompactor : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName); if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName);
} }
return (flowControl: true, value: default); return (flowControl: true, value: default);
@@ -323,7 +328,8 @@ public sealed partial class FileCompactor : IDisposable
var fi = new FileInfo(filePath); var fi = new FileInfo(filePath);
if (!fi.Exists) if (!fi.Exists)
{ {
_logger.LogTrace("[W{worker}] Skip compaction: missing {file}", workerId, filePath); if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("[W{worker}] Skip compaction: missing {file}", workerId, filePath);
return; return;
} }
@@ -338,7 +344,8 @@ public sealed partial class FileCompactor : IDisposable
if (oldSize < minSizeBytes) if (oldSize < minSizeBytes)
{ {
_logger.LogTrace("[W{worker}] Skip compaction: {file} ({size} B) < threshold ({th} B)", workerId, filePath, oldSize, minSizeBytes); if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("[W{worker}] Skip compaction: {file} ({size} B) < threshold ({th} B)", workerId, filePath, oldSize, minSizeBytes);
return; return;
} }
@@ -358,7 +365,8 @@ public sealed partial class FileCompactor : IDisposable
} }
else else
{ {
_logger.LogTrace("[W{worker}] Already NTFS-compressed with XPRESS8K: {file}", workerId, filePath); if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("[W{worker}] Already NTFS-compressed with XPRESS8K: {file}", workerId, filePath);
} }
return; return;
} }
@@ -379,12 +387,14 @@ public sealed partial class FileCompactor : IDisposable
} }
else else
{ {
_logger.LogTrace("[W{worker}] Already Btrfs-compressed with clzo: {file}", workerId, filePath); if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("[W{worker}] Already Btrfs-compressed with clzo: {file}", workerId, filePath);
} }
return; return;
} }
_logger.LogTrace("[W{worker}] Skip compact: unsupported FS for {file}", workerId, filePath); if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("[W{worker}] Skip compact: unsupported FS for {file}", workerId, filePath);
} }
/// <summary> /// <summary>
@@ -1045,10 +1055,10 @@ public sealed partial class FileCompactor : IDisposable
} }
/// <summary> /// <summary>
/// Process the queue with, meant for a worker/thread /// Process the queue, meant for a worker/thread
/// </summary> /// </summary>
/// <param name="token">Cancellation token for the worker whenever it needs to be stopped</param> /// <param name="token">Cancellation token for the worker whenever it needs to be stopped</param>
private async Task ProcessQueueWorkerAsync(CancellationToken token, int workerId) private async Task ProcessQueueWorkerAsync(int workerId, CancellationToken token)
{ {
try try
{ {
@@ -1139,17 +1149,18 @@ public sealed partial class FileCompactor : IDisposable
{ {
try try
{ {
if (_isWindows) var pathToOpen = _isWindows ? winePath : linuxPath;
{
using var _ = new FileStream(winePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); if (string.IsNullOrEmpty(pathToOpen) || !File.Exists(pathToOpen))
} return false;
else
{ using var _ = new FileStream(pathToOpen, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var _ = new FileStream(linuxPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
return true; return true;
} }
catch { return false; } catch
{
return false;
}
} }
/// <summary> /// <summary>

View File

@@ -227,7 +227,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<ConfigurationMigrator>(); collection.AddSingleton<ConfigurationMigrator>();
collection.AddSingleton<ConfigurationSaveService>(); collection.AddSingleton<ConfigurationSaveService>();
collection.AddSingleton<HubFactory>(); collection.AddSingleton<HubFactory>();
collection.AddSingleton(s => new BroadcastScannerService( s.GetRequiredService<ILogger<BroadcastScannerService>>(), clientState, objectTable, framework, s.GetRequiredService<BroadcastService>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<NameplateHandler>(), s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<LightlessConfigService>())); collection.AddSingleton(s => new BroadcastScannerService(s.GetRequiredService<ILogger<BroadcastScannerService>>(), objectTable, framework, s.GetRequiredService<BroadcastService>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<NameplateHandler>()));
// add scoped services // add scoped services

View File

@@ -1,14 +1,13 @@
using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using LightlessSync.API.Dto.User; using LightlessSync.API.Dto.User;
using LightlessSync.LightlessConfiguration;
using LightlessSync.Services.Mediator; using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace LightlessSync.Services; namespace LightlessSync.Services;
public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDisposable public class BroadcastScannerService : DisposableMediatorSubscriberBase
{ {
private readonly ILogger<BroadcastScannerService> _logger; private readonly ILogger<BroadcastScannerService> _logger;
private readonly IObjectTable _objectTable; private readonly IObjectTable _objectTable;
@@ -17,20 +16,19 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
private readonly BroadcastService _broadcastService; private readonly BroadcastService _broadcastService;
private readonly NameplateHandler _nameplateHandler; private readonly NameplateHandler _nameplateHandler;
private readonly ConcurrentDictionary<string, BroadcastEntry> _broadcastCache = new(); private readonly ConcurrentDictionary<string, BroadcastEntry> _broadcastCache = new(StringComparer.Ordinal);
private readonly Queue<string> _lookupQueue = new(); private readonly Queue<string> _lookupQueue = new();
private readonly HashSet<string> _lookupQueuedCids = new(); private readonly HashSet<string> _lookupQueuedCids = [];
private readonly HashSet<string> _syncshellCids = new(); private readonly HashSet<string> _syncshellCids = [];
private static readonly TimeSpan MaxAllowedTtl = TimeSpan.FromMinutes(4); private static readonly TimeSpan MaxAllowedTtl = TimeSpan.FromMinutes(4);
private static readonly TimeSpan RetryDelay = TimeSpan.FromMinutes(1); private static readonly TimeSpan RetryDelay = TimeSpan.FromMinutes(1);
private readonly CancellationTokenSource _cleanupCts = new(); private readonly CancellationTokenSource _cleanupCts = new();
private Task? _cleanupTask; private readonly Task? _cleanupTask;
private readonly int _checkEveryFrames = 20; private readonly int _checkEveryFrames = 20;
private int _frameCounter = 0; private int _frameCounter = 0;
private int _lookupsThisFrame = 0;
private const int MaxLookupsPerFrame = 30; private const int MaxLookupsPerFrame = 30;
private const int MaxQueueSize = 100; private const int MaxQueueSize = 100;
@@ -40,14 +38,11 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
public readonly record struct BroadcastEntry(bool IsBroadcasting, DateTime ExpiryTime, string? GID); public readonly record struct BroadcastEntry(bool IsBroadcasting, DateTime ExpiryTime, string? GID);
public BroadcastScannerService(ILogger<BroadcastScannerService> logger, public BroadcastScannerService(ILogger<BroadcastScannerService> logger,
IClientState clientState,
IObjectTable objectTable, IObjectTable objectTable,
IFramework framework, IFramework framework,
BroadcastService broadcastService, BroadcastService broadcastService,
LightlessMediator mediator, LightlessMediator mediator,
NameplateHandler nameplateHandler, NameplateHandler nameplateHandler) : base(logger, mediator)
DalamudUtilService dalamudUtil,
LightlessConfigService configService) : base(logger, mediator)
{ {
_logger = logger; _logger = logger;
_objectTable = objectTable; _objectTable = objectTable;
@@ -69,7 +64,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
public void Update() public void Update()
{ {
_frameCounter++; _frameCounter++;
_lookupsThisFrame = 0; var lookupsThisFrame = 0;
if (!_broadcastService.IsBroadcasting) if (!_broadcastService.IsBroadcasting)
return; return;
@@ -91,12 +86,12 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
if (_frameCounter % _checkEveryFrames == 0 && _lookupQueue.Count > 0) if (_frameCounter % _checkEveryFrames == 0 && _lookupQueue.Count > 0)
{ {
var cidsToLookup = new List<string>(); var cidsToLookup = new List<string>();
while (_lookupQueue.Count > 0 && _lookupsThisFrame < MaxLookupsPerFrame) while (_lookupQueue.Count > 0 && lookupsThisFrame < MaxLookupsPerFrame)
{ {
var cid = _lookupQueue.Dequeue(); var cid = _lookupQueue.Dequeue();
_lookupQueuedCids.Remove(cid); _lookupQueuedCids.Remove(cid);
cidsToLookup.Add(cid); cidsToLookup.Add(cid);
_lookupsThisFrame++; lookupsThisFrame++;
} }
if (cidsToLookup.Count > 0 && !_batchRunning) if (cidsToLookup.Count > 0 && !_batchRunning)
@@ -156,7 +151,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
var newSet = _broadcastCache var newSet = _broadcastCache
.Where(e => e.Value.IsBroadcasting && e.Value.ExpiryTime > now && !string.IsNullOrEmpty(e.Value.GID)) .Where(e => e.Value.IsBroadcasting && e.Value.ExpiryTime > now && !string.IsNullOrEmpty(e.Value.GID))
.Select(e => e.Key) .Select(e => e.Key)
.ToHashSet(); .ToHashSet(StringComparer.Ordinal);
if (!_syncshellCids.SetEquals(newSet)) if (!_syncshellCids.SetEquals(newSet))
{ {
@@ -172,7 +167,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
{ {
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
return _broadcastCache return [.. _broadcastCache
.Where(e => e.Value.IsBroadcasting && e.Value.ExpiryTime > now && !string.IsNullOrEmpty(e.Value.GID)) .Where(e => e.Value.IsBroadcasting && e.Value.ExpiryTime > now && !string.IsNullOrEmpty(e.Value.GID))
.Select(e => new BroadcastStatusInfoDto .Select(e => new BroadcastStatusInfoDto
{ {
@@ -180,8 +175,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
IsBroadcasting = true, IsBroadcasting = true,
TTL = e.Value.ExpiryTime - now, TTL = e.Value.ExpiryTime - now,
GID = e.Value.GID GID = e.Value.GID
}) })];
.ToList();
} }
private async Task ExpiredBroadcastCleanupLoop() private async Task ExpiredBroadcastCleanupLoop()
@@ -192,7 +186,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
{ {
while (!token.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
await Task.Delay(TimeSpan.FromSeconds(10), token); await Task.Delay(TimeSpan.FromSeconds(10), token).ConfigureAwait(false);
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
foreach (var (cid, entry) in _broadcastCache.ToArray()) foreach (var (cid, entry) in _broadcastCache.ToArray())
@@ -202,7 +196,10 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
} }
} }
} }
catch (OperationCanceledException) { } catch (OperationCanceledException)
{
// No action needed when cancelled
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Broadcast cleanup loop crashed"); _logger.LogError(ex, "Broadcast cleanup loop crashed");
@@ -235,8 +232,14 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos
{ {
base.Dispose(disposing); base.Dispose(disposing);
_framework.Update -= OnFrameworkUpdate; _framework.Update -= OnFrameworkUpdate;
if (_cleanupTask != null)
{
_cleanupTask?.Wait(100, _cleanupCts.Token);
}
_cleanupCts.Cancel(); _cleanupCts.Cancel();
_cleanupTask?.Wait(100); _cleanupCts.Dispose();
_nameplateHandler.Uninit(); _nameplateHandler.Uninit();
} }
} }

View File

@@ -41,8 +41,8 @@ public class EditProfileUi : WindowMediatorSubscriberBase
private Vector4 textColor; private Vector4 textColor;
private Vector4 glowColor; private Vector4 glowColor;
private record VanityState(bool TextEnabled, bool GlowEnabled, Vector4 TextColor, Vector4 GlowColor); private sealed record VanityState(bool TextEnabled, bool GlowEnabled, Vector4 TextColor, Vector4 GlowColor);
private VanityState _savedVanity; private VanityState? _savedVanity;
public EditProfileUi(ILogger<EditProfileUi> logger, LightlessMediator mediator, public EditProfileUi(ILogger<EditProfileUi> logger, LightlessMediator mediator,
ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager, ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager,
@@ -189,25 +189,28 @@ public class EditProfileUi : WindowMediatorSubscriberBase
if (!success) return; if (!success) return;
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
var fileContent = File.ReadAllBytes(file); var fileContent = await File.ReadAllBytesAsync(file).ConfigureAwait(false);
using MemoryStream ms = new(fileContent); MemoryStream ms = new(fileContent);
var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false); await using (ms.ConfigureAwait(false))
if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase))
{ {
_showFileDialogError = true; var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false);
return; if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase))
} {
using var image = Image.Load<Rgba32>(fileContent); _showFileDialogError = true;
return;
}
using var image = Image.Load<Rgba32>(fileContent);
if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024)) if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024))
{ {
_showFileDialogError = true; _showFileDialogError = true;
return; return;
} }
_showFileDialogError = false; _showFileDialogError = false;
await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), BannerPictureBase64: null, Description: null, Tags: null)) await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), BannerPictureBase64: null, Description: null, Tags: null))
.ConfigureAwait(false); .ConfigureAwait(false);
}
}); });
}); });
} }

View File

@@ -114,9 +114,9 @@ public static class VariousExtensions
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var newTail = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase))) var newTail = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var existingTransients = existingFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl"))) var existingTransients = existingFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl", StringComparison.Ordinal) && !g.EndsWith("tex", StringComparison.Ordinal) && !g.EndsWith("mtrl", StringComparison.Ordinal)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
var newTransients = newFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl"))) var newTransients = newFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl", StringComparison.Ordinal) && !g.EndsWith("tex", StringComparison.Ordinal) && !g.EndsWith("mtrl", StringComparison.Ordinal)))
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList(); .OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
logger.LogTrace("[BASE-{appbase}] ExistingFace: {of}, NewFace: {fc}; ExistingHair: {eh}, NewHair: {nh}; ExistingTail: {et}, NewTail: {nt}; ExistingTransient: {etr}, NewTransient: {ntr}", applicationBase, logger.LogTrace("[BASE-{appbase}] ExistingFace: {of}, NewFace: {fc}; ExistingHair: {eh}, NewHair: {nh}; ExistingTail: {et}, NewTail: {nt}; ExistingTransient: {etr}, NewTransient: {ntr}", applicationBase,