From 5c480f551bf8d84298f41543863d65afb5a08b6e Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Sun, 4 Sep 2022 21:19:06 +0200 Subject: [PATCH 1/8] increase api, no further changes --- MareAPI | 2 +- MareSynchronos/MareSynchronos.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MareAPI b/MareAPI index 645dd0b..9bb99a5 160000 --- a/MareAPI +++ b/MareAPI @@ -1 +1 @@ -Subproject commit 645dd0b18f815cb7ccd81f37e84ded9cf650c3f3 +Subproject commit 9bb99a5e68196165b9c1360acff198c4731164d1 diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 093d1cf..29f8e0f 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.3.10 + 0.3.11 https://github.com/Penumbra-Sync/client From 94ff918881b07e6a2db02521302281518a94f172 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Tue, 6 Sep 2022 13:39:33 +0200 Subject: [PATCH 2/8] change downloads to concurrentdictionary --- MareSynchronos/Managers/CachedPlayer.cs | 1 + MareSynchronos/UI/CompactUI.cs | 2 +- MareSynchronos/WebAPI/ApIController.Functions.Files.cs | 9 ++++++--- MareSynchronos/WebAPI/ApiController.Connectivity.cs | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/MareSynchronos/Managers/CachedPlayer.cs b/MareSynchronos/Managers/CachedPlayer.cs index 5070de0..2c8a5ab 100644 --- a/MareSynchronos/Managers/CachedPlayer.cs +++ b/MareSynchronos/Managers/CachedPlayer.cs @@ -146,6 +146,7 @@ public class CachedPlayer { Logger.Debug("Downloading missing files for player " + PlayerName + ", kind: " + objectKind); await _apiController.DownloadFiles(downloadId, toDownloadReplacements, downloadToken); + _apiController.CancelDownload(downloadId); if (downloadToken.IsCancellationRequested) { Logger.Verbose("Detected cancellation"); diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 7191066..1e43155 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -367,7 +367,7 @@ namespace MareSynchronos.UI if (currentDownloads.Any()) { - var totalDownloads = currentDownloads.Count; + var totalDownloads = currentDownloads.Count(); var doneDownloads = currentDownloads.Count(c => c.IsTransferred); var totalDownloaded = currentDownloads.Sum(c => c.Transferred); var totalToDownload = currentDownloads.Sum(c => c.Total); diff --git a/MareSynchronos/WebAPI/ApIController.Functions.Files.cs b/MareSynchronos/WebAPI/ApIController.Functions.Files.cs index 2c224be..e1097c5 100644 --- a/MareSynchronos/WebAPI/ApIController.Functions.Files.cs +++ b/MareSynchronos/WebAPI/ApIController.Functions.Files.cs @@ -95,7 +95,7 @@ namespace MareSynchronos.WebAPI { File.Delete(tempFile); Logger.Debug("Detected cancellation, removing " + currentDownloadId); - CurrentDownloads.Remove(currentDownloadId); + CancelDownload(currentDownloadId); break; } @@ -132,7 +132,7 @@ namespace MareSynchronos.WebAPI } Logger.Debug("Download complete, removing " + currentDownloadId); - CurrentDownloads.Remove(currentDownloadId); + CancelDownload(currentDownloadId); } public async Task PushCharacterData(CharacterCacheDto character, List visibleCharacterIds) @@ -288,7 +288,10 @@ namespace MareSynchronos.WebAPI public void CancelDownload(int downloadId) { - CurrentDownloads.Remove(downloadId); + while (CurrentDownloads.ContainsKey(downloadId)) + { + CurrentDownloads.TryRemove(downloadId, out _); + } } } diff --git a/MareSynchronos/WebAPI/ApiController.Connectivity.cs b/MareSynchronos/WebAPI/ApiController.Connectivity.cs index 0d12587..11c17a9 100644 --- a/MareSynchronos/WebAPI/ApiController.Connectivity.cs +++ b/MareSynchronos/WebAPI/ApiController.Connectivity.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -93,7 +94,7 @@ namespace MareSynchronos.WebAPI public event SimpleStringDelegate? UnpairedFromOther; - public Dictionary> CurrentDownloads { get; } = new(); + public ConcurrentDictionary> CurrentDownloads { get; } = new(); public List CurrentUploads { get; } = new(); From f66fcfab479307b1624f2a9482e3a734f039e6c2 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Tue, 6 Sep 2022 13:20:39 +0200 Subject: [PATCH 3/8] minor fixes --- MareSynchronos/Managers/CachedPlayer.cs | 3 +-- MareSynchronos/Managers/IpcManager.cs | 18 +++++++++----- MareSynchronos/MareSynchronos.csproj | 4 ++++ MareSynchronos/UI/CompactUI.cs | 13 +++++++++- MareSynchronos/UI/DownloadUi.cs | 2 +- MareSynchronos/Utils/Various.cs | 32 +++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 MareSynchronos/Utils/Various.cs diff --git a/MareSynchronos/Managers/CachedPlayer.cs b/MareSynchronos/Managers/CachedPlayer.cs index 2c8a5ab..7df41d9 100644 --- a/MareSynchronos/Managers/CachedPlayer.cs +++ b/MareSynchronos/Managers/CachedPlayer.cs @@ -221,8 +221,7 @@ public class CachedPlayer private void ApplyBaseData(Dictionary moddedPaths) { _ipcManager.PenumbraRemoveTemporaryCollection(PlayerName!); - var tempCollection = _ipcManager.PenumbraCreateTemporaryCollection(PlayerName!); - _ipcManager.PenumbraSetTemporaryMods(tempCollection, moddedPaths, _cachedData.ManipulationData); + _ipcManager.PenumbraSetTemporaryMods(PlayerName!, moddedPaths, _cachedData.ManipulationData); } private unsafe void ApplyCustomizationData(ObjectKind objectKind) diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index e2ce186..32ef623 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; using MareSynchronos.Utils; using MareSynchronos.WebAPI; +using Lumina.Excel.GeneratedSheets; +using Action = System.Action; namespace MareSynchronos.Managers { @@ -227,16 +229,20 @@ namespace MareSynchronos.Managers return resolvedPaths; } - public void PenumbraSetTemporaryMods(string collectionName, Dictionary modPaths, string manipulationData) + public void PenumbraSetTemporaryMods(string characterName, Dictionary modPaths, string manipulationData) { if (!CheckPenumbraApi()) return; - Logger.Verbose("Assigning temp mods for " + collectionName); - foreach (var mod in modPaths) + actionQueue.Enqueue(() => { - Logger.Verbose(mod.Key + " => " + mod.Value); - } - var ret = _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", collectionName, modPaths, manipulationData, 0); + var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); + Logger.Verbose("Assigning temp mods for " + ret.Item2); + foreach (var mod in modPaths) + { + Logger.Verbose(mod.Key + " => " + mod.Value); + } + _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", ret.Item2, modPaths, manipulationData, 0); + }); } private void RedrawEvent(IntPtr objectAddress, int objectTableIndex) diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 29f8e0f..8fc8847 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -32,6 +32,10 @@ + + build$([System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ss:fffZ")) + + diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index 1e43155..c40692a 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -36,7 +36,18 @@ 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") + string dateTime = "DEV VERSION"; + try + { + dateTime = VariousExtensions.GetLinkerTime(Assembly.GetCallingAssembly()).ToString("yyyyMMddHHmmss"); + } + catch (Exception ex) + { + Logger.Warn("Could not get assembly name"); + Logger.Warn(ex.Message); + Logger.Warn(ex.StackTrace); + } + this.WindowName = "Mare Synchronos " + dateTime + "###MareSynchronosMainUI"; #else : base("Mare Synchronos " + Assembly.GetExecutingAssembly().GetName().Version + "###MareSynchronosMainUI") #endif diff --git a/MareSynchronos/UI/DownloadUi.cs b/MareSynchronos/UI/DownloadUi.cs index fe30ce1..9e9f05f 100644 --- a/MareSynchronos/UI/DownloadUi.cs +++ b/MareSynchronos/UI/DownloadUi.cs @@ -101,7 +101,7 @@ public class DownloadUi : Window, IDisposable if (_apiController.CurrentDownloads.Any()) { - var currentDownloads = _apiController.CurrentDownloads.SelectMany(k => k.Value).ToList(); + var currentDownloads = _apiController.CurrentDownloads.Where(d => d.Value != null && d.Value.Any()).ToList().SelectMany(k => k.Value).ToList(); var multBase = currentDownloads.Any() ? 0 : 2; var doneDownloads = currentDownloads.Count(c => c.IsTransferred); var totalDownloads = currentDownloads.Count; diff --git a/MareSynchronos/Utils/Various.cs b/MareSynchronos/Utils/Various.cs new file mode 100644 index 0000000..fa96e17 --- /dev/null +++ b/MareSynchronos/Utils/Various.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace MareSynchronos.Utils +{ + public static class VariousExtensions + { + public static DateTime GetLinkerTime(Assembly assembly) + { + const string BuildVersionMetadataPrefix = "+build"; + + var attribute = assembly.GetCustomAttribute(); + if (attribute?.InformationalVersion != null) + { + var value = attribute.InformationalVersion; + var index = value.IndexOf(BuildVersionMetadataPrefix); + if (index > 0) + { + value = value[(index + BuildVersionMetadataPrefix.Length)..]; + return DateTime.ParseExact(value, "yyyy-MM-ddTHH:mm:ss:fffZ", CultureInfo.InvariantCulture); + } + } + + return default; + } + } +} From dd74b7be86426356de48127c57beb3dac973cb00 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Mon, 5 Sep 2022 11:23:02 +0200 Subject: [PATCH 4/8] move handling of critical ipc to main thread --- MareSynchronos/Managers/IpcManager.cs | 70 +++++++++++++++++++-------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index 32ef623..a71942a 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -34,6 +34,7 @@ namespace MareSynchronos.Managers private readonly ICallGateSubscriber, string, int, int> _penumbraSetTemporaryMod; private readonly DalamudUtil _dalamudUtil; + private readonly Queue actionQueue = new(); public IpcManager(DalamudPluginInterface pi, DalamudUtil dalamudUtil) { @@ -78,6 +79,15 @@ namespace MareSynchronos.Managers } this._dalamudUtil = dalamudUtil; + _dalamudUtil.FrameworkUpdate += HandleActionQueue; + } + + private void HandleActionQueue() + { + while (actionQueue.TryDequeue(out var action)) + { + action(); + } } public event VoidDelegate? PenumbraInitialized; @@ -113,6 +123,9 @@ namespace MareSynchronos.Managers { Logger.Verbose("Disposing " + nameof(IpcManager)); + _dalamudUtil.FrameworkUpdate -= HandleActionQueue; + actionQueue.Clear(); + _penumbraDispose.Unsubscribe(PenumbraDispose); _penumbraInit.Unsubscribe(PenumbraInit); _penumbraObjectIsRedrawn.Unsubscribe(RedrawEvent); @@ -121,25 +134,35 @@ namespace MareSynchronos.Managers public void GlamourerApplyAll(string? customization, IntPtr obj) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return; - var gameObj = _dalamudUtil.CreateGameObject(obj); - if (gameObj != null) + actionQueue.Enqueue(() => { - _glamourerApplyAll!.InvokeAction(customization, gameObj); - } + var gameObj = _dalamudUtil.CreateGameObject(obj); + if (gameObj != null) + { + Logger.Verbose("Glamourer applying for " + gameObj); + _glamourerApplyAll!.InvokeAction(customization, gameObj); + } + }); } public void GlamourerApplyOnlyEquipment(string customization, GameObject character) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return; - Logger.Verbose("Glamourer apply only equipment to " + character); - _glamourerApplyOnlyEquipment!.InvokeAction(customization, character); + actionQueue.Enqueue(() => + { + Logger.Verbose("Glamourer apply only equipment to " + character); + _glamourerApplyOnlyEquipment!.InvokeAction(customization, character); + }); } public void GlamourerApplyOnlyCustomization(string customization, GameObject character) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return; - Logger.Verbose("Glamourer apply only customization to " + character); - _glamourerApplyOnlyCustomization!.InvokeAction(customization, character); + actionQueue.Enqueue(() => + { + Logger.Verbose("Glamourer apply only customization to " + character); + _glamourerApplyOnlyCustomization!.InvokeAction(customization, character); + }); } public string GlamourerGetCharacterCustomization(GameObject character) @@ -163,7 +186,7 @@ namespace MareSynchronos.Managers public void GlamourerRevertCharacterCustomization(GameObject character) { if (!CheckGlamourerApi()) return; - _glamourerRevertCustomization!.InvokeAction(character); + actionQueue.Enqueue(() => _glamourerRevertCustomization!.InvokeAction(character)); } public string PenumbraCreateTemporaryCollection(string characterName) @@ -189,31 +212,37 @@ namespace MareSynchronos.Managers public void PenumbraRedraw(IntPtr obj) { if (!CheckPenumbraApi()) return; - var gameObj = _dalamudUtil.CreateGameObject(obj); - if (gameObj != null) + actionQueue.Enqueue(() => { - _penumbraRedrawObject!.InvokeAction(gameObj, 0); - } + var gameObj = _dalamudUtil.CreateGameObject(obj); + if (gameObj != null) + { + Logger.Verbose("Redrawing " + gameObj); + _penumbraRedrawObject!.InvokeAction(gameObj, 0); + } + }); } public void PenumbraRedraw(string actorName) { if (!CheckPenumbraApi()) return; - _penumbraRedraw!.InvokeAction(actorName, 0); + actionQueue.Enqueue(() => _penumbraRedraw!.InvokeAction(actorName, 0)); } public void PenumbraRemoveTemporaryCollection(string characterName) { if (!CheckPenumbraApi()) return; - Logger.Verbose("Removing temp collection for " + characterName); - _penumbraRemoveTemporaryCollection.InvokeFunc(characterName); + actionQueue.Enqueue(() => + { + Logger.Verbose("Removing temp collection for " + characterName); + _penumbraRemoveTemporaryCollection.InvokeFunc(characterName); + }); } public string? PenumbraResolvePath(string path) { if (!CheckPenumbraApi()) return null; var resolvedPath = _penumbraResolvePlayer!.InvokeFunc(path); - //Logger.Verbose("Resolved " + path + "=>" + string.Join(", ", resolvedPath)); return resolvedPath; } @@ -225,7 +254,6 @@ namespace MareSynchronos.Managers { resolvedPaths = new[] { path }; } - //Logger.Verbose("Reverse Resolved " + path + "=>" + string.Join(", ", resolvedPaths)); return resolvedPaths; } @@ -235,13 +263,12 @@ namespace MareSynchronos.Managers actionQueue.Enqueue(() => { - var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); - Logger.Verbose("Assigning temp mods for " + ret.Item2); + Logger.Verbose("Assigning temp mods for " + collectionName); foreach (var mod in modPaths) { Logger.Verbose(mod.Key + " => " + mod.Value); } - _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", ret.Item2, modPaths, manipulationData, 0); + _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", collectionName, modPaths, manipulationData, 0); }); } @@ -259,6 +286,7 @@ namespace MareSynchronos.Managers private void PenumbraDispose() { PenumbraDisposed?.Invoke(); + actionQueue.Clear(); } } } From 100aba090003120378e810ace40bfb2a92ea21ef Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Tue, 6 Sep 2022 13:43:11 +0200 Subject: [PATCH 5/8] cherrypick from net6 fixes --- MareSynchronos/Managers/IpcManager.cs | 4 ++-- MareSynchronos/UI/CompactUI.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index a71942a..bacd7ec 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -263,12 +263,12 @@ namespace MareSynchronos.Managers actionQueue.Enqueue(() => { - Logger.Verbose("Assigning temp mods for " + collectionName); + Logger.Verbose("Assigning temp mods for " + characterName); foreach (var mod in modPaths) { Logger.Verbose(mod.Key + " => " + mod.Value); } - _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", collectionName, modPaths, manipulationData, 0); + _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", characterName, modPaths, manipulationData, 0); }); } diff --git a/MareSynchronos/UI/CompactUI.cs b/MareSynchronos/UI/CompactUI.cs index c40692a..299c590 100644 --- a/MareSynchronos/UI/CompactUI.cs +++ b/MareSynchronos/UI/CompactUI.cs @@ -34,7 +34,9 @@ namespace MareSynchronos.UI private float _windowContentWidth = 0; public CompactUi(WindowSystem windowSystem, - UiShared uiShared, Configuration configuration, ApiController apiController) + UiShared uiShared, Configuration configuration, ApiController apiController) : base("###MareSynchronosMainUI") + { + #if DEBUG string dateTime = "DEV VERSION"; try @@ -49,10 +51,8 @@ namespace MareSynchronos.UI } this.WindowName = "Mare Synchronos " + dateTime + "###MareSynchronosMainUI"; #else - : base("Mare Synchronos " + Assembly.GetExecutingAssembly().GetName().Version + "###MareSynchronosMainUI") + this.WindowName = "Mare Synchronos " + Assembly.GetExecutingAssembly().GetName().Version; #endif - - { Logger.Verbose("Creating " + nameof(CompactUi)); _windowSystem = windowSystem; From 35ebaed80c9389c412ae50eb5222583290227036 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Tue, 6 Sep 2022 14:03:56 +0200 Subject: [PATCH 6/8] fix for the fix of ipc manager to create temp collections, bump version --- MareSynchronos/Managers/IpcManager.cs | 7 ++++--- MareSynchronos/MareSynchronos.csproj | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index bacd7ec..4731b5f 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; using MareSynchronos.Utils; using MareSynchronos.WebAPI; -using Lumina.Excel.GeneratedSheets; using Action = System.Action; namespace MareSynchronos.Managers @@ -86,6 +85,7 @@ namespace MareSynchronos.Managers { while (actionQueue.TryDequeue(out var action)) { + Logger.Debug("Execution action in queue: " + action.Method); action(); } } @@ -263,12 +263,13 @@ namespace MareSynchronos.Managers actionQueue.Enqueue(() => { - Logger.Verbose("Assigning temp mods for " + characterName); + var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); + Logger.Verbose("Assigning temp mods for " + ret.Item2); foreach (var mod in modPaths) { Logger.Verbose(mod.Key + " => " + mod.Value); } - _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", characterName, modPaths, manipulationData, 0); + _penumbraSetTemporaryMod.InvokeFunc("MareSynchronos", ret.Item2, modPaths, manipulationData, 0); }); } diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 8fc8847..eab3a72 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.3.11 + 0.3.12 https://github.com/Penumbra-Sync/client From 920090c3b14acf948b25a7217c2bf45732a438ae Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 7 Sep 2022 22:03:17 +0200 Subject: [PATCH 7/8] add delayedframework update to dalamudutil, rework to concurrent collections --- .../Factories/CharacterDataFactory.cs | 2 +- MareSynchronos/Managers/CachedPlayer.cs | 54 ++++++++-------- MareSynchronos/Managers/IpcManager.cs | 53 +++++++++------- .../Managers/OnlinePlayerManager.cs | 62 ++++++++++++------- MareSynchronos/Managers/PlayerManager.cs | 11 +--- MareSynchronos/Utils/DalamudUtil.cs | 31 +++++++++- 6 files changed, 128 insertions(+), 85 deletions(-) diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index d9a8606..fd3de0a 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -226,7 +226,7 @@ public class CharacterDataFactory previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); - previousData.GlamourerString[objectKind] = _ipcManager.GlamourerGetCharacterCustomization(chara); + previousData.GlamourerString[objectKind] = _ipcManager.GlamourerGetCharacterCustomization(charaPointer); var human = (Human*)((Character*)charaPointer)->GameObject.GetDrawObject(); for (var mdlIdx = 0; mdlIdx < human->CharacterBase.SlotCount; ++mdlIdx) diff --git a/MareSynchronos/Managers/CachedPlayer.cs b/MareSynchronos/Managers/CachedPlayer.cs index 7df41d9..2380168 100644 --- a/MareSynchronos/Managers/CachedPlayer.cs +++ b/MareSynchronos/Managers/CachedPlayer.cs @@ -47,7 +47,7 @@ public class CachedPlayer private string _originalGlamourerData = string.Empty; - public Dalamud.Game.ClientState.Objects.Types.Character? PlayerCharacter { get; set; } + public IntPtr PlayerCharacter { get; set; } = IntPtr.Zero; public string? PlayerName { get; private set; } @@ -226,27 +226,27 @@ public class CachedPlayer private unsafe void ApplyCustomizationData(ObjectKind objectKind) { - if (PlayerCharacter is null) return; + if (PlayerCharacter == IntPtr.Zero) return; _cachedData.GlamourerData.TryGetValue(objectKind, out var glamourerData); if (objectKind == ObjectKind.Player) { - _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter.Address); + _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter); RequestedPenumbraRedraw = true; Logger.Debug( $"Request Redraw for {PlayerName}"); if (_ipcManager.CheckGlamourerApi() && !string.IsNullOrEmpty(glamourerData)) { - _ipcManager.GlamourerApplyAll(glamourerData, PlayerCharacter.Address); + _ipcManager.GlamourerApplyAll(glamourerData, PlayerCharacter); } else { - _ipcManager.PenumbraRedraw(PlayerCharacter.Address); + _ipcManager.PenumbraRedraw(PlayerCharacter); } } else if (objectKind == ObjectKind.MinionOrMount) { - var minionOrMount = ((Character*)PlayerCharacter.Address)->CompanionObject; + var minionOrMount = ((Character*)PlayerCharacter)->CompanionObject; if (minionOrMount != null) { Logger.Debug($"Request Redraw for Minion/Mount"); @@ -262,7 +262,7 @@ public class CachedPlayer } else if (objectKind == ObjectKind.Pet) { - var pet = _dalamudUtil.GetPet(PlayerCharacter.Address); + var pet = _dalamudUtil.GetPet(PlayerCharacter); if (pet != IntPtr.Zero) { Logger.Debug("Request Redraw for Pet"); @@ -278,7 +278,7 @@ public class CachedPlayer } else if (objectKind == ObjectKind.Companion) { - var companion = _dalamudUtil.GetCompanion(PlayerCharacter.Address); + var companion = _dalamudUtil.GetCompanion(PlayerCharacter); if (companion != IntPtr.Zero) { Logger.Debug("Request Redraw for Companion"); @@ -296,7 +296,7 @@ public class CachedPlayer private unsafe void RevertCustomizationData(ObjectKind objectKind) { - if (PlayerCharacter is null) return; + if (PlayerCharacter == IntPtr.Zero) return; if (objectKind == ObjectKind.Player) { @@ -307,12 +307,12 @@ public class CachedPlayer } else { - _ipcManager.PenumbraRedraw(PlayerCharacter.Address); + _ipcManager.PenumbraRedraw(PlayerCharacter); } } else if (objectKind == ObjectKind.MinionOrMount) { - var minionOrMount = ((Character*)PlayerCharacter.Address)->CompanionObject; + var minionOrMount = ((Character*)PlayerCharacter)->CompanionObject; if (minionOrMount != null) { _ipcManager.PenumbraRedraw((IntPtr)minionOrMount); @@ -320,7 +320,7 @@ public class CachedPlayer } else if (objectKind == ObjectKind.Pet) { - var pet = _dalamudUtil.GetPet(PlayerCharacter.Address); + var pet = _dalamudUtil.GetPet(PlayerCharacter); if (pet != IntPtr.Zero) { _ipcManager.PenumbraRedraw(pet); @@ -328,7 +328,7 @@ public class CachedPlayer } else if (objectKind == ObjectKind.Companion) { - var companion = _dalamudUtil.GetCompanion(PlayerCharacter.Address); + var companion = _dalamudUtil.GetCompanion(PlayerCharacter); if (companion != IntPtr.Zero) { _ipcManager.PenumbraRedraw(companion); @@ -345,10 +345,10 @@ public class CachedPlayer try { Logger.Verbose("Restoring state for " + PlayerName); - _dalamudUtil.FrameworkUpdate -= DalamudUtilOnFrameworkUpdate; + _dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate; _ipcManager.PenumbraRedrawEvent -= IpcManagerOnPenumbraRedrawEvent; _ipcManager.PenumbraRemoveTemporaryCollection(PlayerName); - if (PlayerCharacter != null && PlayerCharacter.IsValid()) + if (PlayerCharacter != IntPtr.Zero) { foreach (var item in _cachedData.FileReplacements) { @@ -367,18 +367,18 @@ public class CachedPlayer { _cachedData = new(); PlayerName = string.Empty; - PlayerCharacter = null; + PlayerCharacter = IntPtr.Zero; IsVisible = false; } } - public void InitializePlayer(PlayerCharacter character, CharacterCacheDto? cache) + public void InitializePlayer(IntPtr character, string name, CharacterCacheDto? cache) { Logger.Debug("Initializing Player " + this + " has cache: " + (cache != null)); IsVisible = true; - PlayerName = character.Name.ToString(); + PlayerName = name; PlayerCharacter = character; - _dalamudUtil.FrameworkUpdate += DalamudUtilOnFrameworkUpdate; + _dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate; _ipcManager.PenumbraRedrawEvent += IpcManagerOnPenumbraRedrawEvent; _originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter); _currentCharacterEquipment = new PlayerRelatedObject(ObjectKind.Player, IntPtr.Zero, IntPtr.Zero, @@ -390,12 +390,12 @@ public class CachedPlayer } } - private void DalamudUtilOnFrameworkUpdate() + private void DalamudUtilOnDelayedFrameworkUpdate() { if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized || !_apiController.IsConnected) return; - PlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!); - if (PlayerCharacter == null) + PlayerCharacter = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(PlayerName!)?.Address ?? IntPtr.Zero; + if (PlayerCharacter == IntPtr.Zero) { DisposePlayer(); return; @@ -412,7 +412,7 @@ public class CachedPlayer public override string ToString() { - return PlayerNameHash + ":" + PlayerName + ":HasChar " + (PlayerCharacter != null); + return PlayerNameHash + ":" + PlayerName + ":HasChar " + (PlayerCharacter != IntPtr.Zero); } private Task? _penumbraRedrawEventTask; @@ -425,10 +425,10 @@ public class CachedPlayer _penumbraRedrawEventTask = Task.Run(() => { - PlayerCharacter = player; + PlayerCharacter = address; using var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(5)); - _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter.Address, cts.Token); + _dalamudUtil.WaitWhileCharacterIsDrawing(PlayerCharacter, cts.Token); if (RequestedPenumbraRedraw == false) { @@ -448,10 +448,10 @@ public class CachedPlayer { Logger.Debug($"Player {PlayerName} changed, PenumbraRedraw is {RequestedPenumbraRedraw}"); _currentCharacterEquipment!.HasUnprocessedUpdate = false; - if (!RequestedPenumbraRedraw && PlayerCharacter is not null) + if (!RequestedPenumbraRedraw && PlayerCharacter != IntPtr.Zero) { Logger.Debug($"Saving new Glamourer data"); - _lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter!); + _lastGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter); } } } \ No newline at end of file diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index 4731b5f..c545764 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -6,6 +6,7 @@ using Dalamud.Game.ClientState.Objects.Types; using MareSynchronos.Utils; using MareSynchronos.WebAPI; using Action = System.Action; +using System.Collections.Concurrent; namespace MareSynchronos.Managers { @@ -33,7 +34,7 @@ namespace MareSynchronos.Managers private readonly ICallGateSubscriber, string, int, int> _penumbraSetTemporaryMod; private readonly DalamudUtil _dalamudUtil; - private readonly Queue actionQueue = new(); + private readonly ConcurrentQueue actionQueue = new(); public IpcManager(DalamudPluginInterface pi, DalamudUtil dalamudUtil) { @@ -83,8 +84,9 @@ namespace MareSynchronos.Managers private void HandleActionQueue() { - while (actionQueue.TryDequeue(out var action)) + if (actionQueue.TryDequeue(out var action)) { + if (action == null) return; Logger.Debug("Execution action in queue: " + action.Method); action(); } @@ -145,37 +147,50 @@ namespace MareSynchronos.Managers }); } - public void GlamourerApplyOnlyEquipment(string customization, GameObject character) + public void GlamourerApplyOnlyEquipment(string customization, IntPtr character) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return; actionQueue.Enqueue(() => { - Logger.Verbose("Glamourer apply only equipment to " + character); - _glamourerApplyOnlyEquipment!.InvokeAction(customization, character); + var gameObj = _dalamudUtil.CreateGameObject(character); + if (gameObj != null) + { + Logger.Verbose("Glamourer apply only equipment to " + character.ToString("X")); + _glamourerApplyOnlyEquipment!.InvokeAction(customization, gameObj); + } }); } - public void GlamourerApplyOnlyCustomization(string customization, GameObject character) + public void GlamourerApplyOnlyCustomization(string customization, IntPtr character) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization)) return; actionQueue.Enqueue(() => { - Logger.Verbose("Glamourer apply only customization to " + character); - _glamourerApplyOnlyCustomization!.InvokeAction(customization, character); + var gameObj = _dalamudUtil.CreateGameObject(character); + if (gameObj != null) + { + Logger.Verbose("Glamourer apply only customization to " + character.ToString("X")); + _glamourerApplyOnlyCustomization!.InvokeAction(customization, gameObj); + } }); } - public string GlamourerGetCharacterCustomization(GameObject character) + public string GlamourerGetCharacterCustomization(IntPtr character) { if (!CheckGlamourerApi()) return string.Empty; try { - var glamourerString = _glamourerGetAllCustomization!.InvokeFunc(character); - byte[] bytes = Convert.FromBase64String(glamourerString); - // ignore transparency - bytes[88] = 128; - bytes[89] = 63; - return Convert.ToBase64String(bytes); + var gameObj = _dalamudUtil.CreateGameObject(character); + if (gameObj != null) + { + var glamourerString = _glamourerGetAllCustomization!.InvokeFunc(gameObj); + byte[] bytes = Convert.FromBase64String(glamourerString); + // ignore transparency + bytes[88] = 128; + bytes[89] = 63; + return Convert.ToBase64String(bytes); + } + return string.Empty; } catch { @@ -189,14 +204,6 @@ namespace MareSynchronos.Managers actionQueue.Enqueue(() => _glamourerRevertCustomization!.InvokeAction(character)); } - public string PenumbraCreateTemporaryCollection(string characterName) - { - if (!CheckPenumbraApi()) return string.Empty; - Logger.Verbose("Creating temp collection for " + characterName); - var ret = _penumbraCreateTemporaryCollection.InvokeFunc("MareSynchronos", characterName, true); - return ret.Item2; - } - public string PenumbraGetMetaManipulations() { if (!CheckPenumbraApi()) return string.Empty; diff --git a/MareSynchronos/Managers/OnlinePlayerManager.cs b/MareSynchronos/Managers/OnlinePlayerManager.cs index 30f0ee9..1dfbed3 100644 --- a/MareSynchronos/Managers/OnlinePlayerManager.cs +++ b/MareSynchronos/Managers/OnlinePlayerManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -19,11 +20,11 @@ public class OnlinePlayerManager : IDisposable private readonly Framework _framework; private readonly IpcManager _ipcManager; private readonly PlayerManager _playerManager; - private readonly List _onlineCachedPlayers = new(); - private readonly Dictionary _temporaryStoredCharacterCache = new(); - private readonly Dictionary _playerTokenDisposal = new(); + private readonly ConcurrentDictionary _onlineCachedPlayers = new(); + private readonly ConcurrentDictionary _temporaryStoredCharacterCache = new(); + private readonly ConcurrentDictionary _playerTokenDisposal = new(); - private List OnlineVisiblePlayerHashes => _onlineCachedPlayers.Where(p => p.PlayerCharacter != null) + private List OnlineVisiblePlayerHashes => _onlineCachedPlayers.Select(p => p.Value).Where(p => p.PlayerCharacter != null) .Select(p => p.PlayerNameHash).ToList(); private DateTime _lastPlayerObjectCheck = DateTime.Now; @@ -58,8 +59,7 @@ public class OnlinePlayerManager : IDisposable private void ApiControllerOnCharacterReceived(object? sender, CharacterReceivedEventArgs e) { - var visiblePlayer = _onlineCachedPlayers.SingleOrDefault(c => c.IsVisible && c.PlayerNameHash == e.CharacterNameHash); - if (visiblePlayer != null) + if (_onlineCachedPlayers.TryGetValue(e.CharacterNameHash, out var visiblePlayer) && visiblePlayer.IsVisible) { Logger.Debug("Received data and applying to " + e.CharacterNameHash); visiblePlayer.ApplyCharacterData(e.CharacterData); @@ -99,7 +99,15 @@ public class OnlinePlayerManager : IDisposable private void IpcManagerOnPenumbraDisposed() { - _onlineCachedPlayers.ForEach(p => p.DisposePlayer()); + DisposePlayers(); + } + + private void DisposePlayers() + { + foreach (var kvp in _onlineCachedPlayers) + { + kvp.Value.DisposePlayer(); + } } private void ApiControllerOnDisconnected() @@ -111,8 +119,11 @@ public class OnlinePlayerManager : IDisposable public void AddInitialPairs(List apiTaskResult) { _onlineCachedPlayers.Clear(); - _onlineCachedPlayers.AddRange(apiTaskResult.Select(CreateCachedPlayer)); - Logger.Verbose("Online and paired users: " + string.Join(Environment.NewLine, _onlineCachedPlayers)); + foreach (var hash in apiTaskResult) + { + _onlineCachedPlayers.TryAdd(hash, CreateCachedPlayer(hash)); + } + Logger.Verbose("Online and paired users: " + string.Join(Environment.NewLine, _onlineCachedPlayers.Select(k => k.Key))); } public void Dispose() @@ -138,7 +149,7 @@ public class OnlinePlayerManager : IDisposable private void RestoreAllCharacters() { - _onlineCachedPlayers.ForEach(p => p.DisposePlayer()); + DisposePlayers(); _onlineCachedPlayers.Clear(); } @@ -171,19 +182,23 @@ public class OnlinePlayerManager : IDisposable private void AddPlayer(string characterNameHash) { - if (_onlineCachedPlayers.Any(p => p.PlayerNameHash == characterNameHash)) + if (_onlineCachedPlayers.TryGetValue(characterNameHash, out var cachedPlayer)) { PushCharacterData(new List() { characterNameHash }); - _playerTokenDisposal.TryGetValue(_onlineCachedPlayers.Single(p => p.PlayerNameHash == characterNameHash), out var cancellationTokenSource); + _playerTokenDisposal.TryGetValue(cachedPlayer, out var cancellationTokenSource); cancellationTokenSource?.Cancel(); return; } - _onlineCachedPlayers.Add(CreateCachedPlayer(characterNameHash)); + _onlineCachedPlayers.TryAdd(characterNameHash, CreateCachedPlayer(characterNameHash)); } private void RemovePlayer(string characterHash) { - var cachedPlayer = _onlineCachedPlayers.First(p => p.PlayerNameHash == characterHash); + if (!_onlineCachedPlayers.TryGetValue(characterHash, out var cachedPlayer)) + { + return; + } + if (_dalamudUtil.IsInGpose) { _playerTokenDisposal.TryGetValue(cachedPlayer, out var cancellationTokenSource); @@ -202,14 +217,14 @@ public class OnlinePlayerManager : IDisposable } cachedPlayer.DisposePlayer(); - _onlineCachedPlayers.RemoveAll(c => c.PlayerNameHash == cachedPlayer.PlayerNameHash); + _onlineCachedPlayers.TryRemove(characterHash, out _); }, token); return; } cachedPlayer.DisposePlayer(); - _onlineCachedPlayers.RemoveAll(c => c.PlayerNameHash == cachedPlayer.PlayerNameHash); + _onlineCachedPlayers.TryRemove(characterHash, out _); } private void FrameworkOnUpdate(Framework framework) @@ -222,22 +237,21 @@ public class OnlinePlayerManager : IDisposable foreach (var pChar in playerCharacters) { var hashedName = Crypto.GetHash256(pChar); - var existingCachedPlayer = _onlineCachedPlayers.SingleOrDefault(p => p.PlayerNameHash == hashedName && !string.IsNullOrEmpty(p.PlayerName)); - if (existingCachedPlayer != null) + if (_onlineCachedPlayers.TryGetValue(hashedName, out var existingPlayer) && !string.IsNullOrEmpty(existingPlayer.PlayerName)) { - existingCachedPlayer.IsVisible = true; + existingPlayer.IsVisible = true; continue; } - if (_temporaryStoredCharacterCache.TryGetValue(hashedName, out var cache)) + if (existingPlayer != null) { - _temporaryStoredCharacterCache.Remove(hashedName); + _temporaryStoredCharacterCache.TryRemove(hashedName, out var cache); + existingPlayer.InitializePlayer(pChar.Address, pChar.Name.ToString(), cache); } - _onlineCachedPlayers.SingleOrDefault(p => p.PlayerNameHash == hashedName)?.InitializePlayer(pChar, cache); } - var newlyVisiblePlayers = _onlineCachedPlayers - .Where(p => p.PlayerCharacter != null && p.IsVisible && !p.WasVisible).Select(p => p.PlayerNameHash) + var newlyVisiblePlayers = _onlineCachedPlayers.Select(v => v.Value) + .Where(p => p.PlayerCharacter != IntPtr.Zero && p.IsVisible && !p.WasVisible).Select(p => p.PlayerNameHash) .ToList(); if (newlyVisiblePlayers.Any()) { diff --git a/MareSynchronos/Managers/PlayerManager.cs b/MareSynchronos/Managers/PlayerManager.cs index feca223..a978fb5 100644 --- a/MareSynchronos/Managers/PlayerManager.cs +++ b/MareSynchronos/Managers/PlayerManager.cs @@ -26,7 +26,6 @@ namespace MareSynchronos.Managers private readonly Dictionary> objectKindsToUpdate = new(); private CancellationTokenSource? _playerChangedCts = new(); - private DateTime _lastPlayerObjectCheck; private List playerRelatedObjects = new List(); @@ -42,7 +41,7 @@ namespace MareSynchronos.Managers _apiController.Connected += ApiControllerOnConnected; _apiController.Disconnected += ApiController_Disconnected; - _dalamudUtil.FrameworkUpdate += DalamudUtilOnFrameworkUpdate; + _dalamudUtil.DelayedFrameworkUpdate += DalamudUtilOnDelayedFrameworkUpdate; Logger.Debug("Watching Player, ApiController is Connected: " + _apiController.IsConnected); if (_apiController.IsConnected) @@ -67,22 +66,18 @@ namespace MareSynchronos.Managers _apiController.Disconnected -= ApiController_Disconnected; _ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent; - _dalamudUtil.FrameworkUpdate -= DalamudUtilOnFrameworkUpdate; + _dalamudUtil.DelayedFrameworkUpdate -= DalamudUtilOnDelayedFrameworkUpdate; } - private unsafe void DalamudUtilOnFrameworkUpdate() + private unsafe void DalamudUtilOnDelayedFrameworkUpdate() { if (!_dalamudUtil.IsPlayerPresent || !_ipcManager.Initialized) return; - if (DateTime.Now < _lastPlayerObjectCheck.AddSeconds(0.25)) return; - playerRelatedObjects.ForEach(k => k.CheckAndUpdateObject()); if (playerRelatedObjects.Any(c => c.HasUnprocessedUpdate && !c.IsProcessing)) { OnPlayerOrAttachedObjectsChanged(); } - - _lastPlayerObjectCheck = DateTime.Now; } private void ApiControllerOnConnected() diff --git a/MareSynchronos/Utils/DalamudUtil.cs b/MareSynchronos/Utils/DalamudUtil.cs index 4588bc5..15e14e2 100644 --- a/MareSynchronos/Utils/DalamudUtil.cs +++ b/MareSynchronos/Utils/DalamudUtil.cs @@ -6,7 +6,6 @@ using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Objects.Types; using FFXIVClientStructs.FFXIV.Client.Game.Character; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -27,6 +26,8 @@ namespace MareSynchronos.Utils public event LogIn? LogIn; public event LogOut? LogOut; public event FrameworkUpdate? FrameworkUpdate; + public event FrameworkUpdate? DelayedFrameworkUpdate; + private DateTime _delayedFrameworkUpdateCheck = DateTime.Now; public DalamudUtil(ClientState clientState, ObjectTable objectTable, Framework framework) { @@ -44,7 +45,33 @@ namespace MareSynchronos.Utils private void FrameworkOnUpdate(Framework framework) { - FrameworkUpdate?.Invoke(); + foreach (FrameworkUpdate frameworkInvocation in (FrameworkUpdate?.GetInvocationList() ?? Array.Empty()).Cast()) + { + try + { + frameworkInvocation.Invoke(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message); + Logger.Warn(ex.StackTrace ?? string.Empty); + } + } + + if (DateTime.Now < _delayedFrameworkUpdateCheck.AddSeconds(0.25)) return; + foreach (FrameworkUpdate frameworkInvocation in (DelayedFrameworkUpdate?.GetInvocationList() ?? Array.Empty()).Cast()) + { + try + { + frameworkInvocation.Invoke(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message); + Logger.Warn(ex.StackTrace ?? string.Empty); + } + } + _delayedFrameworkUpdateCheck = DateTime.Now; } private void ClientStateOnLogout(object? sender, EventArgs e) From 29032d4cbfc528125a51062d57431481615cb660 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 7 Sep 2022 22:03:45 +0200 Subject: [PATCH 8/8] up version --- MareSynchronos/MareSynchronos.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index eab3a72..4a7c6c3 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.3.12 + 0.3.13 https://github.com/Penumbra-Sync/client