From 1d99102c7324c7249b7fd32af5be552ae3b0e1f1 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 20 Mar 2024 15:12:19 +0100 Subject: [PATCH] fix some shit, add triangle count --- MareSynchronos/.editorconfig | 9 +++ MareSynchronos/FileCache/CacheMonitor.cs | 2 + .../Configurations/TransientConfig.cs | 2 +- .../TriangleCalculationConfig.cs | 9 +++ .../TransientConfigService.cs | 2 +- .../TriangleCalculationConfigService.cs | 12 ++++ MareSynchronos/MarePlugin.cs | 6 +- .../Factories/PairHandlerFactory.cs | 6 +- .../PlayerData/Factories/PlayerDataFactory.cs | 4 +- .../PlayerData/Handlers/PairHandler.cs | 31 +++++--- MareSynchronos/PlayerData/Pairs/Pair.cs | 1 + MareSynchronos/Plugin.cs | 7 +- MareSynchronos/Services/CharacterAnalyzer.cs | 23 ++++-- .../Services/CommandManagerService.cs | 2 +- MareSynchronos/Services/ModelAnalyzer.cs | 72 +++++++++++++++++++ MareSynchronos/UI/Components/DrawUserPair.cs | 10 ++- .../Components/Popup/BanUserPopupHandler.cs | 4 +- .../UI/Components/Popup/PopupHandler.cs | 6 +- .../UI/Components/SelectTagForPairUi.cs | 4 +- MareSynchronos/UI/DataAnalysisUi.cs | 19 ++++- MareSynchronos/UI/GposeUi.cs | 5 +- .../WebAPI/Files/FileDownloadManager.cs | 6 +- 22 files changed, 196 insertions(+), 46 deletions(-) create mode 100644 MareSynchronos/MareConfiguration/Configurations/TriangleCalculationConfig.cs create mode 100644 MareSynchronos/MareConfiguration/TriangleCalculationConfigService.cs create mode 100644 MareSynchronos/Services/ModelAnalyzer.cs diff --git a/MareSynchronos/.editorconfig b/MareSynchronos/.editorconfig index a21012b..f6abd9f 100644 --- a/MareSynchronos/.editorconfig +++ b/MareSynchronos/.editorconfig @@ -100,3 +100,12 @@ dotnet_diagnostic.MA0075.severity = silent # S3358: Ternary operators should not be nested dotnet_diagnostic.S3358.severity = suggestion + +# S6678: Use PascalCase for named placeholders +dotnet_diagnostic.S6678.severity = suggestion + +# S6605: Collection-specific "Exists" method should be used instead of the "Any" extension +dotnet_diagnostic.S6605.severity = suggestion + +# S6667: Logging in a catch clause should pass the caught exception as a parameter. +dotnet_diagnostic.S6667.severity = suggestion diff --git a/MareSynchronos/FileCache/CacheMonitor.cs b/MareSynchronos/FileCache/CacheMonitor.cs index 852c144..73d9f95 100644 --- a/MareSynchronos/FileCache/CacheMonitor.cs +++ b/MareSynchronos/FileCache/CacheMonitor.cs @@ -5,6 +5,7 @@ using MareSynchronos.Services.Mediator; using MareSynchronos.Utils; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; +using System.Diagnostics; namespace MareSynchronos.FileCache; @@ -25,6 +26,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase FileCacheManager fileDbManager, MareMediator mediator, PerformanceCollectorService performanceCollector, DalamudUtilService dalamudUtil, FileCompactor fileCompactor) : base(logger, mediator) { + Logger.LogInformation("Creating CacheMonitor from {trace}", Environment.StackTrace); _ipcManager = ipcManager; _configService = configService; _fileDbManager = fileDbManager; diff --git a/MareSynchronos/MareConfiguration/Configurations/TransientConfig.cs b/MareSynchronos/MareConfiguration/Configurations/TransientConfig.cs index 59ed8a5..668dc2b 100644 --- a/MareSynchronos/MareConfiguration/Configurations/TransientConfig.cs +++ b/MareSynchronos/MareConfiguration/Configurations/TransientConfig.cs @@ -4,4 +4,4 @@ public class TransientConfig : IMareConfiguration { public Dictionary> PlayerPersistentTransientCache { get; set; } = new(StringComparer.Ordinal); public int Version { get; set; } = 0; -} \ No newline at end of file +} diff --git a/MareSynchronos/MareConfiguration/Configurations/TriangleCalculationConfig.cs b/MareSynchronos/MareConfiguration/Configurations/TriangleCalculationConfig.cs new file mode 100644 index 0000000..6377e06 --- /dev/null +++ b/MareSynchronos/MareConfiguration/Configurations/TriangleCalculationConfig.cs @@ -0,0 +1,9 @@ +using System.Collections.Concurrent; + +namespace MareSynchronos.MareConfiguration.Configurations; + +public class TriangleCalculationConfig : IMareConfiguration +{ + public ConcurrentDictionary TriangleDictionary { get; set; } = new(StringComparer.OrdinalIgnoreCase); + public int Version { get; set; } = 0; +} \ No newline at end of file diff --git a/MareSynchronos/MareConfiguration/TransientConfigService.cs b/MareSynchronos/MareConfiguration/TransientConfigService.cs index d16da74..409407a 100644 --- a/MareSynchronos/MareConfiguration/TransientConfigService.cs +++ b/MareSynchronos/MareConfiguration/TransientConfigService.cs @@ -11,4 +11,4 @@ public class TransientConfigService : ConfigurationServiceBase } protected override string ConfigurationName => ConfigName; -} \ No newline at end of file +} diff --git a/MareSynchronos/MareConfiguration/TriangleCalculationConfigService.cs b/MareSynchronos/MareConfiguration/TriangleCalculationConfigService.cs new file mode 100644 index 0000000..d653b28 --- /dev/null +++ b/MareSynchronos/MareConfiguration/TriangleCalculationConfigService.cs @@ -0,0 +1,12 @@ +using MareSynchronos.MareConfiguration.Configurations; + +namespace MareSynchronos.MareConfiguration; + +public class TriangleCalculationConfigService : ConfigurationServiceBase +{ + public const string ConfigName = "trianglecache.json"; + + public TriangleCalculationConfigService(string configDir) : base(configDir) { } + + protected override string ConfigurationName => ConfigName; +} \ No newline at end of file diff --git a/MareSynchronos/MarePlugin.cs b/MareSynchronos/MarePlugin.cs index 16ece45..cb2beb8 100644 --- a/MareSynchronos/MarePlugin.cs +++ b/MareSynchronos/MarePlugin.cs @@ -73,6 +73,7 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService private readonly ServerConfigurationManager _serverConfigurationManager; private readonly IServiceScopeFactory _serviceScopeFactory; private IServiceScope? _runtimeServiceScope; + private Task? _launchTask = null; public MarePlugin(ILogger logger, MareConfigService mareConfigService, ServerConfigurationManager serverConfigurationManager, @@ -92,7 +93,7 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(MarePlugin), Services.Events.EventSeverity.Informational, $"Starting Mare Synchronos {version.Major}.{version.Minor}.{version.Build}"))); - Mediator.Subscribe(this, (msg) => _ = Task.Run(WaitForPlayerAndLaunchCharacterManager)); + Mediator.Subscribe(this, (msg) => { if (_launchTask == null || _launchTask.IsCompleted) _launchTask = Task.Run(WaitForPlayerAndLaunchCharacterManager); }); Mediator.Subscribe(this, (_) => DalamudUtilOnLogIn()); Mediator.Subscribe(this, (_) => DalamudUtilOnLogOut()); @@ -115,8 +116,7 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService private void DalamudUtilOnLogIn() { Logger?.LogDebug("Client login"); - - _ = Task.Run(WaitForPlayerAndLaunchCharacterManager); + if (_launchTask == null || _launchTask.IsCompleted) _launchTask = Task.Run(WaitForPlayerAndLaunchCharacterManager); } private void DalamudUtilOnLogOut() diff --git a/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs b/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs index 76df27e..127b245 100644 --- a/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PairHandlerFactory.cs @@ -20,12 +20,13 @@ public class PairHandlerFactory private readonly IpcManager _ipcManager; private readonly ILoggerFactory _loggerFactory; private readonly MareMediator _mareMediator; + private readonly ModelAnalyzer _modelAnalyzer; private readonly PluginWarningNotificationService _pluginWarningNotificationManager; public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager, FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService, PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime, - FileCacheManager fileCacheManager, MareMediator mareMediator) + FileCacheManager fileCacheManager, MareMediator mareMediator, ModelAnalyzer modelAnalyzer) { _loggerFactory = loggerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory; @@ -36,12 +37,13 @@ public class PairHandlerFactory _hostApplicationLifetime = hostApplicationLifetime; _fileCacheManager = fileCacheManager; _mareMediator = mareMediator; + _modelAnalyzer = modelAnalyzer; } public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto) { return new PairHandler(_loggerFactory.CreateLogger(), onlineUserIdentDto, _gameObjectHandlerFactory, _ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime, - _fileCacheManager, _mareMediator); + _fileCacheManager, _mareMediator, _modelAnalyzer); } } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs index 07d4bfa..201047c 100644 --- a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs @@ -30,7 +30,7 @@ public class PlayerDataFactory _transientResourceManager = transientResourceManager; _fileCacheManager = fileReplacementFactory; _performanceCollector = performanceCollector; - _logger.LogTrace("Creating " + nameof(PlayerDataFactory)); + _logger.LogTrace("Creating {this}", nameof(PlayerDataFactory)); } public async Task BuildCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token) @@ -140,7 +140,7 @@ public class PlayerDataFactory // penumbra call, it's currently broken IReadOnlyDictionary? resolvedPaths; - resolvedPaths = (await _ipcManager.Penumbra.GetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false))![0]; + resolvedPaths = (await _ipcManager.Penumbra.GetCharacterData(_logger, playerRelatedObject).ConfigureAwait(false))?[0]; if (resolvedPaths == null) throw new InvalidOperationException("Penumbra returned null data"); previousData.FileReplacements[objectKind] = diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 8292488..cb99b60 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -24,6 +24,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase private readonly DalamudUtilService _dalamudUtil; private readonly FileDownloadManager _downloadManager; private readonly FileCacheManager _fileDbManager; + private readonly ModelAnalyzer _modelAnalyzer; private readonly GameObjectHandlerFactory _gameObjectHandlerFactory; private readonly IpcManager _ipcManager; private readonly IHostApplicationLifetime _lifetime; @@ -40,13 +41,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase private bool _redrawOnNextApplication = false; private CombatData? _dataReceivedInDowntime; public long LastAppliedDataSize { get; private set; } + public long LastAppliedDataTris { get; private set; } public PairHandler(ILogger logger, OnlineUserIdentDto onlineUser, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager, FileDownloadManager transferManager, PluginWarningNotificationService pluginWarningNotificationManager, DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime, - FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator) + FileCacheManager fileDbManager, MareMediator mediator, + ModelAnalyzer modelAnalyzer) : base(logger, mediator) { OnlineUser = onlineUser; _gameObjectHandlerFactory = gameObjectHandlerFactory; @@ -56,6 +59,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _dalamudUtil = dalamudUtil; _lifetime = lifetime; _fileDbManager = fileDbManager; + _modelAnalyzer = modelAnalyzer; _penumbraCollection = _ipcManager.Penumbra.CreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult(); Mediator.Subscribe(this, (_) => FrameworkUpdate()); @@ -99,6 +103,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase }); LastAppliedDataSize = -1; + LastAppliedDataTris = -1; } public bool IsVisible @@ -370,7 +375,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase _ = Task.Run(async () => { - Dictionary moddedPaths = new(StringComparer.Ordinal); + Dictionary<(string GamePath, string? Hash), string> moddedPaths = new(); if (updateModdedPaths) { @@ -437,12 +442,20 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase if (updateModdedPaths) { - await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false); + await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, + moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false); LastAppliedDataSize = -1; + LastAppliedDataTris = -1; foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists)) { + if (LastAppliedDataSize == -1) LastAppliedDataSize = 0; LastAppliedDataSize += path.Length; } + foreach (var key in moddedPaths.Keys.Where(k => !string.IsNullOrEmpty(k.Hash))) + { + if (LastAppliedDataTris == -1) LastAppliedDataTris = 0; + LastAppliedDataTris += await _modelAnalyzer.GetTrianglesByHash(key.Hash!).ConfigureAwait(false); + } } if (updateManip) @@ -597,12 +610,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase } } - private List TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary moddedDictionary, CancellationToken token) + private List TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary<(string GamePath, string? Hash), string> moddedDictionary, CancellationToken token) { Stopwatch st = Stopwatch.StartNew(); ConcurrentBag missingFiles = []; - moddedDictionary = new Dictionary(StringComparer.Ordinal); - ConcurrentDictionary outputDict = new(StringComparer.Ordinal); + moddedDictionary = new Dictionary<(string GamePath, string? Hash), string>(); + ConcurrentDictionary<(string GamePath, string? Hash), string> outputDict = new(); bool hasMigrationChanges = false; try @@ -627,7 +640,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase foreach (var gamePath in item.GamePaths) { - outputDict[gamePath] = fileCache.ResolvedFilepath; + outputDict[(gamePath, item.Hash)] = fileCache.ResolvedFilepath; } } else @@ -637,14 +650,14 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase } }); - moddedDictionary = outputDict.ToDictionary(k => k.Key, k => k.Value, StringComparer.Ordinal); + moddedDictionary = outputDict.ToDictionary(k => k.Key, k => k.Value); foreach (var item in charaData.FileReplacements.SelectMany(k => k.Value.Where(v => !string.IsNullOrEmpty(v.FileSwapPath))).ToList()) { foreach (var gamePath in item.GamePaths) { Logger.LogTrace("[BASE-{appBase}] Adding file swap for {path}: {fileSwap}", applicationBase, gamePath, item.FileSwapPath); - moddedDictionary[gamePath] = item.FileSwapPath; + moddedDictionary[(gamePath, null)] = item.FileSwapPath; } } } diff --git a/MareSynchronos/PlayerData/Pairs/Pair.cs b/MareSynchronos/PlayerData/Pairs/Pair.cs index a674a8f..d9c6f13 100644 --- a/MareSynchronos/PlayerData/Pairs/Pair.cs +++ b/MareSynchronos/PlayerData/Pairs/Pair.cs @@ -45,6 +45,7 @@ public class Pair public CharacterData? LastReceivedCharacterData { get; set; } public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty; public long LastAppliedDataSize => CachedPlayer?.LastAppliedDataSize ?? -1; + public long LastAppliedDataTris => CachedPlayer?.LastAppliedDataTris ?? -1; public UserData UserData => UserPair.User; diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 41ccbbe..d3e6018 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -69,13 +69,14 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); + collection.AddSingleton(s => new(s.GetRequiredService>(), s.GetRequiredService(), + s.GetRequiredService(), gameData)); collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); collection.AddSingleton(); - collection.AddSingleton(); collection.AddSingleton((s) => new IpcProvider(s.GetRequiredService>(), pluginInterface, s.GetRequiredService(), s.GetRequiredService(), @@ -83,7 +84,6 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton(); collection.AddSingleton((s) => new EventAggregator(pluginInterface.ConfigDirectory.FullName, s.GetRequiredService>(), s.GetRequiredService())); - collection.AddSingleton(); collection.AddSingleton((s) => new DalamudContextMenu(pluginInterface)); collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService>(), clientState, objectTable, framework, gameGui, condition, gameData, targetManager, @@ -114,12 +114,15 @@ public sealed class Plugin : IDalamudPlugin collection.AddSingleton((s) => new NotesConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName)); + collection.AddSingleton((s) => new TriangleCalculationConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ConfigurationMigrator(s.GetRequiredService>(), pluginInterface)); collection.AddSingleton(); // add scoped services + collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); + collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); collection.AddScoped(); diff --git a/MareSynchronos/Services/CharacterAnalyzer.cs b/MareSynchronos/Services/CharacterAnalyzer.cs index c883a5c..2f609fa 100644 --- a/MareSynchronos/Services/CharacterAnalyzer.cs +++ b/MareSynchronos/Services/CharacterAnalyzer.cs @@ -12,16 +12,22 @@ namespace MareSynchronos.Services; public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable { private readonly FileCacheManager _fileCacheManager; + private readonly ModelAnalyzer _modelAnalyzer; private CancellationTokenSource? _analysisCts; + private CancellationTokenSource _baseAnalysisCts = new(); private string _lastDataHash = string.Empty; - public CharacterAnalyzer(ILogger logger, MareMediator mediator, FileCacheManager fileCacheManager) : base(logger, mediator) + public CharacterAnalyzer(ILogger logger, MareMediator mediator, FileCacheManager fileCacheManager, ModelAnalyzer modelAnalyzer) + : base(logger, mediator) { Mediator.Subscribe(this, (msg) => { - _ = Task.Run(() => BaseAnalysis(msg.CharacterData.DeepClone())); + _baseAnalysisCts = _baseAnalysisCts.CancelRecreate(); + var token = _baseAnalysisCts.Token; + _ = BaseAnalysis(msg.CharacterData, token); }); _fileCacheManager = fileCacheManager; + _modelAnalyzer = modelAnalyzer; } public int CurrentFile { get; internal set; } @@ -87,7 +93,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable _analysisCts.CancelDispose(); } - private void BaseAnalysis(CharacterData charaData) + private async Task BaseAnalysis(CharacterData charaData, CancellationToken token) { if (string.Equals(charaData.DataHash.Value, _lastDataHash, StringComparison.Ordinal)) return; @@ -98,6 +104,8 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable Dictionary data = new(StringComparer.OrdinalIgnoreCase); foreach (var fileEntry in obj.Value) { + token.ThrowIfCancellationRequested(); + var fileCacheEntries = _fileCacheManager.GetAllFileCachesByHash(fileEntry.Hash, ignoreCacheEntries: true, validate: false).ToList(); if (fileCacheEntries.Count == 0) continue; @@ -113,12 +121,16 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable Logger.LogWarning(ex, "Could not identify extension for {path}", filePath); } + var tris = await _modelAnalyzer.GetTrianglesByHash(fileEntry.Hash).ConfigureAwait(false); + foreach (var entry in fileCacheEntries) { data[fileEntry.Hash] = new FileDataEntry(fileEntry.Hash, ext, [.. fileEntry.GamePaths], fileCacheEntries.Select(c => c.ResolvedFilepath).Distinct().ToList(), - entry.Size > 0 ? entry.Size.Value : 0, entry.CompressedSize > 0 ? entry.CompressedSize.Value : 0); + entry.Size > 0 ? entry.Size.Value : 0, + entry.CompressedSize > 0 ? entry.CompressedSize.Value : 0, + tris); } } @@ -176,7 +188,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable Logger.LogInformation("IMPORTANT NOTES:\n\r- For Mare up- and downloads only the compressed size is relevant.\n\r- An unusually high total files count beyond 200 and up will also increase your download time to others significantly."); } - internal sealed record FileDataEntry(string Hash, string FileType, List GamePaths, List FilePaths, long OriginalSize, long CompressedSize) + internal sealed record FileDataEntry(string Hash, string FileType, List GamePaths, List FilePaths, long OriginalSize, long CompressedSize, long Triangles) { public bool IsComputed => OriginalSize > 0 && CompressedSize > 0; public async Task ComputeSizes(FileCacheManager fileCacheManager, CancellationToken token) @@ -194,6 +206,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable } public long OriginalSize { get; private set; } = OriginalSize; public long CompressedSize { get; private set; } = CompressedSize; + public long Triangles { get; private set; } = Triangles; public Lazy Format = new(() => { diff --git a/MareSynchronos/Services/CommandManagerService.cs b/MareSynchronos/Services/CommandManagerService.cs index 0c6ae18..2095ead 100644 --- a/MareSynchronos/Services/CommandManagerService.cs +++ b/MareSynchronos/Services/CommandManagerService.cs @@ -48,7 +48,7 @@ public sealed class CommandManagerService : IDisposable { var splitArgs = args.ToLowerInvariant().Trim().Split(" ", StringSplitOptions.RemoveEmptyEntries); - if (splitArgs == null || splitArgs.Length == 0) + if (splitArgs.Length == 0) { // Interpret this as toggling the UI if (_mareConfigService.Current.HasValidSetup()) diff --git a/MareSynchronos/Services/ModelAnalyzer.cs b/MareSynchronos/Services/ModelAnalyzer.cs new file mode 100644 index 0000000..20dbcc4 --- /dev/null +++ b/MareSynchronos/Services/ModelAnalyzer.cs @@ -0,0 +1,72 @@ +using Dalamud.Plugin.Services; +using Lumina; +using Lumina.Data.Files; +using MareSynchronos.FileCache; +using MareSynchronos.MareConfiguration; +using Microsoft.Extensions.Logging; + +namespace MareSynchronos.Services; + +public sealed class ModelAnalyzer +{ + private readonly ILogger _logger; + private readonly FileCacheManager _fileCacheManager; + private readonly TriangleCalculationConfigService _configService; + private readonly GameData _luminaGameData; + + public ModelAnalyzer(ILogger logger, FileCacheManager fileCacheManager, + TriangleCalculationConfigService configService, IDataManager gameData) + { + _logger = logger; + _fileCacheManager = fileCacheManager; + _configService = configService; + _luminaGameData = new GameData(gameData.GameData.DataPath.FullName); + } + + public Task GetTrianglesFromGamePath(string gamePath) + { + if (_configService.Current.TriangleDictionary.TryGetValue(gamePath, out var cachedTris)) + return Task.FromResult(cachedTris); + + _logger.LogInformation("Detected Model File {path}, calculating Tris", gamePath); + var file = _luminaGameData.GetFile(gamePath); + if (file == null) + return Task.FromResult((long)0); + + if (file.FileHeader.LodCount <= 0) + return Task.FromResult((long)0); + var meshIdx = file.Lods[0].MeshIndex; + var meshCnt = file.Lods[0].MeshCount; + var tris = file.Meshes.Skip(meshIdx).Take(meshCnt).Sum(p => p.IndexCount) / 3; + + _logger.LogInformation("{filePath} => {tris} triangles", gamePath, tris); + _configService.Current.TriangleDictionary[gamePath] = tris; + _configService.Save(); + return Task.FromResult(tris); + } + + public Task GetTrianglesByHash(string hash) + { + if (_configService.Current.TriangleDictionary.TryGetValue(hash, out var cachedTris)) + return Task.FromResult(cachedTris); + + var path = _fileCacheManager.GetFileCacheByHash(hash); + if (path == null || !path.ResolvedFilepath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase)) + return Task.FromResult((long)0); + + var filePath = path.ResolvedFilepath; + + _logger.LogInformation("Detected Model File {path}, calculating Tris", filePath); + var file = _luminaGameData.GetFileFromDisk(filePath); + if (file.FileHeader.LodCount <= 0) + return Task.FromResult((long)0); + var meshIdx = file.Lods[0].MeshIndex; + var meshCnt = file.Lods[0].MeshCount; + var tris = file.Meshes.Skip(meshIdx).Take(meshCnt).Sum(p => p.IndexCount) / 3; + + _logger.LogInformation("{filePath} => {tris} triangles", filePath, tris); + _configService.Current.TriangleDictionary[hash] = tris; + _configService.Save(); + return Task.FromResult(tris); + } +} diff --git a/MareSynchronos/UI/Components/DrawUserPair.cs b/MareSynchronos/UI/Components/DrawUserPair.cs index 82adee9..0f7acd5 100644 --- a/MareSynchronos/UI/Components/DrawUserPair.cs +++ b/MareSynchronos/UI/Components/DrawUserPair.cs @@ -242,8 +242,14 @@ public class DrawUserPair if (_pair.LastAppliedDataSize >= 0) { - userPairText += UiSharedService.TooltipSeparator + (!_pair.IsVisible ? "(Last) " : string.Empty) + - "Loaded Mods Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true); + userPairText += UiSharedService.TooltipSeparator; + userPairText += ((!_pair.IsPaired) ? "(Last) " : string.Empty) + "Mods Info" + Environment.NewLine; + userPairText += "Files Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true); + if (_pair.LastAppliedDataTris >= 0) + { + userPairText += Environment.NewLine + "Triangle Count (excl. Vanilla): " + + (_pair.LastAppliedDataTris > 1000 ? (_pair.LastAppliedDataTris / 1000d).ToString("0.0'k'") : _pair.LastAppliedDataTris); + } } if (_syncedGroups.Any()) diff --git a/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs b/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs index e512ea3..87985cb 100644 --- a/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/BanUserPopupHandler.cs @@ -11,15 +11,13 @@ namespace MareSynchronos.UI.Components.Popup; public class BanUserPopupHandler : IPopupHandler { private readonly ApiController _apiController; - private readonly UiSharedService _uiSharedService; private string _banReason = string.Empty; private GroupFullInfoDto _group = null!; private Pair _reportedPair = null!; - public BanUserPopupHandler(ApiController apiController, UiSharedService uiSharedService) + public BanUserPopupHandler(ApiController apiController) { _apiController = apiController; - _uiSharedService = uiSharedService; } public Vector2 PopupSize => new(500, 250); diff --git a/MareSynchronos/UI/Components/Popup/PopupHandler.cs b/MareSynchronos/UI/Components/Popup/PopupHandler.cs index 71c0dc5..11880f0 100644 --- a/MareSynchronos/UI/Components/Popup/PopupHandler.cs +++ b/MareSynchronos/UI/Components/Popup/PopupHandler.cs @@ -13,11 +13,10 @@ public class PopupHandler : WindowMediatorSubscriberBase { protected bool _openPopup = false; private readonly HashSet _handlers; - private readonly UiSharedService _uiSharedService; private IPopupHandler? _currentHandler = null; - public PopupHandler(ILogger logger, MareMediator mediator, IEnumerable popupHandlers, PerformanceCollectorService performanceCollectorService, - UiSharedService uiSharedService) + public PopupHandler(ILogger logger, MareMediator mediator, IEnumerable popupHandlers, + PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "MarePopupHandler", performanceCollectorService) { Flags = ImGuiWindowFlags.NoBringToFrontOnFocus @@ -54,7 +53,6 @@ public class PopupHandler : WindowMediatorSubscriberBase _currentHandler = _handlers.OfType().Single(); IsOpen = true; }); - _uiSharedService = uiSharedService; } protected override void DrawInternal() diff --git a/MareSynchronos/UI/Components/SelectTagForPairUi.cs b/MareSynchronos/UI/Components/SelectTagForPairUi.cs index 33edcee..03d9f06 100644 --- a/MareSynchronos/UI/Components/SelectTagForPairUi.cs +++ b/MareSynchronos/UI/Components/SelectTagForPairUi.cs @@ -14,7 +14,6 @@ public class SelectTagForPairUi { private readonly TagHandler _tagHandler; private readonly IdDisplayHandler _uidDisplayHandler; - private readonly UiSharedService _uiSharedService; /// /// The group UI is always open for a specific pair. This defines which pair the UI is open for. @@ -32,13 +31,12 @@ public class SelectTagForPairUi /// private string _tagNameToAdd = ""; - public SelectTagForPairUi(TagHandler tagHandler, IdDisplayHandler uidDisplayHandler, UiSharedService uiSharedService) + public SelectTagForPairUi(TagHandler tagHandler, IdDisplayHandler uidDisplayHandler) { _show = false; _pair = null; _tagHandler = tagHandler; _uidDisplayHandler = uidDisplayHandler; - _uiSharedService = uiSharedService; } public void Draw() diff --git a/MareSynchronos/UI/DataAnalysisUi.cs b/MareSynchronos/UI/DataAnalysisUi.cs index 31347a5..0dc698a 100644 --- a/MareSynchronos/UI/DataAnalysisUi.cs +++ b/MareSynchronos/UI/DataAnalysisUi.cs @@ -161,7 +161,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase ImGui.TextUnformatted("Total size (compressed):"); ImGui.SameLine(); ImGui.TextUnformatted(UiSharedService.ByteToString(_cachedAnalysis!.Sum(c => c.Value.Sum(c => c.Value.CompressedSize)))); - + ImGui.TextUnformatted($"Total modded model triangles: {_cachedAnalysis.Sum(c => c.Value.Sum(f => f.Value.Triangles))}"); ImGui.Separator(); using var tabbar = ImRaii.TabBar("objectSelection"); @@ -198,6 +198,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase ImGui.TextUnformatted($"{kvp.Key} size (compressed):"); ImGui.SameLine(); ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.CompressedSize))); + ImGui.TextUnformatted($"{kvp.Key} modded model triangles: {kvp.Value.Sum(f => f.Value.Triangles)}"); ImGui.Separator(); if (_selectedObjectTab != kvp.Key) @@ -337,8 +338,10 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase private void DrawTable(IGrouping fileGroup) { - using var table = ImRaii.Table("Analysis", string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal) ? - (_enableBc7ConversionMode ? 7 : 6) : 5, ImGuiTableFlags.Sortable | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit, + var tableColumns = string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal) + ? (_enableBc7ConversionMode ? 7 : 6) + : (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal) ? 6 : 5); + using var table = ImRaii.Table("Analysis", tableColumns, ImGuiTableFlags.Sortable | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit, new Vector2(0, 300)); if (!table.Success) return; ImGui.TableSetupColumn("Hash"); @@ -351,6 +354,10 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase ImGui.TableSetupColumn("Format"); if (_enableBc7ConversionMode) ImGui.TableSetupColumn("Convert to BC7"); } + if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal)) + { + ImGui.TableSetupColumn("Triangles"); + } ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableHeadersRow(); @@ -444,6 +451,12 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase } } } + if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal)) + { + ImGui.TableNextColumn(); + ImGui.TextUnformatted(item.Triangles.ToString()); + if (ImGui.IsItemClicked()) _selectedHash = item.Hash; + } } } } \ No newline at end of file diff --git a/MareSynchronos/UI/GposeUi.cs b/MareSynchronos/UI/GposeUi.cs index 4b49304..6b80ecb 100644 --- a/MareSynchronos/UI/GposeUi.cs +++ b/MareSynchronos/UI/GposeUi.cs @@ -12,7 +12,6 @@ namespace MareSynchronos.UI; public class GposeUi : WindowMediatorSubscriberBase { private readonly MareConfigService _configService; - private readonly UiSharedService _uiSharedService; private readonly DalamudUtilService _dalamudUtil; private readonly FileDialogManager _fileDialogManager; private readonly MareCharaFileManager _mareCharaFileManager; @@ -21,15 +20,13 @@ public class GposeUi : WindowMediatorSubscriberBase public GposeUi(ILogger logger, MareCharaFileManager mareCharaFileManager, DalamudUtilService dalamudUtil, FileDialogManager fileDialogManager, MareConfigService configService, - MareMediator mediator, PerformanceCollectorService performanceCollectorService, - UiSharedService uiSharedService) + MareMediator mediator, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "Mare Synchronos Gpose Import UI###MareSynchronosGposeUI", performanceCollectorService) { _mareCharaFileManager = mareCharaFileManager; _dalamudUtil = dalamudUtil; _fileDialogManager = fileDialogManager; _configService = configService; - _uiSharedService = uiSharedService; Mediator.Subscribe(this, (_) => StartGpose()); Mediator.Subscribe(this, (_) => EndGpose()); IsOpen = _dalamudUtil.IsInGpose; diff --git a/MareSynchronos/WebAPI/Files/FileDownloadManager.cs b/MareSynchronos/WebAPI/Files/FileDownloadManager.cs index de99f3f..578f97c 100644 --- a/MareSynchronos/WebAPI/Files/FileDownloadManager.cs +++ b/MareSynchronos/WebAPI/Files/FileDownloadManager.cs @@ -90,7 +90,11 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase { stream.Dispose(); } - catch { } + catch + { + // do nothing + // + } } base.Dispose(disposing); }