From e4a28c70ebcb236721d6d9c89d3ed56c7de8a811 Mon Sep 17 00:00:00 2001 From: cake Date: Sat, 15 Nov 2025 00:46:14 +0100 Subject: [PATCH] Warning cleansing process --- LightlessSync/FileCache/FileCompactor.cs | 53 +++++++++++-------- LightlessSync/Plugin.cs | 2 +- .../Services/BroadcastScanningService.cs | 45 ++++++++-------- LightlessSync/UI/EditProfileUi.cs | 39 +++++++------- LightlessSync/Utils/VariousExtensions.cs | 4 +- 5 files changed, 80 insertions(+), 63 deletions(-) diff --git a/LightlessSync/FileCache/FileCompactor.cs b/LightlessSync/FileCache/FileCompactor.cs index c321f94..c1a0532 100644 --- a/LightlessSync/FileCache/FileCompactor.cs +++ b/LightlessSync/FileCache/FileCompactor.cs @@ -85,7 +85,7 @@ public sealed partial class FileCompactor : IDisposable int workerId = i; _workers.Add(Task.Factory.StartNew( - () => ProcessQueueWorkerAsync(_compactionCts.Token, workerId), + () => ProcessQueueWorkerAsync(workerId, _compactionCts.Token), _compactionCts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap()); @@ -120,7 +120,8 @@ public sealed partial class FileCompactor : IDisposable var folder = _lightlessConfigService.Current.CacheFolder; 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"; return; } @@ -281,12 +282,15 @@ public sealed partial class FileCompactor : IDisposable 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); } 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); } } @@ -307,7 +311,8 @@ public sealed partial class FileCompactor : IDisposable } 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); @@ -323,7 +328,8 @@ public sealed partial class FileCompactor : IDisposable var fi = new FileInfo(filePath); 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; } @@ -338,7 +344,8 @@ public sealed partial class FileCompactor : IDisposable 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; } @@ -358,7 +365,8 @@ public sealed partial class FileCompactor : IDisposable } 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; } @@ -379,12 +387,14 @@ public sealed partial class FileCompactor : IDisposable } 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; } - _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); } /// @@ -1045,10 +1055,10 @@ public sealed partial class FileCompactor : IDisposable } /// - /// Process the queue with, meant for a worker/thread + /// Process the queue, meant for a worker/thread /// /// Cancellation token for the worker whenever it needs to be stopped - private async Task ProcessQueueWorkerAsync(CancellationToken token, int workerId) + private async Task ProcessQueueWorkerAsync(int workerId, CancellationToken token) { try { @@ -1139,17 +1149,18 @@ public sealed partial class FileCompactor : IDisposable { try { - if (_isWindows) - { - using var _ = new FileStream(winePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - } - else - { - using var _ = new FileStream(linuxPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - } + var pathToOpen = _isWindows ? winePath : linuxPath; + + if (string.IsNullOrEmpty(pathToOpen) || !File.Exists(pathToOpen)) + return false; + + using var _ = new FileStream(pathToOpen, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); return true; } - catch { return false; } + catch + { + return false; + } } /// diff --git a/LightlessSync/Plugin.cs b/LightlessSync/Plugin.cs index 01a4de4..19e2bd0 100644 --- a/LightlessSync/Plugin.cs +++ b/LightlessSync/Plugin.cs @@ -227,7 +227,7 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); - collection.AddSingleton(s => new BroadcastScannerService( s.GetRequiredService>(), clientState, objectTable, framework, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + collection.AddSingleton(s => new BroadcastScannerService(s.GetRequiredService>(), objectTable, framework, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); // add scoped services diff --git a/LightlessSync/Services/BroadcastScanningService.cs b/LightlessSync/Services/BroadcastScanningService.cs index 95abdae..f9bf5ee 100644 --- a/LightlessSync/Services/BroadcastScanningService.cs +++ b/LightlessSync/Services/BroadcastScanningService.cs @@ -1,14 +1,13 @@ using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Plugin.Services; using LightlessSync.API.Dto.User; -using LightlessSync.LightlessConfiguration; using LightlessSync.Services.Mediator; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; namespace LightlessSync.Services; -public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDisposable +public class BroadcastScannerService : DisposableMediatorSubscriberBase { private readonly ILogger _logger; private readonly IObjectTable _objectTable; @@ -17,20 +16,19 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos private readonly BroadcastService _broadcastService; private readonly NameplateHandler _nameplateHandler; - private readonly ConcurrentDictionary _broadcastCache = new(); + private readonly ConcurrentDictionary _broadcastCache = new(StringComparer.Ordinal); private readonly Queue _lookupQueue = new(); - private readonly HashSet _lookupQueuedCids = new(); - private readonly HashSet _syncshellCids = new(); + private readonly HashSet _lookupQueuedCids = []; + private readonly HashSet _syncshellCids = []; private static readonly TimeSpan MaxAllowedTtl = TimeSpan.FromMinutes(4); private static readonly TimeSpan RetryDelay = TimeSpan.FromMinutes(1); private readonly CancellationTokenSource _cleanupCts = new(); - private Task? _cleanupTask; + private readonly Task? _cleanupTask; private readonly int _checkEveryFrames = 20; private int _frameCounter = 0; - private int _lookupsThisFrame = 0; private const int MaxLookupsPerFrame = 30; 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 BroadcastScannerService(ILogger logger, - IClientState clientState, IObjectTable objectTable, IFramework framework, BroadcastService broadcastService, LightlessMediator mediator, - NameplateHandler nameplateHandler, - DalamudUtilService dalamudUtil, - LightlessConfigService configService) : base(logger, mediator) + NameplateHandler nameplateHandler) : base(logger, mediator) { _logger = logger; _objectTable = objectTable; @@ -69,7 +64,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos public void Update() { _frameCounter++; - _lookupsThisFrame = 0; + var lookupsThisFrame = 0; if (!_broadcastService.IsBroadcasting) return; @@ -91,12 +86,12 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos if (_frameCounter % _checkEveryFrames == 0 && _lookupQueue.Count > 0) { var cidsToLookup = new List(); - while (_lookupQueue.Count > 0 && _lookupsThisFrame < MaxLookupsPerFrame) + while (_lookupQueue.Count > 0 && lookupsThisFrame < MaxLookupsPerFrame) { var cid = _lookupQueue.Dequeue(); _lookupQueuedCids.Remove(cid); cidsToLookup.Add(cid); - _lookupsThisFrame++; + lookupsThisFrame++; } if (cidsToLookup.Count > 0 && !_batchRunning) @@ -156,7 +151,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos var newSet = _broadcastCache .Where(e => e.Value.IsBroadcasting && e.Value.ExpiryTime > now && !string.IsNullOrEmpty(e.Value.GID)) .Select(e => e.Key) - .ToHashSet(); + .ToHashSet(StringComparer.Ordinal); if (!_syncshellCids.SetEquals(newSet)) { @@ -172,7 +167,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos { var now = DateTime.UtcNow; - return _broadcastCache + return [.. _broadcastCache .Where(e => e.Value.IsBroadcasting && e.Value.ExpiryTime > now && !string.IsNullOrEmpty(e.Value.GID)) .Select(e => new BroadcastStatusInfoDto { @@ -180,8 +175,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos IsBroadcasting = true, TTL = e.Value.ExpiryTime - now, GID = e.Value.GID - }) - .ToList(); + })]; } private async Task ExpiredBroadcastCleanupLoop() @@ -192,7 +186,7 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos { while (!token.IsCancellationRequested) { - await Task.Delay(TimeSpan.FromSeconds(10), token); + await Task.Delay(TimeSpan.FromSeconds(10), token).ConfigureAwait(false); var now = DateTime.UtcNow; 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) { _logger.LogError(ex, "Broadcast cleanup loop crashed"); @@ -235,8 +232,14 @@ public class BroadcastScannerService : DisposableMediatorSubscriberBase, IDispos { base.Dispose(disposing); _framework.Update -= OnFrameworkUpdate; + if (_cleanupTask != null) + { + _cleanupTask?.Wait(100, _cleanupCts.Token); + } + _cleanupCts.Cancel(); - _cleanupTask?.Wait(100); + _cleanupCts.Dispose(); + _nameplateHandler.Uninit(); } } diff --git a/LightlessSync/UI/EditProfileUi.cs b/LightlessSync/UI/EditProfileUi.cs index ef64f7f..0588797 100644 --- a/LightlessSync/UI/EditProfileUi.cs +++ b/LightlessSync/UI/EditProfileUi.cs @@ -41,8 +41,8 @@ public class EditProfileUi : WindowMediatorSubscriberBase private Vector4 textColor; private Vector4 glowColor; - private record VanityState(bool TextEnabled, bool GlowEnabled, Vector4 TextColor, Vector4 GlowColor); - private VanityState _savedVanity; + private sealed record VanityState(bool TextEnabled, bool GlowEnabled, Vector4 TextColor, Vector4 GlowColor); + private VanityState? _savedVanity; public EditProfileUi(ILogger logger, LightlessMediator mediator, ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager, @@ -189,25 +189,28 @@ public class EditProfileUi : WindowMediatorSubscriberBase if (!success) return; _ = Task.Run(async () => { - var fileContent = File.ReadAllBytes(file); - using MemoryStream ms = new(fileContent); - var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false); - if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase)) + var fileContent = await File.ReadAllBytesAsync(file).ConfigureAwait(false); + MemoryStream ms = new(fileContent); + await using (ms.ConfigureAwait(false)) { - _showFileDialogError = true; - return; - } - using var image = Image.Load(fileContent); + var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false); + if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase)) + { + _showFileDialogError = true; + return; + } + using var image = Image.Load(fileContent); - if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024)) - { - _showFileDialogError = true; - return; - } + if (image.Width > 256 || image.Height > 256 || (fileContent.Length > 250 * 1024)) + { + _showFileDialogError = true; + return; + } - _showFileDialogError = false; - await _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, Convert.ToBase64String(fileContent), BannerPictureBase64: null, Description: null, Tags: null)) - .ConfigureAwait(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)) + .ConfigureAwait(false); + } }); }); } diff --git a/LightlessSync/Utils/VariousExtensions.cs b/LightlessSync/Utils/VariousExtensions.cs index 9215893..41e3cad 100644 --- a/LightlessSync/Utils/VariousExtensions.cs +++ b/LightlessSync/Utils/VariousExtensions.cs @@ -114,9 +114,9 @@ public static class VariousExtensions .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))) .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(); - 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(); logger.LogTrace("[BASE-{appbase}] ExistingFace: {of}, NewFace: {fc}; ExistingHair: {eh}, NewHair: {nh}; ExistingTail: {et}, NewTail: {nt}; ExistingTransient: {etr}, NewTransient: {ntr}", applicationBase,