From d2c3f5cdc63dfc22c11ff64125646b06fb54c6d5 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 15 Mar 2023 09:52:33 +0100 Subject: [PATCH 1/4] check for framework thread on running actions, fix palette+ constantly sending update for chara data to create --- MareSynchronos/Interop/IpcManager.cs | 2 +- MareSynchronos/MareSynchronos.csproj | 2 +- .../Services/CacheCreationService.cs | 51 +++++++++++++------ MareSynchronos/Services/DalamudUtilService.cs | 14 +++-- MareSynchronos/Services/Mediator/Messages.cs | 5 +- 5 files changed, 51 insertions(+), 23 deletions(-) diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index d124a1c..d430687 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -581,7 +581,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private void OnPalettePlusPaletteChange(Character character, string palette) { - Mediator.Publish(new PalettePlusMessage()); + Mediator.Publish(new PalettePlusMessage(character)); } private void PenumbraDispose() diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index a5454f7..25cf3bb 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.8.3 + 0.8.4 https://github.com/Penumbra-Sync/client diff --git a/MareSynchronos/PlayerData/Services/CacheCreationService.cs b/MareSynchronos/PlayerData/Services/CacheCreationService.cs index c50f8b9..c8d040e 100644 --- a/MareSynchronos/PlayerData/Services/CacheCreationService.cs +++ b/MareSynchronos/PlayerData/Services/CacheCreationService.cs @@ -15,7 +15,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase private readonly PlayerDataFactory _characterDataFactory; private readonly CancellationTokenSource _cts = new(); private readonly CharacterData _playerData = new(); - private readonly List _playerRelatedObjects = new(); + private readonly Dictionary _playerRelatedObjects = new(); private Task? _cacheCreationTask; private CancellationTokenSource _palettePlusCts = new(); @@ -24,21 +24,23 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase { _characterDataFactory = characterDataFactory; + _playerRelatedObjects[ObjectKind.Player] = + gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.PlayerPointer, true); + _playerRelatedObjects[ObjectKind.MinionOrMount] = + gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.GetMinionOrMount(), true); + _playerRelatedObjects[ObjectKind.Pet] = + gameObjectHandlerFactory(ObjectKind.Pet, () => dalamudUtil.GetPet(), true); + _playerRelatedObjects[ObjectKind.Companion] = + gameObjectHandlerFactory(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), true); + Mediator.Subscribe(this, (msg) => { + Logger.LogDebug("Received CreateCacheForObject for {handler}, updating player", msg.ObjectToCreateFor); _cacheCreateLock.Wait(); _cachesToCreate[msg.ObjectToCreateFor.ObjectKind] = msg.ObjectToCreateFor; _cacheCreateLock.Release(); }); - _playerRelatedObjects.AddRange(new List() - { - gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.PlayerPointer, true), - gameObjectHandlerFactory(ObjectKind.MinionOrMount, () => dalamudUtil.GetMinionOrMount(), true), - gameObjectHandlerFactory(ObjectKind.Pet, () => dalamudUtil.GetPet(), true), - gameObjectHandlerFactory(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), true), - }); - Mediator.Subscribe(this, (msg) => { Task.Run(() => @@ -50,24 +52,43 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase }); Mediator.Subscribe(this, (msg) => ProcessCacheCreation()); - Mediator.Subscribe(this, async (_) => await AddPlayerCacheToCreate().ConfigureAwait(false)); - Mediator.Subscribe(this, async (_) => await AddPlayerCacheToCreate().ConfigureAwait(false)); - Mediator.Subscribe(this, (_) => PalettePlusChanged()); - Mediator.Subscribe(this, async (msg) => await AddPlayerCacheToCreate().ConfigureAwait(false)); + Mediator.Subscribe(this, async (_) => + { + Logger.LogDebug("Received CustomizePlus change, updating player"); + await AddPlayerCacheToCreate().ConfigureAwait(false); + }); + Mediator.Subscribe(this, async (_) => + { + Logger.LogDebug("Received Heels Offset change, updating player"); + await AddPlayerCacheToCreate().ConfigureAwait(false); + }); + Mediator.Subscribe(this, (msg) => + { + if (msg.Character.Address == _playerRelatedObjects[ObjectKind.Player].Address) + { + Logger.LogDebug("Received PalettePlus change, updating player"); + PalettePlusChanged(); + } + }); + Mediator.Subscribe(this, async (msg) => + { + Logger.LogDebug("Received Penumbra Mod settings change, updating player"); + await AddPlayerCacheToCreate().ConfigureAwait(false); + }); } protected override void Dispose(bool disposing) { base.Dispose(disposing); - _playerRelatedObjects.ForEach(p => p.Dispose()); + _playerRelatedObjects.Values.ToList().ForEach(p => p.Dispose()); _cts.Dispose(); } private async Task AddPlayerCacheToCreate() { await _cacheCreateLock.WaitAsync().ConfigureAwait(false); - _cachesToCreate[ObjectKind.Player] = _playerRelatedObjects.First(p => p.ObjectKind == ObjectKind.Player); + _cachesToCreate[ObjectKind.Player] = _playerRelatedObjects[ObjectKind.Player]; _cacheCreateLock.Release(); } diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index 36ff33a..0e90bc9 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -157,14 +157,20 @@ public class DalamudUtilService : IHostedService public async Task RunOnFrameworkThread(Action act) { - _logger.LogTrace("Running Action on framework thread: {act}", act); - await _framework.RunOnFrameworkThread(act).ConfigureAwait(false); + _logger.LogTrace("Running Action on framework thread (FrameworkContext: {ctx}): {act}", _framework.IsInFrameworkUpdateThread, act); + if (!_framework.IsInFrameworkUpdateThread) + await _framework.RunOnFrameworkThread(act).ConfigureAwait(false); + else + act(); } public async Task RunOnFrameworkThread(Func func) { - _logger.LogTrace("Running Func on framework thread: {func}", func); - return await _framework.RunOnFrameworkThread(func).ConfigureAwait(false); + _logger.LogTrace("Running Func on framework thread (FrameworkContext: {ctx}): {act}", _framework.IsInFrameworkUpdateThread, func); + if (!_framework.IsInFrameworkUpdateThread) + return await _framework.RunOnFrameworkThread(func).ConfigureAwait(false); + else + return func.Invoke(); } public Task StartAsync(CancellationToken cancellationToken) diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index cdb3533..59841ed 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -1,4 +1,5 @@ -using Dalamud.Interface.Internal.Notifications; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Interface.Internal.Notifications; using MareSynchronos.API.Dto; using MareSynchronos.PlayerData.Handlers; using MareSynchronos.WebAPI.Files.Models; @@ -30,7 +31,7 @@ public record PenumbraRedrawMessage(IntPtr Address, int ObjTblIdx, bool WasReque public record HeelsOffsetMessage : IMessage; public record PenumbraResourceLoadMessage(IntPtr GameObject, string GamePath, string FilePath) : IMessage; public record CustomizePlusMessage : IMessage; -public record PalettePlusMessage : IMessage; +public record PalettePlusMessage(Character Character) : IMessage; public record PlayerChangedMessage(API.Data.CharacterData Data) : IMessage; public record CharacterChangedMessage(GameObjectHandler GameObjectHandler) : IMessage; public record TransientResourceChangedMessage(IntPtr Address) : IMessage; From 01723152d8d8a3e2711de1da1dc6a2918bb33334 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 15 Mar 2023 10:14:05 +0100 Subject: [PATCH 2/4] fix initial cache creation --- MareSynchronos/MareSynchronos.csproj | 2 +- .../Services/CacheCreationService.cs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 25cf3bb..7db4f1b 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.8.4 + 0.8.5 https://github.com/Penumbra-Sync/client diff --git a/MareSynchronos/PlayerData/Services/CacheCreationService.cs b/MareSynchronos/PlayerData/Services/CacheCreationService.cs index c8d040e..0d5c55d 100644 --- a/MareSynchronos/PlayerData/Services/CacheCreationService.cs +++ b/MareSynchronos/PlayerData/Services/CacheCreationService.cs @@ -24,15 +24,6 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase { _characterDataFactory = characterDataFactory; - _playerRelatedObjects[ObjectKind.Player] = - gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.PlayerPointer, true); - _playerRelatedObjects[ObjectKind.MinionOrMount] = - gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.GetMinionOrMount(), true); - _playerRelatedObjects[ObjectKind.Pet] = - gameObjectHandlerFactory(ObjectKind.Pet, () => dalamudUtil.GetPet(), true); - _playerRelatedObjects[ObjectKind.Companion] = - gameObjectHandlerFactory(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), true); - Mediator.Subscribe(this, (msg) => { Logger.LogDebug("Received CreateCacheForObject for {handler}, updating player", msg.ObjectToCreateFor); @@ -75,6 +66,15 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase Logger.LogDebug("Received Penumbra Mod settings change, updating player"); await AddPlayerCacheToCreate().ConfigureAwait(false); }); + + _playerRelatedObjects[ObjectKind.Player] = + gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.PlayerPointer, true); + _playerRelatedObjects[ObjectKind.MinionOrMount] = + gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.GetMinionOrMount(), true); + _playerRelatedObjects[ObjectKind.Pet] = + gameObjectHandlerFactory(ObjectKind.Pet, () => dalamudUtil.GetPet(), true); + _playerRelatedObjects[ObjectKind.Companion] = + gameObjectHandlerFactory(ObjectKind.Companion, () => dalamudUtil.GetCompanion(), true); } protected override void Dispose(bool disposing) From 4b4e7b55635ef28a3a31c57da036da03c60049af Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 15 Mar 2023 10:17:29 +0100 Subject: [PATCH 3/4] fix array --- MareSynchronos/PlayerData/Services/CacheCreationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MareSynchronos/PlayerData/Services/CacheCreationService.cs b/MareSynchronos/PlayerData/Services/CacheCreationService.cs index 0d5c55d..3585975 100644 --- a/MareSynchronos/PlayerData/Services/CacheCreationService.cs +++ b/MareSynchronos/PlayerData/Services/CacheCreationService.cs @@ -70,7 +70,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase _playerRelatedObjects[ObjectKind.Player] = gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.PlayerPointer, true); _playerRelatedObjects[ObjectKind.MinionOrMount] = - gameObjectHandlerFactory(ObjectKind.Player, () => dalamudUtil.GetMinionOrMount(), true); + gameObjectHandlerFactory(ObjectKind.MinionOrMount, () => dalamudUtil.GetMinionOrMount(), true); _playerRelatedObjects[ObjectKind.Pet] = gameObjectHandlerFactory(ObjectKind.Pet, () => dalamudUtil.GetPet(), true); _playerRelatedObjects[ObjectKind.Companion] = From 40b3bd01b33d72a44aee37f2d2f04699bdfdbbe6 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 15 Mar 2023 12:44:46 +0100 Subject: [PATCH 4/4] get fresh pointer on dispose --- MareSynchronos/MareSynchronos.csproj | 2 +- .../PlayerData/Handlers/GameObjectHandler.cs | 11 +++---- .../PlayerData/Pairs/CachedPlayer.cs | 30 ++++++++++++++++--- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/MareSynchronos/MareSynchronos.csproj b/MareSynchronos/MareSynchronos.csproj index 7db4f1b..74f4686 100644 --- a/MareSynchronos/MareSynchronos.csproj +++ b/MareSynchronos/MareSynchronos.csproj @@ -3,7 +3,7 @@ - 0.8.5 + 0.8.6 https://github.com/Penumbra-Sync/client diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index 211ca85..4cf2fc0 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -96,11 +96,12 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase { return await _dalamudUtil.RunOnFrameworkThread(() => { - var curPtr = _getAddress.Invoke(); - + nint curPtr = IntPtr.Zero; try { - var drawObj = GetDrawObj(); + curPtr = _getAddress.Invoke(); + + var drawObj = GetDrawObj(curPtr); return IsBeingDrawn(drawObj, curPtr); } catch (Exception) @@ -258,9 +259,9 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase } } - private unsafe IntPtr GetDrawObj() + private unsafe IntPtr GetDrawObj(nint curPtr) { - return (IntPtr)((GameObject*)_getAddress.Invoke())->GetDrawObject(); + return (IntPtr)((GameObject*)curPtr)->GetDrawObject(); } private unsafe bool IsBeingDrawn(IntPtr drawObj, IntPtr curPtr) diff --git a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs index e71a75e..eb8dc7c 100644 --- a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs +++ b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs @@ -155,17 +155,25 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase _downloadCancellationTokenSource?.Cancel(); _downloadCancellationTokenSource?.Dispose(); _downloadCancellationTokenSource = null; - nint ptr = PlayerCharacter; _charaHandler?.Dispose(); _charaHandler = null; - if (!_lifetime.ApplicationStopping.IsCancellationRequested && ptr != IntPtr.Zero && !_dalamudUtil.IsZoning) + + if (!_lifetime.ApplicationStopping.IsCancellationRequested && !_dalamudUtil.IsZoning) { Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser); _ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, name); foreach (KeyValuePair> item in _cachedData.FileReplacements) { - RevertCustomizationData(ptr, item.Key, name, applicationId).GetAwaiter().GetResult(); + try + { + RevertCustomizationData(item.Key, name, applicationId).GetAwaiter().GetResult(); + } + catch (InvalidOperationException ex) + { + Logger.LogWarning("Failed disposing player (not present anymore?)", ex); + break; + } } } } @@ -472,8 +480,17 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase } } - private async Task RevertCustomizationData(IntPtr address, ObjectKind objectKind, string name, Guid applicationId) + private void CheckForNameAndThrow(GameObjectHandler handler, string name) { + if (!string.Equals(handler.Name, name, StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("Player name not equal to requested name, pointer invalid"); + } + } + + private async Task RevertCustomizationData(ObjectKind objectKind, string name, Guid applicationId) + { + nint address = _dalamudUtil.GetPlayerCharacterFromObjectTableByName(name)?.Address ?? IntPtr.Zero; if (address == IntPtr.Zero) return; var cancelToken = new CancellationTokenSource(); @@ -484,14 +501,19 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (objectKind == ObjectKind.Player) { using GameObjectHandler tempHandler = _gameObjectHandlerFactory(ObjectKind.Player, () => address, false); + CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring Customization for {alias}/{name}: {data}", applicationId, OnlineUser.User.AliasOrUID, name, _originalGlamourerData); await _ipcManager.GlamourerApplyOnlyCustomization(Logger, tempHandler, _originalGlamourerData, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); + CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring Equipment for {alias}/{name}: {data}", applicationId, OnlineUser.User.AliasOrUID, name, _lastGlamourerData); await _ipcManager.GlamourerApplyOnlyEquipment(Logger, tempHandler, _lastGlamourerData, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); + CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); await _ipcManager.HeelsRestoreOffsetForPlayer(address).ConfigureAwait(false); + CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring C+ for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); await _ipcManager.CustomizePlusRevert(address).ConfigureAwait(false); + CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring Palette+ for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); await _ipcManager.PalettePlusRemovePalette(address).ConfigureAwait(false); }