Add Pair Character Analysis for funsies
This commit is contained in:
@@ -141,12 +141,13 @@ public sealed class FileCacheManager : IHostedService
|
|||||||
|
|
||||||
public async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
|
public async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
|
||||||
{
|
{
|
||||||
var fileCache = GetFileCacheByHash(fileHash)!.ResolvedFilepath;
|
var fileCache = GetFileCacheByHash(fileHash)!;
|
||||||
using var fs = File.OpenRead(fileCache);
|
using var fs = File.OpenRead(fileCache.ResolvedFilepath);
|
||||||
var ms = new MemoryStream(64 * 1024);
|
var ms = new MemoryStream(64 * 1024);
|
||||||
using var encstream = LZ4Stream.Encode(ms, new LZ4EncoderSettings(){CompressionLevel=K4os.Compression.LZ4.LZ4Level.L09_HC});
|
using var encstream = LZ4Stream.Encode(ms, new LZ4EncoderSettings(){CompressionLevel=K4os.Compression.LZ4.LZ4Level.L09_HC});
|
||||||
await fs.CopyToAsync(encstream, uploadToken).ConfigureAwait(false);
|
await fs.CopyToAsync(encstream, uploadToken).ConfigureAwait(false);
|
||||||
encstream.Close();
|
encstream.Close();
|
||||||
|
fileCache.CompressedSize = encstream.Length;
|
||||||
return (fileHash, ms.ToArray());
|
return (fileHash, ms.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
MareSynchronos/PlayerData/Factories/PairAnalyzerFactory.cs
Normal file
30
MareSynchronos/PlayerData/Factories/PairAnalyzerFactory.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using MareSynchronos.FileCache;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.PlayerData.Factories;
|
||||||
|
|
||||||
|
public class PairAnalyzerFactory
|
||||||
|
{
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
private readonly MareMediator _mareMediator;
|
||||||
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
|
private readonly XivDataAnalyzer _modelAnalyzer;
|
||||||
|
|
||||||
|
public PairAnalyzerFactory(ILoggerFactory loggerFactory, MareMediator mareMediator,
|
||||||
|
FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer)
|
||||||
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
_fileCacheManager = fileCacheManager;
|
||||||
|
_mareMediator = mareMediator;
|
||||||
|
_modelAnalyzer = modelAnalyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PairAnalyzer Create(Pair pair)
|
||||||
|
{
|
||||||
|
return new PairAnalyzer(_loggerFactory.CreateLogger<PairAnalyzer>(), pair, _mareMediator,
|
||||||
|
_fileCacheManager, _modelAnalyzer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,12 +23,13 @@ public class PairHandlerFactory
|
|||||||
private readonly PlayerPerformanceService _playerPerformanceService;
|
private readonly PlayerPerformanceService _playerPerformanceService;
|
||||||
private readonly ServerConfigurationManager _serverConfigManager;
|
private readonly ServerConfigurationManager _serverConfigManager;
|
||||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
|
private readonly PairAnalyzerFactory _pairAnalyzerFactory;
|
||||||
|
|
||||||
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
||||||
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)
|
ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
@@ -41,11 +42,12 @@ public class PairHandlerFactory
|
|||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
_playerPerformanceService = playerPerformanceService;
|
_playerPerformanceService = playerPerformanceService;
|
||||||
_serverConfigManager = serverConfigManager;
|
_serverConfigManager = serverConfigManager;
|
||||||
|
_pairAnalyzerFactory = pairAnalyzerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PairHandler Create(Pair pair)
|
public PairHandler Create(Pair pair)
|
||||||
{
|
{
|
||||||
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), 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);
|
_fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
private Guid _penumbraCollection = Guid.Empty;
|
private Guid _penumbraCollection = Guid.Empty;
|
||||||
private bool _redrawOnNextApplication = false;
|
private bool _redrawOnNextApplication = false;
|
||||||
|
|
||||||
public PairHandler(ILogger<PairHandler> logger, Pair pair,
|
public PairHandler(ILogger<PairHandler> logger, Pair pair, PairAnalyzer pairAnalyzer,
|
||||||
GameObjectHandlerFactory gameObjectHandlerFactory,
|
GameObjectHandlerFactory gameObjectHandlerFactory,
|
||||||
IpcManager ipcManager, FileDownloadManager transferManager,
|
IpcManager ipcManager, FileDownloadManager transferManager,
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager,
|
PluginWarningNotificationService pluginWarningNotificationManager,
|
||||||
@@ -53,6 +53,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
ServerConfigurationManager serverConfigManager) : base(logger, mediator)
|
ServerConfigurationManager serverConfigManager) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
Pair = pair;
|
Pair = pair;
|
||||||
|
PairAnalyzer = pairAnalyzer;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
_downloadManager = transferManager;
|
_downloadManager = transferManager;
|
||||||
@@ -129,6 +130,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public long LastAppliedDataBytes { get; private set; }
|
public long LastAppliedDataBytes { get; private set; }
|
||||||
public Pair Pair { get; private set; }
|
public Pair Pair { get; private set; }
|
||||||
|
public PairAnalyzer PairAnalyzer { get; private init; }
|
||||||
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
|
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
|
||||||
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
|
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
|
||||||
? uint.MaxValue
|
? uint.MaxValue
|
||||||
@@ -159,6 +161,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
.Any(p => p.Value.Contains(PlayerChanges.ModManip) || p.Value.Contains(PlayerChanges.ModFiles));
|
.Any(p => p.Value.Contains(PlayerChanges.ModManip) || p.Value.Contains(PlayerChanges.ModFiles));
|
||||||
_forceApplyMods = hasDiffMods || _forceApplyMods || (PlayerCharacter == IntPtr.Zero && _cachedData == null);
|
_forceApplyMods = hasDiffMods || _forceApplyMods || (PlayerCharacter == IntPtr.Zero && _cachedData == null);
|
||||||
_cachedData = characterData;
|
_cachedData = characterData;
|
||||||
|
Mediator.Publish(new PairDataAppliedMessage(Pair.UserData.UID, characterData));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -176,6 +179,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
.Any(p => p.Value.Contains(PlayerChanges.ModManip) || p.Value.Contains(PlayerChanges.ModFiles));
|
.Any(p => p.Value.Contains(PlayerChanges.ModManip) || p.Value.Contains(PlayerChanges.ModFiles));
|
||||||
_forceApplyMods = hasDiffMods || _forceApplyMods || (PlayerCharacter == IntPtr.Zero && _cachedData == null);
|
_forceApplyMods = hasDiffMods || _forceApplyMods || (PlayerCharacter == IntPtr.Zero && _cachedData == null);
|
||||||
_cachedData = characterData;
|
_cachedData = characterData;
|
||||||
|
Mediator.Publish(new PairDataAppliedMessage(Pair.UserData.UID, characterData));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
Logger.LogDebug("[BASE-{appBase}] Setting data: {hash}, forceApplyMods: {force}", applicationBase, _cachedData.DataHash.Value, _forceApplyMods);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -274,6 +278,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
PlayerName = null;
|
PlayerName = null;
|
||||||
_cachedData = null;
|
_cachedData = null;
|
||||||
|
Mediator.Publish(new PairDataAppliedMessage(Pair.UserData.UID, null));
|
||||||
Logger.LogDebug("Disposing {name} complete", name);
|
Logger.LogDebug("Disposing {name} complete", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -581,6 +586,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
_cachedData = charaData;
|
_cachedData = charaData;
|
||||||
|
Mediator.Publish(new PairDataAppliedMessage(Pair.UserData.UID, charaData));
|
||||||
|
|
||||||
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
|
Logger.LogDebug("[{applicationId}] Application finished", _applicationId);
|
||||||
}
|
}
|
||||||
@@ -591,6 +597,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
IsVisible = false;
|
IsVisible = false;
|
||||||
_forceApplyMods = true;
|
_forceApplyMods = true;
|
||||||
_cachedData = charaData;
|
_cachedData = charaData;
|
||||||
|
Mediator.Publish(new PairDataAppliedMessage(Pair.UserData.UID, charaData));
|
||||||
Logger.LogDebug("[{applicationId}] Cancelled, player turned null during application", _applicationId);
|
Logger.LogDebug("[{applicationId}] Cancelled, player turned null during application", _applicationId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using MareSynchronos.API.Dto.User;
|
|||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
@@ -67,6 +68,7 @@ public class Pair : DisposableMediatorSubscriberBase
|
|||||||
public long LastAppliedDataTris { get; set; } = -1;
|
public long LastAppliedDataTris { get; set; } = -1;
|
||||||
public long LastAppliedApproximateVRAMBytes { get; set; } = -1;
|
public long LastAppliedApproximateVRAMBytes { get; set; } = -1;
|
||||||
public string Ident => _onlineUserIdentDto?.Ident ?? string.Empty;
|
public string Ident => _onlineUserIdentDto?.Ident ?? string.Empty;
|
||||||
|
public PairAnalyzer? PairAnalyzer => CachedPlayer?.PairAnalyzer;
|
||||||
|
|
||||||
public UserData UserData { get; init; }
|
public UserData UserData { get; init; }
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<GameObjectHandlerFactory>();
|
collection.AddSingleton<GameObjectHandlerFactory>();
|
||||||
collection.AddSingleton<FileDownloadManagerFactory>();
|
collection.AddSingleton<FileDownloadManagerFactory>();
|
||||||
collection.AddSingleton<PairHandlerFactory>();
|
collection.AddSingleton<PairHandlerFactory>();
|
||||||
|
collection.AddSingleton<PairAnalyzerFactory>();
|
||||||
collection.AddSingleton<PairFactory>();
|
collection.AddSingleton<PairFactory>();
|
||||||
collection.AddSingleton<XivDataAnalyzer>();
|
collection.AddSingleton<XivDataAnalyzer>();
|
||||||
collection.AddSingleton<CharacterAnalyzer>();
|
collection.AddSingleton<CharacterAnalyzer>();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace MareSynchronos.Services;
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
public sealed class CharacterAnalyzer : DisposableMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly FileCacheManager _fileCacheManager;
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
private readonly XivDataAnalyzer _xivDataAnalyzer;
|
private readonly XivDataAnalyzer _xivDataAnalyzer;
|
||||||
@@ -63,7 +63,7 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
foreach (var file in remaining)
|
foreach (var file in remaining)
|
||||||
{
|
{
|
||||||
Logger.LogDebug("Computing file {file}", file.FilePaths[0]);
|
Logger.LogDebug("Computing file {file}", file.FilePaths[0]);
|
||||||
await file.ComputeSizes(_fileCacheManager, cancelToken).ConfigureAwait(false);
|
await file.ComputeSizes(_fileCacheManager, cancelToken, ignoreCacheEntries: true).ConfigureAwait(false);
|
||||||
CurrentFile++;
|
CurrentFile++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,9 +88,14 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
if (print) PrintAnalysis();
|
if (print) PrintAnalysis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
_analysisCts.CancelDispose();
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (!disposing) return;
|
||||||
|
|
||||||
|
_analysisCts?.CancelDispose();
|
||||||
|
_baseAnalysisCts.CancelDispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task BaseAnalysis(CharacterData charaData, CancellationToken token)
|
private async Task BaseAnalysis(CharacterData charaData, CancellationToken token)
|
||||||
@@ -191,11 +196,11 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
internal sealed record FileDataEntry(string Hash, string FileType, List<string> GamePaths, List<string> FilePaths, long OriginalSize, long CompressedSize, long Triangles)
|
internal sealed record FileDataEntry(string Hash, string FileType, List<string> GamePaths, List<string> FilePaths, long OriginalSize, long CompressedSize, long Triangles)
|
||||||
{
|
{
|
||||||
public bool IsComputed => OriginalSize > 0 && CompressedSize > 0;
|
public bool IsComputed => OriginalSize > 0 && CompressedSize > 0;
|
||||||
public async Task ComputeSizes(FileCacheManager fileCacheManager, CancellationToken token)
|
public async Task ComputeSizes(FileCacheManager fileCacheManager, CancellationToken token, bool ignoreCacheEntries = true)
|
||||||
{
|
{
|
||||||
var compressedsize = await fileCacheManager.GetCompressedFileData(Hash, token).ConfigureAwait(false);
|
var compressedsize = await fileCacheManager.GetCompressedFileData(Hash, token).ConfigureAwait(false);
|
||||||
var normalSize = new FileInfo(FilePaths[0]).Length;
|
var normalSize = new FileInfo(FilePaths[0]).Length;
|
||||||
var entries = fileCacheManager.GetAllFileCachesByHash(Hash, ignoreCacheEntries: true, validate: false);
|
var entries = fileCacheManager.GetAllFileCachesByHash(Hash, ignoreCacheEntries: ignoreCacheEntries, validate: false);
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
{
|
{
|
||||||
entry.Size = normalSize;
|
entry.Size = normalSize;
|
||||||
@@ -220,7 +225,9 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
|
|||||||
using var reader = new BinaryReader(stream);
|
using var reader = new BinaryReader(stream);
|
||||||
reader.BaseStream.Position = 4;
|
reader.BaseStream.Position = 4;
|
||||||
var format = (TexFile.TextureFormat)reader.ReadInt32();
|
var format = (TexFile.TextureFormat)reader.ReadInt32();
|
||||||
return format.ToString();
|
var width = reader.ReadInt16();
|
||||||
|
var height = reader.ReadInt16();
|
||||||
|
return $"{format} ({width}x{height})";
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public sealed class MareMediator : IHostedService
|
|||||||
throw new InvalidOperationException("Already subscribed");
|
throw new InvalidOperationException("Already subscribed");
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Subscriber added for message {message}: {sub}", typeof(T).Name, subscriber.GetType().Name);
|
_logger.LogTrace("Subscriber added for message {message}: {sub}", typeof(T).Name, subscriber.GetType().Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ public sealed class MareMediator : IHostedService
|
|||||||
throw new InvalidOperationException("Already subscribed");
|
throw new InvalidOperationException("Already subscribed");
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Subscriber added for message {message}:{key}: {sub}", typeof(T).Name, key, subscriber.GetType().Name);
|
_logger.LogTrace("Subscriber added for message {message}:{key}: {sub}", typeof(T).Name, key, subscriber.GetType().Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public record OpenReportPopupMessage(Pair PairToReport) : MessageBase;
|
|||||||
public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase;
|
public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase;
|
||||||
public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase;
|
public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase;
|
||||||
public record OpenPermissionWindow(Pair Pair) : MessageBase;
|
public record OpenPermissionWindow(Pair Pair) : MessageBase;
|
||||||
|
public record OpenPairAnalysisWindow(Pair Pair) : MessageBase;
|
||||||
public record DownloadLimitChangedMessage() : SameThreadMessage;
|
public record DownloadLimitChangedMessage() : SameThreadMessage;
|
||||||
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
|
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
|
||||||
public record TargetPairMessage(Pair Pair) : MessageBase;
|
public record TargetPairMessage(Pair Pair) : MessageBase;
|
||||||
@@ -94,5 +95,7 @@ public record HoldPairApplicationMessage(string UID, string Source) : KeyedMessa
|
|||||||
public record UnholdPairApplicationMessage(string UID, string Source) : KeyedMessage(UID);
|
public record UnholdPairApplicationMessage(string UID, string Source) : KeyedMessage(UID);
|
||||||
public record HoldPairDownloadsMessage(string UID, string Source) : KeyedMessage(UID);
|
public record HoldPairDownloadsMessage(string UID, string Source) : KeyedMessage(UID);
|
||||||
public record UnholdPairDownloadsMessage(string UID, string Source) : KeyedMessage(UID);
|
public record UnholdPairDownloadsMessage(string UID, string Source) : KeyedMessage(UID);
|
||||||
|
public record PairDataAppliedMessage(string UID, CharacterData? CharacterData) : KeyedMessage(UID);
|
||||||
|
public record PairDataAnalyzedMessage(string UID) : KeyedMessage(UID);
|
||||||
#pragma warning restore S2094
|
#pragma warning restore S2094
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
211
MareSynchronos/Services/PairAnalyzer.cs
Normal file
211
MareSynchronos/Services/PairAnalyzer.cs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
using Lumina.Data.Files;
|
||||||
|
using MareSynchronos.API.Data;
|
||||||
|
using MareSynchronos.API.Data.Enum;
|
||||||
|
using MareSynchronos.FileCache;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.UI;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
|
public sealed class PairAnalyzer : DisposableMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly FileCacheManager _fileCacheManager;
|
||||||
|
private readonly XivDataAnalyzer _xivDataAnalyzer;
|
||||||
|
private CancellationTokenSource? _analysisCts;
|
||||||
|
private CancellationTokenSource _baseAnalysisCts = new();
|
||||||
|
private string _lastDataHash = string.Empty;
|
||||||
|
|
||||||
|
public PairAnalyzer(ILogger<PairAnalyzer> logger, Pair pair, MareMediator mediator, FileCacheManager fileCacheManager, XivDataAnalyzer modelAnalyzer)
|
||||||
|
: base(logger, mediator)
|
||||||
|
{
|
||||||
|
Pair = pair;
|
||||||
|
Mediator.SubscribeKeyed<PairDataAppliedMessage>(this, pair.UserData.UID, (msg) =>
|
||||||
|
{
|
||||||
|
_baseAnalysisCts = _baseAnalysisCts.CancelRecreate();
|
||||||
|
var token = _baseAnalysisCts.Token;
|
||||||
|
if (msg.CharacterData != null)
|
||||||
|
{
|
||||||
|
_ = BaseAnalysis(msg.CharacterData, token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LastAnalysis.Clear();
|
||||||
|
_lastDataHash = string.Empty;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_fileCacheManager = fileCacheManager;
|
||||||
|
_xivDataAnalyzer = modelAnalyzer;
|
||||||
|
|
||||||
|
var lastReceivedData = pair.LastReceivedCharacterData;
|
||||||
|
if (lastReceivedData != null)
|
||||||
|
_ = BaseAnalysis(lastReceivedData, _baseAnalysisCts.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair Pair { get; init; }
|
||||||
|
public int CurrentFile { get; internal set; }
|
||||||
|
public bool IsAnalysisRunning => _analysisCts != null;
|
||||||
|
public int TotalFiles { get; internal set; }
|
||||||
|
internal Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>> LastAnalysis { get; } = [];
|
||||||
|
internal string LastPlayerName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public void CancelAnalyze()
|
||||||
|
{
|
||||||
|
_analysisCts?.CancelDispose();
|
||||||
|
_analysisCts = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ComputeAnalysis(bool print = true, bool recalculate = false)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("=== Calculating Character Analysis ===");
|
||||||
|
|
||||||
|
_analysisCts = _analysisCts?.CancelRecreate() ?? new();
|
||||||
|
|
||||||
|
var cancelToken = _analysisCts.Token;
|
||||||
|
|
||||||
|
var allFiles = LastAnalysis.SelectMany(v => v.Value.Select(d => d.Value)).ToList();
|
||||||
|
if (allFiles.Exists(c => !c.IsComputed || recalculate))
|
||||||
|
{
|
||||||
|
var remaining = allFiles.Where(c => !c.IsComputed || recalculate).ToList();
|
||||||
|
TotalFiles = remaining.Count;
|
||||||
|
CurrentFile = 1;
|
||||||
|
Logger.LogDebug("=== Computing {amount} remaining files ===", remaining.Count);
|
||||||
|
|
||||||
|
Mediator.Publish(new HaltScanMessage(nameof(PairAnalyzer)));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var file in remaining)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("Computing file {file}", file.FilePaths[0]);
|
||||||
|
await file.ComputeSizes(_fileCacheManager, cancelToken, ignoreCacheEntries: false).ConfigureAwait(false);
|
||||||
|
CurrentFile++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileCacheManager.WriteOutFullCsv();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "Failed to analyze files");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Mediator.Publish(new ResumeScanMessage(nameof(PairAnalyzer)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LastPlayerName = Pair.PlayerName ?? string.Empty;
|
||||||
|
Mediator.Publish(new PairDataAnalyzedMessage(Pair.UserData.UID));
|
||||||
|
|
||||||
|
_analysisCts.CancelDispose();
|
||||||
|
_analysisCts = null;
|
||||||
|
|
||||||
|
if (print) PrintAnalysis();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (!disposing) return;
|
||||||
|
|
||||||
|
_analysisCts?.CancelDispose();
|
||||||
|
_baseAnalysisCts.CancelDispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BaseAnalysis(CharacterData charaData, CancellationToken token)
|
||||||
|
{
|
||||||
|
if (string.Equals(charaData.DataHash.Value, _lastDataHash, StringComparison.Ordinal)) return;
|
||||||
|
|
||||||
|
LastAnalysis.Clear();
|
||||||
|
|
||||||
|
foreach (var obj in charaData.FileReplacements)
|
||||||
|
{
|
||||||
|
Dictionary<string, CharacterAnalyzer.FileDataEntry> data = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var fileEntry in obj.Value)
|
||||||
|
{
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var fileCacheEntries = _fileCacheManager.GetAllFileCachesByHash(fileEntry.Hash, ignoreCacheEntries: false, validate: false).ToList();
|
||||||
|
if (fileCacheEntries.Count == 0) continue;
|
||||||
|
|
||||||
|
var filePath = fileCacheEntries[0].ResolvedFilepath;
|
||||||
|
FileInfo fi = new(filePath);
|
||||||
|
string ext = "unk?";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ext = fi.Extension[1..];
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "Could not identify extension for {path}", filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tris = await _xivDataAnalyzer.GetTrianglesByHash(fileEntry.Hash).ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (var entry in fileCacheEntries)
|
||||||
|
{
|
||||||
|
data[fileEntry.Hash] = new CharacterAnalyzer.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,
|
||||||
|
tris);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LastAnalysis[obj.Key] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mediator.Publish(new PairDataAnalyzedMessage(Pair.UserData.UID));
|
||||||
|
|
||||||
|
_lastDataHash = charaData.DataHash.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintAnalysis()
|
||||||
|
{
|
||||||
|
if (LastAnalysis.Count == 0) return;
|
||||||
|
foreach (var kvp in LastAnalysis)
|
||||||
|
{
|
||||||
|
int fileCounter = 1;
|
||||||
|
int totalFiles = kvp.Value.Count;
|
||||||
|
Logger.LogInformation("=== Analysis for {uid}:{obj} ===", Pair.UserData.UID, kvp.Key);
|
||||||
|
|
||||||
|
foreach (var entry in kvp.Value.OrderBy(b => b.Value.GamePaths.OrderBy(p => p, StringComparer.Ordinal).First(), StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
Logger.LogInformation("File {x}/{y}: {hash}", fileCounter++, totalFiles, entry.Key);
|
||||||
|
foreach (var path in entry.Value.GamePaths)
|
||||||
|
{
|
||||||
|
Logger.LogInformation(" Game Path: {path}", path);
|
||||||
|
}
|
||||||
|
if (entry.Value.FilePaths.Count > 1) Logger.LogInformation(" Multiple fitting files detected for {key}", entry.Key);
|
||||||
|
foreach (var filePath in entry.Value.FilePaths)
|
||||||
|
{
|
||||||
|
Logger.LogInformation(" File Path: {path}", filePath);
|
||||||
|
}
|
||||||
|
Logger.LogInformation(" Size: {size}, Compressed: {compressed}", UiSharedService.ByteToString(entry.Value.OriginalSize),
|
||||||
|
UiSharedService.ByteToString(entry.Value.CompressedSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var kvp in LastAnalysis)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("=== Detailed summary by file type for {obj} ===", kvp.Key);
|
||||||
|
foreach (var entry in kvp.Value.Select(v => v.Value).GroupBy(v => v.FileType, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
Logger.LogInformation("{ext} files: {count}, size extracted: {size}, size compressed: {sizeComp}", entry.Key, entry.Count(),
|
||||||
|
UiSharedService.ByteToString(entry.Sum(v => v.OriginalSize)), UiSharedService.ByteToString(entry.Sum(v => v.CompressedSize)));
|
||||||
|
}
|
||||||
|
Logger.LogInformation("=== Total summary for {obj} ===", kvp.Key);
|
||||||
|
Logger.LogInformation("Total files: {count}, size extracted: {size}, size compressed: {sizeComp}", kvp.Value.Count,
|
||||||
|
UiSharedService.ByteToString(kvp.Value.Sum(v => v.Value.OriginalSize)), UiSharedService.ByteToString(kvp.Value.Sum(v => v.Value.CompressedSize)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation("=== Total summary for all currently present objects ===");
|
||||||
|
Logger.LogInformation("Total files: {count}, size extracted: {size}, size compressed: {sizeComp}",
|
||||||
|
LastAnalysis.Values.Sum(v => v.Values.Count),
|
||||||
|
UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.OriginalSize))),
|
||||||
|
UiSharedService.ByteToString(LastAnalysis.Values.Sum(c => c.Values.Sum(v => v.CompressedSize))));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,4 +51,10 @@ public class UiFactory
|
|||||||
return new PermissionWindowUI(_loggerFactory.CreateLogger<PermissionWindowUI>(), pair,
|
return new PermissionWindowUI(_loggerFactory.CreateLogger<PermissionWindowUI>(), pair,
|
||||||
_mareMediator, _uiSharedService, _apiController, _performanceCollectorService);
|
_mareMediator, _uiSharedService, _apiController, _performanceCollectorService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlayerAnalysisUI CreatePlayerAnalysisUi(Pair pair)
|
||||||
|
{
|
||||||
|
return new PlayerAnalysisUI(_loggerFactory.CreateLogger<PlayerAnalysisUI>(), pair,
|
||||||
|
_mareMediator, _uiSharedService, _performanceCollectorService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,17 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Mediator.Subscribe<OpenPairAnalysisWindow>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (!_createdWindows.Exists(p => p is PlayerAnalysisUI ui
|
||||||
|
&& msg.Pair == ui.Pair))
|
||||||
|
{
|
||||||
|
var window = _uiFactory.CreatePlayerAnalysisUi(msg.Pair);
|
||||||
|
_createdWindows.Add(window);
|
||||||
|
_windowSystem.AddWindow(window);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Mediator.Subscribe<RemoveWindowMessage>(this, (msg) =>
|
Mediator.Subscribe<RemoveWindowMessage>(this, (msg) =>
|
||||||
{
|
{
|
||||||
_windowSystem.RemoveWindow(msg.Window);
|
_windowSystem.RemoveWindow(msg.Window);
|
||||||
|
|||||||
@@ -335,6 +335,11 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
}
|
}
|
||||||
if (_pair.IsVisible)
|
if (_pair.IsVisible)
|
||||||
{
|
{
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Open Analysis"))
|
||||||
|
{
|
||||||
|
_displayHandler.OpenAnalysis(_pair);
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
}
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
||||||
{
|
{
|
||||||
_pair.ApplyLastReceivedData(forced: true);
|
_pair.ApplyLastReceivedData(forced: true);
|
||||||
|
|||||||
@@ -225,6 +225,11 @@ public class DrawUserPair : DrawPairBase
|
|||||||
}
|
}
|
||||||
if (entry.IsVisible)
|
if (entry.IsVisible)
|
||||||
{
|
{
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Open Analysis"))
|
||||||
|
{
|
||||||
|
_displayHandler.OpenAnalysis(_pair);
|
||||||
|
ImGui.CloseCurrentPopup();
|
||||||
|
}
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
||||||
{
|
{
|
||||||
entry.ApplyLastReceivedData(forced: true);
|
entry.ApplyLastReceivedData(forced: true);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
private Task? _conversionTask;
|
private Task? _conversionTask;
|
||||||
private bool _enableBc7ConversionMode = false;
|
private bool _enableBc7ConversionMode = false;
|
||||||
private bool _hasUpdate = false;
|
private bool _hasUpdate = false;
|
||||||
|
private bool _sortDirty = true;
|
||||||
private bool _modalOpen = false;
|
private bool _modalOpen = false;
|
||||||
private string _selectedFileTypeTab = string.Empty;
|
private string _selectedFileTypeTab = string.Empty;
|
||||||
private string _selectedHash = string.Empty;
|
private string _selectedHash = string.Empty;
|
||||||
@@ -102,11 +103,12 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
_cachedAnalysis = _characterAnalyzer.LastAnalysis.DeepClone();
|
_cachedAnalysis = _characterAnalyzer.LastAnalysis.DeepClone();
|
||||||
_hasUpdate = false;
|
_hasUpdate = false;
|
||||||
|
_sortDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSharedService.TextWrapped("This window shows you all files and their sizes that are currently in use through your character and associated entities");
|
UiSharedService.TextWrapped("This window shows you all files and their sizes that are currently in use through your character and associated entities");
|
||||||
|
|
||||||
if (_cachedAnalysis!.Count == 0) return;
|
if (_cachedAnalysis == null || _cachedAnalysis.Count == 0) return;
|
||||||
|
|
||||||
bool isAnalyzing = _characterAnalyzer.IsAnalysisRunning;
|
bool isAnalyzing = _characterAnalyzer.IsAnalysisRunning;
|
||||||
bool needAnalysis = _cachedAnalysis!.Any(c => c.Value.Any(f => !f.Value.IsComputed));
|
bool needAnalysis = _cachedAnalysis!.Any(c => c.Value.Any(f => !f.Value.IsComputed));
|
||||||
@@ -161,7 +163,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted("Total size (actual):");
|
ImGui.TextUnformatted("Total size (actual):");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(_cachedAnalysis!.Sum(c => c.Value.Sum(c => c.Value.OriginalSize))));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(_cachedAnalysis!.Sum(c => c.Value.Sum(c => c.Value.OriginalSize))));
|
||||||
ImGui.TextUnformatted("Total size (compressed for up/download only):");
|
ImGui.TextUnformatted("Total size (download size):");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, needAnalysis))
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, needAnalysis))
|
||||||
{
|
{
|
||||||
@@ -182,7 +184,6 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(kvp.Key.ToString());
|
using var id = ImRaii.PushId(kvp.Key.ToString());
|
||||||
string tabText = kvp.Key.ToString();
|
string tabText = kvp.Key.ToString();
|
||||||
if (kvp.Value.Any(f => !f.Value.IsComputed)) tabText += " (!)";
|
|
||||||
using var tab = ImRaii.TabItem(tabText + "###" + kvp.Key.ToString());
|
using var tab = ImRaii.TabItem(tabText + "###" + kvp.Key.ToString());
|
||||||
if (tab.Success)
|
if (tab.Success)
|
||||||
{
|
{
|
||||||
@@ -209,7 +210,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted($"{kvp.Key} size (actual):");
|
ImGui.TextUnformatted($"{kvp.Key} size (actual):");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.OriginalSize)));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.OriginalSize)));
|
||||||
ImGui.TextUnformatted($"{kvp.Key} size (compressed for up/download only):");
|
ImGui.TextUnformatted($"{kvp.Key} size (download size):");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, needAnalysis))
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, needAnalysis))
|
||||||
{
|
{
|
||||||
@@ -248,10 +249,6 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
string fileGroupText = fileGroup.Key + " [" + fileGroup.Count() + "]";
|
string fileGroupText = fileGroup.Key + " [" + fileGroup.Count() + "]";
|
||||||
var requiresCompute = fileGroup.Any(k => !k.IsComputed);
|
var requiresCompute = fileGroup.Any(k => !k.IsComputed);
|
||||||
using var tabcol = ImRaii.PushColor(ImGuiCol.Tab, UiSharedService.Color(ImGuiColors.DalamudYellow), requiresCompute);
|
using var tabcol = ImRaii.PushColor(ImGuiCol.Tab, UiSharedService.Color(ImGuiColors.DalamudYellow), requiresCompute);
|
||||||
if (requiresCompute)
|
|
||||||
{
|
|
||||||
fileGroupText += " (!)";
|
|
||||||
}
|
|
||||||
ImRaii.IEndObject fileTab;
|
ImRaii.IEndObject fileTab;
|
||||||
using (var textcol = ImRaii.PushColor(ImGuiCol.Text, UiSharedService.Color(new(0, 0, 0, 1)),
|
using (var textcol = ImRaii.PushColor(ImGuiCol.Text, UiSharedService.Color(new(0, 0, 0, 1)),
|
||||||
requiresCompute && !string.Equals(_selectedFileTypeTab, fileGroup.Key, StringComparison.Ordinal)))
|
requiresCompute && !string.Equals(_selectedFileTypeTab, fileGroup.Key, StringComparison.Ordinal)))
|
||||||
@@ -277,7 +274,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.OriginalSize)));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.OriginalSize)));
|
||||||
|
|
||||||
ImGui.TextUnformatted($"{fileGroup.Key} files size (compressed for up/download only):");
|
ImGui.TextUnformatted($"{fileGroup.Key} files size (download size):");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.CompressedSize)));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.CompressedSize)));
|
||||||
|
|
||||||
@@ -376,10 +373,10 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
new Vector2(0, 300));
|
new Vector2(0, 300));
|
||||||
if (!table.Success) return;
|
if (!table.Success) return;
|
||||||
ImGui.TableSetupColumn("Hash");
|
ImGui.TableSetupColumn("Hash");
|
||||||
ImGui.TableSetupColumn("Filepaths");
|
ImGui.TableSetupColumn("Filepaths", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
ImGui.TableSetupColumn("Gamepaths");
|
ImGui.TableSetupColumn("Gamepaths", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
ImGui.TableSetupColumn("Original Size");
|
ImGui.TableSetupColumn("File Size", ImGuiTableColumnFlags.DefaultSort | ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
ImGui.TableSetupColumn("Compressed Size");
|
ImGui.TableSetupColumn("Download Size", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal))
|
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ImGui.TableSetupColumn("Format");
|
ImGui.TableSetupColumn("Format");
|
||||||
@@ -387,13 +384,13 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal))
|
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ImGui.TableSetupColumn("Triangles");
|
ImGui.TableSetupColumn("Triangles", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
}
|
}
|
||||||
ImGui.TableSetupScrollFreeze(0, 1);
|
ImGui.TableSetupScrollFreeze(0, 1);
|
||||||
ImGui.TableHeadersRow();
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
var sortSpecs = ImGui.TableGetSortSpecs();
|
var sortSpecs = ImGui.TableGetSortSpecs();
|
||||||
if (sortSpecs.SpecsDirty)
|
if (sortSpecs.SpecsDirty || _sortDirty)
|
||||||
{
|
{
|
||||||
var idx = sortSpecs.Specs.ColumnIndex;
|
var idx = sortSpecs.Specs.ColumnIndex;
|
||||||
|
|
||||||
@@ -427,6 +424,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.Format.Value, StringComparer.Ordinal).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.Format.Value, StringComparer.Ordinal).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
|
||||||
sortSpecs.SpecsDirty = false;
|
sortSpecs.SpecsDirty = false;
|
||||||
|
_sortDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in fileGroup)
|
foreach (var item in fileGroup)
|
||||||
@@ -462,7 +460,8 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
if (_enableBc7ConversionMode)
|
if (_enableBc7ConversionMode)
|
||||||
{
|
{
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (string.Equals(item.Format.Value, "BC7", StringComparison.Ordinal))
|
if (item.Format.Value.StartsWith("BC") || item.Format.Value.StartsWith("DXT")
|
||||||
|
|| item.Format.Value.StartsWith("24864")) // BC4
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted("");
|
ImGui.TextUnformatted("");
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -194,6 +194,11 @@ public class UidDisplayHandler
|
|||||||
_mediator.Publish(new ProfileOpenStandaloneMessage(entry));
|
_mediator.Publish(new ProfileOpenStandaloneMessage(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void OpenAnalysis(Pair entry)
|
||||||
|
{
|
||||||
|
_mediator.Publish(new OpenPairAnalysisWindow(entry));
|
||||||
|
}
|
||||||
|
|
||||||
private bool ShowUidInsteadOfName(Pair pair)
|
private bool ShowUidInsteadOfName(Pair pair)
|
||||||
{
|
{
|
||||||
_showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
|
_showUidForEntry.TryGetValue(pair.UserData.UID, out var showUidInsteadOfName);
|
||||||
|
|||||||
370
MareSynchronos/UI/PlayerAnalysisUI.cs
Normal file
370
MareSynchronos/UI/PlayerAnalysisUI.cs
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using ImGuiNET;
|
||||||
|
using MareSynchronos.API.Data.Enum;
|
||||||
|
using MareSynchronos.API.Data.Extensions;
|
||||||
|
using MareSynchronos.Interop.Ipc;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
|
using MareSynchronos.WebAPI;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace MareSynchronos.UI;
|
||||||
|
|
||||||
|
public class PlayerAnalysisUI : WindowMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly UiSharedService _uiSharedService;
|
||||||
|
private Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>>? _cachedAnalysis;
|
||||||
|
private bool _hasUpdate = true;
|
||||||
|
private bool _sortDirty = true;
|
||||||
|
private string _selectedFileTypeTab = string.Empty;
|
||||||
|
private string _selectedHash = string.Empty;
|
||||||
|
private ObjectKind _selectedObjectTab;
|
||||||
|
|
||||||
|
public PlayerAnalysisUI(ILogger<PlayerAnalysisUI> logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService,
|
||||||
|
PerformanceCollectorService performanceCollectorService)
|
||||||
|
: base(logger, mediator, "Character Data Analysis for " + pair.UserData.AliasOrUID + "###LoporritPairAnalysis" + pair.UserData.UID, performanceCollectorService)
|
||||||
|
{
|
||||||
|
Pair = pair;
|
||||||
|
_uiSharedService = uiSharedService;
|
||||||
|
Mediator.SubscribeKeyed<PairDataAnalyzedMessage>(this, Pair.UserData.UID, (_) =>
|
||||||
|
{
|
||||||
|
_logger.LogInformation("PairDataAnalyzedMessage received for {uid}", Pair.UserData.UID);
|
||||||
|
_hasUpdate = true;
|
||||||
|
});
|
||||||
|
SizeConstraints = new()
|
||||||
|
{
|
||||||
|
MinimumSize = new()
|
||||||
|
{
|
||||||
|
X = 800,
|
||||||
|
Y = 600
|
||||||
|
},
|
||||||
|
MaximumSize = new()
|
||||||
|
{
|
||||||
|
X = 3840,
|
||||||
|
Y = 2160
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IsOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair Pair { get; private init; }
|
||||||
|
public PairAnalyzer? PairAnalyzer => Pair.PairAnalyzer;
|
||||||
|
|
||||||
|
public override void OnClose()
|
||||||
|
{
|
||||||
|
Mediator.Publish(new RemoveWindowMessage(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DrawInternal()
|
||||||
|
{
|
||||||
|
if (PairAnalyzer == null) return;
|
||||||
|
PairAnalyzer analyzer = PairAnalyzer!;
|
||||||
|
|
||||||
|
if (_hasUpdate)
|
||||||
|
{
|
||||||
|
_cachedAnalysis = analyzer.LastAnalysis.DeepClone();
|
||||||
|
_hasUpdate = false;
|
||||||
|
_sortDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiSharedService.TextWrapped($"This window shows you all files and their sizes that are currently in use by {Pair.UserData.AliasOrUID} and associated entities");
|
||||||
|
|
||||||
|
if (_cachedAnalysis == null || _cachedAnalysis.Count == 0) return;
|
||||||
|
|
||||||
|
bool isAnalyzing = analyzer.IsAnalysisRunning;
|
||||||
|
bool needAnalysis = _cachedAnalysis!.Any(c => c.Value.Any(f => !f.Value.IsComputed));
|
||||||
|
if (isAnalyzing)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped($"Analyzing {analyzer.CurrentFile}/{analyzer.TotalFiles}",
|
||||||
|
ImGuiColors.DalamudYellow);
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel analysis"))
|
||||||
|
{
|
||||||
|
analyzer.CancelAnalyze();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (needAnalysis)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Some entries in the analysis have file size not determined yet, press the button below to compute missing data",
|
||||||
|
ImGuiColors.DalamudYellow);
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (missing entries)"))
|
||||||
|
{
|
||||||
|
_ = analyzer.ComputeAnalysis(print: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
ImGui.TextUnformatted("Total files:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(_cachedAnalysis!.Values.Sum(c => c.Values.Count).ToString());
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString());
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
string text = "";
|
||||||
|
var groupedfiles = _cachedAnalysis.Values.SelectMany(f => f.Values).GroupBy(f => f.FileType, StringComparer.Ordinal);
|
||||||
|
text = string.Join(Environment.NewLine, groupedfiles.OrderBy(f => f.Key, StringComparer.Ordinal)
|
||||||
|
.Select(f => f.Key + ": " + f.Count() + " files, size: " + UiSharedService.ByteToString(f.Sum(v => v.OriginalSize))
|
||||||
|
+ ", compressed: " + UiSharedService.ByteToString(f.Sum(v => v.CompressedSize))));
|
||||||
|
ImGui.SetTooltip(text);
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted("Total size (actual):");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(_cachedAnalysis!.Sum(c => c.Value.Sum(c => c.Value.OriginalSize))));
|
||||||
|
ImGui.TextUnformatted("Total size (compressed for up/download only):");
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, needAnalysis))
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(_cachedAnalysis!.Sum(c => c.Value.Sum(c => c.Value.CompressedSize))));
|
||||||
|
if (needAnalysis && !isAnalyzing)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
|
ImGui.TextUnformatted(FontAwesomeIcon.ExclamationCircle.ToIconString());
|
||||||
|
UiSharedService.AttachToolTip("Click \"Start analysis\" to calculate download size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted($"Total modded model triangles: {UiSharedService.TrisToString(_cachedAnalysis.Sum(c => c.Value.Sum(f => f.Value.Triangles)))}");
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var playerName = analyzer.LastPlayerName;
|
||||||
|
|
||||||
|
if (playerName.Length == 0)
|
||||||
|
{
|
||||||
|
playerName = Pair.PlayerName ?? string.Empty;
|
||||||
|
analyzer.LastPlayerName = playerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var tabbar = ImRaii.TabBar("objectSelection");
|
||||||
|
foreach (var kvp in _cachedAnalysis)
|
||||||
|
{
|
||||||
|
using var id = ImRaii.PushId(kvp.Key.ToString());
|
||||||
|
string tabText = kvp.Key == ObjectKind.Player ? playerName : $"{playerName}'s {kvp.Key}";
|
||||||
|
using var tab = ImRaii.TabItem(tabText + "###" + kvp.Key.ToString());
|
||||||
|
if (tab.Success)
|
||||||
|
{
|
||||||
|
var groupedfiles = kvp.Value.Select(v => v.Value).GroupBy(f => f.FileType, StringComparer.Ordinal)
|
||||||
|
.OrderBy(k => k.Key, StringComparer.Ordinal).ToList();
|
||||||
|
|
||||||
|
ImGui.TextUnformatted($"Files for {tabText}");
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(kvp.Value.Count.ToString());
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
using (var font = ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString());
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
{
|
||||||
|
string text = "";
|
||||||
|
text = string.Join(Environment.NewLine, groupedfiles
|
||||||
|
.Select(f => f.Key + ": " + f.Count() + " files, size: " + UiSharedService.ByteToString(f.Sum(v => v.OriginalSize))
|
||||||
|
+ ", compressed: " + UiSharedService.ByteToString(f.Sum(v => v.CompressedSize))));
|
||||||
|
ImGui.SetTooltip(text);
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted($"{kvp.Key} size (actual):");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.OriginalSize)));
|
||||||
|
ImGui.TextUnformatted($"{kvp.Key} size (download size):");
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, needAnalysis))
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(kvp.Value.Sum(c => c.Value.CompressedSize)));
|
||||||
|
if (needAnalysis && !isAnalyzing)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||||
|
ImGui.TextUnformatted(FontAwesomeIcon.ExclamationCircle.ToIconString());
|
||||||
|
UiSharedService.AttachToolTip("Click \"Start analysis\" to calculate download size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted($"{kvp.Key} VRAM usage:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
var vramUsage = groupedfiles.SingleOrDefault(v => string.Equals(v.Key, "tex", StringComparison.Ordinal));
|
||||||
|
if (vramUsage != null)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(vramUsage.Sum(f => f.OriginalSize)));
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted($"{kvp.Key} modded model triangles: {UiSharedService.TrisToString(kvp.Value.Sum(f => f.Value.Triangles))}");
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
if (_selectedObjectTab != kvp.Key)
|
||||||
|
{
|
||||||
|
_selectedHash = string.Empty;
|
||||||
|
_selectedObjectTab = kvp.Key;
|
||||||
|
_selectedFileTypeTab = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var fileTabBar = ImRaii.TabBar("fileTabs");
|
||||||
|
|
||||||
|
foreach (IGrouping<string, CharacterAnalyzer.FileDataEntry>? fileGroup in groupedfiles)
|
||||||
|
{
|
||||||
|
string fileGroupText = fileGroup.Key + " [" + fileGroup.Count() + "]";
|
||||||
|
var requiresCompute = fileGroup.Any(k => !k.IsComputed);
|
||||||
|
using var tabcol = ImRaii.PushColor(ImGuiCol.Tab, UiSharedService.Color(ImGuiColors.DalamudYellow), requiresCompute);
|
||||||
|
ImRaii.IEndObject fileTab;
|
||||||
|
using (var textcol = ImRaii.PushColor(ImGuiCol.Text, UiSharedService.Color(new(0, 0, 0, 1)),
|
||||||
|
requiresCompute && !string.Equals(_selectedFileTypeTab, fileGroup.Key, StringComparison.Ordinal)))
|
||||||
|
{
|
||||||
|
fileTab = ImRaii.TabItem(fileGroupText + "###" + fileGroup.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileTab) { fileTab.Dispose(); continue; }
|
||||||
|
|
||||||
|
if (!string.Equals(fileGroup.Key, _selectedFileTypeTab, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
_selectedFileTypeTab = fileGroup.Key;
|
||||||
|
_selectedHash = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.TextUnformatted($"{fileGroup.Key} files");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(fileGroup.Count().ToString());
|
||||||
|
|
||||||
|
ImGui.TextUnformatted($"{fileGroup.Key} files size (actual):");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.OriginalSize)));
|
||||||
|
|
||||||
|
ImGui.TextUnformatted($"{fileGroup.Key} files size (download size):");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.CompressedSize)));
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
DrawTable(fileGroup);
|
||||||
|
|
||||||
|
fileTab.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
ImGui.TextUnformatted("Selected file:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
UiSharedService.ColorText(_selectedHash, ImGuiColors.DalamudYellow);
|
||||||
|
|
||||||
|
if (_cachedAnalysis[_selectedObjectTab].TryGetValue(_selectedHash, out CharacterAnalyzer.FileDataEntry? item))
|
||||||
|
{
|
||||||
|
var gamepaths = item.GamePaths;
|
||||||
|
ImGui.TextUnformatted("Used by game path:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
UiSharedService.TextWrapped(gamepaths[0]);
|
||||||
|
if (gamepaths.Count > 1)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted($"(and {gamepaths.Count - 1} more)");
|
||||||
|
ImGui.SameLine();
|
||||||
|
_uiSharedService.IconText(FontAwesomeIcon.InfoCircle);
|
||||||
|
UiSharedService.AttachToolTip(string.Join(Environment.NewLine, gamepaths.Skip(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawTable(IGrouping<string, CharacterAnalyzer.FileDataEntry> fileGroup)
|
||||||
|
{
|
||||||
|
var tableColumns = string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal)
|
||||||
|
? 5
|
||||||
|
: (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal) ? 5 : 4);
|
||||||
|
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");
|
||||||
|
ImGui.TableSetupColumn("Gamepaths", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
|
ImGui.TableSetupColumn("File Size", ImGuiTableColumnFlags.DefaultSort | ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
|
ImGui.TableSetupColumn("Download Size", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
|
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn("Format");
|
||||||
|
}
|
||||||
|
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn("Triangles", ImGuiTableColumnFlags.PreferSortDescending);
|
||||||
|
}
|
||||||
|
ImGui.TableSetupScrollFreeze(0, 1);
|
||||||
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
|
var sortSpecs = ImGui.TableGetSortSpecs();
|
||||||
|
if (sortSpecs.SpecsDirty || _sortDirty)
|
||||||
|
{
|
||||||
|
var idx = sortSpecs.Specs.ColumnIndex;
|
||||||
|
|
||||||
|
if (idx == 0 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderBy(k => k.Key, StringComparer.Ordinal).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 0 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Key, StringComparer.Ordinal).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 1 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderBy(k => k.Value.GamePaths.Count).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 1 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.GamePaths.Count).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 2 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderBy(k => k.Value.OriginalSize).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 2 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.OriginalSize).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 3 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderBy(k => k.Value.CompressedSize).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (idx == 3 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.CompressedSize).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal) && idx == 4 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderBy(k => k.Value.Triangles).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal) && idx == 4 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.Triangles).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal) && idx == 4 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderBy(k => k.Value.Format.Value, StringComparer.Ordinal).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal) && idx == 4 && sortSpecs.Specs.SortDirection == ImGuiSortDirection.Descending)
|
||||||
|
_cachedAnalysis![_selectedObjectTab] = _cachedAnalysis[_selectedObjectTab].OrderByDescending(k => k.Value.Format.Value, StringComparer.Ordinal).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
sortSpecs.SpecsDirty = false;
|
||||||
|
_sortDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in fileGroup)
|
||||||
|
{
|
||||||
|
using var text = ImRaii.PushColor(ImGuiCol.Text, new Vector4(0, 0, 0, 1), string.Equals(item.Hash, _selectedHash, StringComparison.Ordinal));
|
||||||
|
using var text2 = ImRaii.PushColor(ImGuiCol.Text, new Vector4(1, 1, 1, 1), !item.IsComputed);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
if (string.Equals(_selectedHash, item.Hash, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg1, UiSharedService.Color(ImGuiColors.DalamudYellow));
|
||||||
|
ImGui.TableSetBgColor(ImGuiTableBgTarget.RowBg0, UiSharedService.Color(ImGuiColors.DalamudYellow));
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted(item.Hash);
|
||||||
|
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(item.GamePaths.Count.ToString());
|
||||||
|
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(item.OriginalSize));
|
||||||
|
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, !item.IsComputed))
|
||||||
|
ImGui.TextUnformatted(UiSharedService.ByteToString(item.CompressedSize));
|
||||||
|
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
|
||||||
|
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(item.Format.Value);
|
||||||
|
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
|
||||||
|
}
|
||||||
|
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(UiSharedService.TrisToString(item.Triangles));
|
||||||
|
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -322,7 +322,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
var filePath = _fileDbManager.GetCacheFilePath(fileHash, fileExtension);
|
var filePath = _fileDbManager.GetCacheFilePath(fileHash, fileExtension);
|
||||||
await _fileCompactor.WriteAllBytesAsync(filePath, decompressedFile.ToArray(), token).ConfigureAwait(false);
|
await _fileCompactor.WriteAllBytesAsync(filePath, decompressedFile.ToArray(), token).ConfigureAwait(false);
|
||||||
|
|
||||||
PersistFileToStorage(fileHash, filePath);
|
PersistFileToStorage(fileHash, filePath, fileLengthBytes);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -355,7 +355,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
return await response.Content.ReadFromJsonAsync<List<DownloadFileDto>>(cancellationToken: ct).ConfigureAwait(false) ?? [];
|
return await response.Content.ReadFromJsonAsync<List<DownloadFileDto>>(cancellationToken: ct).ConfigureAwait(false) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PersistFileToStorage(string fileHash, string filePath)
|
private void PersistFileToStorage(string fileHash, string filePath, long? compressedSize = null)
|
||||||
{
|
{
|
||||||
var fi = new FileInfo(filePath);
|
var fi = new FileInfo(filePath);
|
||||||
Func<DateTime> RandomDayInThePast()
|
Func<DateTime> RandomDayInThePast()
|
||||||
@@ -378,6 +378,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
File.Delete(filePath);
|
File.Delete(filePath);
|
||||||
_fileDbManager.RemoveHashedFile(entry.Hash, entry.PrefixedFilePath);
|
_fileDbManager.RemoveHashedFile(entry.Hash, entry.PrefixedFilePath);
|
||||||
}
|
}
|
||||||
|
if (entry != null)
|
||||||
|
entry.CompressedSize = compressedSize;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -245,6 +245,8 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
var compressedSize = CurrentUploads.Sum(c => c.Total);
|
var compressedSize = CurrentUploads.Sum(c => c.Total);
|
||||||
Logger.LogDebug("Upload complete, compressed {size} to {compressed}", UiSharedService.ByteToString(totalSize), UiSharedService.ByteToString(compressedSize));
|
Logger.LogDebug("Upload complete, compressed {size} to {compressed}", UiSharedService.ByteToString(totalSize), UiSharedService.ByteToString(compressedSize));
|
||||||
|
|
||||||
|
_fileDbManager.WriteOutFullCsv();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var file in unverifiedUploadHashes.Where(c => !CurrentUploads.Exists(u => string.Equals(u.Hash, c, StringComparison.Ordinal))))
|
foreach (var file in unverifiedUploadHashes.Where(c => !CurrentUploads.Exists(u => string.Equals(u.Hash, c, StringComparison.Ordinal))))
|
||||||
|
|||||||
Reference in New Issue
Block a user