diff --git a/MareSynchronos/Factories/CharacterDataFactory.cs b/MareSynchronos/Factories/CharacterDataFactory.cs index c1bfd32..ebadd22 100644 --- a/MareSynchronos/Factories/CharacterDataFactory.cs +++ b/MareSynchronos/Factories/CharacterDataFactory.cs @@ -131,7 +131,7 @@ public class CharacterDataFactory : MediatorSubscriberBase } // wait until chara is not drawing and present so nothing spontaneously explodes - _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, playerRelatedObject, Guid.NewGuid(), 30000, ct: token); + await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, playerRelatedObject, Guid.NewGuid(), 30000, ct: token).ConfigureAwait(false); int totalWaitTime = 10000; while (!DalamudUtil.IsObjectPresent(_dalamudUtil.CreateGameObject(charaPointer)) && totalWaitTime > 0) { diff --git a/MareSynchronos/Factories/GameObjectHandlerFactory.cs b/MareSynchronos/Factories/GameObjectHandlerFactory.cs index 31e6f51..46c19a8 100644 --- a/MareSynchronos/Factories/GameObjectHandlerFactory.cs +++ b/MareSynchronos/Factories/GameObjectHandlerFactory.cs @@ -11,16 +11,18 @@ public class GameObjectHandlerFactory private readonly ILoggerFactory _loggerFactory; private readonly MareMediator _mediator; private readonly PerformanceCollector _performanceCollector; + private readonly DalamudUtil _dalamudUtil; - public GameObjectHandlerFactory(ILoggerFactory loggerFactory, MareMediator mediator, PerformanceCollector performanceCollector) + public GameObjectHandlerFactory(ILoggerFactory loggerFactory, MareMediator mediator, PerformanceCollector performanceCollector, DalamudUtil dalamudUtil) { _loggerFactory = loggerFactory; _mediator = mediator; _performanceCollector = performanceCollector; + _dalamudUtil = dalamudUtil; } public GameObjectHandler Create(ObjectKind objectKind, Func getAddress, bool isWatched) { - return new GameObjectHandler(_loggerFactory.CreateLogger(), _performanceCollector, _mediator, objectKind, getAddress, isWatched); + return new GameObjectHandler(_loggerFactory.CreateLogger(), _performanceCollector, _mediator, _dalamudUtil, objectKind, getAddress, isWatched); } } \ No newline at end of file diff --git a/MareSynchronos/Managers/CachedPlayer.cs b/MareSynchronos/Managers/CachedPlayer.cs index 2303ef2..a659368 100644 --- a/MareSynchronos/Managers/CachedPlayer.cs +++ b/MareSynchronos/Managers/CachedPlayer.cs @@ -310,7 +310,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable } _logger.LogDebug("[{applicationId}] Applying Customization Data for {handler}", applicationId, handler); - _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, handler, applicationId, 30000); + await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, handler, applicationId, 30000).ConfigureAwait(false); foreach (var change in changes.Value) { _logger.LogDebug("[{applicationId}] Processing {change} for {handler}", applicationId, change, handler); @@ -445,7 +445,7 @@ public class CachedPlayer : MediatorSubscriberBase, IDisposable Task.Run(async () => { var applicationId = Guid.NewGuid(); - _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, _currentOtherChara!, applicationId, ct: token); + await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, _currentOtherChara!, applicationId, ct: token).ConfigureAwait(false); _logger.LogDebug("Unauthorized character change detected"); await ApplyCustomizationData(applicationId, new(ObjectKind.Player, new HashSet(new[] { PlayerChanges.Palette, PlayerChanges.Customize, PlayerChanges.Heels, PlayerChanges.Mods })), diff --git a/MareSynchronos/Managers/IpcManager.cs b/MareSynchronos/Managers/IpcManager.cs index 6ed4c87..76bf13b 100644 --- a/MareSynchronos/Managers/IpcManager.cs +++ b/MareSynchronos/Managers/IpcManager.cs @@ -414,7 +414,7 @@ public class IpcManager : MediatorSubscriberBase, IDisposable await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); if (!combinedToken.IsCancellationRequested) - _dalamudUtil.WaitWhileCharacterIsDrawing(logger, obj, applicationId, 30000, combinedToken); + await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, obj, applicationId, 30000, combinedToken).ConfigureAwait(false); _penumbraRedrawRequests[obj.Address] = false; } diff --git a/MareSynchronos/Models/GameObjectHandler.cs b/MareSynchronos/Models/GameObjectHandler.cs index 1539243..2e803f5 100644 --- a/MareSynchronos/Models/GameObjectHandler.cs +++ b/MareSynchronos/Models/GameObjectHandler.cs @@ -15,6 +15,7 @@ public class GameObjectHandler : MediatorSubscriberBase private readonly Func _getAddress; private readonly bool _isOwnedObject; private readonly MareMediator _mediator; + private readonly DalamudUtil _dalamudUtil; private readonly PerformanceCollector _performanceCollector; private CancellationTokenSource? _clearCts = new(); private Task? _clearTask; @@ -22,11 +23,13 @@ public class GameObjectHandler : MediatorSubscriberBase private bool _haltProcessing = false; private bool _ignoreSendAfterRedraw = false; private CancellationTokenSource _zoningCts = new(); - public GameObjectHandler(ILogger logger, PerformanceCollector performanceCollector, MareMediator mediator, ObjectKind objectKind, Func getAddress, bool watchedObject = true) : base(logger, mediator) + public GameObjectHandler(ILogger logger, PerformanceCollector performanceCollector, + MareMediator mediator, DalamudUtil dalamudUtil, ObjectKind objectKind, Func getAddress, bool watchedObject = true) : base(logger, mediator) { _performanceCollector = performanceCollector; _mediator = mediator; ObjectKind = objectKind; + _dalamudUtil = dalamudUtil; _getAddress = getAddress; _isOwnedObject = watchedObject; Name = string.Empty; @@ -86,7 +89,6 @@ public class GameObjectHandler : MediatorSubscriberBase public unsafe Character* Character => (Character*)Address; public IntPtr CurrentAddress => _getAddress.Invoke(); - public bool IsBeingDrawn { get; private set; } public string Name { get; private set; } public ObjectKind ObjectKind { get; } private byte[] CustomizeData { get; set; } = new byte[26]; @@ -110,6 +112,41 @@ public class GameObjectHandler : MediatorSubscriberBase return $"{owned}/{ObjectKind}:{Name} ({Address:X},{DrawObjectAddress:X})"; } + private unsafe IntPtr GetDrawObj() + { + return (IntPtr)((GameObject*)_getAddress.Invoke())->GetDrawObject(); + } + + private unsafe bool IsBeingDrawn(IntPtr drawObj, IntPtr curPtr) + { + return drawObj == IntPtr.Zero || (((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0) + || (((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0) + || (((GameObject*)curPtr)->RenderFlags & 0b100000000000) == 0b100000000000; + } + + public async Task IsBeingDrawn() + { + var curPtr = _getAddress.Invoke(); + try + { + return await _dalamudUtil.RunOnFrameworkThread(() => + { + var drawObj = GetDrawObj(); + return IsBeingDrawn(drawObj, curPtr); + }).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error during checking for draw object for {name}", curPtr); + if (curPtr != IntPtr.Zero) + { + return true; + } + } + + return false; + } + private unsafe void CheckAndUpdateObject() { if (_haltProcessing) return; @@ -123,21 +160,12 @@ public class GameObjectHandler : MediatorSubscriberBase var drawObjAddr = (IntPtr)((GameObject*)curPtr)->GetDrawObject(); drawObjDiff = drawObjAddr != DrawObjectAddress; DrawObjectAddress = drawObjAddr; - - IsBeingDrawn = DrawObjectAddress == IntPtr.Zero || (((CharacterBase*)DrawObjectAddress)->HasModelInSlotLoaded != 0) - || (((CharacterBase*)DrawObjectAddress)->HasModelFilesInSlotLoaded != 0) - || (((GameObject*)curPtr)->RenderFlags & 0b100000000000) == 0b100000000000; } } catch (Exception ex) { var name = new ByteString(((Character*)curPtr)->GameObject.Name).ToString(); - _logger.LogError(ex, "Error during checking for draw object for {name}", this); - if (curPtr != IntPtr.Zero) - { - IsBeingDrawn = true; - } } if (curPtr != IntPtr.Zero && DrawObjectAddress != IntPtr.Zero) @@ -206,7 +234,7 @@ public class GameObjectHandler : MediatorSubscriberBase private unsafe bool CompareAndUpdateCustomizeData(byte* customizeData) { bool hasChanges = false; - + for (int i = 0; i < CustomizeData.Length; i++) { var data = Marshal.ReadByte((IntPtr)customizeData, i); diff --git a/MareSynchronos/Utils/DalamudUtil.cs b/MareSynchronos/Utils/DalamudUtil.cs index 4f858c6..ea9c90c 100644 --- a/MareSynchronos/Utils/DalamudUtil.cs +++ b/MareSynchronos/Utils/DalamudUtil.cs @@ -262,7 +262,7 @@ public class DalamudUtil : IDisposable return await _framework.RunOnFrameworkThread(func).ConfigureAwait(false); } - public unsafe void WaitWhileCharacterIsDrawing(ILogger logger, GameObjectHandler handler, Guid redrawId, int timeOut = 5000, CancellationToken? ct = null) + public async Task WaitWhileCharacterIsDrawing(ILogger logger, GameObjectHandler handler, Guid redrawId, int timeOut = 5000, CancellationToken? ct = null) { if (!_clientState.IsLoggedIn || handler.Address == IntPtr.Zero) return; @@ -275,7 +275,7 @@ public class DalamudUtil : IDisposable // ReSharper disable once LoopVariableIsNeverChangedInsideLoop while ((!ct?.IsCancellationRequested ?? true) && curWaitTime < timeOut - && handler.IsBeingDrawn) // 0b100000000000 is "still rendering" or something + && await handler.IsBeingDrawn().ConfigureAwait(false)) // 0b100000000000 is "still rendering" or something { logger.LogTrace($"[{redrawId}] Waiting for {handler} to finish drawing"); curWaitTime += tick;