diff --git a/Glamourer.Api b/Glamourer.Api index 7e8505c..59a7ab5 160000 --- a/Glamourer.Api +++ b/Glamourer.Api @@ -1 +1 @@ -Subproject commit 7e8505cd6f8dbc5bcf41b72e16785d62b4d218f3 +Subproject commit 59a7ab5fa9941eb754757b62e4cb189e455e9514 diff --git a/MareSynchronos/FileCache/CacheMonitor.cs b/MareSynchronos/FileCache/CacheMonitor.cs index 194dbb0..e8bfde5 100644 --- a/MareSynchronos/FileCache/CacheMonitor.cs +++ b/MareSynchronos/FileCache/CacheMonitor.cs @@ -1,4 +1,5 @@ -using MareSynchronos.Interop.Ipc; +using System; +using MareSynchronos.Interop.Ipc; using MareSynchronos.MareConfiguration; using MareSynchronos.Services; using MareSynchronos.Services.Mediator; @@ -606,14 +607,35 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase protected override void Dispose(bool disposing) { base.Dispose(disposing); - _scanCancellationTokenSource?.Cancel(); + try + { + _scanCancellationTokenSource.Cancel(); + } + catch (ObjectDisposedException) + { + } + _scanCancellationTokenSource.Dispose(); PenumbraWatcher?.Dispose(); MareWatcher?.Dispose(); SubstWatcher?.Dispose(); - _penumbraFswCts?.CancelDispose(); - _mareFswCts?.CancelDispose(); - _substFswCts?.CancelDispose(); - _periodicCalculationTokenSource?.CancelDispose(); + TryCancelAndDispose(_penumbraFswCts); + TryCancelAndDispose(_mareFswCts); + TryCancelAndDispose(_substFswCts); + TryCancelAndDispose(_periodicCalculationTokenSource); + } + + private static void TryCancelAndDispose(CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + cts.Dispose(); } private void FullFileScan(CancellationToken ct) @@ -856,4 +878,4 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase StartPenumbraWatcher(penumbraDir); } } -} \ No newline at end of file +} diff --git a/MareSynchronos/FileCache/FileCompactor.cs b/MareSynchronos/FileCache/FileCompactor.cs index b48c516..6203191 100644 --- a/MareSynchronos/FileCache/FileCompactor.cs +++ b/MareSynchronos/FileCache/FileCompactor.cs @@ -12,7 +12,7 @@ public sealed class FileCompactor private readonly Dictionary _clusterSizes; - private readonly WOF_FILE_COMPRESSION_INFO_V1 _efInfo; + private readonly WofFileCompressionInfoV1 _efInfo; private readonly ILogger _logger; private readonly MareConfigService _mareConfigService; @@ -24,7 +24,7 @@ public sealed class FileCompactor _logger = logger; _mareConfigService = mareConfigService; _dalamudUtilService = dalamudUtilService; - _efInfo = new WOF_FILE_COMPRESSION_INFO_V1 + _efInfo = new WofFileCompressionInfoV1 { Algorithm = CompressionAlgorithm.XPRESS8K, Flags = 0 @@ -123,7 +123,7 @@ public sealed class FileCompactor out uint lpTotalNumberOfClusters); [DllImport("WoFUtil.dll")] - private static extern int WofIsExternalFile([MarshalAs(UnmanagedType.LPWStr)] string Filepath, out int IsExternalFile, out uint Provider, out WOF_FILE_COMPRESSION_INFO_V1 Info, ref uint BufferLength); + private static extern int WofIsExternalFile([MarshalAs(UnmanagedType.LPWStr)] string Filepath, out int IsExternalFile, out uint Provider, out WofFileCompressionInfoV1 Info, ref uint BufferLength); [DllImport("WofUtil.dll")] private static extern int WofSetFileDataLocation(IntPtr FileHandle, ulong Provider, IntPtr ExternalFileInfo, ulong Length); @@ -242,9 +242,9 @@ public sealed class FileCompactor } [StructLayout(LayoutKind.Sequential)] - private struct WOF_FILE_COMPRESSION_INFO_V1 + private struct WofFileCompressionInfoV1 { public CompressionAlgorithm Algorithm; public ulong Flags; } -} \ No newline at end of file +} diff --git a/MareSynchronos/Interop/GameChatHooks.cs b/MareSynchronos/Interop/GameChatHooks.cs index 02c5a22..dbcf4e2 100644 --- a/MareSynchronos/Interop/GameChatHooks.cs +++ b/MareSynchronos/Interop/GameChatHooks.cs @@ -17,8 +17,8 @@ namespace MareSynchronos.Interop; public record ChatChannelOverride { - public string ChannelName = string.Empty; - public Action? ChatMessageHandler; + public string ChannelName { get; set; } = string.Empty; + public Action? ChatMessageHandler { get; set; } } public unsafe sealed class GameChatHooks : IDisposable diff --git a/MareSynchronos/Interop/GameModel/MdlFile.cs b/MareSynchronos/Interop/GameModel/MdlFile.cs index c77d5e3..6358f85 100644 --- a/MareSynchronos/Interop/GameModel/MdlFile.cs +++ b/MareSynchronos/Interop/GameModel/MdlFile.cs @@ -31,11 +31,11 @@ public class MdlFile public ushort Unknown9; // Offsets are stored relative to RuntimeSize instead of file start. - public uint[] VertexOffset = [0, 0, 0]; - public uint[] IndexOffset = [0, 0, 0]; + public uint[] VertexOffset; + public uint[] IndexOffset; - public uint[] VertexBufferSize = [0, 0, 0]; - public uint[] IndexBufferSize = [0, 0, 0]; + public uint[] VertexBufferSize; + public uint[] IndexBufferSize; public byte LodCount; public bool EnableIndexBufferStreaming; public bool EnableEdgeGeometry; @@ -43,15 +43,26 @@ public class MdlFile public ModelFlags1 Flags1; public ModelFlags2 Flags2; - public VertexDeclarationStruct[] VertexDeclarations = []; - public ElementIdStruct[] ElementIds = []; - public MeshStruct[] Meshes = []; - public BoundingBoxStruct[] BoneBoundingBoxes = []; - public LodStruct[] Lods = []; - public ExtraLodStruct[] ExtraLods = []; + public VertexDeclarationStruct[] VertexDeclarations; + public ElementIdStruct[] ElementIds; + public MeshStruct[] Meshes; + public BoundingBoxStruct[] BoneBoundingBoxes; + public LodStruct[] Lods; + public ExtraLodStruct[] ExtraLods; public MdlFile(string filePath) { + VertexOffset = Array.Empty(); + IndexOffset = Array.Empty(); + VertexBufferSize = Array.Empty(); + IndexBufferSize = Array.Empty(); + VertexDeclarations = Array.Empty(); + ElementIds = Array.Empty(); + Meshes = Array.Empty(); + BoneBoundingBoxes = Array.Empty(); + Lods = Array.Empty(); + ExtraLods = Array.Empty(); + using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); using var r = new LuminaBinaryReader(stream); @@ -256,4 +267,4 @@ public class MdlFile } } } -#pragma warning restore S1104 // Fields should not have public accessibility \ No newline at end of file +#pragma warning restore S1104 // Fields should not have public accessibility diff --git a/MareSynchronos/Interop/Ipc/IpcProvider.cs b/MareSynchronos/Interop/Ipc/IpcProvider.cs index 1dd01e2..5ad3da9 100644 --- a/MareSynchronos/Interop/Ipc/IpcProvider.cs +++ b/MareSynchronos/Interop/Ipc/IpcProvider.cs @@ -1,4 +1,5 @@ using Dalamud.Game.ClientState.Objects.Types; +using System; using Dalamud.Plugin; using Dalamud.Plugin.Ipc; using MareSynchronos.MareConfiguration; @@ -29,7 +30,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber private bool _marePluginEnabled = false; private bool _impersonating = false; private DateTime _unregisterTime = DateTime.UtcNow; - private CancellationTokenSource _registerDelayCts = new(); + private CancellationTokenSource? _registerDelayCts = new(); public bool MarePluginEnabled => _marePluginEnabled; public bool ImpersonationActive => _impersonating; @@ -100,7 +101,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber { if (_mareConfig.Current.MareAPI) { - var cancelToken = _registerDelayCts.Token; + var cancelToken = EnsureFreshCts(ref _registerDelayCts).Token; Task.Run(async () => { // Wait before registering to reduce the chance of a race condition @@ -125,7 +126,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber } else { - _registerDelayCts = _registerDelayCts.CancelRecreate(); + EnsureFreshCts(ref _registerDelayCts); if (_impersonating) { _loadFileProviderMare?.UnregisterFunc(); @@ -146,7 +147,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber _loadFileAsyncProvider?.UnregisterFunc(); _handledGameAddresses?.UnregisterFunc(); - _registerDelayCts.Cancel(); + TryCancel(_registerDelayCts); if (_impersonating) { _loadFileProviderMare?.UnregisterFunc(); @@ -155,6 +156,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber } Mediator.UnsubscribeAll(this); + CancelAndDispose(ref _registerDelayCts); return Task.CompletedTask; } @@ -193,4 +195,31 @@ public class IpcProvider : IHostedService, IMediatorSubscriber return _activeGameObjectHandlers.Where(g => g.Address != nint.Zero).Select(g => g.Address).Distinct().ToList(); } + + private static CancellationTokenSource EnsureFreshCts(ref CancellationTokenSource? cts) + { + CancelAndDispose(ref cts); + cts = new CancellationTokenSource(); + return cts; + } + + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + TryCancel(cts); + cts.Dispose(); + cts = null; + } + + private static void TryCancel(CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + } } diff --git a/MareSynchronos/Interop/Ipc/RedrawManager.cs b/MareSynchronos/Interop/Ipc/RedrawManager.cs index e8240fc..0089510 100644 --- a/MareSynchronos/Interop/Ipc/RedrawManager.cs +++ b/MareSynchronos/Interop/Ipc/RedrawManager.cs @@ -1,19 +1,20 @@ using Dalamud.Game.ClientState.Objects.Types; +using System; using MareSynchronos.PlayerData.Handlers; using MareSynchronos.Services; using MareSynchronos.Services.Mediator; -using MareSynchronos.Utils; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; namespace MareSynchronos.Interop.Ipc; -public class RedrawManager +public class RedrawManager : IDisposable { private readonly MareMediator _mareMediator; private readonly DalamudUtilService _dalamudUtil; private readonly ConcurrentDictionary _penumbraRedrawRequests = []; - private CancellationTokenSource _disposalCts = new(); + private CancellationTokenSource? _disposalCts = new(); + private bool _disposed; public SemaphoreSlim RedrawSemaphore { get; init; } = new(2, 2); @@ -32,12 +33,12 @@ public class RedrawManager try { using CancellationTokenSource cancelToken = new CancellationTokenSource(); - using CancellationTokenSource combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, token, _disposalCts.Token); + using CancellationTokenSource combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancelToken.Token, token, EnsureFreshCts(ref _disposalCts).Token); var combinedToken = combinedCts.Token; cancelToken.CancelAfter(TimeSpan.FromSeconds(15)); await handler.ActOnFrameworkAfterEnsureNoDrawAsync(action, combinedToken).ConfigureAwait(false); - if (!_disposalCts.Token.IsCancellationRequested) + if (!_disposalCts!.Token.IsCancellationRequested) await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, handler, applicationId, 30000, combinedToken).ConfigureAwait(false); } finally @@ -49,6 +50,45 @@ public class RedrawManager internal void Cancel() { - _disposalCts = _disposalCts.CancelRecreate(); + EnsureFreshCts(ref _disposalCts); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) return; + if (disposing) + { + CancelAndDispose(ref _disposalCts); + } + + _disposed = true; + } + + private static CancellationTokenSource EnsureFreshCts(ref CancellationTokenSource? cts) + { + CancelAndDispose(ref cts); + cts = new CancellationTokenSource(); + return cts; + } + + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + cts.Dispose(); + cts = null; } } diff --git a/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs b/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs index 0fadae8..9d35df6 100644 --- a/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs @@ -5,7 +5,6 @@ using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services; using MareSynchronos.Services.Mediator; -using MareSynchronos.Services.ServerConfiguration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -23,7 +22,6 @@ public class PairHandlerFactory private readonly ILoggerFactory _loggerFactory; private readonly MareMediator _mareMediator; private readonly PlayerPerformanceService _playerPerformanceService; - private readonly ServerConfigurationManager _serverConfigManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly PairAnalyzerFactory _pairAnalyzerFactory; private readonly VisibilityService _visibilityService; @@ -32,7 +30,7 @@ public class PairHandlerFactory FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService, PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime, FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService, - ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory, + PairAnalyzerFactory pairAnalyzerFactory, MareConfigService configService, VisibilityService visibilityService) { _loggerFactory = loggerFactory; @@ -45,7 +43,6 @@ public class PairHandlerFactory _fileCacheManager = fileCacheManager; _mareMediator = mareMediator; _playerPerformanceService = playerPerformanceService; - _serverConfigManager = serverConfigManager; _pairAnalyzerFactory = pairAnalyzerFactory; _configService = configService; _visibilityService = visibilityService; @@ -55,6 +52,6 @@ public class PairHandlerFactory { return new PairHandler(_loggerFactory.CreateLogger(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory, _ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime, - _fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager, _configService, _visibilityService); + _fileCacheManager, _mareMediator, _playerPerformanceService, _configService, _visibilityService); } -} \ No newline at end of file +} diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 26e3aee..22a0e61 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -7,7 +7,6 @@ using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services; using MareSynchronos.Services.Events; using MareSynchronos.Services.Mediator; -using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Utils; using MareSynchronos.WebAPI.Files; using Microsoft.Extensions.Hosting; @@ -29,7 +28,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase private readonly GameObjectHandlerFactory _gameObjectHandlerFactory; private readonly IpcManager _ipcManager; private readonly PlayerPerformanceService _playerPerformanceService; - private readonly ServerConfigurationManager _serverConfigManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly VisibilityService _visibilityService; private CancellationTokenSource? _applicationCancellationTokenSource = new(); @@ -53,7 +51,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, FileCacheManager fileDbManager, MareMediator mediator, PlayerPerformanceService playerPerformanceService, - ServerConfigurationManager serverConfigManager, MareConfigService configService, VisibilityService visibilityService) : base(logger, mediator) { Pair = pair; @@ -65,7 +62,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _dalamudUtil = dalamudUtil; _fileDbManager = fileDbManager; _playerPerformanceService = playerPerformanceService; - _serverConfigManager = serverConfigManager; _configService = configService; _visibilityService = visibilityService; @@ -887,4 +883,4 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase Logger.LogDebug("[BASE-{appBase}] ModdedPaths calculated in {time}ms, missing files: {count}, total files: {total}", applicationBase, st.ElapsedMilliseconds, missingFiles.Count, moddedDictionary.Keys.Count); return [.. missingFiles]; } -} \ No newline at end of file +} diff --git a/MareSynchronos/PlayerData/Pairs/Pair.cs b/MareSynchronos/PlayerData/Pairs/Pair.cs index 1ca50bc..7994bef 100644 --- a/MareSynchronos/PlayerData/Pairs/Pair.cs +++ b/MareSynchronos/PlayerData/Pairs/Pair.cs @@ -270,6 +270,20 @@ public class Pair : DisposableMediatorSubscriberBase } } + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + try + { + _applicationCts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + _applicationCts.Dispose(); + } + public void SetNote(string note) { _serverConfigurationManager.SetNoteForUid(UserData.UID, note); @@ -368,4 +382,4 @@ public class Pair : DisposableMediatorSubscriberBase return data; } -} \ No newline at end of file +} diff --git a/MareSynchronos/Services/AutoDetect/DiscoveryConfigProvider.cs b/MareSynchronos/Services/AutoDetect/DiscoveryConfigProvider.cs index ffd921d..7ad985e 100644 --- a/MareSynchronos/Services/AutoDetect/DiscoveryConfigProvider.cs +++ b/MareSynchronos/Services/AutoDetect/DiscoveryConfigProvider.cs @@ -17,7 +17,6 @@ public class DiscoveryConfigProvider private readonly TokenProvider _tokenProvider; private WellKnownRoot? _config; - private DateTimeOffset _lastLoad = DateTimeOffset.MinValue; public DiscoveryConfigProvider(ILogger logger, ServerConfigurationManager serverManager, TokenProvider tokenProvider) { @@ -51,7 +50,6 @@ public class DiscoveryConfigProvider root.NearbyDiscovery?.Hydrate(); _config = root; - _lastLoad = DateTimeOffset.UtcNow; _logger.LogDebug("Loaded Nearby well-known (stapled), enabled={enabled}, expires={exp}", NearbyEnabled, _config?.NearbyDiscovery?.SaltExpiresAt); return true; } @@ -97,7 +95,6 @@ public class DiscoveryConfigProvider root.NearbyDiscovery?.Hydrate(); _config = root; - _lastLoad = DateTimeOffset.UtcNow; _logger.LogInformation("Loaded Nearby well-known (http {path}), enabled={enabled}", path, NearbyEnabled); return true; } diff --git a/MareSynchronos/Services/AutoDetect/NearbyDiscoveryService.cs b/MareSynchronos/Services/AutoDetect/NearbyDiscoveryService.cs index 82e9a06..6baa786 100644 --- a/MareSynchronos/Services/AutoDetect/NearbyDiscoveryService.cs +++ b/MareSynchronos/Services/AutoDetect/NearbyDiscoveryService.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; using MareSynchronos.Services.Mediator; @@ -52,6 +53,7 @@ public class NearbyDiscoveryService : IHostedService, IMediatorSubscriber public Task StartAsync(CancellationToken cancellationToken) { + CancelAndDispose(ref _loopCts); _loopCts = new CancellationTokenSource(); _mediator.Subscribe(this, _ => { _isConnected = true; _configProvider.TryLoadFromStapled(); }); _mediator.Subscribe(this, _ => { _isConnected = false; _lastPublishedSignature = null; }); @@ -128,10 +130,25 @@ public class NearbyDiscoveryService : IHostedService, IMediatorSubscriber public Task StopAsync(CancellationToken cancellationToken) { _mediator.UnsubscribeAll(this); - try { _loopCts?.Cancel(); } catch { } + CancelAndDispose(ref _loopCts); return Task.CompletedTask; } + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + cts.Dispose(); + cts = null; + } + private async Task Loop(CancellationToken ct) { _configProvider.TryLoadFromStapled(); diff --git a/MareSynchronos/Services/AutoDetect/NearbyPendingService.cs b/MareSynchronos/Services/AutoDetect/NearbyPendingService.cs index 28a079f..b0c45f4 100644 --- a/MareSynchronos/Services/AutoDetect/NearbyPendingService.cs +++ b/MareSynchronos/Services/AutoDetect/NearbyPendingService.cs @@ -15,8 +15,9 @@ public sealed class NearbyPendingService : IMediatorSubscriber private readonly ApiController _api; private readonly AutoDetectRequestService _requestService; private readonly ConcurrentDictionary _pending = new(StringComparer.Ordinal); - private static readonly Regex ReqRegex = new(@"^Nearby Request: (.+) \[(?[A-Z0-9]+)\]$", RegexOptions.Compiled); - private static readonly Regex AcceptRegex = new(@"^Nearby Accept: (.+) \[(?[A-Z0-9]+)\]$", RegexOptions.Compiled); + private static readonly TimeSpan RegexTimeout = TimeSpan.FromSeconds(1); + private static readonly Regex ReqRegex = new(@"^Nearby Request: .+ \[(?[A-Z0-9]+)\]$", RegexOptions.Compiled | RegexOptions.ExplicitCapture, RegexTimeout); + private static readonly Regex AcceptRegex = new(@"^Nearby Accept: .+ \[(?[A-Z0-9]+)\]$", RegexOptions.Compiled | RegexOptions.ExplicitCapture, RegexTimeout); public NearbyPendingService(ILogger logger, MareMediator mediator, ApiController api, AutoDetectRequestService requestService) { diff --git a/MareSynchronos/Services/CharaData/CharaDataNearbyManager.cs b/MareSynchronos/Services/CharaData/CharaDataNearbyManager.cs index a7ed9fb..4182148 100644 --- a/MareSynchronos/Services/CharaData/CharaDataNearbyManager.cs +++ b/MareSynchronos/Services/CharaData/CharaDataNearbyManager.cs @@ -28,7 +28,7 @@ public sealed class CharaDataNearbyManager : DisposableMediatorSubscriberBase private Task? _filterEntriesRunningTask; private (Guid VfxId, PoseEntryExtended Pose)? _hoveredVfx = null; private DateTime _lastExecutionTime = DateTime.UtcNow; - private SemaphoreSlim _sharedDataUpdateSemaphore = new(1, 1); + private readonly SemaphoreSlim _sharedDataUpdateSemaphore = new(1, 1); public CharaDataNearbyManager(ILogger logger, MareMediator mediator, DalamudUtilService dalamudUtilService, VfxSpawnManager vfxSpawnManager, ServerConfigurationManager serverConfigurationManager, diff --git a/MareSynchronos/Services/CharacterAnalyzer.cs b/MareSynchronos/Services/CharacterAnalyzer.cs index 9f4ca7a..65b2cbd 100644 --- a/MareSynchronos/Services/CharacterAnalyzer.cs +++ b/MareSynchronos/Services/CharacterAnalyzer.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using Lumina.Data.Files; using MareSynchronos.API.Data; using MareSynchronos.API.Data.Enum; @@ -16,7 +17,7 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase private readonly FileCacheManager _fileCacheManager; private readonly XivDataAnalyzer _xivDataAnalyzer; private CancellationTokenSource? _analysisCts; - private CancellationTokenSource _baseAnalysisCts = new(); + private CancellationTokenSource? _baseAnalysisCts = new(); private string _lastDataHash = string.Empty; private CharacterAnalysisSummary _previousSummary = CharacterAnalysisSummary.Empty; private DateTime _lastAutoAnalysis = DateTime.MinValue; @@ -34,8 +35,8 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase { Mediator.Subscribe(this, (msg) => { - _baseAnalysisCts = _baseAnalysisCts.CancelRecreate(); - var token = _baseAnalysisCts.Token; + var tokenSource = EnsureFreshCts(ref _baseAnalysisCts); + var token = tokenSource.Token; _ = BaseAnalysis(msg.CharacterData, token); }); _fileCacheManager = fileCacheManager; @@ -51,17 +52,15 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase public void CancelAnalyze() { - _analysisCts?.CancelDispose(); - _analysisCts = null; + CancelAndDispose(ref _analysisCts); } public async Task ComputeAnalysis(bool print = true, bool recalculate = false) { Logger.LogDebug("=== Calculating Character Analysis ==="); - _analysisCts = _analysisCts?.CancelRecreate() ?? new(); - - var cancelToken = _analysisCts.Token; + var analysisCts = EnsureFreshCts(ref _analysisCts); + var cancelToken = analysisCts.Token; var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList(); if (allFiles.Exists(c => !c.IsComputed || recalculate)) @@ -103,8 +102,7 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase LastCompletedAnalysis = DateTime.UtcNow; } - _analysisCts.CancelDispose(); - _analysisCts = null; + CancelAndDispose(ref _analysisCts); if (print) PrintAnalysis(); } @@ -115,8 +113,8 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase if (!disposing) return; - _analysisCts?.CancelDispose(); - _baseAnalysisCts.CancelDispose(); + CancelAndDispose(ref _analysisCts); + CancelAndDispose(ref _baseAnalysisCts); } private async Task BaseAnalysis(CharacterData charaData, CancellationToken token) @@ -366,6 +364,7 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase } } + [StructLayout(LayoutKind.Auto)] public readonly record struct CharacterAnalysisSummary(int TotalFiles, long TotalOriginalSize, long TotalCompressedSize, long TotalTriangles, bool HasUncomputedEntries) { public static CharacterAnalysisSummary Empty => new(); @@ -418,4 +417,26 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase } }); } + + private static CancellationTokenSource EnsureFreshCts(ref CancellationTokenSource? cts) + { + CancelAndDispose(ref cts); + cts = new CancellationTokenSource(); + return cts; + } + + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + cts.Dispose(); + cts = null; + } } diff --git a/MareSynchronos/Services/GuiHookService.cs b/MareSynchronos/Services/GuiHookService.cs index dfa533f..a973a2c 100644 --- a/MareSynchronos/Services/GuiHookService.cs +++ b/MareSynchronos/Services/GuiHookService.cs @@ -16,37 +16,26 @@ namespace MareSynchronos.Services; public class GuiHookService : DisposableMediatorSubscriberBase { - private readonly ILogger _logger; private readonly DalamudUtilService _dalamudUtil; private readonly MareConfigService _configService; private readonly INamePlateGui _namePlateGui; private readonly IGameConfig _gameConfig; private readonly IPartyList _partyList; private readonly PairManager _pairManager; - private readonly IClientState _clientState; - private readonly ApiController _apiController; - private readonly TypingIndicatorStateService _typingStateService; - - private static readonly TimeSpan TypingDisplayTime = TimeSpan.FromSeconds(2); private bool _isModified = false; private bool _namePlateRoleColorsEnabled = false; public GuiHookService(ILogger logger, DalamudUtilService dalamudUtil, MareMediator mediator, MareConfigService configService, - INamePlateGui namePlateGui, IGameConfig gameConfig, IPartyList partyList, PairManager pairManager, ApiController apiController, - IClientState clientState, TypingIndicatorStateService typingStateService) + INamePlateGui namePlateGui, IGameConfig gameConfig, IPartyList partyList, PairManager pairManager) : base(logger, mediator) { - _logger = logger; _dalamudUtil = dalamudUtil; _configService = configService; _namePlateGui = namePlateGui; _gameConfig = gameConfig; _partyList = partyList; _pairManager = pairManager; - _apiController = apiController; - _clientState = clientState; - _typingStateService = typingStateService; _namePlateGui.OnNamePlateUpdate += OnNamePlateUpdate; _namePlateGui.RequestRedraw(); @@ -60,18 +49,13 @@ public class GuiHookService : DisposableMediatorSubscriberBase public void RequestRedraw(bool force = false) { var useColors = _configService.Current.UseNameColors; - var showTyping = _configService.Current.TypingIndicatorShowOnNameplates; - if (!useColors && !showTyping) + if (!useColors) { if (!_isModified && !force) return; _isModified = false; } - else if (!useColors) - { - _isModified = false; - } _ = Task.Run(async () => { await _dalamudUtil.RunOnFrameworkThread(() => _namePlateGui.RequestRedraw()).ConfigureAwait(false); @@ -91,8 +75,7 @@ public class GuiHookService : DisposableMediatorSubscriberBase private void OnNamePlateUpdate(INamePlateUpdateContext context, IReadOnlyList handlers) { var applyColors = _configService.Current.UseNameColors; - var showTypingIndicator = _configService.Current.TypingIndicatorShowOnNameplates; - if (!applyColors && !showTypingIndicator) + if (!applyColors) return; var visibleUsers = _pairManager.GetOnlineUserPairs().Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue); @@ -105,11 +88,6 @@ public class GuiHookService : DisposableMediatorSubscriberBase for (int i = 0; i < _partyList.Count; ++i) partyMembers[i] = _partyList[i]?.GameObject?.Address ?? nint.MaxValue; - var now = DateTime.UtcNow; - var activeTypers = _typingStateService.GetActiveTypers(TypingDisplayTime); - var selfTypingActive = showTypingIndicator && _typingStateService.TryGetSelfTyping(TypingDisplayTime, out _, out _); - var localPlayerAddress = selfTypingActive ? _clientState.LocalPlayer?.Address ?? nint.Zero : nint.Zero; - foreach (var handler in handlers) { if (handler != null && visibleUsersIds.Contains(handler.GameObjectId)) diff --git a/MareSynchronos/Services/MareProfileManager.cs b/MareSynchronos/Services/MareProfileManager.cs index 6bc3c50..3013cb5 100644 --- a/MareSynchronos/Services/MareProfileManager.cs +++ b/MareSynchronos/Services/MareProfileManager.cs @@ -2,7 +2,6 @@ using MareSynchronos.API.Data.Comparer; using MareSynchronos.MareConfiguration; using MareSynchronos.Services.Mediator; -using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.WebAPI; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; @@ -15,7 +14,6 @@ public class MareProfileManager : MediatorSubscriberBase private const string _nsfw = "Profile not displayed - NSFW"; private readonly ApiController _apiController; private readonly MareConfigService _mareConfigService; - private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ConcurrentDictionary _mareProfiles = new(UserDataComparer.Instance); private readonly MareProfileData _defaultProfileData = new(IsFlagged: false, IsNSFW: false, string.Empty, _noDescription); @@ -23,11 +21,10 @@ public class MareProfileManager : MediatorSubscriberBase private readonly MareProfileData _nsfwProfileData = new(IsFlagged: false, IsNSFW: false, string.Empty, _nsfw); public MareProfileManager(ILogger logger, MareConfigService mareConfigService, - MareMediator mediator, ApiController apiController, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator) + MareMediator mediator, ApiController apiController) : base(logger, mediator) { _mareConfigService = mareConfigService; _apiController = apiController; - _serverConfigurationManager = serverConfigurationManager; Mediator.Subscribe(this, (msg) => { @@ -75,4 +72,4 @@ public class MareProfileManager : MediatorSubscriberBase _mareProfiles[data] = _defaultProfileData; } } -} \ No newline at end of file +} diff --git a/MareSynchronos/Services/Mediator/MareMediator.cs b/MareSynchronos/Services/Mediator/MareMediator.cs index ebd0617..99f66ae 100644 --- a/MareSynchronos/Services/Mediator/MareMediator.cs +++ b/MareSynchronos/Services/Mediator/MareMediator.cs @@ -7,7 +7,7 @@ using System.Text; namespace MareSynchronos.Services.Mediator; -public sealed class MareMediator : IHostedService +public sealed class MareMediator : IHostedService, IDisposable { private readonly Lock _addRemoveLock = new(); private readonly ConcurrentDictionary _lastErrorTime = []; @@ -109,6 +109,12 @@ public sealed class MareMediator : IHostedService } } + public void Dispose() + { + _loopCts.Cancel(); + _loopCts.Dispose(); + } + public void SubscribeKeyed(IMediatorSubscriber subscriber, string key, Action action) where T : MessageBase { lock (_addRemoveLock) @@ -219,4 +225,4 @@ public sealed class MareMediator : IHostedService public object Action { get; } public IMediatorSubscriber Subscriber { get; } } -} \ No newline at end of file +} diff --git a/MareSynchronos/Services/PairAnalyzer.cs b/MareSynchronos/Services/PairAnalyzer.cs index f8f8267..9373b6d 100644 --- a/MareSynchronos/Services/PairAnalyzer.cs +++ b/MareSynchronos/Services/PairAnalyzer.cs @@ -1,4 +1,5 @@ -using MareSynchronos.API.Data; +using System; +using MareSynchronos.API.Data; using MareSynchronos.API.Data.Enum; using MareSynchronos.FileCache; using MareSynchronos.PlayerData.Pairs; @@ -14,7 +15,7 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase private readonly FileCacheManager _fileCacheManager; private readonly XivDataAnalyzer _xivDataAnalyzer; private CancellationTokenSource? _analysisCts; - private CancellationTokenSource _baseAnalysisCts = new(); + private CancellationTokenSource? _baseAnalysisCts = new(); private string _lastDataHash = string.Empty; public PairAnalyzer(ILogger logger, Pair pair, MareMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer) @@ -24,8 +25,8 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase #if DEBUG Mediator.SubscribeKeyed(this, pair.UserData.UID, (msg) => { - _baseAnalysisCts = _baseAnalysisCts.CancelRecreate(); - var token = _baseAnalysisCts.Token; + var tokenSource = EnsureFreshCts(ref _baseAnalysisCts); + var token = tokenSource.Token; if (msg.CharacterData != null) { _ = BaseAnalysis(msg.CharacterData, token); @@ -56,17 +57,15 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase public void CancelAnalyze() { - _analysisCts?.CancelDispose(); - _analysisCts = null; + CancelAndDispose(ref _analysisCts); } public async Task ComputeAnalysis(bool print = true, bool recalculate = false) { Logger.LogDebug("=== Calculating Character Analysis ==="); - _analysisCts = _analysisCts?.CancelRecreate() ?? new(); - - var cancelToken = _analysisCts.Token; + var analysisCts = EnsureFreshCts(ref _analysisCts); + var cancelToken = analysisCts.Token; var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList(); if (allFiles.Exists(c => !c.IsComputed || recalculate)) @@ -102,8 +101,7 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase LastPlayerName = Pair.PlayerName ?? string.Empty; Mediator.Publish(new PairDataAnalyzedMessage(Pair.UserData.UID)); - _analysisCts.CancelDispose(); - _analysisCts = null; + CancelAndDispose(ref _analysisCts); if (print) PrintAnalysis(); } @@ -114,8 +112,8 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase if (!disposing) return; - _analysisCts?.CancelDispose(); - _baseAnalysisCts.CancelDispose(); + CancelAndDispose(ref _analysisCts); + CancelAndDispose(ref _baseAnalysisCts); } private async Task BaseAnalysis(CharacterData charaData, CancellationToken token) @@ -211,4 +209,26 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.OriginalSize))), UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.CompressedSize)))); } -} \ No newline at end of file + + private static CancellationTokenSource EnsureFreshCts(ref CancellationTokenSource? cts) + { + CancelAndDispose(ref cts); + cts = new CancellationTokenSource(); + return cts; + } + + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + cts.Dispose(); + cts = null; + } +} diff --git a/MareSynchronos/Services/PartyListTypingService.cs b/MareSynchronos/Services/PartyListTypingService.cs index b894648..21314a5 100644 --- a/MareSynchronos/Services/PartyListTypingService.cs +++ b/MareSynchronos/Services/PartyListTypingService.cs @@ -21,7 +21,6 @@ public class PartyListTypingService : DisposableMediatorSubscriberBase private readonly PairManager _pairManager; private readonly TypingIndicatorStateService _typingStateService; private static readonly TimeSpan TypingDisplayTime = TimeSpan.FromSeconds(2); - private static readonly TimeSpan TypingDisplayDelay = TimeSpan.FromMilliseconds(500); private static readonly TimeSpan TypingDisplayFade = TypingDisplayTime; public PartyListTypingService(ILogger logger, diff --git a/MareSynchronos/Services/PlayerPerformanceService.cs b/MareSynchronos/Services/PlayerPerformanceService.cs index fed2792..f779c48 100644 --- a/MareSynchronos/Services/PlayerPerformanceService.cs +++ b/MareSynchronos/Services/PlayerPerformanceService.cs @@ -23,7 +23,6 @@ public class PlayerPerformanceService : DisposableMediatorSubscriberBase private readonly MareMediator _mediator; private readonly ServerConfigurationManager _serverConfigurationManager; private readonly PlayerPerformanceConfigService _playerPerformanceConfigService; - private readonly Dictionary _warnedForPlayers = new(StringComparer.Ordinal); public PlayerPerformanceService(ILogger logger, MareMediator mediator, ServerConfigurationManager serverConfigurationManager, @@ -327,4 +326,4 @@ public class PlayerPerformanceService : DisposableMediatorSubscriberBase return shrunken; } -} \ No newline at end of file +} diff --git a/MareSynchronos/Services/UiFactory.cs b/MareSynchronos/Services/UiFactory.cs index c518d4e..0d833be 100644 --- a/MareSynchronos/Services/UiFactory.cs +++ b/MareSynchronos/Services/UiFactory.cs @@ -43,7 +43,7 @@ public class UiFactory public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair) { return new StandaloneProfileUi(_loggerFactory.CreateLogger(), _mareMediator, - _uiSharedService, _serverConfigManager, _mareProfileManager, _pairManager, pair, _performanceCollectorService); + _uiSharedService, _serverConfigManager, _mareProfileManager, pair, _performanceCollectorService); } public PermissionWindowUI CreatePermissionPopupUi(Pair pair) diff --git a/MareSynchronos/UI/AutoDetectUi.cs b/MareSynchronos/UI/AutoDetectUi.cs index 2883619..f58ef79 100644 --- a/MareSynchronos/UI/AutoDetectUi.cs +++ b/MareSynchronos/UI/AutoDetectUi.cs @@ -4,10 +4,8 @@ using System.Linq; using System.Threading.Tasks; using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; -using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; -using Dalamud.Plugin.Services; using MareSynchronos.MareConfiguration; using MareSynchronos.PlayerData.Pairs; using MareSynchronos.Services.Mediator; @@ -25,7 +23,6 @@ public class AutoDetectUi : WindowMediatorSubscriberBase { private readonly MareConfigService _configService; private readonly DalamudUtilService _dalamud; - private readonly IObjectTable _objectTable; private readonly AutoDetectRequestService _requestService; private readonly NearbyPendingService _pendingService; private readonly PairManager _pairManager; @@ -33,14 +30,13 @@ public class AutoDetectUi : WindowMediatorSubscriberBase private readonly HashSet _acceptInFlight = new(StringComparer.Ordinal); public AutoDetectUi(ILogger logger, MareMediator mediator, - MareConfigService configService, DalamudUtilService dalamudUtilService, IObjectTable objectTable, + MareConfigService configService, DalamudUtilService dalamudUtilService, AutoDetectRequestService requestService, NearbyPendingService pendingService, PairManager pairManager, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "AutoDetect", performanceCollectorService) { _configService = configService; _dalamud = dalamudUtilService; - _objectTable = objectTable; _requestService = requestService; _pendingService = pendingService; _pairManager = pairManager; diff --git a/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs b/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs index 121a3a1..f4daa7c 100644 --- a/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs +++ b/MareSynchronos/UI/CharaDataHubUi.McdOnline.cs @@ -544,7 +544,8 @@ internal sealed partial class CharaDataHubUi { if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Download your Online Character Data from Server")) { - _ = _charaDataManager.GetAllData(_disposalCts.Token); + var cts = EnsureFreshCts(ref _disposalCts); + _ = _charaDataManager.GetAllData(cts.Token); } } if (_charaDataManager.DataGetTimeoutTask != null && !_charaDataManager.DataGetTimeoutTask.IsCompleted) @@ -654,7 +655,8 @@ internal sealed partial class CharaDataHubUi { if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "New Character Data Entry")) { - _charaDataManager.CreateCharaDataEntry(_closalCts.Token); + var cts = EnsureFreshCts(ref _closalCts); + _charaDataManager.CreateCharaDataEntry(cts.Token); _selectNewEntry = true; } } @@ -848,4 +850,4 @@ internal sealed partial class CharaDataHubUi } } } -} \ No newline at end of file +} diff --git a/MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs b/MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs index cb183e5..e27b0f4 100644 --- a/MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs +++ b/MareSynchronos/UI/CharaDataHubUi.NearbyPoses.cs @@ -204,7 +204,8 @@ internal partial class CharaDataHubUi { if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Update Data Shared With You")) { - _ = _charaDataManager.GetAllSharedData(_disposalCts.Token).ContinueWith(u => UpdateFilteredItems()); + var cts = EnsureFreshCts(ref _disposalCts); + _ = _charaDataManager.GetAllSharedData(cts.Token).ContinueWith(u => UpdateFilteredItems()); } } if (_charaDataManager.GetSharedWithYouTimeoutTask != null && !_charaDataManager.GetSharedWithYouTimeoutTask.IsCompleted) diff --git a/MareSynchronos/UI/CharaDataHubUi.cs b/MareSynchronos/UI/CharaDataHubUi.cs index 48d5ecd..2680555 100644 --- a/MareSynchronos/UI/CharaDataHubUi.cs +++ b/MareSynchronos/UI/CharaDataHubUi.cs @@ -1,4 +1,5 @@ -using Dalamud.Bindings.ImGui; +using System; +using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.ImGuiFileDialog; @@ -30,9 +31,9 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase private readonly CharaDataGposeTogetherManager _charaDataGposeTogetherManager; private readonly ServerConfigurationManager _serverConfigurationManager; private readonly UiSharedService _uiSharedService; - private CancellationTokenSource _closalCts = new(); + private CancellationTokenSource? _closalCts = new(); private bool _disableUI = false; - private CancellationTokenSource _disposalCts = new(); + private CancellationTokenSource? _disposalCts = new(); private string _exportDescription = string.Empty; private string _filterCodeNote = string.Empty; private string _filterDescription = string.Empty; @@ -123,7 +124,14 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase return; } - _closalCts.Cancel(); + try + { + _closalCts?.Cancel(); + } + catch (ObjectDisposedException) + { + } + EnsureFreshCts(ref _closalCts); SelectedDtoId = string.Empty; _filteredDict = null; _sharedWithYouOwnerFilter = string.Empty; @@ -135,15 +143,15 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase public override void OnOpen() { - _closalCts = _closalCts.CancelRecreate(); + EnsureFreshCts(ref _closalCts); } protected override void Dispose(bool disposing) { if (disposing) { - _closalCts.CancelDispose(); - _disposalCts.CancelDispose(); + CancelAndDispose(ref _closalCts); + CancelAndDispose(ref _disposalCts); } base.Dispose(disposing); @@ -689,7 +697,8 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase { if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Download your Character Data")) { - _ = _charaDataManager.GetAllData(_disposalCts.Token); + var cts = EnsureFreshCts(ref _disposalCts); + _ = _charaDataManager.GetAllData(cts.Token); } } if (_charaDataManager.DataGetTimeoutTask != null && !_charaDataManager.DataGetTimeoutTask.IsCompleted) @@ -1104,4 +1113,26 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase drawAction(); if (_disableUI) ImGui.BeginDisabled(); } -} \ No newline at end of file + + private static CancellationTokenSource EnsureFreshCts(ref CancellationTokenSource? cts) + { + CancelAndDispose(ref cts); + cts = new CancellationTokenSource(); + return cts; + } + + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + + cts.Dispose(); + cts = null; + } +} diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 8c1299c..c104bff 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -34,8 +34,8 @@ namespace MareSynchronos.UI; public class CompactUi : WindowMediatorSubscriberBase { - public float TransferPartHeight; - public float WindowContentWidth; + public float TransferPartHeight { get; internal set; } + public float WindowContentWidth { get; private set; } private readonly ApiController _apiController; private readonly MareConfigService _configService; private readonly ConcurrentDictionary> _currentDownloads = new(); @@ -127,7 +127,7 @@ public class CompactUi : WindowMediatorSubscriberBase _dataAnalysisUi = dataAnalysisUi; var tagHandler = new TagHandler(_serverManager); - _groupPanel = new(this, uiShared, _pairManager, chatService, uidDisplayHandler, _configService, _serverManager, _charaDataManager, _autoDetectRequestService); + _groupPanel = new(this, uiShared, _pairManager, chatService, uidDisplayHandler, _serverManager, _charaDataManager, _autoDetectRequestService); _selectGroupForPairUi = new(tagHandler, uidDisplayHandler, _uiSharedService); _selectPairsForGroupUi = new(tagHandler, uidDisplayHandler); _pairGroupsUi = new(configService, tagHandler, uidDisplayHandler, apiController, _selectPairsForGroupUi, _uiSharedService); diff --git a/MareSynchronos/UI/Components/GroupPanel.cs b/MareSynchronos/UI/Components/GroupPanel.cs index 06a3a4a..c1f4c00 100644 --- a/MareSynchronos/UI/Components/GroupPanel.cs +++ b/MareSynchronos/UI/Components/GroupPanel.cs @@ -30,7 +30,6 @@ internal sealed class GroupPanel private readonly CompactUi _mainUi; private readonly PairManager _pairManager; private readonly ChatService _chatService; - private readonly MareConfigService _mareConfig; private readonly ServerConfigurationManager _serverConfigurationManager; private readonly CharaDataManager _charaDataManager; private readonly AutoDetectRequestService _autoDetectRequestService; @@ -75,7 +74,7 @@ internal sealed class GroupPanel private string _syncShellToJoin = string.Empty; public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, ChatService chatServivce, - UidDisplayHandler uidDisplayHandler, MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager, + UidDisplayHandler uidDisplayHandler, ServerConfigurationManager serverConfigurationManager, CharaDataManager charaDataManager, AutoDetectRequestService autoDetectRequestService) { _mainUi = mainUi; @@ -83,7 +82,6 @@ internal sealed class GroupPanel _pairManager = pairManager; _chatService = chatServivce; _uidDisplayHandler = uidDisplayHandler; - _mareConfig = mareConfig; _serverConfigurationManager = serverConfigurationManager; _charaDataManager = charaDataManager; _autoDetectRequestService = autoDetectRequestService; diff --git a/MareSynchronos/UI/Components/PairGroupsUi.cs b/MareSynchronos/UI/Components/PairGroupsUi.cs index 5e67b7b..7bb296c 100644 --- a/MareSynchronos/UI/Components/PairGroupsUi.cs +++ b/MareSynchronos/UI/Components/PairGroupsUi.cs @@ -249,9 +249,4 @@ public class PairGroupsUi } } - private void ToggleTagOpen(string tag) - { - bool open = !_tagHandler.IsTagOpen(tag); - _tagHandler.SetTagOpen(tag, open); - } } diff --git a/MareSynchronos/UI/DataAnalysisUi.cs b/MareSynchronos/UI/DataAnalysisUi.cs index fd2759e..36868ae 100644 --- a/MareSynchronos/UI/DataAnalysisUi.cs +++ b/MareSynchronos/UI/DataAnalysisUi.cs @@ -9,6 +9,7 @@ using MareSynchronos.Services.Mediator; using MareSynchronos.Utils; using Microsoft.Extensions.Logging; using System.Numerics; +using System; namespace MareSynchronos.UI; @@ -20,7 +21,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase private readonly UiSharedService _uiSharedService; private readonly Dictionary _texturesToConvert = new(StringComparer.Ordinal); private Dictionary>? _cachedAnalysis; - private CancellationTokenSource _conversionCancellationTokenSource = new(); + private CancellationTokenSource? _conversionCancellationTokenSource = new(); private string _conversionCurrentFileName = string.Empty; private int _conversionCurrentFileProgress = 0; private Task? _conversionTask; @@ -74,7 +75,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase UiSharedService.TextWrapped("Current file: " + _conversionCurrentFileName); if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion")) { - _conversionCancellationTokenSource.Cancel(); + TryCancel(_conversionCancellationTokenSource); } UiSharedService.SetScaledWindowSize(500); ImGui.EndPopup(); @@ -294,8 +295,8 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase , ImGuiColors.DalamudYellow); if (_texturesToConvert.Count > 0 && _uiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)")) { - _conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate(); - _conversionTask = _ipcManager.Penumbra.ConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token); + var conversionCts = EnsureFreshCts(ref _conversionCancellationTokenSource); + _conversionTask = _ipcManager.Penumbra.ConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, conversionCts.Token); } } } @@ -354,8 +355,13 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase protected override void Dispose(bool disposing) { + if (disposing) + { + CancelAndDispose(ref _conversionCancellationTokenSource); + _conversionProgress.ProgressChanged -= ConversionProgress_ProgressChanged; + } + base.Dispose(disposing); - _conversionProgress.ProgressChanged -= ConversionProgress_ProgressChanged; } private void ConversionProgress_ProgressChanged(object? sender, (string, int) e) @@ -489,4 +495,31 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase } } } -} \ No newline at end of file + + private static CancellationTokenSource EnsureFreshCts(ref CancellationTokenSource? cts) + { + CancelAndDispose(ref cts); + cts = new CancellationTokenSource(); + return cts; + } + + private static void CancelAndDispose(ref CancellationTokenSource? cts) + { + if (cts == null) return; + TryCancel(cts); + cts.Dispose(); + cts = null; + } + + private static void TryCancel(CancellationTokenSource? cts) + { + if (cts == null) return; + try + { + cts.Cancel(); + } + catch (ObjectDisposedException) + { + } + } +} diff --git a/MareSynchronos/UI/EditProfileUi.cs b/MareSynchronos/UI/EditProfileUi.cs index 32221b8..0893d29 100644 --- a/MareSynchronos/UI/EditProfileUi.cs +++ b/MareSynchronos/UI/EditProfileUi.cs @@ -22,7 +22,6 @@ public class EditProfileUi : WindowMediatorSubscriberBase private readonly FileDialogManager _fileDialogManager; private readonly MareProfileManager _mareProfileManager; private readonly UiSharedService _uiSharedService; - private readonly ServerConfigurationManager _serverConfigurationManager; private bool _adjustedForScollBarsLocalProfile = false; private bool _adjustedForScollBarsOnlineProfile = false; private string _descriptionText = string.Empty; @@ -34,7 +33,6 @@ public class EditProfileUi : WindowMediatorSubscriberBase public EditProfileUi(ILogger logger, MareMediator mediator, ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager, - ServerConfigurationManager serverConfigurationManager, MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "Umbra Edit Profile###UmbraSyncEditProfileUI", performanceCollectorService) { @@ -47,7 +45,6 @@ public class EditProfileUi : WindowMediatorSubscriberBase _apiController = apiController; _uiSharedService = uiSharedService; _fileDialogManager = fileDialogManager; - _serverConfigurationManager = serverConfigurationManager; _mareProfileManager = mareProfileManager; Mediator.Subscribe(this, (_) => { _wasOpen = IsOpen; IsOpen = false; }); diff --git a/MareSynchronos/UI/PopoutProfileUi.cs b/MareSynchronos/UI/PopoutProfileUi.cs index 8bce2e6..6ec4eec 100644 --- a/MareSynchronos/UI/PopoutProfileUi.cs +++ b/MareSynchronos/UI/PopoutProfileUi.cs @@ -16,25 +16,22 @@ namespace MareSynchronos.UI; public class PopoutProfileUi : WindowMediatorSubscriberBase { private readonly MareProfileManager _mareProfileManager; - private readonly PairManager _pairManager; private readonly ServerConfigurationManager _serverManager; private readonly UiSharedService _uiSharedService; private Vector2 _lastMainPos = Vector2.Zero; private Vector2 _lastMainSize = Vector2.Zero; private byte[] _lastProfilePicture = []; - private byte[] _lastSupporterPicture = []; private Pair? _pair; private IDalamudTextureWrap? _supporterTextureWrap; private IDalamudTextureWrap? _textureWrap; public PopoutProfileUi(ILogger logger, MareMediator mediator, UiSharedService uiSharedService, ServerConfigurationManager serverManager, MareConfigService mareConfigService, - MareProfileManager mareProfileManager, PairManager pairManager, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "###UmbraSyncPopoutProfileUI", performanceCollectorService) + MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "###UmbraSyncPopoutProfileUI", performanceCollectorService) { _uiSharedService = uiSharedService; _serverManager = serverManager; _mareProfileManager = mareProfileManager; - _pairManager = pairManager; Flags = ImGuiWindowFlags.NoDecoration; Mediator.Subscribe(this, (msg) => @@ -42,7 +39,6 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase IsOpen = msg.Pair != null; _pair = msg.Pair; _lastProfilePicture = []; - _lastSupporterPicture = []; _textureWrap?.Dispose(); _textureWrap = null; _supporterTextureWrap?.Dispose(); @@ -182,4 +178,4 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase _logger.LogWarning(ex, "Error during draw tooltip"); } } -} \ No newline at end of file +} diff --git a/MareSynchronos/UI/StandaloneProfileUi.cs b/MareSynchronos/UI/StandaloneProfileUi.cs index 810bf6e..8c6933a 100644 --- a/MareSynchronos/UI/StandaloneProfileUi.cs +++ b/MareSynchronos/UI/StandaloneProfileUi.cs @@ -16,7 +16,6 @@ namespace MareSynchronos.UI; public class StandaloneProfileUi : WindowMediatorSubscriberBase { private readonly MareProfileManager _mareProfileManager; - private readonly PairManager _pairManager; private readonly ServerConfigurationManager _serverManager; private readonly UiSharedService _uiSharedService; private bool _adjustedForScrollBars = false; @@ -24,7 +23,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase private IDalamudTextureWrap? _textureWrap; public StandaloneProfileUi(ILogger logger, MareMediator mediator, UiSharedService uiBuilder, - ServerConfigurationManager serverManager, MareProfileManager mareProfileManager, PairManager pairManager, Pair pair, + ServerConfigurationManager serverManager, MareProfileManager mareProfileManager, Pair pair, PerformanceCollectorService performanceCollector) : base(logger, mediator, "Profile of " + pair.UserData.AliasOrUID + "##UmbraSyncStandaloneProfileUI" + pair.UserData.AliasOrUID, performanceCollector) { @@ -32,7 +31,6 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase _serverManager = serverManager; _mareProfileManager = mareProfileManager; Pair = pair; - _pairManager = pairManager; Flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.AlwaysAutoResize; var spacing = ImGui.GetStyle().ItemSpacing; @@ -164,4 +162,4 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase { Mediator.Publish(new RemoveWindowMessage(this)); } -} \ No newline at end of file +} diff --git a/MareSynchronos/UI/TypingIndicatorOverlay.cs b/MareSynchronos/UI/TypingIndicatorOverlay.cs index d1c5fe8..d36418c 100644 --- a/MareSynchronos/UI/TypingIndicatorOverlay.cs +++ b/MareSynchronos/UI/TypingIndicatorOverlay.cs @@ -29,7 +29,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase private static readonly TimeSpan TypingDisplayDelay = TimeSpan.FromMilliseconds(500); private static readonly TimeSpan TypingDisplayFade = TypingDisplayTime; - private readonly ILogger _logger; + private readonly ILogger _typedLogger; private readonly MareConfigService _configService; private readonly IGameGui _gameGui; private readonly ITextureProvider _textureProvider; @@ -47,7 +47,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase TypingIndicatorStateService typingStateService, ApiController apiController) : base(logger, mediator, nameof(TypingIndicatorOverlay), performanceCollectorService) { - _logger = logger; + _typedLogger = logger; _configService = configService; _gameGui = gameGui; _textureProvider = textureProvider; @@ -216,7 +216,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase if (objectId != uint.MaxValue && objectId != 0 && TryDrawNameplateBubble(drawList, iconWrap, objectId)) { - _logger.LogTrace("TypingIndicator: drew nameplate bubble for {uid} (objectId={objectId})", uid, objectId); + _typedLogger.LogTrace("TypingIndicator: drew nameplate bubble for {uid} (objectId={objectId})", uid, objectId); continue; } @@ -228,20 +228,20 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase if (pair == null) { - _logger.LogTrace("TypingIndicator: no pair found for {uid}, attempting fallback", uid); + _typedLogger.LogTrace("TypingIndicator: no pair found for {uid}, attempting fallback", uid); } - _logger.LogTrace("TypingIndicator: fallback draw for {uid} (objectId={objectId}, name={name}, ident={ident})", + _typedLogger.LogTrace("TypingIndicator: fallback draw for {uid} (objectId={objectId}, name={name}, ident={ident})", uid, objectId, pairName, pairIdent); if (hasWorldPosition) { DrawWorldFallbackIcon(drawList, iconWrap, worldPos); - _logger.LogTrace("TypingIndicator: fallback world draw for {uid} at {pos}", uid, worldPos); + _typedLogger.LogTrace("TypingIndicator: fallback world draw for {uid} at {pos}", uid, worldPos); } else { - _logger.LogTrace("TypingIndicator: could not resolve position for {uid}", uid); + _typedLogger.LogTrace("TypingIndicator: could not resolve position for {uid}", uid); } } } @@ -393,7 +393,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase { if (TryGetWorldPosition(objectId, out position)) { - _logger.LogTrace("TypingIndicator: resolved by objectId {objectId}", objectId); + _typedLogger.LogTrace("TypingIndicator: resolved by objectId {objectId}", objectId); return true; } @@ -402,7 +402,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase var name = pair.PlayerName; if (!string.IsNullOrEmpty(name) && TryGetWorldPositionByName(name!, out position)) { - _logger.LogTrace("TypingIndicator: resolved by pair name {name}", name); + _typedLogger.LogTrace("TypingIndicator: resolved by pair name {name}", name); return true; } @@ -412,7 +412,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase var cached = _dalamudUtil.FindPlayerByNameHash(ident); if (!string.IsNullOrEmpty(cached.Name) && TryGetWorldPositionByName(cached.Name, out position)) { - _logger.LogTrace("TypingIndicator: resolved by cached name {name}", cached.Name); + _typedLogger.LogTrace("TypingIndicator: resolved by cached name {name}", cached.Name); return true; } @@ -422,7 +422,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase if (objRef != null) { position = objRef.Position; - _logger.LogTrace("TypingIndicator: resolved by cached address {addr}", cached.Address); + _typedLogger.LogTrace("TypingIndicator: resolved by cached address {addr}", cached.Address); return true; } } @@ -432,7 +432,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase var alias = userData.AliasOrUID; if (!string.IsNullOrEmpty(alias) && TryGetWorldPositionByName(alias, out position)) { - _logger.LogTrace("TypingIndicator: resolved by user alias {alias}", alias); + _typedLogger.LogTrace("TypingIndicator: resolved by user alias {alias}", alias); return true; } diff --git a/MareSynchronos/Utils/PngHdr.cs b/MareSynchronos/Utils/PngHdr.cs index 5723f5a..35d9a69 100644 --- a/MareSynchronos/Utils/PngHdr.cs +++ b/MareSynchronos/Utils/PngHdr.cs @@ -1,6 +1,6 @@ namespace MareSynchronos.Utils; -public class PngHdr +public static class PngHdr { private static readonly byte[] _magicSignature = [137, 80, 78, 71, 13, 10, 26, 10]; private static readonly byte[] _IHDR = [(byte)'I', (byte)'H', (byte)'D', (byte)'R']; diff --git a/MareSynchronos/WebAPI/AccountRegistrationService.cs b/MareSynchronos/WebAPI/AccountRegistrationService.cs index 4b5d81b..5212c07 100644 --- a/MareSynchronos/WebAPI/AccountRegistrationService.cs +++ b/MareSynchronos/WebAPI/AccountRegistrationService.cs @@ -4,7 +4,6 @@ using MareSynchronos.Services; using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Utils; using MareSynchronos.WebAPI.SignalR; -using Microsoft.Extensions.Logging; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Reflection; @@ -15,17 +14,15 @@ namespace MareSynchronos.WebAPI; public sealed class AccountRegistrationService : IDisposable { private readonly HttpClient _httpClient; - private readonly ILogger _logger; private readonly ServerConfigurationManager _serverManager; - private string GenerateSecretKey() + private static string GenerateSecretKey() { return Convert.ToHexString(SHA256.HashData(RandomNumberGenerator.GetBytes(64))); } - public AccountRegistrationService(ILogger logger, ServerConfigurationManager serverManager) + public AccountRegistrationService(ServerConfigurationManager serverManager) { - _logger = logger; _serverManager = serverManager; _httpClient = new( new HttpClientHandler @@ -67,4 +64,4 @@ public sealed class AccountRegistrationService : IDisposable SecretKey = secretKey }; } -} \ No newline at end of file +} diff --git a/MareSynchronos/WebAPI/Files/FileUploadManager.cs b/MareSynchronos/WebAPI/Files/FileUploadManager.cs index 10b7f1e..90aa81a 100644 --- a/MareSynchronos/WebAPI/Files/FileUploadManager.cs +++ b/MareSynchronos/WebAPI/Files/FileUploadManager.cs @@ -17,19 +17,16 @@ namespace MareSynchronos.WebAPI.Files; public sealed class FileUploadManager : DisposableMediatorSubscriberBase { private readonly FileCacheManager _fileDbManager; - private readonly MareConfigService _mareConfigService; private readonly FileTransferOrchestrator _orchestrator; private readonly ServerConfigurationManager _serverManager; private readonly Dictionary _verifiedUploadedHashes = new(StringComparer.Ordinal); private CancellationTokenSource? _uploadCancellationTokenSource = new(); public FileUploadManager(ILogger logger, MareMediator mediator, - MareConfigService mareConfigService, FileTransferOrchestrator orchestrator, FileCacheManager fileDbManager, ServerConfigurationManager serverManager) : base(logger, mediator) { - _mareConfigService = mareConfigService; _orchestrator = orchestrator; _fileDbManager = fileDbManager; _serverManager = serverManager; @@ -286,4 +283,4 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase CurrentUploads.Clear(); } -} \ No newline at end of file +} diff --git a/MareSynchronos/WebAPI/Files/Models/DownloadFileTransfer.cs b/MareSynchronos/WebAPI/Files/Models/DownloadFileTransfer.cs index 92f357a..56f887f 100644 --- a/MareSynchronos/WebAPI/Files/Models/DownloadFileTransfer.cs +++ b/MareSynchronos/WebAPI/Files/Models/DownloadFileTransfer.cs @@ -19,6 +19,6 @@ public class DownloadFileTransfer : FileTransfer get => Dto.Size; } - public long TotalRaw => 0; // XXX + public long TotalRaw => Dto.Size; private DownloadFileDto Dto => (DownloadFileDto)TransferDto; -} \ No newline at end of file +} diff --git a/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Callbacks.cs b/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Callbacks.cs index c59e55d..7dbea87 100644 --- a/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Callbacks.cs +++ b/MareSynchronos/WebAPI/SignalR/ApiController.Functions.Callbacks.cs @@ -231,7 +231,7 @@ public partial class ApiController public Task Client_GposeLobbyPushWorldData(UserData userData, WorldData worldData) { - //Logger.LogDebug("Client_GposeLobbyPushWorldData: {dto}", userData); + Logger.LogDebug("Client_GposeLobbyPushWorldData: {dto}", userData); ExecuteSafely(() => Mediator.Publish(new GPoseLobbyReceiveWorldData(userData, worldData))); return Task.CompletedTask; } @@ -415,4 +415,4 @@ public partial class ApiController Logger.LogCritical(ex, "Error on executing safely"); } } -} \ No newline at end of file +} diff --git a/Penumbra.Api b/Penumbra.Api index 97fe622..c23ee05 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 97fe622e4ec0a5469a26aba8a8c3933fa8ef7fd6 +Subproject commit c23ee05c1e9fa103eaa52e6aa7e855ef568ee669