diff --git a/MareSynchronos/FileCache/TransientResourceManager.cs b/MareSynchronos/FileCache/TransientResourceManager.cs index d560545..733755f 100644 --- a/MareSynchronos/FileCache/TransientResourceManager.cs +++ b/MareSynchronos/FileCache/TransientResourceManager.cs @@ -169,7 +169,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase private void DalamudUtil_FrameworkUpdate() { - _cachedFrameAddresses = _playerRelatedPointers.Select(c => c.CurrentAddress().GetAwaiter().GetResult()).ToHashSet(); + _cachedFrameAddresses = _playerRelatedPointers.Select(c => c.CurrentAddress()).ToHashSet(); _cachedHandledPaths.Clear(); foreach (var item in TransientResources.Where(item => !_dalamudUtil.IsGameObjectPresent(item.Key)).Select(i => i.Key).ToList()) { diff --git a/MareSynchronos/Interop/IpcManager.cs b/MareSynchronos/Interop/IpcManager.cs index 54d49b2..04fffea 100644 --- a/MareSynchronos/Interop/IpcManager.cs +++ b/MareSynchronos/Interop/IpcManager.cs @@ -24,7 +24,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase private readonly ICallGateSubscriber _customizePlusSetBodyScaleToCharacter; private readonly DalamudUtilService _dalamudUtil; private readonly ICallGateSubscriber _glamourerApiVersion; - private readonly SemaphoreSlim _glamourerApplicationSemaphore = new(2); + private readonly SemaphoreSlim _redrawSemaphore = new(2); private readonly ICallGateSubscriber? _glamourerApplyAll; private readonly ICallGateSubscriber? _glamourerApplyOnlyCustomization; private readonly ICallGateSubscriber? _glamourerApplyOnlyEquipment; @@ -167,10 +167,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase public bool CheckPenumbraApi() => _penumbraAvailable; - public async Task CustomizePlusRevert(IntPtr character) + public async Task CustomizePlusRevertAsync(IntPtr character) { if (!CheckCustomizePlusApi()) return; - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is Character c) { Logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X")); @@ -178,10 +178,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task CustomizePlusSetBodyScale(IntPtr character, string scale) + public async Task CustomizePlusSetBodyScaleAsync(IntPtr character, string scale) { if (!CheckCustomizePlusApi() || string.IsNullOrEmpty(scale)) return; - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is Character c) { string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale)); @@ -190,7 +190,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task GetCustomizePlusScale() + public async Task GetCustomizePlusScaleAsync() { if (!CheckCustomizePlusApi()) return string.Empty; var scale = await _dalamudUtil.RunOnFrameworkThread(() => _customizePlusGetBodyScale.InvokeFunc(_dalamudUtil.PlayerName)).ConfigureAwait(false); @@ -204,61 +204,61 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase return _heelsGetOffset.InvokeFunc(); } - public async Task GlamourerApplyAll(ILogger logger, GameObjectHandler handler, string? customization, Guid applicationId, CancellationToken token, bool fireAndForget = false) + public async Task GlamourerApplyAllAsync(ILogger logger, GameObjectHandler handler, string? customization, Guid applicationId, CancellationToken token, bool fireAndForget = false) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization) || _dalamudUtil.IsZoning) return; try { - await _glamourerApplicationSemaphore.WaitAsync(token).ConfigureAwait(false); - var gameObj = await _dalamudUtil.CreateGameObject(handler.Address).ConfigureAwait(false); + await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(handler.Address).ConfigureAwait(false); if (gameObj is Character c) { await PenumbraRedrawAsync(logger, handler, applicationId, () => { logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyAll", applicationId); _glamourerApplyAll!.InvokeAction(customization, c); - }, fireAndForget).ConfigureAwait(false); + }).ConfigureAwait(false); } } finally { - _glamourerApplicationSemaphore.Release(); + _redrawSemaphore.Release(); } } - public async Task GlamourerApplyCustomizationAndEquipment(ILogger logger, GameObjectHandler handler, string customization, string equipment, Guid applicationid, CancellationToken token, bool fireAndForget = false) + public async Task GlamourerApplyCustomizationAndEquipmentAsync(ILogger logger, GameObjectHandler handler, string customization, string equipment, Guid applicationid, CancellationToken token, bool fireAndForget = false) { if (!CheckGlamourerApi() || string.IsNullOrEmpty(customization) || _dalamudUtil.IsZoning) return; try { - await _glamourerApplicationSemaphore.WaitAsync(token).ConfigureAwait(false); - var gameObj = await _dalamudUtil.CreateGameObject(handler.Address).ConfigureAwait(false); + await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(handler.Address).ConfigureAwait(false); if (gameObj is Character c) { await PenumbraRedrawAsync(logger, handler, applicationid, () => { logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyCustomization", applicationid); _glamourerApplyOnlyCustomization!.InvokeAction(customization, c); - }, fireAndForget).ConfigureAwait(false); + }).ConfigureAwait(false); await PenumbraRedrawAsync(logger, handler, applicationid, () => { logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyEquipment", applicationid); _glamourerApplyOnlyEquipment!.InvokeAction(equipment, c); - }, fireAndForget).ConfigureAwait(false); + }).ConfigureAwait(false); } } finally { - _glamourerApplicationSemaphore.Release(); + _redrawSemaphore.Release(); } } - public async Task GlamourerGetCharacterCustomization(IntPtr character) + public async Task GlamourerGetCharacterCustomizationAsync(IntPtr character) { if (!CheckGlamourerApi()) return string.Empty; try { - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is Character c) { var glamourerString = await _dalamudUtil.RunOnFrameworkThread(() => _glamourerGetAllCustomization!.InvokeFunc(c)).ConfigureAwait(false); @@ -276,10 +276,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task HeelsRestoreOffsetForPlayer(IntPtr character) + public async Task HeelsRestoreOffsetForPlayerAsync(IntPtr character) { if (!CheckHeelsApi()) return; - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj != null) { Logger.LogTrace("Restoring Heels data to {chara}", character.ToString("X")); @@ -287,10 +287,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task HeelsSetOffsetForPlayer(IntPtr character, float offset) + public async Task HeelsSetOffsetForPlayerAsync(IntPtr character, float offset) { if (!CheckHeelsApi()) return; - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj != null) { Logger.LogTrace("Applying Heels data to {chara}", character.ToString("X")); @@ -298,10 +298,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task HonorificClearTitle(nint character) + public async Task HonorificClearTitleAsync(nint character) { if (!CheckHonorificApi()) return; - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is PlayerCharacter c) { Logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X")); @@ -316,11 +316,11 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase return string.IsNullOrEmpty(title) ? string.Empty : $"{(isPrefix ? 1 : 0)}{title}"; } - public async Task HonorificSetTitle(IntPtr character, string honorificData) + public async Task HonorificSetTitleAsync(IntPtr character, string honorificData) { if (!CheckHonorificApi()) return; Logger.LogTrace("Applying Honorific data to {chara}", character.ToString("X")); - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is PlayerCharacter pc) { await _dalamudUtil.RunOnFrameworkThread(() => @@ -337,7 +337,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task PalettePlusBuildPalette() + public async Task PalettePlusBuildPaletteAsync() { if (!CheckPalettePlusApi()) return string.Empty; var palette = await _dalamudUtil.RunOnFrameworkThread(() => _palettePlusBuildCharaPalette.InvokeFunc(_dalamudUtil.PlayerCharacter)).ConfigureAwait(false); @@ -345,10 +345,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase return Convert.ToBase64String(Encoding.UTF8.GetBytes(palette)); } - public async Task PalettePlusRemovePalette(IntPtr character) + public async Task PalettePlusRemovePaletteAsync(IntPtr character) { if (!CheckPalettePlusApi()) return; - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is Character c) { Logger.LogTrace("PalettePlus removing for {addr}", c.Address.ToString("X")); @@ -356,11 +356,11 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase } } - public async Task PalettePlusSetPalette(IntPtr character, string palette) + public async Task PalettePlusSetPaletteAsync(IntPtr character, string palette) { if (!CheckPalettePlusApi()) return; string decodedPalette = Encoding.UTF8.GetString(Convert.FromBase64String(palette)); - var gameObj = await _dalamudUtil.CreateGameObject(character).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); if (gameObj is Character c) { await _dalamudUtil.RunOnFrameworkThread(() => @@ -385,29 +385,29 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase return _penumbraGetMetaManipulations.Invoke(); } - public async Task PenumbraRedraw(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token, bool fireAndForget = false) + public async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token, bool fireAndForget = false) { if (!CheckPenumbraApi() || _dalamudUtil.IsZoning) return; try { - await _glamourerApplicationSemaphore.WaitAsync(token).ConfigureAwait(false); - var gameObj = await _dalamudUtil.CreateGameObject(handler.Address).ConfigureAwait(false); + await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false); + var gameObj = await _dalamudUtil.CreateGameObjectAsync(handler.Address).ConfigureAwait(false); if (gameObj is Character c) { await PenumbraRedrawAsync(logger, handler, applicationId, () => { logger.LogDebug("[{appid}] Calling on IPC: PenumbraRedraw", applicationId); _penumbraRedrawObject!.Invoke(c, RedrawType.Redraw); - }, fireAndForget).ConfigureAwait(false); + }).ConfigureAwait(false); } } finally { - _glamourerApplicationSemaphore.Release(); + _redrawSemaphore.Release(); } } - public async Task PenumbraRemoveTemporaryCollection(ILogger logger, Guid applicationId, string characterName) + public async Task PenumbraRemoveTemporaryCollectionAsync(ILogger logger, Guid applicationId, string characterName) { if (!CheckPenumbraApi()) return; await _dalamudUtil.RunOnFrameworkThread(() => @@ -421,12 +421,12 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase }).ConfigureAwait(false); } - public async Task<(string[] forward, string[][] reverse)> PenumbraResolvePaths(string[] forward, string[] reverse) + public async Task<(string[] forward, string[][] reverse)> PenumbraResolvePathsAsync(string[] forward, string[] reverse) { return await _dalamudUtil.RunOnFrameworkThread(() => _penumbraResolvePaths.Invoke(forward, reverse)).ConfigureAwait(false); } - public async Task PenumbraSetTemporaryMods(ILogger logger, Guid applicationId, string characterName, int? idx, Dictionary modPaths, string manipulationData) + public async Task PenumbraSetTemporaryModsAsync(ILogger logger, Guid applicationId, string characterName, int? idx, Dictionary modPaths, string manipulationData) { if (!CheckPenumbraApi() || idx == null) return; @@ -650,35 +650,22 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase _penumbraRedraw!.Invoke("self", RedrawType.Redraw); } - private async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler obj, Guid applicationId, Action action, bool fireAndForget) + private async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler obj, Guid applicationId, Action action) { Mediator.Publish(new PenumbraStartRedrawMessage(obj.Address)); - _penumbraRedrawRequests[obj.Address] = !fireAndForget; + _penumbraRedrawRequests[obj.Address] = true; try { - if (!fireAndForget) - { - while (!await _dalamudUtil.RunOnFrameworkThread(() => - { - if (obj.IsBeingDrawn()) return false; - action(); - return true; - }).ConfigureAwait(false)) - { - await Task.Delay(250).ConfigureAwait(false); - } + CancellationTokenSource cancelToken = new CancellationTokenSource(); + cancelToken.CancelAfter(TimeSpan.FromSeconds(15)); + await obj.ActOnFrameworkAfterEnsureNoDrawAsync(action, cancelToken.Token); - await Task.Delay(TimeSpan.FromSeconds(1), _disposalCts.Token).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromSeconds(1), _disposalCts.Token).ConfigureAwait(false); - if (!_disposalCts.Token.IsCancellationRequested) - await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, obj, applicationId, 30000, _disposalCts.Token).ConfigureAwait(false); - } - else - { - _ = _dalamudUtil.RunOnFrameworkThread(action); - } + if (!_disposalCts.Token.IsCancellationRequested) + await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, obj, applicationId, 30000, _disposalCts.Token).ConfigureAwait(false); } finally { diff --git a/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs b/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs index f950a7c..8709464 100644 --- a/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs +++ b/MareSynchronos/PlayerData/Export/MareCharaFileManager.cs @@ -65,14 +65,14 @@ public class MareCharaFileManager } var applicationId = Guid.NewGuid(); _ipcManager.ToggleGposeQueueMode(on: true); - await _ipcManager.PenumbraRemoveTemporaryCollection(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false); - await _ipcManager.PenumbraSetTemporaryMods(_logger, applicationId, charaTarget.Name.TextValue, charaTarget.ObjectTableIndex(), + await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false); + await _ipcManager.PenumbraSetTemporaryModsAsync(_logger, applicationId, charaTarget.Name.TextValue, charaTarget.ObjectTableIndex(), extractedFiles.Union(fileSwaps).ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal), LoadedCharaFile.CharaFileData.ManipulationData).ConfigureAwait(false); using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => charaTarget.Address, false).ConfigureAwait(false); - await _ipcManager.GlamourerApplyAll(_logger, tempHandler, LoadedCharaFile.CharaFileData.GlamourerData, applicationId, disposeCts.Token).ConfigureAwait(false); + await _ipcManager.GlamourerApplyAllAsync(_logger, tempHandler, LoadedCharaFile.CharaFileData.GlamourerData, applicationId, disposeCts.Token).ConfigureAwait(false); _dalamudUtil.WaitWhileGposeCharacterIsDrawing(charaTarget.Address, 30000); - await _ipcManager.PenumbraRemoveTemporaryCollection(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false); + await _ipcManager.PenumbraRemoveTemporaryCollectionAsync(_logger, applicationId, charaTarget.Name.TextValue).ConfigureAwait(false); _ipcManager.ToggleGposeQueueMode(on: false); } } diff --git a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs index 701927d..cf5d0cb 100644 --- a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs @@ -316,7 +316,7 @@ public class PlayerDataFactory // wait until chara is not drawing and present so nothing spontaneously explodes await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, playerRelatedObject, Guid.NewGuid(), 30000, ct: token).ConfigureAwait(false); int totalWaitTime = 10000; - while (!DalamudUtilService.IsObjectPresent(await _dalamudUtil.RunOnFrameworkThread(() => _dalamudUtil.CreateGameObject(charaPointer).GetAwaiter().GetResult()).ConfigureAwait(false)) && totalWaitTime > 0) + while (!DalamudUtilService.IsObjectPresent(await _dalamudUtil.RunOnFrameworkThread(() => _dalamudUtil.CreateGameObjectAsync(charaPointer).GetAwaiter().GetResult()).ConfigureAwait(false)) && totalWaitTime > 0) { _logger.LogTrace("Character is null but it shouldn't be, waiting"); await Task.Delay(50, token).ConfigureAwait(false); @@ -376,9 +376,9 @@ public class PlayerDataFactory // gather up data from ipc previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); previousData.HeelsOffset = _ipcManager.GetHeelsOffset(); - Task getGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(playerRelatedObject.Address); - Task getCustomizeData = _ipcManager.GetCustomizePlusScale(); - Task getPalettePlusData = _ipcManager.PalettePlusBuildPalette(); + Task getGlamourerData = _ipcManager.GlamourerGetCharacterCustomizationAsync(playerRelatedObject.Address); + Task getCustomizeData = _ipcManager.GetCustomizePlusScaleAsync(); + Task getPalettePlusData = _ipcManager.PalettePlusBuildPaletteAsync(); previousData.GlamourerString[playerRelatedObject.ObjectKind] = await getGlamourerData.ConfigureAwait(false); _logger.LogDebug("Glamourer is now: {data}", previousData.GlamourerString[playerRelatedObject.ObjectKind]); previousData.CustomizePlusScale = await getCustomizeData.ConfigureAwait(false); @@ -399,7 +399,7 @@ public class PlayerDataFactory var forwardPaths = forwardResolve.ToArray(); var reversePaths = reverseResolve.ToArray(); Dictionary> resolvedPaths = new(StringComparer.Ordinal); - var (forward, reverse) = await _ipcManager.PenumbraResolvePaths(forwardPaths, reversePaths).ConfigureAwait(false); + var (forward, reverse) = await _ipcManager.PenumbraResolvePathsAsync(forwardPaths, reversePaths).ConfigureAwait(false); for (int i = 0; i < forwardPaths.Length; i++) { var filePath = forward[i].ToLowerInvariant(); diff --git a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs index 3b4cb72..108c33b 100644 --- a/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/GameObjectHandler.cs @@ -28,7 +28,11 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase _performanceCollector = performanceCollector; ObjectKind = objectKind; _dalamudUtil = dalamudUtil; - _getAddress = getAddress; + _getAddress = () => + { + _dalamudUtil.EnsureIsOnFramework(); + return getAddress.Invoke(); + }; _isOwnedObject = watchedObject; Name = string.Empty; @@ -82,29 +86,25 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase CheckAndUpdateObject(); } - public IntPtr Address { get; set; } - public unsafe Character* Character => (Character*)Address; + public IntPtr Address { get; private set; } public string Name { get; private set; } - public ObjectKind ObjectKind { get; } - private byte[] CustomizeData { get; set; } = new byte[26]; - private IntPtr DrawObjectAddress { get; set; } - private byte[] EquipSlotData { get; set; } = new byte[40]; - public async Task CurrentAddress() + public IntPtr CurrentAddress() { - return await _dalamudUtil.RunOnFrameworkThread(_getAddress.Invoke).ConfigureAwait(true); + _dalamudUtil.EnsureIsOnFramework(); + return _getAddress.Invoke(); } - public async Task GetGameObject() + public Dalamud.Game.ClientState.Objects.Types.GameObject? GetGameObject() { - return await _dalamudUtil.CreateGameObject(Address).ConfigureAwait(true); + return _dalamudUtil.CreateGameObject(Address); } - public bool IsBeingDrawn() + private bool IsBeingDrawn() { var curPtr = _getAddress(); Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, CurPtr: {ptr}", this, curPtr.ToString("X")); @@ -123,7 +123,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase return IsBeingDrawn(drawObj, curPtr); } - public async Task IsBeingDrawnRunOnFramework() + public async Task IsBeingDrawnRunOnFrameworkAsync() { return await _dalamudUtil.RunOnFrameworkThread(IsBeingDrawn).ConfigureAwait(false); } @@ -142,6 +142,19 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase Mediator.Publish(new RemoveWatchedGameObjectHandler(this)); } + public async Task ActOnFrameworkAfterEnsureNoDrawAsync(Action act, CancellationToken token) + { + while (await _dalamudUtil.RunOnFrameworkThread(() => + { + if (IsBeingDrawn()) return true; + act(); + return false; + }).ConfigureAwait(false)) + { + await Task.Delay(250, token).ConfigureAwait(false); + } + } + private unsafe void CheckAndUpdateObject() { if (_haltProcessing) return; @@ -200,12 +213,12 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase _clearCts?.Dispose(); _clearCts = new(); var token = _clearCts.Token; - _ = Task.Run(() => ClearTask(token), token); + _ = Task.Run(() => ClearAsync(token), token); } } } - private async Task ClearTask(CancellationToken token) + private async Task ClearAsync(CancellationToken token) { Logger.LogDebug("[{this}] Running Clear Task", this); await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false); diff --git a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs index c6ca751..3de3695 100644 --- a/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs +++ b/MareSynchronos/PlayerData/Pairs/CachedPlayer.cs @@ -14,8 +14,10 @@ using MareSynchronos.Utils; using MareSynchronos.WebAPI.Files; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using System; using System.Collections.Concurrent; using System.Diagnostics; +using static System.Net.Mime.MediaTypeNames; using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind; namespace MareSynchronos.PlayerData.Pairs; @@ -161,13 +163,13 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (_dalamudUtil is { IsZoning: false, IsInCutscene: false }) { Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser); - _ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, name).GetAwaiter().GetResult(); + _ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, name).GetAwaiter().GetResult(); foreach (KeyValuePair> item in _cachedData?.FileReplacements ?? new()) { try { - RevertCustomizationData(item.Key, name, applicationId).GetAwaiter().GetResult(); + RevertCustomizationDataAsync(item.Key, name, applicationId).GetAwaiter().GetResult(); } catch (InvalidOperationException ex) { @@ -200,19 +202,8 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase throw new InvalidOperationException("Player pointer is zero, pointer invalid"); } } - - private async Task ApplyBaseData(Guid applicationId, Dictionary moddedPaths, string manipulationData, CancellationToken token) - { - await _ipcManager.PenumbraRemoveTemporaryCollection(Logger, applicationId, PlayerName!).ConfigureAwait(true); - token.ThrowIfCancellationRequested(); - var gameObj = await _charaHandler!.GetGameObject().ConfigureAwait(true); - if (gameObj == null) return; - var objTableIndex = await _dalamudUtil.RunOnFrameworkThread(() => gameObj.ObjectTableIndex()).ConfigureAwait(true); - await _ipcManager.PenumbraSetTemporaryMods(Logger, applicationId, PlayerName!, objTableIndex, moddedPaths, manipulationData).ConfigureAwait(true); - token.ThrowIfCancellationRequested(); - } - - private async Task ApplyCustomizationData(Guid applicationId, KeyValuePair> changes, CharacterData charaData, CancellationToken token) + + private async Task ApplyCustomizationDataAsync(Guid applicationId, KeyValuePair> changes, CharacterData charaData, CancellationToken token) { if (PlayerCharacter == IntPtr.Zero) return; var ptr = PlayerCharacter; @@ -242,29 +233,29 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase switch (change) { case PlayerChanges.Palette: - await _ipcManager.PalettePlusSetPalette(handler.Address, charaData.PalettePlusData).ConfigureAwait(false); + await _ipcManager.PalettePlusSetPaletteAsync(handler.Address, charaData.PalettePlusData).ConfigureAwait(false); break; case PlayerChanges.Customize: - await _ipcManager.CustomizePlusSetBodyScale(handler.Address, charaData.CustomizePlusData).ConfigureAwait(false); + await _ipcManager.CustomizePlusSetBodyScaleAsync(handler.Address, charaData.CustomizePlusData).ConfigureAwait(false); break; case PlayerChanges.Heels: - await _ipcManager.HeelsSetOffsetForPlayer(handler.Address, charaData.HeelsOffset).ConfigureAwait(false); + await _ipcManager.HeelsSetOffsetForPlayerAsync(handler.Address, charaData.HeelsOffset).ConfigureAwait(false); break; case PlayerChanges.Honorific: - await _ipcManager.HonorificSetTitle(handler.Address, charaData.HonorificData).ConfigureAwait(false); + await _ipcManager.HonorificSetTitleAsync(handler.Address, charaData.HonorificData).ConfigureAwait(false); break; case PlayerChanges.Mods: if (charaData.GlamourerData.TryGetValue(changes.Key, out var glamourerData)) { - await _ipcManager.GlamourerApplyAll(Logger, handler, glamourerData, applicationId, token).ConfigureAwait(false); + await _ipcManager.GlamourerApplyAllAsync(Logger, handler, glamourerData, applicationId, token).ConfigureAwait(false); } else { - await _ipcManager.PenumbraRedraw(Logger, handler, applicationId, token).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); } break; } @@ -452,26 +443,29 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase token.ThrowIfCancellationRequested(); - var applyBaseData = new Action(() => ApplyBaseData(_applicationId, moddedPaths, charaData.ManipulationData, token).ConfigureAwait(true).GetAwaiter().GetResult()); - if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData))) { - while (!await _dalamudUtil.RunOnFrameworkThread(() => + await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync(() => _ipcManager + .PenumbraRemoveTemporaryCollectionAsync(Logger, _applicationId, PlayerName!) + .ConfigureAwait(true).GetAwaiter().GetResult(), token); + token.ThrowIfCancellationRequested(); + + await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync(() => { - if (_charaHandler!.IsBeingDrawn()) return false; - applyBaseData(); - return true; - }).ConfigureAwait(false)) - { - await Task.Delay(250, token).ConfigureAwait(false); - } + var gameObj = _charaHandler!.GetGameObject(); + if (gameObj == null) return; + var objTableIndex = gameObj.ObjectTableIndex(); + _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, PlayerName!, objTableIndex, moddedPaths, charaData.ManipulationData) + .ConfigureAwait(true).GetAwaiter().GetResult(); + }, token); + token.ThrowIfCancellationRequested(); } token.ThrowIfCancellationRequested(); foreach (var kind in updatedData) { - await ApplyCustomizationData(_applicationId, kind, charaData, token).ConfigureAwait(false); + await ApplyCustomizationDataAsync(_applicationId, kind, charaData, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); } @@ -506,7 +500,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase { Task.Run(async () => { - _lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false); + _lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomizationAsync(PlayerCharacter).ConfigureAwait(false); ApplyCharacterData(_cachedData, true); }); } @@ -527,7 +521,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase PlayerName = name; _charaHandler = _gameObjectHandlerFactory.Create(ObjectKind.Player, () => _dalamudUtil.GetPlayerCharacterFromObjectTableByIdent(OnlineUser.Ident), false).GetAwaiter().GetResult(); - _originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false).GetAwaiter().GetResult(); + _originalGlamourerData = _ipcManager.GlamourerGetCharacterCustomizationAsync(PlayerCharacter).ConfigureAwait(false).GetAwaiter().GetResult(); _lastGlamourerData = _originalGlamourerData; Mediator.Subscribe(this, IpcManagerOnPenumbraRedrawEvent); Mediator.Subscribe(this, async (msg) => @@ -535,7 +529,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (msg.GameObjectHandler == _charaHandler && (_applicationTask?.IsCompleted ?? true)) { Logger.LogTrace("Saving new Glamourer Data for {this}", this); - _lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomization(PlayerCharacter).ConfigureAwait(false); + _lastGlamourerData = await _ipcManager.GlamourerGetCharacterCustomizationAsync(PlayerCharacter).ConfigureAwait(false); } }); @@ -557,7 +551,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase Logger.LogDebug("Unauthorized character change detected"); if (_cachedData != null) { - await ApplyCustomizationData(applicationId, new(ObjectKind.Player, + await ApplyCustomizationDataAsync(applicationId, new(ObjectKind.Player, new HashSet(new[] { PlayerChanges.Palette, PlayerChanges.Customize, PlayerChanges.Heels, PlayerChanges.Mods })), _cachedData, token).ConfigureAwait(false); } @@ -598,7 +592,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase } } - private async Task RevertCustomizationData(ObjectKind objectKind, string name, Guid applicationId) + private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId) { nint address = _dalamudUtil.GetPlayerCharacterFromObjectTableByIdent(OnlineUser.Ident); if (address == IntPtr.Zero) return; @@ -613,19 +607,19 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, false).ConfigureAwait(false); CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}: {data}", applicationId, OnlineUser.User.AliasOrUID, name, _originalGlamourerData); - await _ipcManager.GlamourerApplyCustomizationAndEquipment(Logger, tempHandler, _originalGlamourerData, _lastGlamourerData, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); + await _ipcManager.GlamourerApplyCustomizationAndEquipmentAsync(Logger, tempHandler, _originalGlamourerData, _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); + await _ipcManager.HeelsRestoreOffsetForPlayerAsync(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); + await _ipcManager.CustomizePlusRevertAsync(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); + await _ipcManager.PalettePlusRemovePaletteAsync(address).ConfigureAwait(false); CheckForNameAndThrow(tempHandler, name); Logger.LogDebug("[{applicationId}] Restoring Honorific for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); - await _ipcManager.HonorificClearTitle(address).ConfigureAwait(false); + await _ipcManager.HonorificClearTitleAsync(address).ConfigureAwait(false); } else if (objectKind == ObjectKind.MinionOrMount) { @@ -633,7 +627,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (minionOrMount != IntPtr.Zero) { using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, false).ConfigureAwait(false); - await _ipcManager.PenumbraRedraw(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); } } else if (objectKind == ObjectKind.Pet) @@ -642,7 +636,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (pet != IntPtr.Zero) { using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, false).ConfigureAwait(false); - await _ipcManager.PenumbraRedraw(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); } } else if (objectKind == ObjectKind.Companion) @@ -651,7 +645,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase if (companion != IntPtr.Zero) { using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, false).ConfigureAwait(false); - await _ipcManager.PenumbraRedraw(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); + await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false); } } } diff --git a/MareSynchronos/Services/DalamudUtilService.cs b/MareSynchronos/Services/DalamudUtilService.cs index 02fa5d4..4cb1080 100644 --- a/MareSynchronos/Services/DalamudUtilService.cs +++ b/MareSynchronos/Services/DalamudUtilService.cs @@ -76,11 +76,22 @@ public class DalamudUtilService : IHostedService return obj != null && obj.IsValid(); } - public async Task CreateGameObject(IntPtr reference) + public async Task CreateGameObjectAsync(IntPtr reference) { return await RunOnFrameworkThread(() => _objectTable.CreateObjectReference(reference)).ConfigureAwait(false); } + public void EnsureIsOnFramework() + { + if (!_framework.IsInFrameworkUpdateThread) throw new InvalidOperationException("Can only be run on Framework"); + } + + public Dalamud.Game.ClientState.Objects.Types.GameObject? CreateGameObject(IntPtr reference) + { + EnsureIsOnFramework(); + return _objectTable.CreateObjectReference(reference); + } + public Dalamud.Game.ClientState.Objects.Types.Character? GetCharacterFromObjectTableByIndex(int index) { var objTableObj = _objectTable[index]; @@ -176,7 +187,7 @@ public class DalamudUtilService : IHostedService { while ((!ct?.IsCancellationRequested ?? true) && curWaitTime < timeOut - && await handler.IsBeingDrawnRunOnFramework().ConfigureAwait(false)) // 0b100000000000 is "still rendering" or something + && await handler.IsBeingDrawnRunOnFrameworkAsync().ConfigureAwait(false)) // 0b100000000000 is "still rendering" or something { logger.LogTrace("[{redrawId}] Waiting for {handler} to finish drawing", redrawId, handler); curWaitTime += tick; diff --git a/MareSynchronos/UI/DownloadUi.cs b/MareSynchronos/UI/DownloadUi.cs index e6928c9..227765a 100644 --- a/MareSynchronos/UI/DownloadUi.cs +++ b/MareSynchronos/UI/DownloadUi.cs @@ -139,7 +139,7 @@ public class DownloadUi : WindowMediatorSubscriberBase foreach (var transfer in _currentDownloads.ToList()) { - var screenPos = _dalamudUtilService.WorldToScreen(transfer.Key.GetGameObject().ConfigureAwait(true).GetAwaiter().GetResult()); + var screenPos = _dalamudUtilService.WorldToScreen(transfer.Key.GetGameObject()); if (screenPos == Vector2.Zero) continue; var totalBytes = transfer.Value.Sum(c => c.Value.TotalBytes); @@ -182,7 +182,7 @@ public class DownloadUi : WindowMediatorSubscriberBase { foreach (var player in _uploadingPlayers.Select(p => p.Key).ToList()) { - var screenPos = _dalamudUtilService.WorldToScreen(player.GetGameObject().GetAwaiter().GetResult()); + var screenPos = _dalamudUtilService.WorldToScreen(player.GetGameObject()); if (screenPos == Vector2.Zero) continue; try