Partial roll-up to reduce code divergence

Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
Loporrit
2025-02-15 23:07:46 +00:00
parent 324288652d
commit 7a8de7add6
41 changed files with 963 additions and 464 deletions

View File

@@ -193,16 +193,16 @@ public sealed class CharacterAnalyzer : MediatorSubscriberBase, IDisposable
public bool IsComputed => OriginalSize > 0 && CompressedSize > 0;
public async Task ComputeSizes(FileCacheManager fileCacheManager, CancellationToken token)
{
var compressedsize = await fileCacheManager.GetCompressedFileLength(Hash, token).ConfigureAwait(false);
var compressedsize = await fileCacheManager.GetCompressedFileData(Hash, token).ConfigureAwait(false);
var normalSize = new FileInfo(FilePaths[0]).Length;
var entries = fileCacheManager.GetAllFileCachesByHash(Hash, ignoreCacheEntries: true, validate: false);
foreach (var entry in entries)
{
entry.Size = normalSize;
entry.CompressedSize = compressedsize;
entry.CompressedSize = compressedsize.Item2.LongLength;
}
OriginalSize = normalSize;
CompressedSize = compressedsize;
CompressedSize = compressedsize.Item2.LongLength;
}
public long OriginalSize { get; private set; } = OriginalSize;
public long CompressedSize { get; private set; } = CompressedSize;

View File

@@ -36,6 +36,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
private readonly List<uint> _classJobIdsIgnoredForPets = [30];
private readonly IClientState _clientState;
private readonly ICondition _condition;
private readonly IDataManager _gameData;
private readonly IFramework _framework;
private readonly IGameGui _gameGui;
private readonly IToastGui _toastGui;
@@ -63,6 +64,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
_gameGui = gameGui;
_toastGui = toastGui;
_condition = condition;
_gameData = gameData;
Mediator = mediator;
_performanceCollector = performanceCollector;
WorldData = new(() =>
@@ -98,27 +100,28 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
public bool IsWine { get; init; }
public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget;
public unsafe DalamudGameObject? GposeTargetGameObject => GposeTarget == null ? null : _objectTable[GposeTarget->ObjectIndex];
public unsafe Dalamud.Game.ClientState.Objects.Types.IGameObject? GposeTargetGameObject => GposeTarget == null ? null : _objectTable[GposeTarget->ObjectIndex];
public bool IsAnythingDrawing { get; private set; } = false;
public bool IsInCutscene { get; private set; } = false;
public bool IsInGpose { get; private set; } = false;
public bool IsLoggedIn { get; private set; }
public bool IsOnFrameworkThread => _framework.IsInFrameworkUpdateThread;
public bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51];
public bool IsInCombat { get; private set; } = false;
public bool IsInCombatOrPerforming { get; private set; } = false;
public bool HasModifiedGameFiles => _gameData.HasModifiedGameDataFiles;
public Lazy<Dictionary<ushort, string>> WorldData { get; private set; }
public Lazy<Dictionary<int, Lumina.Excel.Sheets.UIColor>> UiColors { get; private set; }
public MareMediator Mediator { get; }
public DalamudGameObject? CreateGameObject(IntPtr reference)
public Dalamud.Game.ClientState.Objects.Types.IGameObject? CreateGameObject(IntPtr reference)
{
EnsureIsOnFramework();
return _objectTable.CreateObjectReference(reference);
}
public async Task<DalamudGameObject?> CreateGameObjectAsync(IntPtr reference)
public async Task<Dalamud.Game.ClientState.Objects.Types.IGameObject?> CreateGameObjectAsync(IntPtr reference)
{
return await RunOnFrameworkThread(() => _objectTable.CreateObjectReference(reference)).ConfigureAwait(false);
}
@@ -532,19 +535,19 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
Mediator.Publish(new GposeEndMessage());
}
if (_condition[ConditionFlag.InCombat] && !IsInCombat)
if ((_condition[ConditionFlag.Performing] || _condition[ConditionFlag.InCombat]) && !IsInCombatOrPerforming)
{
_logger.LogDebug("Combat start");
IsInCombat = true;
Mediator.Publish(new CombatStartMessage());
Mediator.Publish(new HaltScanMessage(nameof(IsInCombat)));
_logger.LogDebug("Combat/Performance start");
IsInCombatOrPerforming = true;
Mediator.Publish(new CombatOrPerformanceStartMessage());
Mediator.Publish(new HaltScanMessage(nameof(IsInCombatOrPerforming)));
}
else if (!_condition[ConditionFlag.InCombat] && IsInCombat)
else if ((!_condition[ConditionFlag.Performing] && !_condition[ConditionFlag.InCombat]) && IsInCombatOrPerforming)
{
_logger.LogDebug("Combat end");
IsInCombat = false;
Mediator.Publish(new CombatEndMessage());
Mediator.Publish(new ResumeScanMessage(nameof(IsInCombat)));
_logger.LogDebug("Combat/Performance end");
IsInCombatOrPerforming = false;
Mediator.Publish(new CombatOrPerformanceEndMessage());
Mediator.Publish(new ResumeScanMessage(nameof(IsInCombatOrPerforming)));
}
if (_condition[ConditionFlag.WatchingCutscene] && !IsInCutscene)
@@ -595,7 +598,13 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
Mediator.Publish(new ResumeScanMessage(nameof(ConditionFlag.BetweenAreas)));
}
if (!IsInCombat)
var localPlayer = _clientState.LocalPlayer;
if (localPlayer != null)
{
_classJobId = localPlayer.ClassJob.RowId;
}
if (!IsInCombatOrPerforming)
Mediator.Publish(new FrameworkUpdateMessage());
Mediator.Publish(new PriorityFrameworkUpdateMessage());
@@ -603,8 +612,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
if (isNormalFrameworkUpdate)
return;
var localPlayer = _clientState.LocalPlayer;
if (localPlayer != null && !IsLoggedIn)
{
_logger.LogDebug("Logged in");
@@ -619,7 +626,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
Mediator.Publish(new DalamudLogoutMessage());
}
if (IsInCombat)
if (IsInCombatOrPerforming)
Mediator.Publish(new FrameworkUpdateMessage());
Mediator.Publish(new DelayedFrameworkUpdateMessage());

View File

@@ -1,4 +1,4 @@
using MareSynchronos.MareConfiguration;
using MareSynchronos.MareConfiguration;
using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils;
using Microsoft.Extensions.Hosting;
@@ -21,17 +21,12 @@ public class EventAggregator : MediatorSubscriberBase, IHostedService
public EventAggregator(MareConfigService configService, ILogger<EventAggregator> logger, MareMediator mareMediator) : base(logger, mareMediator)
{
Logger.LogInformation("Starting EventAggregatorService");
Logger.LogInformation("Started EventAggregatorService");
_configDirectory = configService.ConfigurationDirectory;
_logger = logger;
_currentTime = DateTime.UnixEpoch;
Mediator.Subscribe<EventMessage>(this, (msg) =>
{
_lock.Wait();
try
{
Logger.LogTrace("Received Event: {evt}", msg.Event.ToString());
_events.Add(msg.Event);
if (configService.Current.LogEvents)
WriteToFile(msg.Event);
@@ -45,6 +40,9 @@ public class EventAggregator : MediatorSubscriberBase, IHostedService
});
EventList = CreateEventLazy();
_configDirectory = configService.ConfigurationDirectory;
_logger = logger;
_currentTime = DateTime.Now - TimeSpan.FromDays(1);
}
private void RecreateLazy()
@@ -103,6 +101,8 @@ public class EventAggregator : MediatorSubscriberBase, IHostedService
public Task StartAsync(CancellationToken cancellationToken)
{
Logger.LogInformation("Starting EventAggregatorService");
Logger.LogInformation("Started EventAggregatorService");
return Task.CompletedTask;
}

View File

@@ -38,7 +38,7 @@ public record PenumbraRedrawMessage(IntPtr Address, int ObjTblIdx, bool WasReque
public record GlamourerChangedMessage(IntPtr Address) : MessageBase;
public record HeelsOffsetMessage : MessageBase;
public record PenumbraResourceLoadMessage(IntPtr GameObject, string GamePath, string FilePath) : SameThreadMessage;
public record CustomizePlusMessage(string ProfileName) : MessageBase;
public record CustomizePlusMessage(nint? Address) : MessageBase;
public record HonorificMessage(string NewHonorificTitle) : MessageBase;
public record PetNamesReadyMessage : MessageBase;
public record PetNamesMessage(string PetNicknamesData) : MessageBase;
@@ -68,6 +68,7 @@ public record UiToggleMessage(Type UiType) : MessageBase;
public record PlayerUploadingMessage(GameObjectHandler Handler, bool IsUploading) : MessageBase;
public record ClearProfileDataMessage(UserData? UserData = null) : MessageBase;
public record CyclePauseMessage(UserData UserData) : MessageBase;
public record PauseMessage(UserData UserData) : MessageBase;
public record ProfilePopoutToggle(Pair? Pair) : MessageBase;
public record CompactUiChange(Vector2 Size, Vector2 Position) : MessageBase;
public record ProfileOpenStandaloneMessage(Pair Pair) : MessageBase;
@@ -80,8 +81,8 @@ public record OpenPermissionWindow(Pair Pair) : MessageBase;
public record DownloadLimitChangedMessage() : SameThreadMessage;
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
public record TargetPairMessage(Pair Pair) : MessageBase;
public record CombatStartMessage : MessageBase;
public record CombatEndMessage : MessageBase;
public record CombatOrPerformanceStartMessage : MessageBase;
public record CombatOrPerformanceEndMessage : MessageBase;
public record EventMessage(Event Event) : MessageBase;
public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBase;
public record PenumbraRedrawCharacterMessage(ICharacter Character) : SameThreadMessage;

View File

@@ -4,24 +4,39 @@ using Dalamud.Plugin.Services;
using MareSynchronos.MareConfiguration;
using MareSynchronos.MareConfiguration.Models;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NotificationType = MareSynchronos.MareConfiguration.Models.NotificationType;
namespace MareSynchronos.Services;
public class NotificationService : DisposableMediatorSubscriberBase
public class NotificationService : DisposableMediatorSubscriberBase, IHostedService
{
private readonly DalamudUtilService _dalamudUtilService;
private readonly INotificationManager _notificationManager;
private readonly IChatGui _chatGui;
private readonly MareConfigService _configurationService;
public NotificationService(ILogger<NotificationService> logger, MareMediator mediator, INotificationManager notificationManager, IChatGui chatGui, MareConfigService configurationService) : base(logger, mediator)
public NotificationService(ILogger<NotificationService> logger, MareMediator mediator,
DalamudUtilService dalamudUtilService,
INotificationManager notificationManager,
IChatGui chatGui, MareConfigService configurationService) : base(logger, mediator)
{
_dalamudUtilService = dalamudUtilService;
_notificationManager = notificationManager;
_chatGui = chatGui;
_configurationService = configurationService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
Mediator.Subscribe<NotificationMessage>(this, ShowNotification);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void PrintErrorChat(string? message)
@@ -64,6 +79,8 @@ public class NotificationService : DisposableMediatorSubscriberBase
{
Logger.LogInformation("{msg}", msg.ToString());
if (!_dalamudUtilService.IsLoggedIn) return;
switch (msg.Type)
{
case NotificationType.Info:

View File

@@ -0,0 +1,115 @@
using MareSynchronos.API.Data;
using MareSynchronos.FileCache;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services.Mediator;
using MareSynchronos.WebAPI.Files.Models;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Services;
public class PlayerPerformanceService
{
private readonly FileCacheManager _fileCacheManager;
private readonly XivDataAnalyzer _xivDataAnalyzer;
private readonly ILogger<PlayerPerformanceService> _logger;
private readonly MareMediator _mediator;
public PlayerPerformanceService(ILogger<PlayerPerformanceService> logger, MareMediator mediator,
FileCacheManager fileCacheManager,
XivDataAnalyzer xivDataAnalyzer)
{
_logger = logger;
_mediator = mediator;
_fileCacheManager = fileCacheManager;
_xivDataAnalyzer = xivDataAnalyzer;
}
public async Task<bool> CheckBothThresholds(PairHandler pairHandler, CharacterData charaData)
{
bool notPausedAfterVram = ComputeAndAutoPauseOnVRAMUsageThresholds(pairHandler, charaData, []);
if (!notPausedAfterVram) return false;
bool notPausedAfterTris = await CheckTriangleUsageThresholds(pairHandler, charaData).ConfigureAwait(false);
if (!notPausedAfterTris) return false;
return true;
}
public async Task<bool> CheckTriangleUsageThresholds(PairHandler pairHandler, CharacterData charaData)
{
var pair = pairHandler.Pair;
long triUsage = 0;
if (!charaData.FileReplacements.TryGetValue(API.Data.Enum.ObjectKind.Player, out List<FileReplacementData>? playerReplacements))
{
pair.LastAppliedDataTris = 0;
return true;
}
var moddedModelHashes = playerReplacements.Where(p => string.IsNullOrEmpty(p.FileSwapPath) && p.GamePaths.Any(g => g.EndsWith("mdl", StringComparison.OrdinalIgnoreCase)))
.Select(p => p.Hash)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
foreach (var hash in moddedModelHashes)
{
triUsage += await _xivDataAnalyzer.GetTrianglesByHash(hash).ConfigureAwait(false);
}
pair.LastAppliedDataTris = triUsage;
_logger.LogDebug("Calculated VRAM usage for {p}", pairHandler);
return true;
}
public bool ComputeAndAutoPauseOnVRAMUsageThresholds(PairHandler pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles)
{
var pair = pairHandler.Pair;
long vramUsage = 0;
if (!charaData.FileReplacements.TryGetValue(API.Data.Enum.ObjectKind.Player, out List<FileReplacementData>? playerReplacements))
{
pair.LastAppliedApproximateVRAMBytes = 0;
return true;
}
var moddedTextureHashes = playerReplacements.Where(p => string.IsNullOrEmpty(p.FileSwapPath) && p.GamePaths.Any(g => g.EndsWith(".tex", StringComparison.OrdinalIgnoreCase)))
.Select(p => p.Hash)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
foreach (var hash in moddedTextureHashes)
{
long fileSize = 0;
var download = toDownloadFiles.Find(f => string.Equals(hash, f.Hash, StringComparison.OrdinalIgnoreCase));
if (download != null)
{
fileSize = download.TotalRaw;
}
else
{
var fileEntry = _fileCacheManager.GetFileCacheByHash(hash);
if (fileEntry == null) continue;
if (fileEntry.Size == null)
{
fileEntry.Size = new FileInfo(fileEntry.ResolvedFilepath).Length;
_fileCacheManager.UpdateHashedFile(fileEntry, computeProperties: true);
}
fileSize = fileEntry.Size.Value;
}
vramUsage += fileSize;
}
pair.LastAppliedApproximateVRAMBytes = vramUsage;
_logger.LogDebug("Calculated VRAM usage for {p}", pairHandler);
return true;
}
}

View File

@@ -147,15 +147,19 @@ public class ServerConfigurationManager
Save();
}
internal void AddCurrentCharacterToServer(int serverSelectionIndex = -1, bool addLastSecretKey = false)
internal void AddCurrentCharacterToServer(int serverSelectionIndex = -1)
{
if (serverSelectionIndex == -1) serverSelectionIndex = CurrentServerIndex;
var server = GetServerByIndex(serverSelectionIndex);
if (server.Authentications.Any(c => string.Equals(c.CharacterName, _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(), StringComparison.Ordinal)
&& c.WorldId == _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult()))
return;
server.Authentications.Add(new Authentication()
{
CharacterName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(),
WorldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(),
SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1,
SecretKeyIdx = server.SecretKeys.Last().Key,
});
Save();
}
@@ -163,7 +167,10 @@ public class ServerConfigurationManager
internal void AddEmptyCharacterToServer(int serverSelectionIndex)
{
var server = GetServerByIndex(serverSelectionIndex);
server.Authentications.Add(new Authentication());
server.Authentications.Add(new Authentication()
{
SecretKeyIdx = server.SecretKeys.Any() ? server.SecretKeys.First().Key : -1,
});
Save();
}

View File

@@ -1,12 +1,10 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.Havok.Animation;
using FFXIVClientStructs.Havok.Common.Base.Types;
using FFXIVClientStructs.Havok.Common.Serialize.Util;
using Lumina;
using Lumina.Data.Files;
using MareSynchronos.FileCache;
using MareSynchronos.Interop.GameModel;
using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Handlers;
using Microsoft.Extensions.Logging;
@@ -19,15 +17,14 @@ public sealed class XivDataAnalyzer
private readonly ILogger<XivDataAnalyzer> _logger;
private readonly FileCacheManager _fileCacheManager;
private readonly XivDataStorageService _configService;
private readonly GameData _luminaGameData;
private readonly List<string> _failedCalculatedTris = [];
public XivDataAnalyzer(ILogger<XivDataAnalyzer> logger, FileCacheManager fileCacheManager,
XivDataStorageService configService, IDataManager gameData)
XivDataStorageService configService)
{
_logger = logger;
_fileCacheManager = fileCacheManager;
_configService = configService;
_luminaGameData = new GameData(gameData.GameData.DataPath.FullName);
}
public unsafe Dictionary<string, List<ushort>>? GetSkeletonBoneIndices(GameObjectHandler handler)
@@ -153,50 +150,65 @@ public sealed class XivDataAnalyzer
return output;
}
public Task<long> GetTrianglesFromGamePath(string gamePath)
public async Task<long> GetTrianglesByHash(string hash)
{
if (_configService.Current.TriangleDictionary.TryGetValue(gamePath, out var cachedTris))
return Task.FromResult(cachedTris);
if (_configService.Current.TriangleDictionary.TryGetValue(hash, out var cachedTris) && cachedTris > 0)
return cachedTris;
_logger.LogDebug("Detected Model File {path}, calculating Tris", gamePath);
var file = _luminaGameData.GetFile<MdlFile>(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.LogDebug("{filePath} => {tris} triangles", gamePath, tris);
_configService.Current.TriangleDictionary[gamePath] = tris;
_configService.Save();
return Task.FromResult(tris);
}
public Task<long> GetTrianglesByHash(string hash)
{
if (_configService.Current.TriangleDictionary.TryGetValue(hash, out var cachedTris))
return Task.FromResult(cachedTris);
if (_failedCalculatedTris.Contains(hash, StringComparer.Ordinal))
return 0;
var path = _fileCacheManager.GetFileCacheByHash(hash);
if (path == null || !path.ResolvedFilepath.EndsWith(".mdl", StringComparison.OrdinalIgnoreCase))
return Task.FromResult((long)0);
return 0;
var filePath = path.ResolvedFilepath;
_logger.LogDebug("Detected Model File {path}, calculating Tris", filePath);
var file = _luminaGameData.GetFileFromDisk<MdlFile>(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;
try
{
_logger.LogDebug("Detected Model File {path}, calculating Tris", filePath);
var file = new MdlFile(filePath);
if (file.LodCount <= 0)
{
_failedCalculatedTris.Add(hash);
_configService.Current.TriangleDictionary[hash] = 0;
_configService.Save();
return 0;
}
_logger.LogDebug("{filePath} => {tris} triangles", filePath, tris);
_configService.Current.TriangleDictionary[hash] = tris;
_configService.Save();
return Task.FromResult(tris);
long tris = 0;
for (int i = 0; i < file.LodCount; i++)
{
try
{
var meshIdx = file.Lods[i].MeshIndex;
var meshCnt = file.Lods[i].MeshCount;
tris = file.Meshes.Skip(meshIdx).Take(meshCnt).Sum(p => p.IndexCount) / 3;
}
catch (Exception ex)
{
_logger.LogDebug(ex, "Could not load lod mesh {mesh} from path {path}", i, filePath);
continue;
}
if (tris > 0)
{
_logger.LogDebug("TriAnalysis: {filePath} => {tris} triangles", filePath, tris);
_configService.Current.TriangleDictionary[hash] = tris;
_configService.Save();
break;
}
}
return tris;
}
catch (Exception e)
{
_failedCalculatedTris.Add(hash);
_configService.Current.TriangleDictionary[hash] = 0;
_configService.Save();
_logger.LogWarning(e, "Could not parse file {file}", filePath);
return 0;
}
}
}