Compare commits

4 Commits

Author SHA1 Message Date
513845b811 UI Update & Fix Nearby 2025-11-01 01:09:06 +01:00
84586cac3d UI Update 2025-10-31 23:58:11 +01:00
b4108c7803 Fix warning 2025-10-30 22:13:38 +01:00
d891dceb28 UI Update 2025-10-19 21:56:19 +02:00
47 changed files with 1737 additions and 819 deletions

View File

@@ -1,4 +1,5 @@
using MareSynchronos.Interop.Ipc; using System;
using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
@@ -606,14 +607,35 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); base.Dispose(disposing);
_scanCancellationTokenSource?.Cancel(); try
{
_scanCancellationTokenSource.Cancel();
}
catch (ObjectDisposedException)
{
}
_scanCancellationTokenSource.Dispose();
PenumbraWatcher?.Dispose(); PenumbraWatcher?.Dispose();
MareWatcher?.Dispose(); MareWatcher?.Dispose();
SubstWatcher?.Dispose(); SubstWatcher?.Dispose();
_penumbraFswCts?.CancelDispose(); TryCancelAndDispose(_penumbraFswCts);
_mareFswCts?.CancelDispose(); TryCancelAndDispose(_mareFswCts);
_substFswCts?.CancelDispose(); TryCancelAndDispose(_substFswCts);
_periodicCalculationTokenSource?.CancelDispose(); TryCancelAndDispose(_periodicCalculationTokenSource);
}
private static void TryCancelAndDispose(CancellationTokenSource? cts)
{
if (cts == null) return;
try
{
cts.Cancel();
}
catch (ObjectDisposedException)
{
}
cts.Dispose();
} }
private void FullFileScan(CancellationToken ct) private void FullFileScan(CancellationToken ct)
@@ -856,4 +878,4 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
StartPenumbraWatcher(penumbraDir); StartPenumbraWatcher(penumbraDir);
} }
} }
} }

View File

@@ -12,7 +12,7 @@ public sealed class FileCompactor
private readonly Dictionary<string, int> _clusterSizes; private readonly Dictionary<string, int> _clusterSizes;
private readonly WOF_FILE_COMPRESSION_INFO_V1 _efInfo; private readonly WofFileCompressionInfoV1 _efInfo;
private readonly ILogger<FileCompactor> _logger; private readonly ILogger<FileCompactor> _logger;
private readonly MareConfigService _mareConfigService; private readonly MareConfigService _mareConfigService;
@@ -24,7 +24,7 @@ public sealed class FileCompactor
_logger = logger; _logger = logger;
_mareConfigService = mareConfigService; _mareConfigService = mareConfigService;
_dalamudUtilService = dalamudUtilService; _dalamudUtilService = dalamudUtilService;
_efInfo = new WOF_FILE_COMPRESSION_INFO_V1 _efInfo = new WofFileCompressionInfoV1
{ {
Algorithm = CompressionAlgorithm.XPRESS8K, Algorithm = CompressionAlgorithm.XPRESS8K,
Flags = 0 Flags = 0
@@ -123,7 +123,7 @@ public sealed class FileCompactor
out uint lpTotalNumberOfClusters); out uint lpTotalNumberOfClusters);
[DllImport("WoFUtil.dll")] [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")] [DllImport("WofUtil.dll")]
private static extern int WofSetFileDataLocation(IntPtr FileHandle, ulong Provider, IntPtr ExternalFileInfo, ulong Length); private static extern int WofSetFileDataLocation(IntPtr FileHandle, ulong Provider, IntPtr ExternalFileInfo, ulong Length);
@@ -242,9 +242,9 @@ public sealed class FileCompactor
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
private struct WOF_FILE_COMPRESSION_INFO_V1 private struct WofFileCompressionInfoV1
{ {
public CompressionAlgorithm Algorithm; public CompressionAlgorithm Algorithm;
public ulong Flags; public ulong Flags;
} }
} }

View File

@@ -17,8 +17,8 @@ namespace MareSynchronos.Interop;
public record ChatChannelOverride public record ChatChannelOverride
{ {
public string ChannelName = string.Empty; public string ChannelName { get; set; } = string.Empty;
public Action<byte[]>? ChatMessageHandler; public Action<byte[]>? ChatMessageHandler { get; set; }
} }
public unsafe sealed class GameChatHooks : IDisposable public unsafe sealed class GameChatHooks : IDisposable

View File

@@ -31,11 +31,11 @@ public class MdlFile
public ushort Unknown9; public ushort Unknown9;
// Offsets are stored relative to RuntimeSize instead of file start. // Offsets are stored relative to RuntimeSize instead of file start.
public uint[] VertexOffset = [0, 0, 0]; public uint[] VertexOffset;
public uint[] IndexOffset = [0, 0, 0]; public uint[] IndexOffset;
public uint[] VertexBufferSize = [0, 0, 0]; public uint[] VertexBufferSize;
public uint[] IndexBufferSize = [0, 0, 0]; public uint[] IndexBufferSize;
public byte LodCount; public byte LodCount;
public bool EnableIndexBufferStreaming; public bool EnableIndexBufferStreaming;
public bool EnableEdgeGeometry; public bool EnableEdgeGeometry;
@@ -43,15 +43,26 @@ public class MdlFile
public ModelFlags1 Flags1; public ModelFlags1 Flags1;
public ModelFlags2 Flags2; public ModelFlags2 Flags2;
public VertexDeclarationStruct[] VertexDeclarations = []; public VertexDeclarationStruct[] VertexDeclarations;
public ElementIdStruct[] ElementIds = []; public ElementIdStruct[] ElementIds;
public MeshStruct[] Meshes = []; public MeshStruct[] Meshes;
public BoundingBoxStruct[] BoneBoundingBoxes = []; public BoundingBoxStruct[] BoneBoundingBoxes;
public LodStruct[] Lods = []; public LodStruct[] Lods;
public ExtraLodStruct[] ExtraLods = []; public ExtraLodStruct[] ExtraLods;
public MdlFile(string filePath) public MdlFile(string filePath)
{ {
VertexOffset = Array.Empty<uint>();
IndexOffset = Array.Empty<uint>();
VertexBufferSize = Array.Empty<uint>();
IndexBufferSize = Array.Empty<uint>();
VertexDeclarations = Array.Empty<VertexDeclarationStruct>();
ElementIds = Array.Empty<ElementIdStruct>();
Meshes = Array.Empty<MeshStruct>();
BoneBoundingBoxes = Array.Empty<BoundingBoxStruct>();
Lods = Array.Empty<LodStruct>();
ExtraLods = Array.Empty<ExtraLodStruct>();
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
using var r = new LuminaBinaryReader(stream); using var r = new LuminaBinaryReader(stream);
@@ -256,4 +267,4 @@ public class MdlFile
} }
} }
} }
#pragma warning restore S1104 // Fields should not have public accessibility #pragma warning restore S1104 // Fields should not have public accessibility

View File

@@ -1,4 +1,5 @@
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using System;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Ipc; using Dalamud.Plugin.Ipc;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
@@ -29,7 +30,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
private bool _marePluginEnabled = false; private bool _marePluginEnabled = false;
private bool _impersonating = false; private bool _impersonating = false;
private DateTime _unregisterTime = DateTime.UtcNow; private DateTime _unregisterTime = DateTime.UtcNow;
private CancellationTokenSource _registerDelayCts = new(); private CancellationTokenSource? _registerDelayCts = new();
public bool MarePluginEnabled => _marePluginEnabled; public bool MarePluginEnabled => _marePluginEnabled;
public bool ImpersonationActive => _impersonating; public bool ImpersonationActive => _impersonating;
@@ -100,7 +101,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
{ {
if (_mareConfig.Current.MareAPI) if (_mareConfig.Current.MareAPI)
{ {
var cancelToken = _registerDelayCts.Token; var cancelToken = EnsureFreshCts(ref _registerDelayCts).Token;
Task.Run(async () => Task.Run(async () =>
{ {
// Wait before registering to reduce the chance of a race condition // Wait before registering to reduce the chance of a race condition
@@ -125,7 +126,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
} }
else else
{ {
_registerDelayCts = _registerDelayCts.CancelRecreate(); EnsureFreshCts(ref _registerDelayCts);
if (_impersonating) if (_impersonating)
{ {
_loadFileProviderMare?.UnregisterFunc(); _loadFileProviderMare?.UnregisterFunc();
@@ -146,7 +147,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
_loadFileAsyncProvider?.UnregisterFunc(); _loadFileAsyncProvider?.UnregisterFunc();
_handledGameAddresses?.UnregisterFunc(); _handledGameAddresses?.UnregisterFunc();
_registerDelayCts.Cancel(); TryCancel(_registerDelayCts);
if (_impersonating) if (_impersonating)
{ {
_loadFileProviderMare?.UnregisterFunc(); _loadFileProviderMare?.UnregisterFunc();
@@ -155,6 +156,7 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
} }
Mediator.UnsubscribeAll(this); Mediator.UnsubscribeAll(this);
CancelAndDispose(ref _registerDelayCts);
return Task.CompletedTask; 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(); 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)
{
}
}
} }

View File

@@ -1,19 +1,20 @@
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using System;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace MareSynchronos.Interop.Ipc; namespace MareSynchronos.Interop.Ipc;
public class RedrawManager public class RedrawManager : IDisposable
{ {
private readonly MareMediator _mareMediator; private readonly MareMediator _mareMediator;
private readonly DalamudUtilService _dalamudUtil; private readonly DalamudUtilService _dalamudUtil;
private readonly ConcurrentDictionary<nint, bool> _penumbraRedrawRequests = []; private readonly ConcurrentDictionary<nint, bool> _penumbraRedrawRequests = [];
private CancellationTokenSource _disposalCts = new(); private CancellationTokenSource? _disposalCts = new();
private bool _disposed;
public SemaphoreSlim RedrawSemaphore { get; init; } = new(2, 2); public SemaphoreSlim RedrawSemaphore { get; init; } = new(2, 2);
@@ -32,12 +33,12 @@ public class RedrawManager
try try
{ {
using CancellationTokenSource cancelToken = new CancellationTokenSource(); 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; var combinedToken = combinedCts.Token;
cancelToken.CancelAfter(TimeSpan.FromSeconds(15)); cancelToken.CancelAfter(TimeSpan.FromSeconds(15));
await handler.ActOnFrameworkAfterEnsureNoDrawAsync(action, combinedToken).ConfigureAwait(false); 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); await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, handler, applicationId, 30000, combinedToken).ConfigureAwait(false);
} }
finally finally
@@ -49,6 +50,45 @@ public class RedrawManager
internal void Cancel() 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;
} }
} }

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<AssemblyName>UmbraSync</AssemblyName> <AssemblyName>UmbraSync</AssemblyName>
<RootNamespace>UmbraSync</RootNamespace> <RootNamespace>UmbraSync</RootNamespace>
<Version>0.1.9.6</Version> <Version>0.1.9.9</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -5,7 +5,6 @@ using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -23,7 +22,6 @@ public class PairHandlerFactory
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private readonly MareMediator _mareMediator; private readonly MareMediator _mareMediator;
private readonly PlayerPerformanceService _playerPerformanceService; private readonly PlayerPerformanceService _playerPerformanceService;
private readonly ServerConfigurationManager _serverConfigManager;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly PairAnalyzerFactory _pairAnalyzerFactory; private readonly PairAnalyzerFactory _pairAnalyzerFactory;
private readonly VisibilityService _visibilityService; private readonly VisibilityService _visibilityService;
@@ -32,7 +30,7 @@ public class PairHandlerFactory
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService, FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime, PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService, FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService,
ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory, PairAnalyzerFactory pairAnalyzerFactory,
MareConfigService configService, VisibilityService visibilityService) MareConfigService configService, VisibilityService visibilityService)
{ {
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
@@ -45,7 +43,6 @@ public class PairHandlerFactory
_fileCacheManager = fileCacheManager; _fileCacheManager = fileCacheManager;
_mareMediator = mareMediator; _mareMediator = mareMediator;
_playerPerformanceService = playerPerformanceService; _playerPerformanceService = playerPerformanceService;
_serverConfigManager = serverConfigManager;
_pairAnalyzerFactory = pairAnalyzerFactory; _pairAnalyzerFactory = pairAnalyzerFactory;
_configService = configService; _configService = configService;
_visibilityService = visibilityService; _visibilityService = visibilityService;
@@ -55,6 +52,6 @@ public class PairHandlerFactory
{ {
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory, return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory,
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime, _ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
_fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager, _configService, _visibilityService); _fileCacheManager, _mareMediator, _playerPerformanceService, _configService, _visibilityService);
} }
} }

View File

@@ -7,7 +7,6 @@ using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Events; using MareSynchronos.Services.Events;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using MareSynchronos.WebAPI.Files; using MareSynchronos.WebAPI.Files;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@@ -29,7 +28,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory; private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
private readonly IpcManager _ipcManager; private readonly IpcManager _ipcManager;
private readonly PlayerPerformanceService _playerPerformanceService; private readonly PlayerPerformanceService _playerPerformanceService;
private readonly ServerConfigurationManager _serverConfigManager;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly VisibilityService _visibilityService; private readonly VisibilityService _visibilityService;
private CancellationTokenSource? _applicationCancellationTokenSource = new(); private CancellationTokenSource? _applicationCancellationTokenSource = new();
@@ -53,7 +51,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
FileCacheManager fileDbManager, MareMediator mediator, FileCacheManager fileDbManager, MareMediator mediator,
PlayerPerformanceService playerPerformanceService, PlayerPerformanceService playerPerformanceService,
ServerConfigurationManager serverConfigManager,
MareConfigService configService, VisibilityService visibilityService) : base(logger, mediator) MareConfigService configService, VisibilityService visibilityService) : base(logger, mediator)
{ {
Pair = pair; Pair = pair;
@@ -65,7 +62,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_dalamudUtil = dalamudUtil; _dalamudUtil = dalamudUtil;
_fileDbManager = fileDbManager; _fileDbManager = fileDbManager;
_playerPerformanceService = playerPerformanceService; _playerPerformanceService = playerPerformanceService;
_serverConfigManager = serverConfigManager;
_configService = configService; _configService = configService;
_visibilityService = visibilityService; _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); 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]; return [.. missingFiles];
} }
} }

View File

@@ -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) public void SetNote(string note)
{ {
_serverConfigurationManager.SetNoteForUid(UserData.UID, note); _serverConfigurationManager.SetNoteForUid(UserData.UID, note);
@@ -368,4 +382,4 @@ public class Pair : DisposableMediatorSubscriberBase
return data; return data;
} }
} }

View File

@@ -182,17 +182,22 @@ public sealed class Plugin : IDalamudPlugin
// add scoped services // add scoped services
collection.AddScoped<CacheMonitor>(); collection.AddScoped<CacheMonitor>();
collection.AddScoped<UiFactory>(); collection.AddScoped<UiFactory>();
collection.AddScoped<WindowMediatorSubscriberBase, SettingsUi>(); collection.AddScoped<SettingsUi>();
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>(); collection.AddScoped<CompactUi>();
collection.AddScoped<EditProfileUi>();
collection.AddScoped<DataAnalysisUi>();
collection.AddScoped<AutoDetectUi>();
collection.AddScoped<WindowMediatorSubscriberBase>(sp => sp.GetRequiredService<SettingsUi>());
collection.AddScoped<WindowMediatorSubscriberBase>(sp => sp.GetRequiredService<CompactUi>());
collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>(); collection.AddScoped<WindowMediatorSubscriberBase, IntroUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>(); collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
collection.AddScoped<WindowMediatorSubscriberBase, AutoDetectUi>(); collection.AddScoped<WindowMediatorSubscriberBase>(sp => sp.GetRequiredService<AutoDetectUi>());
collection.AddScoped<WindowMediatorSubscriberBase, ChangelogUi>(); collection.AddScoped<WindowMediatorSubscriberBase, ChangelogUi>();
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>(); collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>(); collection.AddScoped<WindowMediatorSubscriberBase>(sp => sp.GetRequiredService<DataAnalysisUi>());
collection.AddScoped<WindowMediatorSubscriberBase, EventViewerUI>(); collection.AddScoped<WindowMediatorSubscriberBase, EventViewerUI>();
collection.AddScoped<WindowMediatorSubscriberBase, CharaDataHubUi>(); collection.AddScoped<WindowMediatorSubscriberBase, CharaDataHubUi>();
collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>(); collection.AddScoped<WindowMediatorSubscriberBase>(sp => sp.GetRequiredService<EditProfileUi>());
collection.AddScoped<WindowMediatorSubscriberBase, PopupHandler>(); collection.AddScoped<WindowMediatorSubscriberBase, PopupHandler>();
collection.AddScoped<WindowMediatorSubscriberBase, TypingIndicatorOverlay>(); collection.AddScoped<WindowMediatorSubscriberBase, TypingIndicatorOverlay>();
collection.AddScoped<IPopupHandler, ReportPopupHandler>(); collection.AddScoped<IPopupHandler, ReportPopupHandler>();

View File

@@ -17,7 +17,6 @@ public class DiscoveryConfigProvider
private readonly TokenProvider _tokenProvider; private readonly TokenProvider _tokenProvider;
private WellKnownRoot? _config; private WellKnownRoot? _config;
private DateTimeOffset _lastLoad = DateTimeOffset.MinValue;
public DiscoveryConfigProvider(ILogger<DiscoveryConfigProvider> logger, ServerConfigurationManager serverManager, TokenProvider tokenProvider) public DiscoveryConfigProvider(ILogger<DiscoveryConfigProvider> logger, ServerConfigurationManager serverManager, TokenProvider tokenProvider)
{ {
@@ -51,7 +50,6 @@ public class DiscoveryConfigProvider
root.NearbyDiscovery?.Hydrate(); root.NearbyDiscovery?.Hydrate();
_config = root; _config = root;
_lastLoad = DateTimeOffset.UtcNow;
_logger.LogDebug("Loaded Nearby well-known (stapled), enabled={enabled}, expires={exp}", NearbyEnabled, _config?.NearbyDiscovery?.SaltExpiresAt); _logger.LogDebug("Loaded Nearby well-known (stapled), enabled={enabled}, expires={exp}", NearbyEnabled, _config?.NearbyDiscovery?.SaltExpiresAt);
return true; return true;
} }
@@ -97,7 +95,6 @@ public class DiscoveryConfigProvider
root.NearbyDiscovery?.Hydrate(); root.NearbyDiscovery?.Hydrate();
_config = root; _config = root;
_lastLoad = DateTimeOffset.UtcNow;
_logger.LogInformation("Loaded Nearby well-known (http {path}), enabled={enabled}", path, NearbyEnabled); _logger.LogInformation("Loaded Nearby well-known (http {path}), enabled={enabled}", path, NearbyEnabled);
return true; return true;
} }

View File

@@ -1,3 +1,4 @@
using System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
@@ -7,6 +8,7 @@ using MareSynchronos.WebAPI.AutoDetect;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using System.Numerics; using System.Numerics;
using System.Linq; using System.Linq;
using System.Collections.Generic;
using MareSynchronos.Utils; using MareSynchronos.Utils;
namespace MareSynchronos.Services.AutoDetect; namespace MareSynchronos.Services.AutoDetect;
@@ -34,6 +36,8 @@ public class NearbyDiscoveryService : IHostedService, IMediatorSubscriber
private bool _lastAutoDetectState; private bool _lastAutoDetectState;
private DateTime _lastHeartbeat = DateTime.MinValue; private DateTime _lastHeartbeat = DateTime.MinValue;
private static readonly TimeSpan HeartbeatInterval = TimeSpan.FromSeconds(75); private static readonly TimeSpan HeartbeatInterval = TimeSpan.FromSeconds(75);
private readonly object _entriesLock = new();
private List<NearbyEntry> _lastEntries = [];
public NearbyDiscoveryService(ILogger<NearbyDiscoveryService> logger, MareMediator mediator, public NearbyDiscoveryService(ILogger<NearbyDiscoveryService> logger, MareMediator mediator,
MareConfigService config, DiscoveryConfigProvider configProvider, DalamudUtilService dalamudUtilService, MareConfigService config, DiscoveryConfigProvider configProvider, DalamudUtilService dalamudUtilService,
@@ -52,6 +56,7 @@ public class NearbyDiscoveryService : IHostedService, IMediatorSubscriber
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
{ {
CancelAndDispose(ref _loopCts);
_loopCts = new CancellationTokenSource(); _loopCts = new CancellationTokenSource();
_mediator.Subscribe<ConnectedMessage>(this, _ => { _isConnected = true; _configProvider.TryLoadFromStapled(); }); _mediator.Subscribe<ConnectedMessage>(this, _ => { _isConnected = true; _configProvider.TryLoadFromStapled(); });
_mediator.Subscribe<DisconnectedMessage>(this, _ => { _isConnected = false; _lastPublishedSignature = null; }); _mediator.Subscribe<DisconnectedMessage>(this, _ => { _isConnected = false; _lastPublishedSignature = null; });
@@ -128,10 +133,41 @@ public class NearbyDiscoveryService : IHostedService, IMediatorSubscriber
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
{ {
_mediator.UnsubscribeAll(this); _mediator.UnsubscribeAll(this);
try { _loopCts?.Cancel(); } catch { } CancelAndDispose(ref _loopCts);
return Task.CompletedTask; return Task.CompletedTask;
} }
public List<NearbyEntry> SnapshotEntries()
{
lock (_entriesLock)
{
return _lastEntries.ToList();
}
}
private void UpdateSnapshot(List<NearbyEntry> entries)
{
lock (_entriesLock)
{
_lastEntries = entries.ToList();
}
}
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) private async Task Loop(CancellationToken ct)
{ {
_configProvider.TryLoadFromStapled(); _configProvider.TryLoadFromStapled();
@@ -426,6 +462,7 @@ public class NearbyDiscoveryService : IHostedService, IMediatorSubscriber
_logger.LogDebug("Nearby: well-known not available or disabled; running in local-only mode"); _logger.LogDebug("Nearby: well-known not available or disabled; running in local-only mode");
} }
} }
UpdateSnapshot(entries);
_mediator.Publish(new DiscoveryListUpdated(entries)); _mediator.Publish(new DiscoveryListUpdated(entries));
var delayMs = Math.Max(1000, _configProvider.MinQueryIntervalMs); var delayMs = Math.Max(1000, _configProvider.MinQueryIntervalMs);

View File

@@ -15,8 +15,9 @@ public sealed class NearbyPendingService : IMediatorSubscriber
private readonly ApiController _api; private readonly ApiController _api;
private readonly AutoDetectRequestService _requestService; private readonly AutoDetectRequestService _requestService;
private readonly ConcurrentDictionary<string, string> _pending = new(StringComparer.Ordinal); private readonly ConcurrentDictionary<string, string> _pending = new(StringComparer.Ordinal);
private static readonly Regex ReqRegex = new(@"^Nearby Request: (.+) \[(?<uid>[A-Z0-9]+)\]$", RegexOptions.Compiled); private static readonly TimeSpan RegexTimeout = TimeSpan.FromSeconds(1);
private static readonly Regex AcceptRegex = new(@"^Nearby Accept: (.+) \[(?<uid>[A-Z0-9]+)\]$", RegexOptions.Compiled); private static readonly Regex ReqRegex = new(@"^Nearby Request: .+ \[(?<uid>[A-Z0-9]+)\]$", RegexOptions.Compiled | RegexOptions.ExplicitCapture, RegexTimeout);
private static readonly Regex AcceptRegex = new(@"^Nearby Accept: .+ \[(?<uid>[A-Z0-9]+)\]$", RegexOptions.Compiled | RegexOptions.ExplicitCapture, RegexTimeout);
public NearbyPendingService(ILogger<NearbyPendingService> logger, MareMediator mediator, ApiController api, AutoDetectRequestService requestService) public NearbyPendingService(ILogger<NearbyPendingService> logger, MareMediator mediator, ApiController api, AutoDetectRequestService requestService)
{ {

View File

@@ -28,7 +28,7 @@ public sealed class CharaDataNearbyManager : DisposableMediatorSubscriberBase
private Task? _filterEntriesRunningTask; private Task? _filterEntriesRunningTask;
private (Guid VfxId, PoseEntryExtended Pose)? _hoveredVfx = null; private (Guid VfxId, PoseEntryExtended Pose)? _hoveredVfx = null;
private DateTime _lastExecutionTime = DateTime.UtcNow; private DateTime _lastExecutionTime = DateTime.UtcNow;
private SemaphoreSlim _sharedDataUpdateSemaphore = new(1, 1); private readonly SemaphoreSlim _sharedDataUpdateSemaphore = new(1, 1);
public CharaDataNearbyManager(ILogger<CharaDataNearbyManager> logger, MareMediator mediator, public CharaDataNearbyManager(ILogger<CharaDataNearbyManager> logger, MareMediator mediator,
DalamudUtilService dalamudUtilService, VfxSpawnManager vfxSpawnManager, DalamudUtilService dalamudUtilService, VfxSpawnManager vfxSpawnManager,
ServerConfigurationManager serverConfigurationManager, ServerConfigurationManager serverConfigurationManager,

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.InteropServices;
using Lumina.Data.Files; using Lumina.Data.Files;
using MareSynchronos.API.Data; using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
@@ -16,9 +17,8 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase
{ {
private readonly FileCacheManager _fileCacheManager; private readonly FileCacheManager _fileCacheManager;
private readonly XivDataAnalyzer _xivDataAnalyzer; private readonly XivDataAnalyzer _xivDataAnalyzer;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private CancellationTokenSource? _analysisCts; private CancellationTokenSource? _analysisCts;
private CancellationTokenSource _baseAnalysisCts = new(); private CancellationTokenSource? _baseAnalysisCts = new();
private string _lastDataHash = string.Empty; private string _lastDataHash = string.Empty;
private CharacterAnalysisSummary _previousSummary = CharacterAnalysisSummary.Empty; private CharacterAnalysisSummary _previousSummary = CharacterAnalysisSummary.Empty;
private DateTime _lastAutoAnalysis = DateTime.MinValue; private DateTime _lastAutoAnalysis = DateTime.MinValue;
@@ -30,14 +30,15 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase
private const long NotificationTriangleThreshold = 150_000; private const long NotificationTriangleThreshold = 150_000;
private bool _sizeWarningShown; private bool _sizeWarningShown;
private bool _triangleWarningShown; private bool _triangleWarningShown;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
public CharacterAnalyzer(ILogger<CharacterAnalyzer> logger, MareMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer, PlayerPerformanceConfigService playerPerformanceConfigService) public CharacterAnalyzer(ILogger<CharacterAnalyzer> logger, MareMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer, PlayerPerformanceConfigService playerPerformanceConfigService)
: base(logger, mediator) : base(logger, mediator)
{ {
Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) => Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) =>
{ {
_baseAnalysisCts = _baseAnalysisCts.CancelRecreate(); var tokenSource = EnsureFreshCts(ref _baseAnalysisCts);
var token = _baseAnalysisCts.Token; var token = tokenSource.Token;
_ = BaseAnalysis(msg.CharacterData, token); _ = BaseAnalysis(msg.CharacterData, token);
}); });
_fileCacheManager = fileCacheManager; _fileCacheManager = fileCacheManager;
@@ -54,17 +55,15 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase
public void CancelAnalyze() public void CancelAnalyze()
{ {
_analysisCts?.CancelDispose(); CancelAndDispose(ref _analysisCts);
_analysisCts = null;
} }
public async Task ComputeAnalysis(bool print = true, bool recalculate = false) public async Task ComputeAnalysis(bool print = true, bool recalculate = false)
{ {
Logger.LogDebug("=== Calculating Character Analysis ==="); Logger.LogDebug("=== Calculating Character Analysis ===");
_analysisCts = _analysisCts?.CancelRecreate() ?? new(); var analysisCts = EnsureFreshCts(ref _analysisCts);
var cancelToken = analysisCts.Token;
var cancelToken = _analysisCts.Token;
var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList(); var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList();
if (allFiles.Exists(c => !c.IsComputed || recalculate)) if (allFiles.Exists(c => !c.IsComputed || recalculate))
@@ -106,8 +105,7 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase
LastCompletedAnalysis = DateTime.UtcNow; LastCompletedAnalysis = DateTime.UtcNow;
} }
_analysisCts.CancelDispose(); CancelAndDispose(ref _analysisCts);
_analysisCts = null;
if (print) PrintAnalysis(); if (print) PrintAnalysis();
} }
@@ -118,8 +116,8 @@ public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase
if (!disposing) return; if (!disposing) return;
_analysisCts?.CancelDispose(); CancelAndDispose(ref _analysisCts);
_baseAnalysisCts.CancelDispose(); CancelAndDispose(ref _baseAnalysisCts);
} }
private async Task BaseAnalysis(CharacterData charaData, CancellationToken token) private async Task BaseAnalysis(CharacterData charaData, CancellationToken token)
@@ -375,6 +373,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 readonly record struct CharacterAnalysisSummary(int TotalFiles, long TotalOriginalSize, long TotalCompressedSize, long TotalTriangles, bool HasUncomputedEntries)
{ {
public static CharacterAnalysisSummary Empty => new(); public static CharacterAnalysisSummary Empty => new();
@@ -427,4 +426,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;
}
} }

View File

@@ -16,37 +16,26 @@ namespace MareSynchronos.Services;
public class GuiHookService : DisposableMediatorSubscriberBase public class GuiHookService : DisposableMediatorSubscriberBase
{ {
private readonly ILogger<GuiHookService> _logger;
private readonly DalamudUtilService _dalamudUtil; private readonly DalamudUtilService _dalamudUtil;
private readonly MareConfigService _configService; private readonly MareConfigService _configService;
private readonly INamePlateGui _namePlateGui; private readonly INamePlateGui _namePlateGui;
private readonly IGameConfig _gameConfig; private readonly IGameConfig _gameConfig;
private readonly IPartyList _partyList; private readonly IPartyList _partyList;
private readonly PairManager _pairManager; 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 _isModified = false;
private bool _namePlateRoleColorsEnabled = false; private bool _namePlateRoleColorsEnabled = false;
public GuiHookService(ILogger<GuiHookService> logger, DalamudUtilService dalamudUtil, MareMediator mediator, MareConfigService configService, public GuiHookService(ILogger<GuiHookService> logger, DalamudUtilService dalamudUtil, MareMediator mediator, MareConfigService configService,
INamePlateGui namePlateGui, IGameConfig gameConfig, IPartyList partyList, PairManager pairManager, ApiController apiController, INamePlateGui namePlateGui, IGameConfig gameConfig, IPartyList partyList, PairManager pairManager)
IClientState clientState, TypingIndicatorStateService typingStateService)
: base(logger, mediator) : base(logger, mediator)
{ {
_logger = logger;
_dalamudUtil = dalamudUtil; _dalamudUtil = dalamudUtil;
_configService = configService; _configService = configService;
_namePlateGui = namePlateGui; _namePlateGui = namePlateGui;
_gameConfig = gameConfig; _gameConfig = gameConfig;
_partyList = partyList; _partyList = partyList;
_pairManager = pairManager; _pairManager = pairManager;
_apiController = apiController;
_clientState = clientState;
_typingStateService = typingStateService;
_namePlateGui.OnNamePlateUpdate += OnNamePlateUpdate; _namePlateGui.OnNamePlateUpdate += OnNamePlateUpdate;
_namePlateGui.RequestRedraw(); _namePlateGui.RequestRedraw();
@@ -60,18 +49,13 @@ public class GuiHookService : DisposableMediatorSubscriberBase
public void RequestRedraw(bool force = false) public void RequestRedraw(bool force = false)
{ {
var useColors = _configService.Current.UseNameColors; var useColors = _configService.Current.UseNameColors;
var showTyping = _configService.Current.TypingIndicatorShowOnNameplates;
if (!useColors && !showTyping) if (!useColors)
{ {
if (!_isModified && !force) if (!_isModified && !force)
return; return;
_isModified = false; _isModified = false;
} }
else if (!useColors)
{
_isModified = false;
}
_ = Task.Run(async () => { _ = Task.Run(async () => {
await _dalamudUtil.RunOnFrameworkThread(() => _namePlateGui.RequestRedraw()).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() => _namePlateGui.RequestRedraw()).ConfigureAwait(false);
@@ -91,8 +75,7 @@ public class GuiHookService : DisposableMediatorSubscriberBase
private void OnNamePlateUpdate(INamePlateUpdateContext context, IReadOnlyList<INamePlateUpdateHandler> handlers) private void OnNamePlateUpdate(INamePlateUpdateContext context, IReadOnlyList<INamePlateUpdateHandler> handlers)
{ {
var applyColors = _configService.Current.UseNameColors; var applyColors = _configService.Current.UseNameColors;
var showTypingIndicator = _configService.Current.TypingIndicatorShowOnNameplates; if (!applyColors)
if (!applyColors && !showTypingIndicator)
return; return;
var visibleUsers = _pairManager.GetOnlineUserPairs().Where(u => u.IsVisible && u.PlayerCharacterId != uint.MaxValue); 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) for (int i = 0; i < _partyList.Count; ++i)
partyMembers[i] = _partyList[i]?.GameObject?.Address ?? nint.MaxValue; 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) foreach (var handler in handlers)
{ {
if (handler != null && visibleUsersIds.Contains(handler.GameObjectId)) if (handler != null && visibleUsersIds.Contains(handler.GameObjectId))

View File

@@ -2,7 +2,6 @@
using MareSynchronos.API.Data.Comparer; using MareSynchronos.API.Data.Comparer;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.WebAPI; using MareSynchronos.WebAPI;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -15,7 +14,6 @@ public class MareProfileManager : MediatorSubscriberBase
private const string _nsfw = "Profile not displayed - NSFW"; private const string _nsfw = "Profile not displayed - NSFW";
private readonly ApiController _apiController; private readonly ApiController _apiController;
private readonly MareConfigService _mareConfigService; private readonly MareConfigService _mareConfigService;
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly ConcurrentDictionary<UserData, MareProfileData> _mareProfiles = new(UserDataComparer.Instance); private readonly ConcurrentDictionary<UserData, MareProfileData> _mareProfiles = new(UserDataComparer.Instance);
private readonly MareProfileData _defaultProfileData = new(IsFlagged: false, IsNSFW: false, string.Empty, _noDescription); 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); private readonly MareProfileData _nsfwProfileData = new(IsFlagged: false, IsNSFW: false, string.Empty, _nsfw);
public MareProfileManager(ILogger<MareProfileManager> logger, MareConfigService mareConfigService, public MareProfileManager(ILogger<MareProfileManager> logger, MareConfigService mareConfigService,
MareMediator mediator, ApiController apiController, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator) MareMediator mediator, ApiController apiController) : base(logger, mediator)
{ {
_mareConfigService = mareConfigService; _mareConfigService = mareConfigService;
_apiController = apiController; _apiController = apiController;
_serverConfigurationManager = serverConfigurationManager;
Mediator.Subscribe<ClearProfileDataMessage>(this, (msg) => Mediator.Subscribe<ClearProfileDataMessage>(this, (msg) =>
{ {
@@ -75,4 +72,4 @@ public class MareProfileManager : MediatorSubscriberBase
_mareProfiles[data] = _defaultProfileData; _mareProfiles[data] = _defaultProfileData;
} }
} }
} }

View File

@@ -7,7 +7,7 @@ using System.Text;
namespace MareSynchronos.Services.Mediator; namespace MareSynchronos.Services.Mediator;
public sealed class MareMediator : IHostedService public sealed class MareMediator : IHostedService, IDisposable
{ {
private readonly Lock _addRemoveLock = new(); private readonly Lock _addRemoveLock = new();
private readonly ConcurrentDictionary<SubscriberAction, DateTime> _lastErrorTime = []; private readonly ConcurrentDictionary<SubscriberAction, DateTime> _lastErrorTime = [];
@@ -109,6 +109,12 @@ public sealed class MareMediator : IHostedService
} }
} }
public void Dispose()
{
_loopCts.Cancel();
_loopCts.Dispose();
}
public void SubscribeKeyed<T>(IMediatorSubscriber subscriber, string key, Action<T> action) where T : MessageBase public void SubscribeKeyed<T>(IMediatorSubscriber subscriber, string key, Action<T> action) where T : MessageBase
{ {
lock (_addRemoveLock) lock (_addRemoveLock)
@@ -219,4 +225,4 @@ public sealed class MareMediator : IHostedService
public object Action { get; } public object Action { get; }
public IMediatorSubscriber Subscriber { get; } public IMediatorSubscriber Subscriber { get; }
} }
} }

View File

@@ -1,4 +1,5 @@
using MareSynchronos.API.Data; using System;
using MareSynchronos.API.Data;
using MareSynchronos.API.Data.Enum; using MareSynchronos.API.Data.Enum;
using MareSynchronos.FileCache; using MareSynchronos.FileCache;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
@@ -14,7 +15,7 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase
private readonly FileCacheManager _fileCacheManager; private readonly FileCacheManager _fileCacheManager;
private readonly XivDataAnalyzer _xivDataAnalyzer; private readonly XivDataAnalyzer _xivDataAnalyzer;
private CancellationTokenSource? _analysisCts; private CancellationTokenSource? _analysisCts;
private CancellationTokenSource _baseAnalysisCts = new(); private CancellationTokenSource? _baseAnalysisCts = new();
private string _lastDataHash = string.Empty; private string _lastDataHash = string.Empty;
public PairAnalyzer(ILogger<PairAnalyzer> logger, Pair pair, MareMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer) public PairAnalyzer(ILogger<PairAnalyzer> logger, Pair pair, MareMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer)
@@ -24,8 +25,8 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase
#if DEBUG #if DEBUG
Mediator.SubscribeKeyed<PairDataAppliedMessage>(this, pair.UserData.UID, (msg) => Mediator.SubscribeKeyed<PairDataAppliedMessage>(this, pair.UserData.UID, (msg) =>
{ {
_baseAnalysisCts = _baseAnalysisCts.CancelRecreate(); var tokenSource = EnsureFreshCts(ref _baseAnalysisCts);
var token = _baseAnalysisCts.Token; var token = tokenSource.Token;
if (msg.CharacterData != null) if (msg.CharacterData != null)
{ {
_ = BaseAnalysis(msg.CharacterData, token); _ = BaseAnalysis(msg.CharacterData, token);
@@ -56,17 +57,15 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase
public void CancelAnalyze() public void CancelAnalyze()
{ {
_analysisCts?.CancelDispose(); CancelAndDispose(ref _analysisCts);
_analysisCts = null;
} }
public async Task ComputeAnalysis(bool print = true, bool recalculate = false) public async Task ComputeAnalysis(bool print = true, bool recalculate = false)
{ {
Logger.LogDebug("=== Calculating Character Analysis ==="); Logger.LogDebug("=== Calculating Character Analysis ===");
_analysisCts = _analysisCts?.CancelRecreate() ?? new(); var analysisCts = EnsureFreshCts(ref _analysisCts);
var cancelToken = analysisCts.Token;
var cancelToken = _analysisCts.Token;
var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList(); var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList();
if (allFiles.Exists(c => !c.IsComputed || recalculate)) if (allFiles.Exists(c => !c.IsComputed || recalculate))
@@ -102,8 +101,7 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase
LastPlayerName = Pair.PlayerName ?? string.Empty; LastPlayerName = Pair.PlayerName ?? string.Empty;
Mediator.Publish(new PairDataAnalyzedMessage(Pair.UserData.UID)); Mediator.Publish(new PairDataAnalyzedMessage(Pair.UserData.UID));
_analysisCts.CancelDispose(); CancelAndDispose(ref _analysisCts);
_analysisCts = null;
if (print) PrintAnalysis(); if (print) PrintAnalysis();
} }
@@ -114,8 +112,8 @@ public sealed class PairAnalyzer : DisposableMediatorSubscriberBase
if (!disposing) return; if (!disposing) return;
_analysisCts?.CancelDispose(); CancelAndDispose(ref _analysisCts);
_baseAnalysisCts.CancelDispose(); CancelAndDispose(ref _baseAnalysisCts);
} }
private async Task BaseAnalysis(CharacterData charaData, CancellationToken token) 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.OriginalSize))),
UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.CompressedSize)))); UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.CompressedSize))));
} }
}
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;
}
}

View File

@@ -21,7 +21,6 @@ public class PartyListTypingService : DisposableMediatorSubscriberBase
private readonly PairManager _pairManager; private readonly PairManager _pairManager;
private readonly TypingIndicatorStateService _typingStateService; private readonly TypingIndicatorStateService _typingStateService;
private static readonly TimeSpan TypingDisplayTime = TimeSpan.FromSeconds(2); private static readonly TimeSpan TypingDisplayTime = TimeSpan.FromSeconds(2);
private static readonly TimeSpan TypingDisplayDelay = TimeSpan.FromMilliseconds(500);
private static readonly TimeSpan TypingDisplayFade = TypingDisplayTime; private static readonly TimeSpan TypingDisplayFade = TypingDisplayTime;
public PartyListTypingService(ILogger<PartyListTypingService> logger, public PartyListTypingService(ILogger<PartyListTypingService> logger,

View File

@@ -23,7 +23,6 @@ public class PlayerPerformanceService : DisposableMediatorSubscriberBase
private readonly MareMediator _mediator; private readonly MareMediator _mediator;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService; private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
private readonly Dictionary<string, bool> _warnedForPlayers = new(StringComparer.Ordinal);
public PlayerPerformanceService(ILogger<PlayerPerformanceService> logger, MareMediator mediator, public PlayerPerformanceService(ILogger<PlayerPerformanceService> logger, MareMediator mediator,
ServerConfigurationManager serverConfigurationManager, ServerConfigurationManager serverConfigurationManager,
@@ -327,4 +326,4 @@ public class PlayerPerformanceService : DisposableMediatorSubscriberBase
return shrunken; return shrunken;
} }
} }

View File

@@ -43,7 +43,7 @@ public class UiFactory
public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair) public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair)
{ {
return new StandaloneProfileUi(_loggerFactory.CreateLogger<StandaloneProfileUi>(), _mareMediator, return new StandaloneProfileUi(_loggerFactory.CreateLogger<StandaloneProfileUi>(), _mareMediator,
_uiSharedService, _serverConfigManager, _mareProfileManager, _pairManager, pair, _performanceCollectorService); _uiSharedService, _serverConfigManager, _mareProfileManager, pair, _performanceCollectorService);
} }
public PermissionWindowUI CreatePermissionPopupUi(Pair pair) public PermissionWindowUI CreatePermissionPopupUi(Pair pair)

View File

@@ -4,10 +4,8 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin.Services;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
@@ -25,25 +23,28 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
{ {
private readonly MareConfigService _configService; private readonly MareConfigService _configService;
private readonly DalamudUtilService _dalamud; private readonly DalamudUtilService _dalamud;
private readonly IObjectTable _objectTable;
private readonly AutoDetectRequestService _requestService; private readonly AutoDetectRequestService _requestService;
private readonly NearbyDiscoveryService _discoveryService;
private readonly NearbyPendingService _pendingService; private readonly NearbyPendingService _pendingService;
private readonly PairManager _pairManager; private readonly PairManager _pairManager;
private List<Services.Mediator.NearbyEntry> _entries = new(); private List<Services.Mediator.NearbyEntry> _entries;
private readonly HashSet<string> _acceptInFlight = new(StringComparer.Ordinal); private readonly HashSet<string> _acceptInFlight = new(StringComparer.Ordinal);
public AutoDetectUi(ILogger<AutoDetectUi> logger, MareMediator mediator, public AutoDetectUi(ILogger<AutoDetectUi> logger, MareMediator mediator,
MareConfigService configService, DalamudUtilService dalamudUtilService, IObjectTable objectTable, MareConfigService configService, DalamudUtilService dalamudUtilService,
AutoDetectRequestService requestService, NearbyPendingService pendingService, PairManager pairManager, AutoDetectRequestService requestService, NearbyPendingService pendingService, PairManager pairManager,
NearbyDiscoveryService discoveryService,
PerformanceCollectorService performanceCollectorService) PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "AutoDetect", performanceCollectorService) : base(logger, mediator, "AutoDetect", performanceCollectorService)
{ {
_configService = configService; _configService = configService;
_dalamud = dalamudUtilService; _dalamud = dalamudUtilService;
_objectTable = objectTable;
_requestService = requestService; _requestService = requestService;
_pendingService = pendingService; _pendingService = pendingService;
_pairManager = pairManager; _pairManager = pairManager;
_discoveryService = discoveryService;
Mediator.Subscribe<Services.Mediator.DiscoveryListUpdated>(this, OnDiscoveryUpdated);
_entries = _discoveryService.SnapshotEntries();
Flags |= ImGuiWindowFlags.NoScrollbar; Flags |= ImGuiWindowFlags.NoScrollbar;
SizeConstraints = new WindowSizeConstraints() SizeConstraints = new WindowSizeConstraints()
@@ -90,6 +91,11 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
} }
} }
public void DrawInline()
{
DrawInternal();
}
private static void DrawStyledTab(string label, Vector4 accent, Vector4 inactive, Vector4 hover, Action draw, bool disabled = false) private static void DrawStyledTab(string label, Vector4 accent, Vector4 inactive, Vector4 hover, Action draw, bool disabled = false)
{ {
var tabColor = disabled ? ImGuiColors.DalamudGrey3 : inactive; var tabColor = disabled ? ImGuiColors.DalamudGrey3 : inactive;
@@ -213,61 +219,102 @@ public class AutoDetectUi : WindowMediatorSubscriberBase
ImGuiHelpers.ScaledDummy(6); ImGuiHelpers.ScaledDummy(6);
// Table header var sourceEntries = _entries.Count > 0 ? _entries : _discoveryService.SnapshotEntries();
if (ImGui.BeginTable("autodetect-nearby", 5, ImGuiTableFlags.SizingStretchProp)) var orderedEntries = sourceEntries
{ .OrderBy(e => float.IsNaN(e.Distance) ? float.MaxValue : e.Distance)
ImGui.TableSetupColumn("Name"); .ToList();
ImGui.TableSetupColumn("World");
ImGui.TableSetupColumn("Distance");
ImGui.TableSetupColumn("Status");
ImGui.TableSetupColumn("Action");
ImGui.TableHeadersRow();
var data = _entries.Count > 0 ? _entries.Where(e => e.IsMatch).ToList() : new List<Services.Mediator.NearbyEntry>(); if (orderedEntries.Count == 0)
foreach (var e in data) {
UiSharedService.ColorTextWrapped("Aucune présence UmbraSync détectée à proximité pour le moment.", ImGuiColors.DalamudGrey3);
return;
}
if (!ImGui.BeginTable("autodetect-nearby", 5, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.RowBg))
{
return;
}
ImGui.TableSetupColumn("Nom");
ImGui.TableSetupColumn("Monde");
ImGui.TableSetupColumn("Distance");
ImGui.TableSetupColumn("Statut");
ImGui.TableSetupColumn("Action");
ImGui.TableHeadersRow();
for (int i = 0; i < orderedEntries.Count; i++)
{
var entry = orderedEntries[i];
bool isMatch = entry.IsMatch;
bool alreadyPaired = IsAlreadyPairedByUidOrAlias(entry);
bool overDistance = !float.IsNaN(entry.Distance) && entry.Distance > maxDist;
bool canRequest = isMatch && entry.AcceptPairRequests && !string.IsNullOrEmpty(entry.Token) && !alreadyPaired;
string displayName = entry.DisplayName ?? entry.Name;
string worldName = entry.WorldId == 0
? "-"
: (_dalamud.WorldData.Value.TryGetValue(entry.WorldId, out var mappedWorld) ? mappedWorld : entry.WorldId.ToString(CultureInfo.InvariantCulture));
string distanceText = float.IsNaN(entry.Distance) ? "-" : $"{entry.Distance:0.0} m";
string status = alreadyPaired
? "Déjà appairé"
: overDistance
? $"Hors portée (> {maxDist} m)"
: !isMatch
? "Umbra non activé"
: !entry.AcceptPairRequests
? "Invitations refusées"
: string.IsNullOrEmpty(entry.Token)
? "Indisponible"
: "Disponible";
ImGui.TableNextColumn();
ImGui.TextUnformatted(displayName);
ImGui.TableNextColumn();
ImGui.TextUnformatted(worldName);
ImGui.TableNextColumn();
ImGui.TextUnformatted(distanceText);
ImGui.TableNextColumn();
ImGui.TextUnformatted(status);
ImGui.TableNextColumn();
using (ImRaii.PushId(i))
{ {
ImGui.TableNextColumn(); if (canRequest && !overDistance)
ImGui.TextUnformatted(e.Name);
ImGui.TableNextColumn();
ImGui.TextUnformatted(e.WorldId == 0 ? "-" : (_dalamud.WorldData.Value.TryGetValue(e.WorldId, out var w) ? w : e.WorldId.ToString()));
ImGui.TableNextColumn();
ImGui.TextUnformatted(float.IsNaN(e.Distance) ? "-" : $"{e.Distance:0.0} m");
ImGui.TableNextColumn();
bool alreadyPaired = IsAlreadyPairedByUidOrAlias(e);
string status = alreadyPaired ? "Paired" : (string.IsNullOrEmpty(e.Token) ? "Requests disabled" : "On Umbra");
ImGui.TextUnformatted(status);
ImGui.TableNextColumn();
using (ImRaii.Disabled(alreadyPaired || string.IsNullOrEmpty(e.Token)))
{ {
if (alreadyPaired) if (ImGui.Button("Envoyer invitation"))
{ {
ImGui.Button($"Already sync##{e.Name}"); _ = _requestService.SendRequestAsync(entry.Token!, entry.Uid, entry.DisplayName);
} }
else if (string.IsNullOrEmpty(e.Token)) UiSharedService.AttachToolTip("Envoie une demande d'appairage via AutoDetect.");
}
else
{
string reason = alreadyPaired
? "Vous êtes déjà appairé avec ce joueur."
: overDistance
? $"Ce joueur est au-delà de la distance maximale configurée ({maxDist} m)."
: !isMatch
? "Ce joueur n'utilise pas UmbraSync ou ne s'est pas rendu détectable."
: !entry.AcceptPairRequests
? "Ce joueur a désactivé la réception automatique des invitations."
: string.IsNullOrEmpty(entry.Token)
? "Impossible d'obtenir un jeton d'invitation pour ce joueur."
: string.Empty;
ImGui.TextDisabled(status);
if (!string.IsNullOrEmpty(reason))
{ {
ImGui.Button($"Requests disabled##{e.Name}"); UiSharedService.AttachToolTip(reason);
}
else if (ImGui.Button($"Send request##{e.Name}"))
{
_ = _requestService.SendRequestAsync(e.Token!, e.Uid, e.DisplayName);
} }
} }
} }
ImGui.EndTable();
} }
}
public override void OnOpen() ImGui.EndTable();
{
base.OnOpen();
Mediator.Subscribe<Services.Mediator.DiscoveryListUpdated>(this, OnDiscoveryUpdated);
}
public override void OnClose()
{
Mediator.Unsubscribe<Services.Mediator.DiscoveryListUpdated>(this);
base.OnClose();
} }
private void OnDiscoveryUpdated(Services.Mediator.DiscoveryListUpdated msg) private void OnDiscoveryUpdated(Services.Mediator.DiscoveryListUpdated msg)

View File

@@ -169,10 +169,6 @@ public sealed class ChangelogUi : WindowMediatorSubscriberBase
{ {
return new List<ChangelogEntry> return new List<ChangelogEntry>
{ {
new(new Version(0, 1, 9, 6), "0.1.9.6", new List<ChangelogLine>
{
new("Possibilité de désactiver l'alerte self-analysis (Settings => Performance)."),
}),
new(new Version(0, 1, 9, 5), "0.1.9.5", new List<ChangelogLine> new(new Version(0, 1, 9, 5), "0.1.9.5", new List<ChangelogLine>
{ {
new("Fix l'affichage de la bulle dans la liste du groupe."), new("Fix l'affichage de la bulle dans la liste du groupe."),

View File

@@ -544,7 +544,8 @@ internal sealed partial class CharaDataHubUi
{ {
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Download your Online Character Data from Server")) 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) 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")) if (_uiSharedService.IconTextButton(FontAwesomeIcon.Plus, "New Character Data Entry"))
{ {
_charaDataManager.CreateCharaDataEntry(_closalCts.Token); var cts = EnsureFreshCts(ref _closalCts);
_charaDataManager.CreateCharaDataEntry(cts.Token);
_selectNewEntry = true; _selectNewEntry = true;
} }
} }
@@ -848,4 +850,4 @@ internal sealed partial class CharaDataHubUi
} }
} }
} }
} }

View File

@@ -204,7 +204,8 @@ internal partial class CharaDataHubUi
{ {
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Update Data Shared With You")) 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) if (_charaDataManager.GetSharedWithYouTimeoutTask != null && !_charaDataManager.GetSharedWithYouTimeoutTask.IsCompleted)

View File

@@ -1,4 +1,5 @@
using Dalamud.Bindings.ImGui; using System;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.ImGuiFileDialog;
@@ -30,9 +31,9 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
private readonly CharaDataGposeTogetherManager _charaDataGposeTogetherManager; private readonly CharaDataGposeTogetherManager _charaDataGposeTogetherManager;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private CancellationTokenSource _closalCts = new(); private CancellationTokenSource? _closalCts = new();
private bool _disableUI = false; private bool _disableUI = false;
private CancellationTokenSource _disposalCts = new(); private CancellationTokenSource? _disposalCts = new();
private string _exportDescription = string.Empty; private string _exportDescription = string.Empty;
private string _filterCodeNote = string.Empty; private string _filterCodeNote = string.Empty;
private string _filterDescription = string.Empty; private string _filterDescription = string.Empty;
@@ -123,7 +124,14 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
return; return;
} }
_closalCts.Cancel(); try
{
_closalCts?.Cancel();
}
catch (ObjectDisposedException)
{
}
EnsureFreshCts(ref _closalCts);
SelectedDtoId = string.Empty; SelectedDtoId = string.Empty;
_filteredDict = null; _filteredDict = null;
_sharedWithYouOwnerFilter = string.Empty; _sharedWithYouOwnerFilter = string.Empty;
@@ -135,15 +143,15 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
public override void OnOpen() public override void OnOpen()
{ {
_closalCts = _closalCts.CancelRecreate(); EnsureFreshCts(ref _closalCts);
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
_closalCts.CancelDispose(); CancelAndDispose(ref _closalCts);
_disposalCts.CancelDispose(); CancelAndDispose(ref _disposalCts);
} }
base.Dispose(disposing); base.Dispose(disposing);
@@ -689,7 +697,8 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
{ {
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleDown, "Download your Character Data")) 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) if (_charaDataManager.DataGetTimeoutTask != null && !_charaDataManager.DataGetTimeoutTask.IsCompleted)
@@ -1104,4 +1113,26 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
drawAction(); drawAction();
if (_disableUI) ImGui.BeginDisabled(); if (_disableUI) ImGui.BeginDisabled();
} }
}
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,43 @@ public class DrawGroupPair : DrawPairBase
_serverConfigurationManager = serverConfigurationManager; _serverConfigurationManager = serverConfigurationManager;
} }
protected override float GetRightSideExtraWidth()
{
float width = 0f;
float spacing = ImGui.GetStyle().ItemSpacing.X;
var soundsDisabled = _fullInfoDto.GroupUserPermissions.IsDisableSounds();
var animDisabled = _fullInfoDto.GroupUserPermissions.IsDisableAnimations();
var vfxDisabled = _fullInfoDto.GroupUserPermissions.IsDisableVFX();
var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false);
var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false);
var individualVFXDisabled = (_pair.UserPair?.OwnPermissions.IsDisableVFX() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableVFX() ?? false);
bool showInfo = individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled || animDisabled || soundsDisabled || vfxDisabled;
bool showShared = _charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData);
bool showPlus = _pair.UserPair == null && _pair.IsOnline;
if (showShared)
{
width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X + spacing;
}
if (showInfo)
{
var icon = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled)
? FontAwesomeIcon.ExclamationTriangle
: FontAwesomeIcon.InfoCircle;
width += UiSharedService.GetIconSize(icon).X + spacing;
}
if (showPlus)
{
width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X + spacing;
}
return width;
}
protected override void DrawLeftSide(float textPosY, float originalY) protected override void DrawLeftSide(float textPosY, float originalY)
{ {
var entryUID = _pair.UserData.AliasOrUID; var entryUID = _pair.UserData.AliasOrUID;
@@ -164,7 +201,7 @@ public class DrawGroupPair : DrawPairBase
var individualVFXDisabled = (_pair.UserPair?.OwnPermissions.IsDisableVFX() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableVFX() ?? false); var individualVFXDisabled = (_pair.UserPair?.OwnPermissions.IsDisableVFX() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableVFX() ?? false);
bool showShared = _charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData); bool showShared = _charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData);
bool showInfo = (individualAnimDisabled || individualSoundsDisabled || animDisabled || soundsDisabled); bool showInfo = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled || animDisabled || soundsDisabled || vfxDisabled);
bool showPlus = _pair.UserPair == null && _pair.IsOnline; bool showPlus = _pair.UserPair == null && _pair.IsOnline;
bool showBars = (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) || !_pair.IsPaused; bool showBars = (userIsOwner || (userIsModerator && !entryIsMod && !entryIsOwner)) || !_pair.IsPaused;
bool showPause = true; bool showPause = true;
@@ -173,23 +210,47 @@ public class DrawGroupPair : DrawPairBase
var permIcon = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled) ? FontAwesomeIcon.ExclamationTriangle var permIcon = (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled) ? FontAwesomeIcon.ExclamationTriangle
: ((soundsDisabled || animDisabled || vfxDisabled) ? FontAwesomeIcon.InfoCircle : FontAwesomeIcon.None); : ((soundsDisabled || animDisabled || vfxDisabled) ? FontAwesomeIcon.InfoCircle : FontAwesomeIcon.None);
var runningIconWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X; var runningIconWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X;
var infoIconWidth = UiSharedService.GetIconSize(permIcon).X; var infoIconWidth = showInfo && permIcon != FontAwesomeIcon.None ? UiSharedService.GetIconSize(permIcon).X : 0f;
var plusButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X; var plusButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Plus).X;
var pauseIcon = _fullInfoDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseIcon = _fullInfoDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseButtonWidth = _uiSharedService.GetIconButtonSize(pauseIcon).X; var pauseButtonWidth = _uiSharedService.GetIconButtonSize(pauseIcon).X;
var barButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X; var barButtonWidth = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X;
var pos = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() + spacing float totalWidth = 0f;
- (showShared ? (runningIconWidth + spacing) : 0) void Accumulate(bool condition, float width)
- (showInfo ? (infoIconWidth + spacing) : 0) {
- (showPlus ? (plusButtonWidth + spacing) : 0) if (!condition || width <= 0f) return;
- (showPause ? (pauseButtonWidth + spacing) : 0) if (totalWidth > 0f) totalWidth += spacing;
- (showBars ? (barButtonWidth + spacing) : 0); totalWidth += width;
}
ImGui.SameLine(pos); Accumulate(showShared, runningIconWidth);
Accumulate(showInfo && infoIconWidth > 0f, infoIconWidth);
Accumulate(showPlus, plusButtonWidth);
Accumulate(showPause, pauseButtonWidth);
if (showBars)
{
if (totalWidth > 0f) totalWidth += spacing;
totalWidth += barButtonWidth;
}
if (showPause && showBars)
{
totalWidth -= spacing * 0.5f;
if (totalWidth < 0f) totalWidth = 0f;
}
float cardPaddingX = UiSharedService.GetCardContentPaddingX();
float rightMargin = cardPaddingX + 6f * ImGuiHelpers.GlobalScale;
float baseX = MathF.Max(ImGui.GetCursorPosX(),
ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - rightMargin - totalWidth);
float currentX = baseX;
ImGui.SameLine();
ImGui.SetCursorPosX(baseX);
if (showShared) if (showShared)
{ {
ImGui.SetCursorPosY(textPosY);
_uiSharedService.IconText(FontAwesomeIcon.Running); _uiSharedService.IconText(FontAwesomeIcon.Running);
UiSharedService.AttachToolTip($"This user has shared {sharedData!.Count} Character Data Sets with you." + UiSharedService.TooltipSeparator UiSharedService.AttachToolTip($"This user has shared {sharedData!.Count} Character Data Sets with you." + UiSharedService.TooltipSeparator
@@ -199,95 +260,99 @@ public class DrawGroupPair : DrawPairBase
{ {
_mediator.Publish(new OpenCharaDataHubWithFilterMessage(_pair.UserData)); _mediator.Publish(new OpenCharaDataHubWithFilterMessage(_pair.UserData));
} }
ImGui.SameLine(); currentX += runningIconWidth + spacing;
ImGui.SetCursorPosX(currentX);
} }
if (individualAnimDisabled || individualSoundsDisabled) if (showInfo && infoIconWidth > 0f)
{ {
ImGui.SetCursorPosY(textPosY); ImGui.SetCursorPosY(textPosY);
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); if (individualAnimDisabled || individualSoundsDisabled || individualVFXDisabled)
_uiSharedService.IconText(permIcon);
ImGui.PopStyleColor();
if (ImGui.IsItemHovered())
{ {
ImGui.BeginTooltip(); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow);
_uiSharedService.IconText(permIcon);
ImGui.TextUnformatted("Individual User permissions"); ImGui.PopStyleColor();
if (ImGui.IsItemHovered())
if (individualSoundsDisabled)
{ {
var userSoundsText = "Sound sync disabled with " + _pair.UserData.AliasOrUID; ImGui.BeginTooltip();
_uiSharedService.IconText(FontAwesomeIcon.VolumeMute);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userSoundsText);
ImGui.NewLine();
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted("You: " + (_pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled"));
}
if (individualAnimDisabled) ImGui.TextUnformatted("Individual User permissions");
{
var userAnimText = "Animation sync disabled with " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.WindowClose);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userAnimText);
ImGui.NewLine();
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted("You: " + (_pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled"));
}
if (individualVFXDisabled) if (individualSoundsDisabled)
{ {
var userVFXText = "VFX sync disabled with " + _pair.UserData.AliasOrUID; var userSoundsText = "Sound sync disabled with " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.TimesCircle); _uiSharedService.IconText(FontAwesomeIcon.VolumeMute);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userVFXText); ImGui.TextUnformatted(userSoundsText);
ImGui.NewLine(); ImGui.NewLine();
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted("You: " + (_pair.UserPair!.OwnPermissions.IsDisableVFX() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableVFX() ? "Disabled" : "Enabled")); ImGui.TextUnformatted("You: " + (_pair.UserPair!.OwnPermissions.IsDisableSounds() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableSounds() ? "Disabled" : "Enabled"));
} }
ImGui.EndTooltip(); if (individualAnimDisabled)
{
var userAnimText = "Animation sync disabled with " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.WindowClose);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userAnimText);
ImGui.NewLine();
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted("You: " + (_pair.UserPair!.OwnPermissions.IsDisableAnimations() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableAnimations() ? "Disabled" : "Enabled"));
}
if (individualVFXDisabled)
{
var userVFXText = "VFX sync disabled with " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.TimesCircle);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userVFXText);
ImGui.NewLine();
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted("You: " + (_pair.UserPair!.OwnPermissions.IsDisableVFX() ? "Disabled" : "Enabled") + ", They: " + (_pair.UserPair!.OtherPermissions.IsDisableVFX() ? "Disabled" : "Enabled"));
}
ImGui.EndTooltip();
}
} }
ImGui.SameLine(); else
}
else if ((animDisabled || soundsDisabled))
{
ImGui.SetCursorPosY(textPosY);
_uiSharedService.IconText(permIcon);
if (ImGui.IsItemHovered())
{ {
ImGui.BeginTooltip(); _uiSharedService.IconText(permIcon);
if (ImGui.IsItemHovered())
ImGui.TextUnformatted("Syncshell User permissions");
if (soundsDisabled)
{ {
var userSoundsText = "Sound sync disabled by " + _pair.UserData.AliasOrUID; ImGui.BeginTooltip();
_uiSharedService.IconText(FontAwesomeIcon.VolumeMute);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userSoundsText);
}
if (animDisabled) ImGui.TextUnformatted("Syncshell User permissions");
{
var userAnimText = "Animation sync disabled by " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.WindowClose);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userAnimText);
}
if (vfxDisabled) if (soundsDisabled)
{ {
var userVFXText = "VFX sync disabled by " + _pair.UserData.AliasOrUID; var userSoundsText = "Sound sync disabled by " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.TimesCircle); _uiSharedService.IconText(FontAwesomeIcon.VolumeMute);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale); ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userVFXText); ImGui.TextUnformatted(userSoundsText);
} }
ImGui.EndTooltip(); if (animDisabled)
{
var userAnimText = "Animation sync disabled by " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.WindowClose);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userAnimText);
}
if (vfxDisabled)
{
var userVFXText = "VFX sync disabled by " + _pair.UserData.AliasOrUID;
_uiSharedService.IconText(FontAwesomeIcon.TimesCircle);
ImGui.SameLine(40 * ImGuiHelpers.GlobalScale);
ImGui.TextUnformatted(userVFXText);
}
ImGui.EndTooltip();
}
} }
ImGui.SameLine();
currentX += infoIconWidth + spacing;
ImGui.SetCursorPosX(currentX);
} }
if (showPlus) if (showPlus)
@@ -303,13 +368,14 @@ public class DrawGroupPair : DrawPairBase
} }
} }
UiSharedService.AttachToolTip(AppendSeenInfo("Send pairing invite to " + entryUID)); UiSharedService.AttachToolTip(AppendSeenInfo("Send pairing invite to " + entryUID));
ImGui.SameLine(); currentX += plusButtonWidth + spacing;
ImGui.SetCursorPosX(currentX);
} }
if (showPause) if (showPause)
{ {
float gapToBars = showBars ? spacing * 0.5f : spacing;
ImGui.SetCursorPosY(originalY); ImGui.SetCursorPosY(originalY);
if (_uiSharedService.IconButton(pauseIcon)) if (_uiSharedService.IconButton(pauseIcon))
{ {
var newPermissions = _fullInfoDto.GroupUserPermissions ^ GroupUserPermissions.Paused; var newPermissions = _fullInfoDto.GroupUserPermissions ^ GroupUserPermissions.Paused;
@@ -318,19 +384,20 @@ public class DrawGroupPair : DrawPairBase
} }
UiSharedService.AttachToolTip(AppendSeenInfo((_fullInfoDto.GroupUserPermissions.IsPaused() ? "Resume" : "Pause") + " syncing with " + entryUID)); UiSharedService.AttachToolTip(AppendSeenInfo((_fullInfoDto.GroupUserPermissions.IsPaused() ? "Resume" : "Pause") + " syncing with " + entryUID));
ImGui.SameLine(); currentX += pauseButtonWidth + gapToBars;
ImGui.SetCursorPosX(currentX);
} }
if (showBars) if (showBars)
{ {
ImGui.SetCursorPosY(originalY); ImGui.SetCursorPosY(originalY);
if (_uiSharedService.IconButton(FontAwesomeIcon.Bars)) if (_uiSharedService.IconButton(FontAwesomeIcon.Bars))
{ {
ImGui.OpenPopup("Popup"); ImGui.OpenPopup("Syncshell Flyout Menu");
} }
currentX += barButtonWidth;
ImGui.SetCursorPosX(currentX);
} }
if (ImGui.BeginPopup("Popup")) if (ImGui.BeginPopup("Popup"))
{ {
if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner)) if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner))
@@ -416,7 +483,7 @@ public class DrawGroupPair : DrawPairBase
ImGui.EndPopup(); ImGui.EndPopup();
} }
return pos - spacing; return baseX - spacing;
} }
private string AppendSeenInfo(string tooltip) private string AppendSeenInfo(string tooltip)

View File

@@ -1,5 +1,8 @@
using Dalamud.Bindings.ImGui; using System;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility;
using MareSynchronos.PlayerData.Pairs; using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.UI.Handlers; using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI; using MareSynchronos.WebAPI;
@@ -28,38 +31,77 @@ public abstract class DrawPairBase
public void DrawPairedClient() public void DrawPairedClient()
{ {
var originalY = ImGui.GetCursorPosY(); var style = ImGui.GetStyle();
var pauseIconSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Play); var padding = style.FramePadding;
var textSize = ImGui.CalcTextSize(_pair.UserData.AliasOrUID); var spacing = style.ItemSpacing;
var rowStartCursor = ImGui.GetCursorPos();
var rowStartScreen = ImGui.GetCursorScreenPos();
var startPos = ImGui.GetCursorStartPos(); var pauseButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Pause);
var playButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Play);
var menuButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var framePadding = ImGui.GetStyle().FramePadding; float pauseClusterWidth = Math.Max(pauseButtonSize.X, playButtonSize.X);
var lineHeight = textSize.Y + framePadding.Y * 2; float pauseClusterHeight = Math.Max(pauseButtonSize.Y, playButtonSize.Y);
float reservedSpacing = style.ItemSpacing.X * 2.4f;
float rightButtonWidth =
menuButtonSize.X +
pauseClusterWidth +
reservedSpacing +
GetRightSideExtraWidth();
var off = startPos.Y; float availableWidth = Math.Max(ImGui.GetContentRegionAvail().X - rightButtonWidth, 1f);
var height = UiSharedService.GetWindowContentRegionHeight(); float textHeight = ImGui.GetFontSize();
var presenceIconSize = UiSharedService.GetIconSize(FontAwesomeIcon.Moon);
float iconHeight = presenceIconSize.Y;
float contentHeight = Math.Max(textHeight, Math.Max(iconHeight, pauseClusterHeight));
float rowHeight = contentHeight + padding.Y * 2f;
float totalHeight = rowHeight + spacing.Y;
if ((originalY + off) < -lineHeight || (originalY + off) > height) var origin = ImGui.GetCursorStartPos();
var top = origin.Y + rowStartCursor.Y;
var bottom = top + totalHeight;
var visibleHeight = UiSharedService.GetWindowContentRegionHeight();
if (bottom < 0 || top > visibleHeight)
{ {
ImGui.Dummy(new System.Numerics.Vector2(0f, lineHeight)); ImGui.SetCursorPos(new Vector2(rowStartCursor.X, rowStartCursor.Y + totalHeight));
return; return;
} }
var textPosY = originalY + pauseIconSize.Y / 2 - textSize.Y / 2; var drawList = ImGui.GetWindowDrawList();
DrawLeftSide(textPosY, originalY); var backgroundColor = new Vector4(0.10f, 0.10f, 0.14f, 0.95f);
var borderColor = new Vector4(0f, 0f, 0f, 0.9f);
float rounding = Math.Max(style.FrameRounding, 7f * ImGuiHelpers.GlobalScale);
var panelMin = rowStartScreen + new Vector2(0f, spacing.Y * 0.15f);
var panelMax = panelMin + new Vector2(availableWidth, rowHeight - spacing.Y * 0.3f);
drawList.AddRectFilled(panelMin, panelMax, ImGui.ColorConvertFloat4ToU32(backgroundColor), rounding);
drawList.AddRect(panelMin, panelMax, ImGui.ColorConvertFloat4ToU32(borderColor), rounding);
float iconTop = rowStartCursor.Y + (rowHeight - iconHeight) / 2f;
float textTop = rowStartCursor.Y + (rowHeight - textHeight) / 2f - padding.Y * 0.6f;
float buttonTop = rowStartCursor.Y + (rowHeight - pauseClusterHeight) / 2f;
ImGui.SetCursorPos(new Vector2(rowStartCursor.X + padding.X, iconTop));
DrawLeftSide(iconTop, iconTop);
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetCursorPosY(textTop);
var posX = ImGui.GetCursorPosX(); var posX = ImGui.GetCursorPosX();
var rightSide = DrawRightSide(textPosY, originalY); var rightSide = DrawRightSide(buttonTop, buttonTop);
DrawName(originalY, posX, rightSide); DrawName(textTop + padding.Y * 0.15f, posX, rightSide);
ImGui.SetCursorPos(new Vector2(rowStartCursor.X, rowStartCursor.Y + totalHeight));
ImGui.SetCursorPosX(rowStartCursor.X);
} }
protected abstract void DrawLeftSide(float textPosY, float originalY); protected abstract void DrawLeftSide(float textPosY, float originalY);
protected abstract float DrawRightSide(float textPosY, float originalY); protected abstract float DrawRightSide(float textPosY, float originalY);
protected virtual float GetRightSideExtraWidth() => 0f;
private void DrawName(float originalY, float leftSide, float rightSide) private void DrawName(float originalY, float leftSide, float rightSide)
{ {
_displayHandler.DrawPairText(_id, _pair, leftSide, originalY, () => rightSide - leftSide); _displayHandler.DrawPairText(_id, _pair, leftSide, originalY, () => rightSide - leftSide);
} }
} }

View File

@@ -41,6 +41,28 @@ public class DrawUserPair : DrawPairBase
public bool IsVisible => _pair.IsVisible; public bool IsVisible => _pair.IsVisible;
public UserPairDto UserPair => _pair.UserPair!; public UserPairDto UserPair => _pair.UserPair!;
protected override float GetRightSideExtraWidth()
{
float width = 0f;
var spacingX = ImGui.GetStyle().ItemSpacing.X;
var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false);
var individualAnimDisabled = (_pair.UserPair?.OwnPermissions.IsDisableAnimations() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableAnimations() ?? false);
var individualVFXDisabled = (_pair.UserPair?.OwnPermissions.IsDisableVFX() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableVFX() ?? false);
if (individualSoundsDisabled || individualAnimDisabled || individualVFXDisabled)
{
width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.ExclamationTriangle).X + spacingX * 0.5f;
}
if (_charaDataManager.SharedWithYouData.TryGetValue(_pair.UserData, out var sharedData))
{
width += _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Running).X + spacingX * 0.5f;
}
return width;
}
protected override void DrawLeftSide(float textPosY, float originalY) protected override void DrawLeftSide(float textPosY, float originalY)
{ {
var online = _pair.IsOnline; var online = _pair.IsOnline;
@@ -110,7 +132,8 @@ public class DrawUserPair : DrawPairBase
var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars); var barButtonSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var entryUID = _pair.UserData.AliasOrUID; var entryUID = _pair.UserData.AliasOrUID;
var spacingX = ImGui.GetStyle().ItemSpacing.X; var spacingX = ImGui.GetStyle().ItemSpacing.X;
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); var edgePadding = UiSharedService.GetCardContentPaddingX() + 6f * ImGuiHelpers.GlobalScale;
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - edgePadding;
var rightSidePos = windowEndX - barButtonSize.X; var rightSidePos = windowEndX - barButtonSize.X;
// Flyout Menu // Flyout Menu

View File

@@ -30,7 +30,6 @@ internal sealed class GroupPanel
private readonly CompactUi _mainUi; private readonly CompactUi _mainUi;
private readonly PairManager _pairManager; private readonly PairManager _pairManager;
private readonly ChatService _chatService; private readonly ChatService _chatService;
private readonly MareConfigService _mareConfig;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly CharaDataManager _charaDataManager; private readonly CharaDataManager _charaDataManager;
private readonly AutoDetectRequestService _autoDetectRequestService; private readonly AutoDetectRequestService _autoDetectRequestService;
@@ -75,7 +74,7 @@ internal sealed class GroupPanel
private string _syncShellToJoin = string.Empty; private string _syncShellToJoin = string.Empty;
public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, ChatService chatServivce, public GroupPanel(CompactUi mainUi, UiSharedService uiShared, PairManager pairManager, ChatService chatServivce,
UidDisplayHandler uidDisplayHandler, MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager, UidDisplayHandler uidDisplayHandler, ServerConfigurationManager serverConfigurationManager,
CharaDataManager charaDataManager, AutoDetectRequestService autoDetectRequestService) CharaDataManager charaDataManager, AutoDetectRequestService autoDetectRequestService)
{ {
_mainUi = mainUi; _mainUi = mainUi;
@@ -83,7 +82,6 @@ internal sealed class GroupPanel
_pairManager = pairManager; _pairManager = pairManager;
_chatService = chatServivce; _chatService = chatServivce;
_uidDisplayHandler = uidDisplayHandler; _uidDisplayHandler = uidDisplayHandler;
_mareConfig = mareConfig;
_serverConfigurationManager = serverConfigurationManager; _serverConfigurationManager = serverConfigurationManager;
_charaDataManager = charaDataManager; _charaDataManager = charaDataManager;
_autoDetectRequestService = autoDetectRequestService; _autoDetectRequestService = autoDetectRequestService;
@@ -93,6 +91,7 @@ internal sealed class GroupPanel
public void DrawSyncshells() public void DrawSyncshells()
{ {
using var fontScale = UiSharedService.PushFontScale(UiSharedService.ContentFontScale);
using (ImRaii.PushId("addsyncshell")) DrawAddSyncshell(); using (ImRaii.PushId("addsyncshell")) DrawAddSyncshell();
using (ImRaii.PushId("syncshelllist")) DrawSyncshellList(); using (ImRaii.PushId("syncshelllist")) DrawSyncshellList();
_mainUi.TransferPartHeight = ImGui.GetCursorPosY(); _mainUi.TransferPartHeight = ImGui.GetCursorPosY();
@@ -313,19 +312,20 @@ internal sealed class GroupPanel
int shellNumber = _serverConfigurationManager.GetShellNumberForGid(groupDto.GID); int shellNumber = _serverConfigurationManager.GetShellNumberForGid(groupDto.GID);
var name = groupDto.Group.Alias ?? groupDto.GID; var name = groupDto.Group.Alias ?? groupDto.GID;
if (!_expandedGroupState.TryGetValue(groupDto.GID, out bool isExpanded)) if (!_expandedGroupState.ContainsKey(groupDto.GID))
{ {
isExpanded = false; _expandedGroupState[groupDto.GID] = false;
_expandedGroupState.Add(groupDto.GID, isExpanded);
} }
var icon = isExpanded ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight; UiSharedService.DrawCard($"syncshell-card-{groupDto.GID}", () =>
_uiShared.IconText(icon);
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{ {
_expandedGroupState[groupDto.GID] = !_expandedGroupState[groupDto.GID]; bool expandedState = _expandedGroupState[groupDto.GID];
UiSharedService.DrawArrowToggle(ref expandedState, $"##syncshell-toggle-{groupDto.GID}");
if (expandedState != _expandedGroupState[groupDto.GID])
{
_expandedGroupState[groupDto.GID] = expandedState;
} }
ImGui.SameLine(); ImGui.SameLine(0f, 6f * ImGuiHelpers.GlobalScale);
var textIsGid = true; var textIsGid = true;
string groupName = groupDto.GroupAliasOrGID; string groupName = groupDto.GroupAliasOrGID;
@@ -547,7 +547,7 @@ internal sealed class GroupPanel
bool hideOfflineUsers = pairsInGroup.Count > 1000; bool hideOfflineUsers = pairsInGroup.Count > 1000;
ImGui.Indent(20); ImGui.Indent(20);
if (_expandedGroupState[groupDto.GID]) if (expandedState)
{ {
var sortedPairs = pairsInGroup var sortedPairs = pairsInGroup
.OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal)) .OrderByDescending(u => string.Equals(u.UserData.UID, groupDto.OwnerUID, StringComparison.Ordinal))
@@ -614,6 +614,9 @@ internal sealed class GroupPanel
ImGui.Separator(); ImGui.Separator();
} }
ImGui.Unindent(20); ImGui.Unindent(20);
}, stretchWidth: true);
ImGuiHelpers.ScaledDummy(4f);
} }
private void DrawSyncShellButtons(GroupFullInfoDto groupDto, List<Pair> groupPairs) private void DrawSyncShellButtons(GroupFullInfoDto groupDto, List<Pair> groupPairs)
@@ -644,7 +647,8 @@ internal sealed class GroupPanel
var isOwner = string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal); var isOwner = string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal);
var spacingX = ImGui.GetStyle().ItemSpacing.X; var spacingX = ImGui.GetStyle().ItemSpacing.X;
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth(); var cardPaddingX = UiSharedService.GetCardContentPaddingX();
var windowEndX = ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth() - cardPaddingX - 6f * ImGuiHelpers.GlobalScale;
var pauseIcon = groupDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseIcon = groupDto.GroupUserPermissions.IsPaused() ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var pauseIconSize = _uiShared.GetIconButtonSize(pauseIcon); var pauseIconSize = _uiShared.GetIconButtonSize(pauseIcon);
@@ -828,9 +832,22 @@ internal sealed class GroupPanel
private void DrawSyncshellList() private void DrawSyncshellList()
{ {
var ySize = _mainUi.TransferPartHeight == 0 float availableHeight = ImGui.GetContentRegionAvail().Y;
? 1 float ySize;
: (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - _mainUi.TransferPartHeight - ImGui.GetCursorPosY(); if (_mainUi.TransferPartHeight <= 0)
{
float reserve = ImGui.GetFrameHeightWithSpacing() * 2f;
ySize = availableHeight - reserve;
if (ySize <= 0)
{
ySize = System.Math.Max(availableHeight, 1f);
}
}
else
{
ySize = (ImGui.GetWindowContentRegionMax().Y - ImGui.GetWindowContentRegionMin().Y) - _mainUi.TransferPartHeight - ImGui.GetCursorPosY();
}
ImGui.BeginChild("list", new Vector2(_mainUi.WindowContentWidth, ySize), border: false); ImGui.BeginChild("list", new Vector2(_mainUi.WindowContentWidth, ySize), border: false);
foreach (var entry in _pairManager.GroupPairs.OrderBy(g => g.Key.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase).ToList()) foreach (var entry in _pairManager.GroupPairs.OrderBy(g => g.Key.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase).ToList())
{ {

View File

@@ -1,10 +1,14 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using MareSynchronos.API.Data.Extensions; using MareSynchronos.API.Data.Extensions;
using MareSynchronos.MareConfiguration; using MareSynchronos.MareConfiguration;
using MareSynchronos.UI.Handlers; using MareSynchronos.UI.Handlers;
using MareSynchronos.WebAPI; using MareSynchronos.WebAPI;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MareSynchronos.UI.Components; namespace MareSynchronos.UI.Components;
@@ -28,7 +32,7 @@ public class PairGroupsUi
_uiSharedService = uiSharedService; _uiSharedService = uiSharedService;
} }
public void Draw<T>(List<T> visibleUsers, List<T> onlineUsers, List<T> offlineUsers) where T : DrawPairBase public void Draw<T>(List<T> visibleUsers, List<T> onlineUsers, List<T> offlineUsers, Action? drawVisibleExtras = null) where T : DrawPairBase
{ {
// Only render those tags that actually have pairs in them, otherwise // Only render those tags that actually have pairs in them, otherwise
// we can end up with a bunch of useless pair groups // we can end up with a bunch of useless pair groups
@@ -36,7 +40,7 @@ public class PairGroupsUi
var allUsers = onlineUsers.Concat(offlineUsers).ToList(); var allUsers = onlineUsers.Concat(offlineUsers).ToList();
if (typeof(T) == typeof(DrawUserPair)) if (typeof(T) == typeof(DrawUserPair))
{ {
DrawUserPairs(tagsWithPairsInThem, allUsers.Cast<DrawUserPair>().ToList(), visibleUsers.Cast<DrawUserPair>(), onlineUsers.Cast<DrawUserPair>(), offlineUsers.Cast<DrawUserPair>()); DrawUserPairs(tagsWithPairsInThem, allUsers.Cast<DrawUserPair>().ToList(), visibleUsers.Cast<DrawUserPair>(), onlineUsers.Cast<DrawUserPair>(), offlineUsers.Cast<DrawUserPair>(), drawVisibleExtras);
} }
} }
@@ -44,14 +48,15 @@ public class PairGroupsUi
{ {
var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused()); var allArePaused = availablePairsInThisTag.All(pair => pair.UserPair!.OwnPermissions.IsPaused());
var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause; var pauseButton = allArePaused ? FontAwesomeIcon.Play : FontAwesomeIcon.Pause;
var flyoutMenuX = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X; var flyoutMenuSize = _uiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
var pauseButtonX = _uiSharedService.GetIconButtonSize(pauseButton).X; var pauseButtonSize = _uiSharedService.GetIconButtonSize(pauseButton);
var windowX = ImGui.GetWindowContentRegionMin().X;
var windowWidth = UiSharedService.GetWindowContentRegionWidth();
var spacingX = ImGui.GetStyle().ItemSpacing.X; var spacingX = ImGui.GetStyle().ItemSpacing.X;
var currentX = ImGui.GetCursorPosX();
var availableWidth = ImGui.GetContentRegionAvail().X;
var buttonsWidth = pauseButtonSize.X + flyoutMenuSize.X + spacingX;
var pauseStart = Math.Max(currentX, currentX + availableWidth - buttonsWidth);
var buttonPauseOffset = windowX + windowWidth - flyoutMenuX - spacingX - pauseButtonX; ImGui.SameLine(pauseStart);
ImGui.SameLine(buttonPauseOffset);
if (_uiSharedService.IconButton(pauseButton)) if (_uiSharedService.IconButton(pauseButton))
{ {
if (allArePaused) if (allArePaused)
@@ -72,8 +77,8 @@ public class PairGroupsUi
UiSharedService.AttachToolTip($"Pause pairing with all pairs in {tag}"); UiSharedService.AttachToolTip($"Pause pairing with all pairs in {tag}");
} }
var buttonDeleteOffset = windowX + windowWidth - flyoutMenuX; var menuStart = Math.Max(pauseStart + pauseButtonSize.X + spacingX, currentX);
ImGui.SameLine(buttonDeleteOffset); ImGui.SameLine(menuStart);
if (_uiSharedService.IconButton(FontAwesomeIcon.Bars)) if (_uiSharedService.IconButton(FontAwesomeIcon.Bars))
{ {
ImGui.OpenPopup("Group Flyout Menu"); ImGui.OpenPopup("Group Flyout Menu");
@@ -86,7 +91,7 @@ public class PairGroupsUi
} }
} }
private void DrawCategory(string tag, IEnumerable<DrawPairBase> onlineUsers, IEnumerable<DrawPairBase> allUsers, IEnumerable<DrawPairBase>? visibleUsers = null) private void DrawCategory(string tag, IEnumerable<DrawPairBase> onlineUsers, IEnumerable<DrawPairBase> allUsers, IEnumerable<DrawPairBase>? visibleUsers = null, Action? drawExtraContent = null)
{ {
IEnumerable<DrawPairBase> usersInThisTag; IEnumerable<DrawPairBase> usersInThisTag;
HashSet<string>? otherUidsTaggedWithTag = null; HashSet<string>? otherUidsTaggedWithTag = null;
@@ -108,26 +113,25 @@ public class PairGroupsUi
if (isSpecialTag && !usersInThisTag.Any()) return; if (isSpecialTag && !usersInThisTag.Any()) return;
DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count); UiSharedService.DrawCard($"pair-group-{tag}", () =>
if (!isSpecialTag)
{ {
using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList()); DrawName(tag, isSpecialTag, visibleInThisTag, usersInThisTag.Count(), otherUidsTaggedWithTag?.Count);
} if (!isSpecialTag)
else
{
if (!_tagHandler.IsTagOpen(tag))
{ {
var size = ImGui.CalcTextSize("").Y + ImGui.GetStyle().FramePadding.Y * 2f; using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList());
ImGui.SameLine();
ImGui.Dummy(new(size, size));
} }
}
if (!_tagHandler.IsTagOpen(tag)) return; if (!_tagHandler.IsTagOpen(tag)) return;
ImGui.Indent(20); ImGuiHelpers.ScaledDummy(4f);
DrawPairs(tag, usersInThisTag); var indent = 18f * ImGuiHelpers.GlobalScale;
ImGui.Unindent(20); ImGui.Indent(indent);
DrawPairs(tag, usersInThisTag);
drawExtraContent?.Invoke();
ImGui.Unindent(indent);
}, stretchWidth: true);
ImGuiHelpers.ScaledDummy(4f);
} }
private void DrawGroupMenu(string tag) private void DrawGroupMenu(string tag)
@@ -157,17 +161,21 @@ public class PairGroupsUi
}; };
string resultFolderName = !isSpecialTag ? $"{displayedName} ({visible}/{online}/{total} Pairs)" : $"{displayedName} ({online} Pairs)"; string resultFolderName = !isSpecialTag ? $"{displayedName} ({visible}/{online}/{total} Pairs)" : $"{displayedName} ({online} Pairs)";
var icon = _tagHandler.IsTagOpen(tag) ? FontAwesomeIcon.CaretSquareDown : FontAwesomeIcon.CaretSquareRight; bool isOpen = _tagHandler.IsTagOpen(tag);
_uiSharedService.IconText(icon); bool previousState = isOpen;
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) UiSharedService.DrawArrowToggle(ref isOpen, $"##group-toggle-{tag}");
if (isOpen != previousState)
{ {
ToggleTagOpen(tag); _tagHandler.SetTagOpen(tag, isOpen);
} }
ImGui.SameLine(); ImGui.SameLine(0f, 6f * ImGuiHelpers.GlobalScale);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(resultFolderName); ImGui.TextUnformatted(resultFolderName);
if (ImGui.IsItemClicked(ImGuiMouseButton.Left)) if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
{ {
ToggleTagOpen(tag); bool newState = !_tagHandler.IsTagOpen(tag);
_tagHandler.SetTagOpen(tag, newState);
isOpen = newState;
} }
if (!isSpecialTag && ImGui.IsItemHovered()) if (!isSpecialTag && ImGui.IsItemHovered())
@@ -186,15 +194,11 @@ public class PairGroupsUi
{ {
// These are all the OtherUIDs that are tagged with this tag // These are all the OtherUIDs that are tagged with this tag
_uidDisplayHandler.RenderPairList(availablePairsInThisCategory); _uidDisplayHandler.RenderPairList(availablePairsInThisCategory);
ImGui.Separator();
} }
private void DrawUserPairs(List<string> tagsWithPairsInThem, List<DrawUserPair> allUsers, IEnumerable<DrawUserPair> visibleUsers, IEnumerable<DrawUserPair> onlineUsers, IEnumerable<DrawUserPair> offlineUsers) private void DrawUserPairs(List<string> tagsWithPairsInThem, List<DrawUserPair> allUsers, IEnumerable<DrawUserPair> visibleUsers, IEnumerable<DrawUserPair> onlineUsers, IEnumerable<DrawUserPair> offlineUsers, Action? drawVisibleExtras)
{ {
if (_mareConfig.Current.ShowVisibleUsersSeparately) // Visible section intentionally omitted for Individual Pairs view.
{
using (ImRaii.PushId("$group-VisibleCustomTag")) DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers);
}
foreach (var tag in tagsWithPairsInThem) foreach (var tag in tagsWithPairsInThem)
{ {
if (_mareConfig.Current.ShowOfflineUsersSeparately) if (_mareConfig.Current.ShowOfflineUsersSeparately)
@@ -242,9 +246,4 @@ public class PairGroupsUi
} }
} }
private void ToggleTagOpen(string tag) }
{
bool open = !_tagHandler.IsTagOpen(tag);
_tagHandler.SetTagOpen(tag, open);
}
}

View File

@@ -9,6 +9,7 @@ using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Numerics; using System.Numerics;
using System;
namespace MareSynchronos.UI; namespace MareSynchronos.UI;
@@ -20,7 +21,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private readonly Dictionary<string, string[]> _texturesToConvert = new(StringComparer.Ordinal); private readonly Dictionary<string, string[]> _texturesToConvert = new(StringComparer.Ordinal);
private Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>>? _cachedAnalysis; private Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>>? _cachedAnalysis;
private CancellationTokenSource _conversionCancellationTokenSource = new(); private CancellationTokenSource? _conversionCancellationTokenSource = new();
private string _conversionCurrentFileName = string.Empty; private string _conversionCurrentFileName = string.Empty;
private int _conversionCurrentFileProgress = 0; private int _conversionCurrentFileProgress = 0;
private Task? _conversionTask; private Task? _conversionTask;
@@ -74,7 +75,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
UiSharedService.TextWrapped("Current file: " + _conversionCurrentFileName); UiSharedService.TextWrapped("Current file: " + _conversionCurrentFileName);
if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion")) if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion"))
{ {
_conversionCancellationTokenSource.Cancel(); TryCancel(_conversionCancellationTokenSource);
} }
UiSharedService.SetScaledWindowSize(500); UiSharedService.SetScaledWindowSize(500);
ImGui.EndPopup(); ImGui.EndPopup();
@@ -294,8 +295,8 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
, ImGuiColors.DalamudYellow); , ImGuiColors.DalamudYellow);
if (_texturesToConvert.Count > 0 && _uiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)")) if (_texturesToConvert.Count > 0 && _uiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)"))
{ {
_conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate(); var conversionCts = EnsureFreshCts(ref _conversionCancellationTokenSource);
_conversionTask = _ipcManager.Penumbra.ConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token); _conversionTask = _ipcManager.Penumbra.ConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, conversionCts.Token);
} }
} }
} }
@@ -354,8 +355,13 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (disposing)
{
CancelAndDispose(ref _conversionCancellationTokenSource);
_conversionProgress.ProgressChanged -= ConversionProgress_ProgressChanged;
}
base.Dispose(disposing); base.Dispose(disposing);
_conversionProgress.ProgressChanged -= ConversionProgress_ProgressChanged;
} }
private void ConversionProgress_ProgressChanged(object? sender, (string, int) e) private void ConversionProgress_ProgressChanged(object? sender, (string, int) e)
@@ -489,4 +495,31 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
} }
} }
} }
}
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)
{
}
}
}

View File

@@ -22,7 +22,6 @@ public class EditProfileUi : WindowMediatorSubscriberBase
private readonly FileDialogManager _fileDialogManager; private readonly FileDialogManager _fileDialogManager;
private readonly MareProfileManager _mareProfileManager; private readonly MareProfileManager _mareProfileManager;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private readonly ServerConfigurationManager _serverConfigurationManager;
private bool _adjustedForScollBarsLocalProfile = false; private bool _adjustedForScollBarsLocalProfile = false;
private bool _adjustedForScollBarsOnlineProfile = false; private bool _adjustedForScollBarsOnlineProfile = false;
private string _descriptionText = string.Empty; private string _descriptionText = string.Empty;
@@ -34,7 +33,6 @@ public class EditProfileUi : WindowMediatorSubscriberBase
public EditProfileUi(ILogger<EditProfileUi> logger, MareMediator mediator, public EditProfileUi(ILogger<EditProfileUi> logger, MareMediator mediator,
ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager, ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager,
ServerConfigurationManager serverConfigurationManager,
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService) MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "Umbra Edit Profile###UmbraSyncEditProfileUI", performanceCollectorService) : base(logger, mediator, "Umbra Edit Profile###UmbraSyncEditProfileUI", performanceCollectorService)
{ {
@@ -47,7 +45,6 @@ public class EditProfileUi : WindowMediatorSubscriberBase
_apiController = apiController; _apiController = apiController;
_uiSharedService = uiSharedService; _uiSharedService = uiSharedService;
_fileDialogManager = fileDialogManager; _fileDialogManager = fileDialogManager;
_serverConfigurationManager = serverConfigurationManager;
_mareProfileManager = mareProfileManager; _mareProfileManager = mareProfileManager;
Mediator.Subscribe<GposeStartMessage>(this, (_) => { _wasOpen = IsOpen; IsOpen = false; }); Mediator.Subscribe<GposeStartMessage>(this, (_) => { _wasOpen = IsOpen; IsOpen = false; });
@@ -64,6 +61,16 @@ public class EditProfileUi : WindowMediatorSubscriberBase
} }
protected override void DrawInternal() protected override void DrawInternal()
{
DrawProfileContent();
}
public void DrawInline()
{
DrawProfileContent();
}
private void DrawProfileContent()
{ {
_uiSharedService.BigText("Current Profile (as saved on server)"); _uiSharedService.BigText("Current Profile (as saved on server)");
ImGuiHelpers.ScaledDummy(new Vector2(0f, ImGui.GetStyle().ItemSpacing.Y / 2)); ImGuiHelpers.ScaledDummy(new Vector2(0f, ImGui.GetStyle().ItemSpacing.Y / 2));

View File

@@ -144,12 +144,12 @@ public partial class IntroUi : WindowMediatorSubscriberBase
} }
ImGui.Separator(); ImGui.Separator();
ImGui.SetWindowFontScale(1.5f); UiSharedService.SetFontScale(1.5f);
string readThis = "MERCI DE LIRE ATTENTIVEMENT"; string readThis = "MERCI DE LIRE ATTENTIVEMENT";
Vector2 textSize = ImGui.CalcTextSize(readThis); Vector2 textSize = ImGui.CalcTextSize(readThis);
ImGui.SetCursorPosX(ImGui.GetWindowSize().X / 2 - textSize.X / 2); ImGui.SetCursorPosX(ImGui.GetWindowSize().X / 2 - textSize.X / 2);
UiSharedService.ColorText(readThis, UiSharedService.AccentColor); UiSharedService.ColorText(readThis, UiSharedService.AccentColor);
ImGui.SetWindowFontScale(1.0f); UiSharedService.SetFontScale(1.0f);
ImGui.Separator(); ImGui.Separator();
UiSharedService.TextWrapped(""" UiSharedService.TextWrapped("""
Pour utiliser les services UmbraSync, vous devez être â de plus de 18 ans, plus de 21 ans dans certaines juridictions. Pour utiliser les services UmbraSync, vous devez être â de plus de 18 ans, plus de 21 ans dans certaines juridictions.

View File

@@ -16,25 +16,22 @@ namespace MareSynchronos.UI;
public class PopoutProfileUi : WindowMediatorSubscriberBase public class PopoutProfileUi : WindowMediatorSubscriberBase
{ {
private readonly MareProfileManager _mareProfileManager; private readonly MareProfileManager _mareProfileManager;
private readonly PairManager _pairManager;
private readonly ServerConfigurationManager _serverManager; private readonly ServerConfigurationManager _serverManager;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private Vector2 _lastMainPos = Vector2.Zero; private Vector2 _lastMainPos = Vector2.Zero;
private Vector2 _lastMainSize = Vector2.Zero; private Vector2 _lastMainSize = Vector2.Zero;
private byte[] _lastProfilePicture = []; private byte[] _lastProfilePicture = [];
private byte[] _lastSupporterPicture = [];
private Pair? _pair; private Pair? _pair;
private IDalamudTextureWrap? _supporterTextureWrap; private IDalamudTextureWrap? _supporterTextureWrap;
private IDalamudTextureWrap? _textureWrap; private IDalamudTextureWrap? _textureWrap;
public PopoutProfileUi(ILogger<PopoutProfileUi> logger, MareMediator mediator, UiSharedService uiSharedService, public PopoutProfileUi(ILogger<PopoutProfileUi> logger, MareMediator mediator, UiSharedService uiSharedService,
ServerConfigurationManager serverManager, MareConfigService mareConfigService, 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; _uiSharedService = uiSharedService;
_serverManager = serverManager; _serverManager = serverManager;
_mareProfileManager = mareProfileManager; _mareProfileManager = mareProfileManager;
_pairManager = pairManager;
Flags = ImGuiWindowFlags.NoDecoration; Flags = ImGuiWindowFlags.NoDecoration;
Mediator.Subscribe<ProfilePopoutToggle>(this, (msg) => Mediator.Subscribe<ProfilePopoutToggle>(this, (msg) =>
@@ -42,7 +39,6 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase
IsOpen = msg.Pair != null; IsOpen = msg.Pair != null;
_pair = msg.Pair; _pair = msg.Pair;
_lastProfilePicture = []; _lastProfilePicture = [];
_lastSupporterPicture = [];
_textureWrap?.Dispose(); _textureWrap?.Dispose();
_textureWrap = null; _textureWrap = null;
_supporterTextureWrap?.Dispose(); _supporterTextureWrap?.Dispose();
@@ -182,4 +178,4 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase
_logger.LogWarning(ex, "Error during draw tooltip"); _logger.LogWarning(ex, "Error during draw tooltip");
} }
} }
} }

View File

@@ -131,6 +131,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
DrawSettingsContent(); DrawSettingsContent();
} }
public void DrawInline()
{
DrawSettingsContent();
}
public override void OnClose() public override void OnClose()
{ {
_uiShared.EditTrackerPosition = false; _uiShared.EditTrackerPosition = false;
@@ -529,9 +534,9 @@ public class SettingsUi : WindowMediatorSubscriberBase
}, globalChatTypeIdx); }, globalChatTypeIdx);
_uiShared.DrawHelpText("FFXIV chat channel to output chat messages on."); _uiShared.DrawHelpText("FFXIV chat channel to output chat messages on.");
ImGui.SetWindowFontScale(0.6f); UiSharedService.SetFontScale(0.6f);
_uiShared.BigText("\"Chat 2\" Plugin Integration"); _uiShared.BigText("\"Chat 2\" Plugin Integration");
ImGui.SetWindowFontScale(1.0f); UiSharedService.SetFontScale(1.0f);
var extraChatTags = _configService.Current.ExtraChatTags; var extraChatTags = _configService.Current.ExtraChatTags;
if (ImGui.Checkbox("Tag messages as ExtraChat", ref extraChatTags)) if (ImGui.Checkbox("Tag messages as ExtraChat", ref extraChatTags))
@@ -572,9 +577,9 @@ public class SettingsUi : WindowMediatorSubscriberBase
if (shellEnabled) if (shellEnabled)
shellName = $"[{shellNumber}] {shellName}"; shellName = $"[{shellNumber}] {shellName}";
ImGui.SetWindowFontScale(0.6f); UiSharedService.SetFontScale(0.6f);
_uiShared.BigText(shellName); _uiShared.BigText(shellName);
ImGui.SetWindowFontScale(1.0f); UiSharedService.SetFontScale(1.0f);
using var pushIndent = ImRaii.PushIndent(); using var pushIndent = ImRaii.PushIndent();
@@ -1002,7 +1007,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
_lastTab = "General"; _lastTab = "General";
_uiShared.BigText("Notes"); _uiShared.BigText("Notes");
if (_uiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard")) if (_uiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
{ {
ImGui.SetClipboardText(UiSharedService.GetNotes(_pairManager.DirectPairs.UnionBy(_pairManager.GroupPairs.SelectMany(p => p.Value), p => p.UserData, UserDataComparer.Instance).ToList())); ImGui.SetClipboardText(UiSharedService.GetNotes(_pairManager.DirectPairs.UnionBy(_pairManager.GroupPairs.SelectMany(p => p.Value), p => p.UserData, UserDataComparer.Instance).ToList()));

View File

@@ -16,7 +16,6 @@ namespace MareSynchronos.UI;
public class StandaloneProfileUi : WindowMediatorSubscriberBase public class StandaloneProfileUi : WindowMediatorSubscriberBase
{ {
private readonly MareProfileManager _mareProfileManager; private readonly MareProfileManager _mareProfileManager;
private readonly PairManager _pairManager;
private readonly ServerConfigurationManager _serverManager; private readonly ServerConfigurationManager _serverManager;
private readonly UiSharedService _uiSharedService; private readonly UiSharedService _uiSharedService;
private bool _adjustedForScrollBars = false; private bool _adjustedForScrollBars = false;
@@ -24,7 +23,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
private IDalamudTextureWrap? _textureWrap; private IDalamudTextureWrap? _textureWrap;
public StandaloneProfileUi(ILogger<StandaloneProfileUi> logger, MareMediator mediator, UiSharedService uiBuilder, public StandaloneProfileUi(ILogger<StandaloneProfileUi> logger, MareMediator mediator, UiSharedService uiBuilder,
ServerConfigurationManager serverManager, MareProfileManager mareProfileManager, PairManager pairManager, Pair pair, ServerConfigurationManager serverManager, MareProfileManager mareProfileManager, Pair pair,
PerformanceCollectorService performanceCollector) PerformanceCollectorService performanceCollector)
: base(logger, mediator, "Profile of " + pair.UserData.AliasOrUID + "##UmbraSyncStandaloneProfileUI" + pair.UserData.AliasOrUID, performanceCollector) : base(logger, mediator, "Profile of " + pair.UserData.AliasOrUID + "##UmbraSyncStandaloneProfileUI" + pair.UserData.AliasOrUID, performanceCollector)
{ {
@@ -32,7 +31,6 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
_serverManager = serverManager; _serverManager = serverManager;
_mareProfileManager = mareProfileManager; _mareProfileManager = mareProfileManager;
Pair = pair; Pair = pair;
_pairManager = pairManager;
Flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.AlwaysAutoResize; Flags = ImGuiWindowFlags.NoResize | ImGuiWindowFlags.AlwaysAutoResize;
var spacing = ImGui.GetStyle().ItemSpacing; var spacing = ImGui.GetStyle().ItemSpacing;
@@ -164,4 +162,4 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
{ {
Mediator.Publish(new RemoveWindowMessage(this)); Mediator.Publish(new RemoveWindowMessage(this));
} }
} }

View File

@@ -29,7 +29,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
private static readonly TimeSpan TypingDisplayDelay = TimeSpan.FromMilliseconds(500); private static readonly TimeSpan TypingDisplayDelay = TimeSpan.FromMilliseconds(500);
private static readonly TimeSpan TypingDisplayFade = TypingDisplayTime; private static readonly TimeSpan TypingDisplayFade = TypingDisplayTime;
private readonly ILogger<TypingIndicatorOverlay> _logger; private readonly ILogger<TypingIndicatorOverlay> _typedLogger;
private readonly MareConfigService _configService; private readonly MareConfigService _configService;
private readonly IGameGui _gameGui; private readonly IGameGui _gameGui;
private readonly ITextureProvider _textureProvider; private readonly ITextureProvider _textureProvider;
@@ -47,7 +47,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
TypingIndicatorStateService typingStateService, ApiController apiController) TypingIndicatorStateService typingStateService, ApiController apiController)
: base(logger, mediator, nameof(TypingIndicatorOverlay), performanceCollectorService) : base(logger, mediator, nameof(TypingIndicatorOverlay), performanceCollectorService)
{ {
_logger = logger; _typedLogger = logger;
_configService = configService; _configService = configService;
_gameGui = gameGui; _gameGui = gameGui;
_textureProvider = textureProvider; _textureProvider = textureProvider;
@@ -216,7 +216,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
if (objectId != uint.MaxValue && objectId != 0 && TryDrawNameplateBubble(drawList, iconWrap, objectId)) 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; continue;
} }
@@ -228,20 +228,20 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
if (pair == null) 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); uid, objectId, pairName, pairIdent);
if (hasWorldPosition) if (hasWorldPosition)
{ {
DrawWorldFallbackIcon(drawList, iconWrap, worldPos); 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 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)) if (TryGetWorldPosition(objectId, out position))
{ {
_logger.LogTrace("TypingIndicator: resolved by objectId {objectId}", objectId); _typedLogger.LogTrace("TypingIndicator: resolved by objectId {objectId}", objectId);
return true; return true;
} }
@@ -402,7 +402,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
var name = pair.PlayerName; var name = pair.PlayerName;
if (!string.IsNullOrEmpty(name) && TryGetWorldPositionByName(name!, out position)) 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; return true;
} }
@@ -412,7 +412,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
var cached = _dalamudUtil.FindPlayerByNameHash(ident); var cached = _dalamudUtil.FindPlayerByNameHash(ident);
if (!string.IsNullOrEmpty(cached.Name) && TryGetWorldPositionByName(cached.Name, out position)) 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; return true;
} }
@@ -422,7 +422,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
if (objRef != null) if (objRef != null)
{ {
position = objRef.Position; 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; return true;
} }
} }
@@ -432,7 +432,7 @@ public sealed class TypingIndicatorOverlay : WindowMediatorSubscriberBase
var alias = userData.AliasOrUID; var alias = userData.AliasOrUID;
if (!string.IsNullOrEmpty(alias) && TryGetWorldPositionByName(alias, out position)) 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; return true;
} }

View File

@@ -20,6 +20,8 @@ using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.WebAPI; using MareSynchronos.WebAPI;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@@ -36,6 +38,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollbar |
ImGuiWindowFlags.NoScrollWithMouse; ImGuiWindowFlags.NoScrollWithMouse;
public const float ContentFontScale = 0.92f;
public static Vector4 AccentColor { get; set; } = ImGuiColors.DalamudViolet; public static Vector4 AccentColor { get; set; } = ImGuiColors.DalamudViolet;
public static Vector4 AccentHoverColor { get; set; } = new Vector4(0x3A / 255f, 0x15 / 255f, 0x50 / 255f, 1f); public static Vector4 AccentHoverColor { get; set; } = new Vector4(0x3A / 255f, 0x15 / 255f, 0x50 / 255f, 1f);
public static Vector4 AccentActiveColor { get; set; } = AccentHoverColor; public static Vector4 AccentActiveColor { get; set; } = AccentHoverColor;
@@ -59,6 +63,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
private readonly Dictionary<string, object> _selectedComboItems = new(StringComparer.Ordinal); private readonly Dictionary<string, object> _selectedComboItems = new(StringComparer.Ordinal);
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private bool _cacheDirectoryHasOtherFilesThanCache = false; private bool _cacheDirectoryHasOtherFilesThanCache = false;
private static readonly Stack<float> _fontScaleStack = new();
private static float _currentWindowFontScale = 1f;
private bool _cacheDirectoryIsValidPath = true; private bool _cacheDirectoryIsValidPath = true;
@@ -117,7 +123,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
{ {
e.OnPreBuild(tk => tk.AddDalamudAssetFont(Dalamud.DalamudAsset.NotoSansJpMedium, new() e.OnPreBuild(tk => tk.AddDalamudAssetFont(Dalamud.DalamudAsset.NotoSansJpMedium, new()
{ {
SizePx = 35, SizePx = 27,
GlyphRanges = [0x20, 0x7E, 0] GlyphRanges = [0x20, 0x7E, 0]
})); }));
}); });
@@ -216,7 +222,38 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0; public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
public static void DrawGrouped(Action imguiDrawAction, float rounding = 5f, float? expectedWidth = null) public static IDisposable PushFontScale(float scale)
{
var previous = _currentWindowFontScale;
_fontScaleStack.Push(previous);
if (Math.Abs(previous - scale) > float.Epsilon)
{
SetFontScale(scale);
}
return new FontScaleScope();
}
private sealed class FontScaleScope : IDisposable
{
public void Dispose()
{
if (_fontScaleStack.Count == 0) return;
var previous = _fontScaleStack.Pop();
if (Math.Abs(previous - _currentWindowFontScale) > float.Epsilon)
{
SetFontScale(previous);
}
}
}
public static void SetFontScale(float scale)
{
ImGui.SetWindowFontScale(scale);
_currentWindowFontScale = scale;
}
public static void DrawGrouped(Action imguiDrawAction, float rounding = 5f, float? expectedWidth = null, bool drawBorder = true)
{ {
var cursorPos = ImGui.GetCursorPos(); var cursorPos = ImGui.GetCursorPos();
using (ImRaii.Group()) using (ImRaii.Group())
@@ -230,10 +267,128 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
imguiDrawAction.Invoke(); imguiDrawAction.Invoke();
} }
ImGui.GetWindowDrawList().AddRect( if (drawBorder)
ImGui.GetItemRectMin() - ImGui.GetStyle().ItemInnerSpacing, {
ImGui.GetItemRectMax() + ImGui.GetStyle().ItemInnerSpacing, ImGui.GetWindowDrawList().AddRect(
Color(ImGuiColors.DalamudGrey2), rounding); ImGui.GetItemRectMin() - ImGui.GetStyle().ItemInnerSpacing,
ImGui.GetItemRectMax() + ImGui.GetStyle().ItemInnerSpacing,
Color(ImGuiColors.DalamudGrey2), rounding);
}
}
public static void DrawCard(string id, Action draw, Vector2? padding = null, Vector4? background = null,
Vector4? border = null, float? rounding = null, bool stretchWidth = false)
{
var style = ImGui.GetStyle();
var padBase = style.FramePadding;
var pad = padding ?? new Vector2(
padBase.X + 4f * ImGuiHelpers.GlobalScale,
padBase.Y + 3f * ImGuiHelpers.GlobalScale);
var cardBg = background ?? new Vector4(0.08f, 0.08f, 0.10f, 0.94f);
var cardBorder = border ?? new Vector4(0f, 0f, 0f, 0.85f);
float cardRounding = rounding ?? Math.Max(style.FrameRounding, 8f * ImGuiHelpers.GlobalScale);
float borderThickness = Math.Max(1f, Math.Max(style.FrameBorderSize, 1f) * ImGuiHelpers.GlobalScale);
float borderInset = borderThickness;
var originalCursor = ImGui.GetCursorPos();
if (stretchWidth)
{
ImGui.SetCursorPosX(ImGui.GetWindowContentRegionMin().X);
}
var startCursor = ImGui.GetCursorPos();
var drawList = ImGui.GetWindowDrawList();
drawList.ChannelsSplit(2);
drawList.ChannelsSetCurrent(1);
ImGui.PushID(id);
ImGui.SetCursorPos(new Vector2(startCursor.X + pad.X, startCursor.Y + pad.Y));
ImGui.BeginGroup();
draw();
ImGui.EndGroup();
ImGui.PopID();
var contentMin = ImGui.GetItemRectMin();
var contentMax = ImGui.GetItemRectMax();
var cardMin = contentMin - pad;
var cardMax = contentMax + pad;
var outerMin = cardMin;
var outerMax = cardMax;
if (stretchWidth)
{
var windowPos = ImGui.GetWindowPos();
var regionMin = ImGui.GetWindowContentRegionMin();
var regionMax = ImGui.GetWindowContentRegionMax();
var scrollX = ImGui.GetScrollX();
cardMin.X = windowPos.X + regionMin.X + scrollX;
cardMax.X = windowPos.X + regionMax.X + scrollX;
outerMin.X = cardMin.X;
outerMax.X = cardMax.X;
startCursor.X = ImGui.GetWindowContentRegionMin().X;
}
var drawMin = new Vector2(cardMin.X + borderInset, cardMin.Y + borderInset);
var drawMax = new Vector2(cardMax.X - borderInset, cardMax.Y - borderInset);
var clipMin = drawList.GetClipRectMin();
var clipMax = drawList.GetClipRectMax();
var clipInset = new Vector2(borderThickness * 0.5f + 0.5f, borderThickness * 0.5f + 0.5f);
drawMin = Vector2.Max(drawMin, clipMin + clipInset);
drawMax = Vector2.Min(drawMax, clipMax - clipInset);
if (drawMax.X <= drawMin.X)
{
drawMax.X = drawMin.X + borderThickness;
}
if (drawMax.Y <= drawMin.Y)
{
drawMax.Y = drawMin.Y + borderThickness;
}
drawList.ChannelsSetCurrent(0);
drawList.AddRectFilled(drawMin, drawMax, ImGui.ColorConvertFloat4ToU32(cardBg), cardRounding);
if (cardBorder.W > 0f && borderThickness > 0f)
{
drawList.AddRect(drawMin, drawMax, ImGui.ColorConvertFloat4ToU32(cardBorder), cardRounding, ImDrawFlags.None, borderThickness);
}
drawList.ChannelsMerge();
ImGui.SetCursorPos(startCursor);
var dummyWidth = outerMax.X - outerMin.X;
var dummyHeight = outerMax.Y - outerMin.Y;
ImGui.Dummy(new Vector2(dummyWidth, dummyHeight));
ImGui.SetCursorPos(new Vector2(startCursor.X, startCursor.Y + dummyHeight));
if (!stretchWidth)
{
ImGui.SetCursorPosX(originalCursor.X);
}
else
{
ImGui.SetCursorPosX(startCursor.X);
}
}
public static bool DrawArrowToggle(ref bool state, string id)
{
var framePadding = ImGui.GetStyle().FramePadding;
ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(framePadding.X, framePadding.Y * 0.85f));
ImGui.PushStyleColor(ImGuiCol.Button, Vector4.Zero);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, new Vector4(1f, 1f, 1f, 0.08f));
ImGui.PushStyleColor(ImGuiCol.ButtonActive, new Vector4(1f, 1f, 1f, 0.16f));
bool clicked = ImGui.ArrowButton(id, state ? ImGuiDir.Down : ImGuiDir.Right);
ImGui.PopStyleColor(3);
ImGui.PopStyleVar();
if (clicked)
{
state = !state;
}
return state;
}
public static float GetCardContentPaddingX()
{
var style = ImGui.GetStyle();
return style.FramePadding.X + 4f * ImGuiHelpers.GlobalScale;
} }
public static void DrawGroupedCenteredColorText(string text, Vector4 color, float? maxWidth = null) public static void DrawGroupedCenteredColorText(string text, Vector4 color, float? maxWidth = null)
@@ -775,9 +930,9 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
if (intro) if (intro)
{ {
ImGui.SetWindowFontScale(0.8f); SetFontScale(0.8f);
BigText("Mandatory Plugins"); BigText("Mandatory Plugins");
ImGui.SetWindowFontScale(1.0f); SetFontScale(1.0f);
} }
else else
{ {
@@ -798,9 +953,9 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
if (intro) if (intro)
{ {
ImGui.SetWindowFontScale(0.8f); SetFontScale(0.8f);
BigText("Optional Addons"); BigText("Optional Addons");
ImGui.SetWindowFontScale(1.0f); SetFontScale(1.0f);
UiSharedService.TextWrapped("These addons are not required for basic operation, but without them you may not see others as intended."); UiSharedService.TextWrapped("These addons are not required for basic operation, but without them you may not see others as intended.");
} }
else else

View File

@@ -1,6 +1,6 @@
namespace MareSynchronos.Utils; 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[] _magicSignature = [137, 80, 78, 71, 13, 10, 26, 10];
private static readonly byte[] _IHDR = [(byte)'I', (byte)'H', (byte)'D', (byte)'R']; private static readonly byte[] _IHDR = [(byte)'I', (byte)'H', (byte)'D', (byte)'R'];

View File

@@ -4,7 +4,6 @@ using MareSynchronos.Services;
using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using MareSynchronos.WebAPI.SignalR; using MareSynchronos.WebAPI.SignalR;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Http.Json; using System.Net.Http.Json;
using System.Reflection; using System.Reflection;
@@ -15,17 +14,15 @@ namespace MareSynchronos.WebAPI;
public sealed class AccountRegistrationService : IDisposable public sealed class AccountRegistrationService : IDisposable
{ {
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly ILogger<AccountRegistrationService> _logger;
private readonly ServerConfigurationManager _serverManager; private readonly ServerConfigurationManager _serverManager;
private string GenerateSecretKey() private static string GenerateSecretKey()
{ {
return Convert.ToHexString(SHA256.HashData(RandomNumberGenerator.GetBytes(64))); return Convert.ToHexString(SHA256.HashData(RandomNumberGenerator.GetBytes(64)));
} }
public AccountRegistrationService(ILogger<AccountRegistrationService> logger, ServerConfigurationManager serverManager) public AccountRegistrationService(ServerConfigurationManager serverManager)
{ {
_logger = logger;
_serverManager = serverManager; _serverManager = serverManager;
_httpClient = new( _httpClient = new(
new HttpClientHandler new HttpClientHandler
@@ -67,4 +64,4 @@ public sealed class AccountRegistrationService : IDisposable
SecretKey = secretKey SecretKey = secretKey
}; };
} }
} }

View File

@@ -17,19 +17,16 @@ namespace MareSynchronos.WebAPI.Files;
public sealed class FileUploadManager : DisposableMediatorSubscriberBase public sealed class FileUploadManager : DisposableMediatorSubscriberBase
{ {
private readonly FileCacheManager _fileDbManager; private readonly FileCacheManager _fileDbManager;
private readonly MareConfigService _mareConfigService;
private readonly FileTransferOrchestrator _orchestrator; private readonly FileTransferOrchestrator _orchestrator;
private readonly ServerConfigurationManager _serverManager; private readonly ServerConfigurationManager _serverManager;
private readonly Dictionary<string, DateTime> _verifiedUploadedHashes = new(StringComparer.Ordinal); private readonly Dictionary<string, DateTime> _verifiedUploadedHashes = new(StringComparer.Ordinal);
private CancellationTokenSource? _uploadCancellationTokenSource = new(); private CancellationTokenSource? _uploadCancellationTokenSource = new();
public FileUploadManager(ILogger<FileUploadManager> logger, MareMediator mediator, public FileUploadManager(ILogger<FileUploadManager> logger, MareMediator mediator,
MareConfigService mareConfigService,
FileTransferOrchestrator orchestrator, FileTransferOrchestrator orchestrator,
FileCacheManager fileDbManager, FileCacheManager fileDbManager,
ServerConfigurationManager serverManager) : base(logger, mediator) ServerConfigurationManager serverManager) : base(logger, mediator)
{ {
_mareConfigService = mareConfigService;
_orchestrator = orchestrator; _orchestrator = orchestrator;
_fileDbManager = fileDbManager; _fileDbManager = fileDbManager;
_serverManager = serverManager; _serverManager = serverManager;
@@ -286,4 +283,4 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase
CurrentUploads.Clear(); CurrentUploads.Clear();
} }
} }

View File

@@ -19,6 +19,6 @@ public class DownloadFileTransfer : FileTransfer
get => Dto.Size; get => Dto.Size;
} }
public long TotalRaw => 0; // XXX public long TotalRaw => Dto.Size;
private DownloadFileDto Dto => (DownloadFileDto)TransferDto; private DownloadFileDto Dto => (DownloadFileDto)TransferDto;
} }

View File

@@ -231,7 +231,7 @@ public partial class ApiController
public Task Client_GposeLobbyPushWorldData(UserData userData, WorldData worldData) 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))); ExecuteSafely(() => Mediator.Publish(new GPoseLobbyReceiveWorldData(userData, worldData)));
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -415,4 +415,4 @@ public partial class ApiController
Logger.LogCritical(ex, "Error on executing safely"); Logger.LogCritical(ex, "Error on executing safely");
} }
} }
} }