From 3ee082d3715fb7cde945543f1f9ffb522e75d5d9 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Tue, 28 Jun 2022 12:20:40 +0200 Subject: [PATCH] some fixes for models sharing materials and code cleanup --- .../Factories/CharacterDataFactory.cs | 88 ++++++++++--------- MareSynchronos/Managers/IpcManager.cs | 14 +-- MareSynchronos/Managers/PlayerManager.cs | 15 +--- MareSynchronos/Models/CachedPlayer.cs | 2 - MareSynchronos/Models/CharacterData.cs | 59 ++----------- MareSynchronos/Models/FileReplacement.cs | 48 ++-------- MareSynchronos/UI/UIShared.cs | 2 +- MareSynchronos/Utils/Logger.cs | 6 ++ MareSynchronos/WebAPI/ApiController.cs | 31 ++++--- 9 files changed, 95 insertions(+), 170 deletions(-) diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index d3b970a..3900470 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Threading; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; @@ -25,9 +27,33 @@ namespace MareSynchronos.Factories _ipcManager = ipcManager; } - private FileReplacement CreateBaseFileReplacement() + private FileReplacement CreateFileReplacement(string path) { - return new FileReplacement(_ipcManager.PenumbraModDirectory()!); + var fileReplacement = new FileReplacement(_ipcManager.PenumbraModDirectory()!); + if (!path.Contains(".tex", StringComparison.OrdinalIgnoreCase)) + { + fileReplacement.GamePaths = + _ipcManager.PenumbraReverseResolvePath(path, _dalamudUtil.PlayerName).ToList(); + fileReplacement.SetResolvedPath(path); + } + else + { + fileReplacement.GamePaths = new List { path }; + fileReplacement.SetResolvedPath(_ipcManager.PenumbraResolvePath(path, _dalamudUtil.PlayerName)!); + if (!fileReplacement.HasFileReplacement) + { + // try resolving tex with -- in name instead + path = path.Insert(path.LastIndexOf('/') + 1, "--"); + var reResolvedPath = _ipcManager.PenumbraResolvePath(path, _dalamudUtil.PlayerName)!; + if (reResolvedPath != path) + { + fileReplacement.GamePaths = new List() { path }; + fileReplacement.SetResolvedPath(reResolvedPath); + } + } + } + + return fileReplacement; } public CharacterData BuildCharacterData() @@ -55,9 +81,9 @@ namespace MareSynchronos.Factories ManipulationString = _ipcManager.PenumbraGetMetaManipulations(_dalamudUtil.PlayerName) }; var model = (CharacterBase*)((Character*)_dalamudUtil.PlayerPointer)->GameObject.GetDrawObject(); - for (var idx = 0; idx < model->SlotCount; ++idx) + for (var mdlIdx = 0; mdlIdx < model->SlotCount; ++mdlIdx) { - var mdl = (RenderModel*)model->ModelArray[idx]; + var mdl = (RenderModel*)model->ModelArray[mdlIdx]; if (mdl == null || mdl->ResourceHandle == null || mdl->ResourceHandle->Category != ResourceCategory.Chara) { continue; @@ -65,58 +91,40 @@ namespace MareSynchronos.Factories var mdlPath = new Utf8String(mdl->ResourceHandle->FileName()).ToString(); - FileReplacement cachedMdlResource = CreateBaseFileReplacement(); - cachedMdlResource.GamePaths = _ipcManager.PenumbraReverseResolvePath(mdlPath, _dalamudUtil.PlayerName); - //Logger.Debug("Model " + string.Join(", ", cachedMdlResource.GamePaths)); - cachedMdlResource.SetResolvedPath(mdlPath); - //Logger.Debug("\t\t=> " + cachedMdlResource.ResolvedPath); + FileReplacement mdlFileReplacement = CreateFileReplacement(mdlPath); + Logger.Verbose("Model " + string.Join(", ", mdlFileReplacement.GamePaths)); + Logger.Verbose("\t\t=> " + mdlFileReplacement.ResolvedPath); - cache.AddAssociatedResource(cachedMdlResource, null!, null!); + cache.AddFileReplacement(mdlFileReplacement); - for (int mtrlIdx = 0; mtrlIdx < mdl->MaterialCount; mtrlIdx++) + for (var mtrlIdx = 0; mtrlIdx < mdl->MaterialCount; mtrlIdx++) { var mtrl = (Material*)mdl->Materials[mtrlIdx]; if (mtrl == null) continue; - //var mtrlFileResource = factory.CreateBaseFileReplacement(); var mtrlPath = new Utf8String(mtrl->ResourceHandle->FileName()).ToString().Split("|")[2]; - var cachedMtrlResource = CreateBaseFileReplacement(); - cachedMtrlResource.GamePaths = _ipcManager.PenumbraReverseResolvePath(mtrlPath, _dalamudUtil.PlayerName); - //Logger.Debug("\tMaterial " + string.Join(", ", cachedMtrlResource.GamePaths)); - cachedMtrlResource.SetResolvedPath(mtrlPath); - cache.AddAssociatedResource(cachedMtrlResource, cachedMdlResource, null!); - //Logger.Debug("\t\t\t=> " + cachedMtrlResource.ResolvedPath); + var mtrlFileReplacement = CreateFileReplacement(mtrlPath); + Logger.Verbose("\tMaterial " + string.Join(", ", mtrlFileReplacement.GamePaths)); + Logger.Verbose("\t\t\t=> " + mtrlFileReplacement.ResolvedPath); + cache.AddFileReplacement(mtrlFileReplacement); - var mtrlResource = (MtrlResource*)mtrl->ResourceHandle; - for (int resIdx = 0; resIdx < mtrlResource->NumTex; resIdx++) + var mtrlResourceHandle = (MtrlResource*)mtrl->ResourceHandle; + for (var resIdx = 0; resIdx < mtrlResourceHandle->NumTex; resIdx++) { - var texPath = new Utf8String(mtrlResource->TexString(resIdx)).ToString(); + var texPath = new Utf8String(mtrlResourceHandle->TexString(resIdx)).ToString(); - if (string.IsNullOrEmpty(texPath.ToString())) continue; + if (string.IsNullOrEmpty(texPath)) continue; - var cachedTexResource = CreateBaseFileReplacement(); - cachedTexResource.GamePaths = new[] { texPath }; - cachedTexResource.SetResolvedPath(_ipcManager.PenumbraResolvePath(texPath, _dalamudUtil.PlayerName)!); - if (!cachedTexResource.HasFileReplacement) - { - // try resolving tex with -- in name instead - texPath = texPath.Insert(texPath.LastIndexOf('/') + 1, "--"); - var reResolvedPath = _ipcManager.PenumbraResolvePath(texPath, _dalamudUtil.PlayerName)!; - if (reResolvedPath != texPath) - { - cachedTexResource.GamePaths = new[] { texPath }; - cachedTexResource.SetResolvedPath(reResolvedPath); - } - } - //Logger.Debug("\t\tTexture " + string.Join(", ", cachedTexResource.GamePaths)); - //Logger.Debug("\t\t\t\t=> " + cachedTexResource.ResolvedPath); - cache.AddAssociatedResource(cachedTexResource, cachedMdlResource, cachedMtrlResource); + var texFileReplacement = CreateFileReplacement(texPath); + Logger.Verbose("\t\tTexture " + string.Join(", ", texFileReplacement.GamePaths)); + Logger.Verbose("\t\t\t\t=> " + texFileReplacement.ResolvedPath); + cache.AddFileReplacement(texFileReplacement); } } } st.Stop(); - Logger.Debug("Building Character Data took " + st.Elapsed); + Logger.Verbose("Building Character Data took " + st.Elapsed); return cache; } diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index fd5cf7e..843bc8a 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -113,21 +113,21 @@ namespace MareSynchronos.Managers public void GlamourerApplyAll(string customization, string characterName) { if (!CheckGlamourerApi()) return; - Logger.Debug("GlamourerString: " + customization); + Logger.Debug("Glamourer apply all to " + characterName); _glamourerApplyAll!.InvokeAction(customization, characterName); } public void GlamourerApplyOnlyEquipment(string customization, string characterName) { if (!CheckGlamourerApi()) return; - Logger.Debug("GlamourerString: " + customization); + Logger.Debug("Glamourer apply only equipment to " + characterName); _glamourerApplyOnlyEquipment!.InvokeAction(customization, characterName); } public void GlamourerApplyOnlyCustomization(string customization, string characterName) { if (!CheckGlamourerApi()) return; - Logger.Debug("GlamourerString: " + customization); + Logger.Debug("Glamourer apply only customization to " + characterName); _glamourerApplyOnlyCustomization!.InvokeAction(customization, characterName); } @@ -148,7 +148,6 @@ namespace MareSynchronos.Managers if (!CheckPenumbraApi()) return string.Empty; Logger.Debug("Creating temp collection for " + characterName); var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); - Logger.Debug("Penumbra ret: " + ret.Item1); return ret.Item2; } @@ -198,13 +197,6 @@ namespace MareSynchronos.Managers if (!CheckPenumbraApi()) return; Logger.Debug("Assigning temp mods for " + collectionName); - Logger.Debug("ManipulationString: " + manipulationData); - var orderedModPaths = modPaths.OrderBy(p => p.Key.EndsWith(".mdl") ? 0 : p.Key.EndsWith(".mtrl") ? 1 : 2) - .ToDictionary(k => k.Key, k => k.Value); - foreach (var item in orderedModPaths) - { - //Logger.Debug(item.Key + " => " + item.Value); - } var ret = _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", collectionName, modPaths, manipulationData, 0); Logger.Debug("Penumbra Ret: " + ret.ToString()); } diff --git a/MareSynchronos/Managers/PlayerManager.cs b/MareSynchronos/Managers/PlayerManager.cs index 4afc67f..4c231d6 100644 --- a/MareSynchronos/Managers/PlayerManager.cs +++ b/MareSynchronos/Managers/PlayerManager.cs @@ -20,7 +20,6 @@ namespace MareSynchronos.Managers private readonly DalamudUtil _dalamudUtil; private readonly IpcManager _ipcManager; private string _lastSentHash = string.Empty; - private Task? _playerChangedTask; private CancellationTokenSource? _playerChangedCts; public PlayerManager(ApiController apiController, IpcManager ipcManager, @@ -68,8 +67,8 @@ namespace MareSynchronos.Managers _cachedPlayersManager.AddInitialPairs(apiTask.Result); _ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent; - _ipcManager.PenumbraRedraw(_dalamudUtil.PlayerName); _dalamudUtil.PlayerChanged += Watcher_PlayerChanged; + PlayerChanged(_dalamudUtil.PlayerName); } private void ApiController_Disconnected(object? sender, EventArgs args) @@ -111,24 +110,18 @@ namespace MareSynchronos.Managers private void PlayerChanged(string name) { - //if (sender == null) return; Logger.Debug("Player changed: " + name); _playerChangedCts?.Cancel(); _playerChangedCts = new CancellationTokenSource(); - var token = _playerChangedCts.Token;/* - if (_playerChangedTask is { IsCompleted: false }) - { - PluginLog.Warning("PlayerChanged Task still running"); - return; - }*/ + var token = _playerChangedCts.Token; if (!_ipcManager.Initialized) { - PluginLog.Warning("Penumbra not active, doing nothing."); + Logger.Warn("Penumbra not active, doing nothing."); return; } - _playerChangedTask = Task.Run(async () => + Task.Run(async () => { int attempts = 0; while (!_apiController.IsConnected && attempts < 10 && !token.IsCancellationRequested) diff --git a/MareSynchronos/Models/CachedPlayer.cs b/MareSynchronos/Models/CachedPlayer.cs index 1d0d577..f5b6c16 100644 --- a/MareSynchronos/Models/CachedPlayer.cs +++ b/MareSynchronos/Models/CachedPlayer.cs @@ -146,7 +146,6 @@ public class CachedPlayer Logger.Debug( $"Request Redraw for {PlayerName}"); _ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, cache.ManipulationData); - _ipcManager.GlamourerRevertCharacterCustomization(PlayerName!); _ipcManager.GlamourerApplyAll(cache.GlamourerData, PlayerName!); } @@ -164,7 +163,6 @@ public class CachedPlayer _ipcManager.PenumbraRemoveTemporaryCollection(PlayerName); if (IsVisible) { - _ipcManager.GlamourerRevertCharacterCustomization(PlayerName); _ipcManager.GlamourerApplyOnlyCustomization(_originalGlamourerData, PlayerName); _ipcManager.GlamourerApplyOnlyEquipment(_lastGlamourerData, PlayerName); } diff --git a/MareSynchronos/Models/CharacterData.cs b/MareSynchronos/Models/CharacterData.cs index 83b0ee5..3515284 100644 --- a/MareSynchronos/Models/CharacterData.cs +++ b/MareSynchronos/Models/CharacterData.cs @@ -1,10 +1,8 @@ using Newtonsoft.Json; -using System; using System.Collections.Generic; using System.Linq; using System.Text; using MareSynchronos.API; -using MareSynchronos.Utils; namespace MareSynchronos.Models { @@ -12,12 +10,7 @@ namespace MareSynchronos.Models public class CharacterData { [JsonProperty] - public List AllReplacements => - FileReplacements.Where(f => f.HasFileReplacement) - .Concat(FileReplacements.SelectMany(f => f.Associated)).Where(f => f.HasFileReplacement) - .Concat(FileReplacements.SelectMany(f => f.Associated).SelectMany(f => f.Associated)).Where(f => f.HasFileReplacement) - .Distinct().OrderBy(f => f.GamePaths[0]) - .ToList(); + public List AllReplacements => FileReplacements.Where(f => f.HasFileReplacement).GroupBy(f => f.Hash).Select(g => g.First()).ToList(); [JsonProperty] public string CacheHash { get; set; } = string.Empty; @@ -34,53 +27,18 @@ namespace MareSynchronos.Models public string ManipulationString { get; set; } = string.Empty; - public void AddAssociatedResource(FileReplacement resource, FileReplacement? mdlParent, FileReplacement? mtrlParent) + public void AddFileReplacement(FileReplacement fileReplacement) { - try - { - if (mdlParent == null) - { - resource.IsInUse = true; - FileReplacements.Add(resource); - return; - } + if (!fileReplacement.HasFileReplacement) return; - var mdlReplacements = FileReplacements.Where(f => f == mdlParent && mtrlParent == null); - foreach (var mdlReplacement in mdlReplacements) - { - mdlReplacement.AddAssociated(resource); - } - - var mtrlReplacements = FileReplacements.Where(f => f == mdlParent).SelectMany(a => a.Associated).Where(f => f == mtrlParent); - foreach (var mtrlReplacement in mtrlReplacements) - { - mtrlReplacement.AddAssociated(resource); - } - } - catch (Exception ex) + var existingReplacement = FileReplacements.SingleOrDefault(f => f.ResolvedPath == fileReplacement.ResolvedPath); + if (existingReplacement != null) { - Logger.Debug(ex.Message); + existingReplacement.GamePaths.AddRange(fileReplacement.GamePaths.Where(e => !existingReplacement.GamePaths.Contains(e))); } - } - - public void Invalidate(List? fileReplacements = null) - { - try + else { - var fileReplacement = fileReplacements ?? FileReplacements.ToList(); - foreach (var item in fileReplacement) - { - item.IsInUse = false; - Invalidate(item.Associated); - if (FileReplacements.Contains(item)) - { - FileReplacements.Remove(item); - } - } - } - catch (Exception ex) - { - Logger.Debug(ex.Message); + FileReplacements.Add(fileReplacement); } } @@ -95,6 +53,7 @@ namespace MareSynchronos.Models ManipulationData = ManipulationString }; } + public override string ToString() { StringBuilder stringBuilder = new(); diff --git a/MareSynchronos/Models/FileReplacement.cs b/MareSynchronos/Models/FileReplacement.cs index ffae5a0..0a65c7d 100644 --- a/MareSynchronos/Models/FileReplacement.cs +++ b/MareSynchronos/Models/FileReplacement.cs @@ -24,14 +24,12 @@ namespace MareSynchronos.Models this._penumbraDirectory = penumbraDirectory; } - public List Associated { get; set; } = new(); - - public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true)) && Associated.All(f => f.Computed); + public bool Computed => (_computationTask == null || (_computationTask?.IsCompleted ?? true)); [JsonProperty] - public string[] GamePaths { get; set; } = Array.Empty(); + public List GamePaths { get; set; } = new(); - public bool HasFileReplacement => GamePaths.Length >= 1 && GamePaths[0] != ResolvedPath; + public bool HasFileReplacement => GamePaths.Count >= 1 && GamePaths.Any(p => p != ResolvedPath); [JsonProperty] public string Hash { get; set; } = string.Empty; @@ -43,35 +41,7 @@ namespace MareSynchronos.Models [JsonProperty] public string ResolvedPath { get; set; } = string.Empty; - - public void AddAssociated(FileReplacement fileReplacement) - { - fileReplacement.IsInUse = true; - - Associated.Add(fileReplacement); - } - - public override bool Equals(object? obj) - { - if (obj == null) return true; - if (obj.GetType() == typeof(FileReplacement)) - { - return Hash == ((FileReplacement)obj).Hash; - } - - return base.Equals(obj); - } - - public override int GetHashCode() - { - int result = 13; - result *= 397; - result += Hash.GetHashCode(); - result += ResolvedPath.GetHashCode(); - - return result; - } - + public void SetResolvedPath(string path) { ResolvedPath = path.ToLower().Replace('/', '\\').Replace(_penumbraDirectory, "").Replace('\\', '/'); @@ -113,7 +83,7 @@ namespace MareSynchronos.Models { return new FileReplacementDto { - GamePaths = GamePaths, + GamePaths = GamePaths.ToArray(), Hash = Hash, }; } @@ -121,14 +91,6 @@ namespace MareSynchronos.Models { StringBuilder builder = new(); builder.AppendLine($"Modded: {HasFileReplacement} - {string.Join(",", GamePaths)} => {ResolvedPath}"); - foreach (var l1 in Associated) - { - builder.AppendLine($" + Modded: {l1.HasFileReplacement} - {string.Join(",", l1.GamePaths)} => {l1.ResolvedPath}"); - foreach (var l2 in l1.Associated) - { - builder.AppendLine($" + Modded: {l2.HasFileReplacement} - {string.Join(",", l2.GamePaths)} => {l2.ResolvedPath}"); - } - } return builder.ToString(); } diff --git a/MareSynchronos/UI/UIShared.cs b/MareSynchronos/UI/UIShared.cs index b1592aa..f67b7a6 100644 --- a/MareSynchronos/UI/UIShared.cs +++ b/MareSynchronos/UI/UIShared.cs @@ -184,7 +184,7 @@ namespace MareSynchronos.UI { _pluginConfiguration.FullPause = false; _pluginConfiguration.Save(); - Task.WaitAll(_apiController.Register()); + Task.Run(_apiController.Register); } } else diff --git a/MareSynchronos/Utils/Logger.cs b/MareSynchronos/Utils/Logger.cs index 05e799d..225b87c 100644 --- a/MareSynchronos/Utils/Logger.cs +++ b/MareSynchronos/Utils/Logger.cs @@ -16,5 +16,11 @@ namespace MareSynchronos.Utils var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; PluginLog.Warning($"[{caller}] {warn}"); } + + public static void Verbose(string verbose) + { + var caller = new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Name ?? "Unknown"; + PluginLog.Verbose($"[{caller}] {verbose}"); + } } } diff --git a/MareSynchronos/WebAPI/ApiController.cs b/MareSynchronos/WebAPI/ApiController.cs index 1df49a7..b18a563 100644 --- a/MareSynchronos/WebAPI/ApiController.cs +++ b/MareSynchronos/WebAPI/ApiController.cs @@ -13,6 +13,7 @@ using MareSynchronos.API; using MareSynchronos.FileCacheDB; using MareSynchronos.Utils; using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.EntityFrameworkCore.Metadata.Conventions; namespace MareSynchronos.WebAPI { @@ -226,7 +227,7 @@ namespace MareSynchronos.WebAPI { if (_uploadCancellationTokenSource != null) { - PluginLog.Warning("Cancelling upload"); + Logger.Warn("Cancelling upload"); _uploadCancellationTokenSource?.Cancel(); _fileHub!.InvokeAsync("AbortUpload"); } @@ -319,7 +320,7 @@ namespace MareSynchronos.WebAPI public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash) { - Logger.Debug("Received DTO for " + characterHash); + Logger.Verbose("Received DTO for " + characterHash); CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterHash, character)); return Task.CompletedTask; } @@ -342,33 +343,39 @@ namespace MareSynchronos.WebAPI CancelUpload(); _uploadCancellationTokenSource = new CancellationTokenSource(); var uploadToken = _uploadCancellationTokenSource.Token; - Logger.Debug("New Token Created"); + Logger.Verbose("New Token Created"); var filesToUpload = await _fileHub!.InvokeAsync>("SendFiles", character.FileReplacements.Select(c => c.Hash).Distinct(), uploadToken); IsUploading = true; - Logger.Debug("Compressing files"); foreach (var file in filesToUpload) { - Logger.Debug(file); + await using var db = new FileCacheContext(); + CurrentUploads[file] = (0, new FileInfo(db.FileCaches.First(f => f.Hash == file).Filepath).Length); + } + + Logger.Verbose("Compressing and uploading files"); + foreach (var file in filesToUpload) + { + Logger.Verbose("Compressing and uploading " + file); var data = await GetCompressedFileData(file, uploadToken); CurrentUploads[data.Item1] = (0, data.Item2.Length); _ = UploadFile(data.Item2, file, uploadToken); if (!uploadToken.IsCancellationRequested) continue; - PluginLog.Warning("Cancel in filesToUpload loop detected"); + Logger.Warn("Cancel in filesToUpload loop detected"); CurrentUploads.Clear(); break; } - Logger.Debug("Upload tasks complete, waiting for server to confirm"); + Logger.Verbose("Upload tasks complete, waiting for server to confirm"); var anyUploadsOpen = await _fileHub!.InvokeAsync("IsUploadFinished", uploadToken); - Logger.Debug("Uploads open: " + anyUploadsOpen); + Logger.Verbose("Uploads open: " + anyUploadsOpen); while (anyUploadsOpen && !uploadToken.IsCancellationRequested) { anyUploadsOpen = await _fileHub!.InvokeAsync("IsUploadFinished", uploadToken); await Task.Delay(TimeSpan.FromSeconds(0.5), uploadToken); - Logger.Debug("Waiting for uploads to finish"); + Logger.Verbose("Waiting for uploads to finish"); } CurrentUploads.Clear(); @@ -376,15 +383,15 @@ namespace MareSynchronos.WebAPI if (!uploadToken.IsCancellationRequested) { - Logger.Debug("=== Pushing character data ==="); + Logger.Verbose("=== Pushing character data ==="); await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds, uploadToken); } else { - PluginLog.Warning("=== Upload operation was cancelled ==="); + Logger.Warn("=== Upload operation was cancelled ==="); } - Logger.Debug("== Upload complete for " + character.JobId); + Logger.Verbose("Upload complete for " + character.Hash); _uploadCancellationTokenSource = null; }