From bd947d8f2a1ecb6776fb330bcf7f2403d216f084 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Sat, 3 Sep 2022 22:06:28 +0200 Subject: [PATCH] add semi transient resource handling, wait max 3s for pets to change --- .../Factories/CharacterDataFactory.cs | 13 ++++- MareSynchronos/Managers/CachedPlayer.cs | 21 ++++++-- MareSynchronos/Managers/IpcManager.cs | 2 + MareSynchronos/Managers/PlayerManager.cs | 12 +++-- .../Managers/TransientResourceManager.cs | 52 +++++++++++++++++-- MareSynchronos/UI/CompactUI.cs | 2 +- MareSynchronos/Utils/DalamudUtil.cs | 15 +++--- 7 files changed, 95 insertions(+), 22 deletions(-) diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index f5b1a08..8d14826 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -237,14 +237,15 @@ public class CharacterDataFactory previousData.FileReplacements[objectKind].Clear(); } - Stopwatch st = Stopwatch.StartNew(); var chara = _dalamudUtil.CreateGameObject(charaPointer)!; while (!_dalamudUtil.IsObjectPresent(chara)) { Logger.Verbose("Character is null but it shouldn't be, waiting"); Thread.Sleep(50); } - _dalamudUtil.WaitWhileCharacterIsDrawing(charaPointer); + //_dalamudUtil.WaitWhileCharacterIsDrawing(charaPointer); + + Stopwatch st = Stopwatch.StartNew(); previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); @@ -339,6 +340,14 @@ public class CharacterDataFactory AddReplacement(item, objectKind, previousData, 1); } + foreach (var item in transientResourceManager.GetSemiTransientResources(objectKind)) + { + Logger.Verbose("Found semi transient resource: " + item); + AddReplacement(item, objectKind, previousData, 1); + } + + transientResourceManager.PersistTransientResources(charaPointer, objectKind); + st.Stop(); Logger.Verbose("Building " + objectKind + " Data took " + st.Elapsed); return previousData; diff --git a/MareSynchronos/Managers/CachedPlayer.cs b/MareSynchronos/Managers/CachedPlayer.cs index 2845e46..004421d 100644 --- a/MareSynchronos/Managers/CachedPlayer.cs +++ b/MareSynchronos/Managers/CachedPlayer.cs @@ -8,7 +8,6 @@ using Dalamud.Logging; using FFXIVClientStructs.FFXIV.Client.Game.Character; using MareSynchronos.API; using MareSynchronos.FileCacheDB; -using MareSynchronos.Interop; using MareSynchronos.Models; using MareSynchronos.Utils; using MareSynchronos.WebAPI; @@ -250,6 +249,7 @@ public class CachedPlayer if (minionOrMount != null) { Logger.Debug($"Request Redraw for Minion/Mount"); + _dalamudUtil.WaitWhileCharacterIsDrawing((IntPtr)minionOrMount); if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData)) { _ipcManager.GlamourerApplyAll(glamourerData, obj: (IntPtr)minionOrMount); @@ -262,17 +262,29 @@ public class CachedPlayer } else if (objectKind == ObjectKind.Pet) { + int tick = 16; var pet = _dalamudUtil.GetPet(PlayerCharacter.Address); if (pet != IntPtr.Zero) { - Logger.Debug("Request Redraw for Pet"); + var totalWait = 0; + var newPet = IntPtr.Zero; + const int maxWait = 3000; + Logger.Debug($"Request Redraw for Pet, waiting {maxWait}ms"); + + do + { + Thread.Sleep(tick); + totalWait += tick; + newPet = _dalamudUtil.GetPet(PlayerCharacter.Address); + } while (newPet == pet && totalWait < maxWait); + if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData)) { - _ipcManager.GlamourerApplyAll(glamourerData, pet); + _ipcManager.GlamourerApplyAll(glamourerData, newPet); } else { - _ipcManager.PenumbraRedraw(pet); + _ipcManager.PenumbraRedraw(newPet); } } } @@ -282,6 +294,7 @@ public class CachedPlayer if (companion != IntPtr.Zero) { Logger.Debug("Request Redraw for Companion"); + _dalamudUtil.WaitWhileCharacterIsDrawing(companion); if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData)) { _ipcManager.GlamourerApplyAll(glamourerData, companion); diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index 0466e99..a651d72 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -137,6 +137,7 @@ namespace MareSynchronos.Managers var gameObj = _dalamudUtil.CreateGameObject(obj); if (gameObj != null) { + Logger.Verbose("Glamourer applying for " + gameObj); _glamourerApplyAll!.InvokeAction(customization, gameObj); } } @@ -205,6 +206,7 @@ namespace MareSynchronos.Managers var gameObj = _dalamudUtil.CreateGameObject(obj); if (gameObj != null) { + Logger.Verbose("Redrawing " + gameObj); _penumbraRedrawObject!.InvokeAction(gameObj, 0); } } diff --git a/MareSynchronos/Managers/PlayerManager.cs b/MareSynchronos/Managers/PlayerManager.cs index 1616997..6e29937 100644 --- a/MareSynchronos/Managers/PlayerManager.cs +++ b/MareSynchronos/Managers/PlayerManager.cs @@ -61,11 +61,11 @@ namespace MareSynchronos.Managers }; } - public void HandleTransientResourceLoad(IntPtr drawObj) + public void HandleTransientResourceLoad(IntPtr gameObj) { foreach (var obj in playerRelatedObjects) { - if (obj.Address == drawObj && !obj.HasUnprocessedUpdate) + if (obj.Address == gameObj && !obj.HasUnprocessedUpdate) { obj.HasUnprocessedUpdate = true; OnPlayerOrAttachedObjectsChanged(); @@ -91,7 +91,7 @@ namespace MareSynchronos.Managers private unsafe void DalamudUtilOnFrameworkUpdate() { - //if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return; + if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return; //if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return; @@ -154,6 +154,7 @@ namespace MareSynchronos.Managers if (address == item.Address) { Logger.Debug("Penumbra redraw Event for " + item.ObjectKind); + _transientResourceManager.CleanSemiTransientResources(item.ObjectKind); item.HasUnprocessedUpdate = true; } } @@ -201,7 +202,10 @@ namespace MareSynchronos.Managers Task.Run(async () => { - _dalamudUtil.WaitWhileSelfIsDrawing(token); + foreach(var item in unprocessedObjects) + { + _dalamudUtil.WaitWhileCharacterIsDrawing(item.Address, token); + } CharacterCacheDto? cacheDto = (await CreateFullCharacterCacheDto(token)); if (cacheDto == null || token.IsCancellationRequested) return; diff --git a/MareSynchronos/Managers/TransientResourceManager.cs b/MareSynchronos/Managers/TransientResourceManager.cs index 857740a..e88dd03 100644 --- a/MareSynchronos/Managers/TransientResourceManager.cs +++ b/MareSynchronos/Managers/TransientResourceManager.cs @@ -1,4 +1,5 @@ -using MareSynchronos.Models; +using MareSynchronos.API; +using MareSynchronos.Models; using MareSynchronos.Utils; using System; using System.Collections.Generic; @@ -17,6 +18,7 @@ namespace MareSynchronos.Managers public event TransientResourceLoadedEvent? TransientResourceLoaded; private Dictionary> TransientResources { get; } = new(); + private Dictionary> SemiTransientResources { get; } = new(); public TransientResourceManager(IpcManager manager, DalamudUtil dalamudUtil) { manager.PenumbraResourceLoadEvent += Manager_PenumbraResourceLoadEvent; @@ -37,6 +39,14 @@ namespace MareSynchronos.Managers } } + public void CleanSemiTransientResources(ObjectKind objectKind) + { + if (SemiTransientResources.ContainsKey(objectKind)) + { + SemiTransientResources[objectKind].Clear(); + } + } + public List GetTransientResources(IntPtr gameObject) { if (TransientResources.TryGetValue(gameObject, out var result)) @@ -47,6 +57,16 @@ namespace MareSynchronos.Managers return new List(); } + public List GetSemiTransientResources(ObjectKind objectKind) + { + if (SemiTransientResources.TryGetValue(objectKind, out var result)) + { + return result.ToList(); + } + + return new List(); + } + private void Manager_PenumbraResourceLoadEvent(IntPtr gameObject, string gamePath, string filePath) { if (!TransientResources.ContainsKey(gameObject)) @@ -61,7 +81,7 @@ namespace MareSynchronos.Managers var newPath = filePath.ToLowerInvariant().Replace("\\", "/"); - if (filePath != gamePath && !TransientResources[gameObject].Contains(newPath)) + if (filePath != gamePath && !TransientResources[gameObject].Contains(newPath) && !SemiTransientResources.Any(r => r.Value.Contains(newPath))) { TransientResources[gameObject].Add(newPath); Logger.Debug($"Adding {filePath.ToLowerInvariant().Replace("\\", "/")} for {gameObject}"); @@ -69,14 +89,36 @@ namespace MareSynchronos.Managers } } - public void RemoveTransientResource(IntPtr drawObject, FileReplacement fileReplacement) + public void RemoveTransientResource(IntPtr gameObject, FileReplacement fileReplacement) { - if (TransientResources.ContainsKey(drawObject)) + if (TransientResources.ContainsKey(gameObject)) { - TransientResources[drawObject].RemoveWhere(f => fileReplacement.ResolvedPath == f); + TransientResources[gameObject].RemoveWhere(f => fileReplacement.ResolvedPath == f); } } + public void PersistTransientResources(IntPtr gameObject, ObjectKind objectKind) + { + if (!SemiTransientResources.ContainsKey(objectKind)) + { + SemiTransientResources[objectKind] = new HashSet(); + } + + if (!TransientResources.TryGetValue(gameObject, out var resources)) + { + return; + } + + var transientResources = resources.ToList(); + Logger.Debug("Persisting " + transientResources.Count + " transient resources"); + foreach (var item in transientResources) + { + SemiTransientResources[objectKind].Add(item); + } + + TransientResources[gameObject].Clear(); + } + public void Dispose() { dalamudUtil.FrameworkUpdate -= DalamudUtil_FrameworkUpdate; diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index f3f5252..f50a774 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -36,7 +36,7 @@ namespace MareSynchronos.UI public CompactUi(WindowSystem windowSystem, UiShared uiShared, Configuration configuration, ApiController apiController) #if DEBUG - : base("Mare Synchronos " + new FileInfo(Assembly.GetExecutingAssembly().Location) .LastWriteTime.ToString("yyyyMMddHHmmss")+ "###MareSynchronosMainUI") + : base("Mare Synchronos " + new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime.ToString("yyyyMMddHHmmss")+ "###MareSynchronosMainUI") #else : base("Mare Synchronos " + Assembly.GetExecutingAssembly().GetName().Version + "###MareSynchronosMainUI") #endif diff --git a/MareSynchronos/Utils/DalamudUtil.cs b/MareSynchronos/Utils/DalamudUtil.cs index 850b001..47a39f2 100644 --- a/MareSynchronos/Utils/DalamudUtil.cs +++ b/MareSynchronos/Utils/DalamudUtil.cs @@ -140,23 +140,26 @@ namespace MareSynchronos.Utils public unsafe void WaitWhileCharacterIsDrawing(IntPtr characterAddress, CancellationToken? ct = null) { - if (!_clientState.IsLoggedIn) return; + if (!_clientState.IsLoggedIn || characterAddress == IntPtr.Zero) return; var obj = (GameObject*)characterAddress; - + const int maxWaitTime = 10000; + const int tick = 250; + int curWaitTime = 0; // ReSharper disable once LoopVariableIsNeverChangedInsideLoop - while ((obj->RenderFlags & 0b100000000000) == 0b100000000000 && (!ct?.IsCancellationRequested ?? true)) // 0b100000000000 is "still rendering" or something + while ((obj->RenderFlags & 0b100000000000) == 0b100000000000 && (!ct?.IsCancellationRequested ?? true) && curWaitTime < maxWaitTime) // 0b100000000000 is "still rendering" or something { Logger.Verbose("Waiting for character to finish drawing"); - Thread.Sleep(250); + curWaitTime += tick; + Thread.Sleep(tick); } if (ct?.IsCancellationRequested ?? false) return; // wait quarter a second just in case - Thread.Sleep(250); + Thread.Sleep(tick); } - public void WaitWhileSelfIsDrawing(CancellationToken? token) => WaitWhileCharacterIsDrawing(_clientState.LocalPlayer?.Address ?? new IntPtr(), token); + public void WaitWhileSelfIsDrawing(CancellationToken? token) => WaitWhileCharacterIsDrawing(_clientState.LocalPlayer?.Address ?? IntPtr.Zero, token); public void Dispose() {