various fixes and improvements for drawing checks and data sending
This commit is contained in:
@@ -185,9 +185,9 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logger.LogDebug("Penumbra Mod Settings changed, verifying SemiTransientResources");
|
||||
foreach (var item in SemiTransientResources)
|
||||
foreach (var item in _playerRelatedPointers)
|
||||
{
|
||||
Mediator.Publish(new TransientResourceChangedMessage(_dalamudUtil.GetPlayerPointer()));
|
||||
Mediator.Publish(new TransientResourceChangedMessage(item.Address));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -170,24 +170,30 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
public async Task CustomizePlusRevertAsync(IntPtr character)
|
||||
{
|
||||
if (!CheckCustomizePlusApi()) return;
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
{
|
||||
Logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X"));
|
||||
await _dalamudUtil.RunOnFrameworkThread(() => _customizePlusRevert!.InvokeAction(c)).ConfigureAwait(false);
|
||||
_customizePlusRevert!.InvokeAction(c);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task CustomizePlusSetBodyScaleAsync(IntPtr character, string scale)
|
||||
{
|
||||
if (!CheckCustomizePlusApi() || string.IsNullOrEmpty(scale)) return;
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
{
|
||||
string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale));
|
||||
Logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X"));
|
||||
await _dalamudUtil.RunOnFrameworkThread(() => _customizePlusSetBodyScaleToCharacter!.InvokeAction(decodedScale, c)).ConfigureAwait(false);
|
||||
_customizePlusSetBodyScaleToCharacter!.InvokeAction(decodedScale, c);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<string> GetCustomizePlusScaleAsync()
|
||||
@@ -198,10 +204,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(scale));
|
||||
}
|
||||
|
||||
public float GetHeelsOffset()
|
||||
public async Task<float> GetHeelsOffsetAsync()
|
||||
{
|
||||
if (!CheckHeelsApi()) return 0.0f;
|
||||
return _heelsGetOffset.InvokeFunc();
|
||||
return await _dalamudUtil.RunOnFrameworkThread(_heelsGetOffset.InvokeFunc).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task GlamourerApplyAllAsync(ILogger logger, GameObjectHandler handler, string? customization, Guid applicationId, CancellationToken token, bool fireAndForget = false)
|
||||
@@ -210,16 +216,13 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
try
|
||||
{
|
||||
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, () =>
|
||||
|
||||
await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
|
||||
{
|
||||
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyAll", applicationId);
|
||||
_glamourerApplyAll!.InvokeAction(customization, c);
|
||||
_glamourerApplyAll!.InvokeAction(customization, chara);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_redrawSemaphore.Release();
|
||||
@@ -232,21 +235,17 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
try
|
||||
{
|
||||
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, () =>
|
||||
await PenumbraRedrawInternalAsync(logger, handler, applicationid, (chara) =>
|
||||
{
|
||||
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyCustomization", applicationid);
|
||||
_glamourerApplyOnlyCustomization!.InvokeAction(customization, c);
|
||||
_glamourerApplyOnlyCustomization!.InvokeAction(customization, chara);
|
||||
}).ConfigureAwait(false);
|
||||
await PenumbraRedrawAsync(logger, handler, applicationid, () =>
|
||||
await PenumbraRedrawInternalAsync(logger, handler, applicationid, (chara) =>
|
||||
{
|
||||
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyEquipment", applicationid);
|
||||
_glamourerApplyOnlyEquipment!.InvokeAction(equipment, c);
|
||||
_glamourerApplyOnlyEquipment!.InvokeAction(equipment, chara);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_redrawSemaphore.Release();
|
||||
@@ -258,10 +257,12 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
if (!CheckGlamourerApi()) return string.Empty;
|
||||
try
|
||||
{
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
{
|
||||
var glamourerString = await _dalamudUtil.RunOnFrameworkThread(() => _glamourerGetAllCustomization!.InvokeFunc(c)).ConfigureAwait(false);
|
||||
var glamourerString = _glamourerGetAllCustomization!.InvokeFunc(c);
|
||||
byte[] bytes = Convert.FromBase64String(glamourerString);
|
||||
// ignore transparency
|
||||
bytes[88] = 128;
|
||||
@@ -269,6 +270,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
return Convert.ToBase64String(bytes);
|
||||
}
|
||||
return string.Empty;
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -279,34 +281,43 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
public async Task HeelsRestoreOffsetForPlayerAsync(IntPtr character)
|
||||
{
|
||||
if (!CheckHeelsApi()) return;
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj != null)
|
||||
{
|
||||
Logger.LogTrace("Restoring Heels data to {chara}", character.ToString("X"));
|
||||
await _dalamudUtil.RunOnFrameworkThread(() => _heelsUnregisterPlayer.InvokeAction(gameObj)).ConfigureAwait(false);
|
||||
_heelsUnregisterPlayer.InvokeAction(gameObj);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task HeelsSetOffsetForPlayerAsync(IntPtr character, float offset)
|
||||
{
|
||||
if (!CheckHeelsApi()) return;
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj != null)
|
||||
{
|
||||
Logger.LogTrace("Applying Heels data to {chara}", character.ToString("X"));
|
||||
await _dalamudUtil.RunOnFrameworkThread(() => _heelsRegisterPlayer.InvokeAction(gameObj, offset)).ConfigureAwait(false);
|
||||
_heelsRegisterPlayer.InvokeAction(gameObj, offset);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task HonorificClearTitleAsync(nint character)
|
||||
{
|
||||
if (!CheckHonorificApi()) return;
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is PlayerCharacter c)
|
||||
{
|
||||
Logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X"));
|
||||
await _dalamudUtil.RunOnFrameworkThread(() => _honorificClearCharacterTitle!.InvokeAction(c)).ConfigureAwait(false);
|
||||
_honorificClearCharacterTitle!.InvokeAction(c);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public string HonorificGetTitle()
|
||||
@@ -320,10 +331,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
{
|
||||
if (!CheckHonorificApi()) return;
|
||||
Logger.LogTrace("Applying Honorific data to {chara}", character.ToString("X"));
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
if (gameObj is PlayerCharacter pc)
|
||||
{
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is PlayerCharacter pc)
|
||||
{
|
||||
if (string.IsNullOrEmpty(honorificData))
|
||||
{
|
||||
@@ -333,8 +344,8 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
{
|
||||
_honorificSetCharacterTitle!.InvokeAction(pc, honorificData[1..], honorificData[0] == '1');
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<string> PalettePlusBuildPaletteAsync()
|
||||
@@ -348,22 +359,25 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
public async Task PalettePlusRemovePaletteAsync(IntPtr character)
|
||||
{
|
||||
if (!CheckPalettePlusApi()) return;
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
{
|
||||
Logger.LogTrace("PalettePlus removing for {addr}", c.Address.ToString("X"));
|
||||
await _dalamudUtil.RunOnFrameworkThread(() => _palettePlusRemoveCharaPalette!.InvokeAction(c)).ConfigureAwait(false);
|
||||
_palettePlusRemoveCharaPalette!.InvokeAction(c);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task PalettePlusSetPaletteAsync(IntPtr character, string palette)
|
||||
{
|
||||
if (!CheckPalettePlusApi()) return;
|
||||
string decodedPalette = Encoding.UTF8.GetString(Convert.FromBase64String(palette));
|
||||
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false);
|
||||
if (gameObj is Character c)
|
||||
{
|
||||
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
var gameObj = _dalamudUtil.CreateGameObject(character);
|
||||
if (gameObj is Character c)
|
||||
{
|
||||
if (string.IsNullOrEmpty(decodedPalette))
|
||||
{
|
||||
@@ -375,8 +389,8 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
Logger.LogTrace("PalettePlus applying for {addr}", c.Address.ToString("X"));
|
||||
_palettePlusSetCharaPalette!.InvokeAction(c, decodedPalette);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public string PenumbraGetMetaManipulations()
|
||||
@@ -385,22 +399,18 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
return _penumbraGetMetaManipulations.Invoke();
|
||||
}
|
||||
|
||||
public async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token, bool fireAndForget = false)
|
||||
public async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token)
|
||||
{
|
||||
if (!CheckPenumbraApi() || _dalamudUtil.IsZoning) return;
|
||||
try
|
||||
{
|
||||
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, () =>
|
||||
await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
|
||||
{
|
||||
logger.LogDebug("[{appid}] Calling on IPC: PenumbraRedraw", applicationId);
|
||||
_penumbraRedrawObject!.Invoke(c, RedrawType.Redraw);
|
||||
_penumbraRedrawObject!.Invoke(chara, RedrawType.Redraw);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_redrawSemaphore.Release();
|
||||
@@ -442,6 +452,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value);
|
||||
}
|
||||
|
||||
logger.LogTrace("[{applicationId}] Manip: {data}", applicationId, manipulationData);
|
||||
var ret2 = _penumbraAddTemporaryMod.Invoke("MareChara", collName, modPaths, manipulationData, 0);
|
||||
logger.LogTrace("[{applicationId}] Setting temp mods for {collName}, Success: {ret2}", applicationId, collName, ret2);
|
||||
}).ConfigureAwait(false);
|
||||
@@ -650,29 +661,27 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
||||
_penumbraRedraw!.Invoke("self", RedrawType.Redraw);
|
||||
}
|
||||
|
||||
private async Task PenumbraRedrawAsync(ILogger logger, GameObjectHandler obj, Guid applicationId, Action action)
|
||||
private async Task PenumbraRedrawInternalAsync(ILogger logger, GameObjectHandler handler, Guid applicationId, Action<Character> action)
|
||||
{
|
||||
Mediator.Publish(new PenumbraStartRedrawMessage(obj.Address));
|
||||
Mediator.Publish(new PenumbraStartRedrawMessage(handler.Address));
|
||||
|
||||
_penumbraRedrawRequests[obj.Address] = true;
|
||||
_penumbraRedrawRequests[handler.Address] = true;
|
||||
|
||||
try
|
||||
{
|
||||
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 handler.ActOnFrameworkAfterEnsureNoDrawAsync(action, cancelToken.Token).ConfigureAwait(false);
|
||||
|
||||
if (!_disposalCts.Token.IsCancellationRequested)
|
||||
await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, obj, applicationId, 30000, _disposalCts.Token).ConfigureAwait(false);
|
||||
await _dalamudUtil.WaitWhileCharacterIsDrawing(logger, handler, applicationId, 30000, _disposalCts.Token).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_penumbraRedrawRequests[obj.Address] = false;
|
||||
_penumbraRedrawRequests[handler.Address] = false;
|
||||
}
|
||||
|
||||
Mediator.Publish(new PenumbraEndRedrawMessage(obj.Address));
|
||||
Mediator.Publish(new PenumbraEndRedrawMessage(handler.Address));
|
||||
}
|
||||
|
||||
private void PeriodicApiStateCheck()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<Authors></Authors>
|
||||
<Company></Company>
|
||||
<Version>0.8.32</Version>
|
||||
<Version>0.8.33</Version>
|
||||
<Description></Description>
|
||||
<Copyright></Copyright>
|
||||
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>
|
||||
|
||||
@@ -375,7 +375,7 @@ public class PlayerDataFactory
|
||||
|
||||
// gather up data from ipc
|
||||
previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations();
|
||||
previousData.HeelsOffset = _ipcManager.GetHeelsOffset();
|
||||
Task<float> getHeelsOffset = _ipcManager.GetHeelsOffsetAsync();
|
||||
Task<string> getGlamourerData = _ipcManager.GlamourerGetCharacterCustomizationAsync(playerRelatedObject.Address);
|
||||
Task<string> getCustomizeData = _ipcManager.GetCustomizePlusScaleAsync();
|
||||
Task<string> getPalettePlusData = _ipcManager.PalettePlusBuildPaletteAsync();
|
||||
@@ -387,6 +387,8 @@ public class PlayerDataFactory
|
||||
_logger.LogDebug("Palette is now: {data}", previousData.PalettePlusPalette);
|
||||
previousData.HonorificData = _ipcManager.HonorificGetTitle();
|
||||
_logger.LogDebug("Honorific is now: {data}", previousData.HonorificData);
|
||||
previousData.HeelsOffset = await getHeelsOffset.ConfigureAwait(false);
|
||||
_logger.LogDebug("Heels is now: {heels}", previousData.HeelsOffset);
|
||||
|
||||
st.Stop();
|
||||
_logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(st.ElapsedTicks).TotalMilliseconds);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using MareSynchronos.Services;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -61,6 +59,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
Mediator.Subscribe<CutsceneEndMessage>(this, (_) =>
|
||||
{
|
||||
_haltProcessing = false;
|
||||
ZoneSwitchEnd();
|
||||
});
|
||||
Mediator.Subscribe<PenumbraStartRedrawMessage>(this, (msg) =>
|
||||
{
|
||||
@@ -93,12 +92,16 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
private IntPtr DrawObjectAddress { get; set; }
|
||||
private byte[] EquipSlotData { get; set; } = new byte[40];
|
||||
|
||||
public async Task ActOnFrameworkAfterEnsureNoDrawAsync(Action act, CancellationToken token)
|
||||
public async Task ActOnFrameworkAfterEnsureNoDrawAsync(Action<Dalamud.Game.ClientState.Objects.Types.Character> act, CancellationToken token)
|
||||
{
|
||||
while (await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||
{
|
||||
if (IsBeingDrawn()) return true;
|
||||
act();
|
||||
var gameObj = _dalamudUtil.CreateGameObject(Address);
|
||||
if (gameObj is Dalamud.Game.ClientState.Objects.Types.Character chara)
|
||||
{
|
||||
act.Invoke(chara);
|
||||
}
|
||||
return false;
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
@@ -117,6 +120,12 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
return _dalamudUtil.CreateGameObject(Address);
|
||||
}
|
||||
|
||||
public void Invalidate()
|
||||
{
|
||||
Address = IntPtr.Zero;
|
||||
DrawObjectAddress = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public async Task<bool> IsBeingDrawnRunOnFrameworkAsync()
|
||||
{
|
||||
return await _dalamudUtil.RunOnFrameworkThread(IsBeingDrawn).ConfigureAwait(false);
|
||||
@@ -146,7 +155,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
Address = _getAddress();
|
||||
if (Address != IntPtr.Zero)
|
||||
{
|
||||
var drawObjAddr = (IntPtr)((GameObject*)Address)->DrawObject;
|
||||
var drawObjAddr = (IntPtr)((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address)->DrawObject;
|
||||
DrawObjectAddress = drawObjAddr;
|
||||
}
|
||||
else
|
||||
@@ -165,7 +174,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
_clearCts?.Cancel();
|
||||
_clearCts = null;
|
||||
}
|
||||
var chara = (Character*)Address;
|
||||
var chara = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)Address;
|
||||
var name = new ByteString(chara->GameObject.Name).ToString();
|
||||
bool nameChange = !string.Equals(name, Name, StringComparison.Ordinal);
|
||||
Name = name;
|
||||
@@ -259,11 +268,17 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
private unsafe IntPtr GetDrawObj(nint curPtr)
|
||||
{
|
||||
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Getting new DrawObject", this);
|
||||
return (IntPtr)((GameObject*)curPtr)->DrawObject;
|
||||
return (IntPtr)((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)curPtr)->DrawObject;
|
||||
}
|
||||
|
||||
private bool IsBeingDrawn()
|
||||
{
|
||||
if (_dalamudUtil.IsAnythingDrawing)
|
||||
{
|
||||
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Global draw block", this);
|
||||
return true;
|
||||
}
|
||||
|
||||
var curPtr = _getAddress();
|
||||
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, CurPtr: {ptr}", this, curPtr.ToString("X"));
|
||||
|
||||
@@ -289,7 +304,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
var drawObjZero = drawObj == IntPtr.Zero;
|
||||
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Condition IsDrawObjZero: {cond}", this, drawObjZero);
|
||||
if (drawObjZero) return true;
|
||||
var renderFlags = (((GameObject*)curPtr)->RenderFlags) != 0x0;
|
||||
var renderFlags = (((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)curPtr)->RenderFlags) != 0x0;
|
||||
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Condition RenderFlags: {cond}", this, renderFlags);
|
||||
if (renderFlags) return true;
|
||||
var modelInSlotLoaded = (((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0);
|
||||
@@ -303,7 +318,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
||||
}
|
||||
|
||||
return drawObj == IntPtr.Zero
|
||||
|| ((GameObject*)curPtr)->RenderFlags != 0x0;
|
||||
|| ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)curPtr)->RenderFlags != 0x0;
|
||||
}
|
||||
|
||||
private void ZoneSwitchEnd()
|
||||
|
||||
@@ -14,10 +14,8 @@ 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;
|
||||
@@ -55,6 +53,12 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
||||
_lifetime = lifetime;
|
||||
_fileDbManager = fileDbManager;
|
||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
||||
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
|
||||
{
|
||||
_charaHandler?.Invalidate();
|
||||
IsVisible = false;
|
||||
}
|
||||
);
|
||||
_pluginWarnings ??= new()
|
||||
{
|
||||
ShownCustomizePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
|
||||
@@ -445,16 +449,14 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
||||
|
||||
if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData)))
|
||||
{
|
||||
await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync(() => _ipcManager
|
||||
await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync((_) => _ipcManager
|
||||
.PenumbraRemoveTemporaryCollectionAsync(Logger, _applicationId, PlayerName!)
|
||||
.ConfigureAwait(true).GetAwaiter().GetResult(), token).ConfigureAwait(false);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync(() =>
|
||||
await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync((chara) =>
|
||||
{
|
||||
var gameObj = _charaHandler!.GetGameObject();
|
||||
if (gameObj == null) return;
|
||||
var objTableIndex = gameObj.ObjectTableIndex();
|
||||
var objTableIndex = chara.ObjectTableIndex();
|
||||
_ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, PlayerName!, objTableIndex, moddedPaths, charaData.ManipulationData)
|
||||
.ConfigureAwait(true).GetAwaiter().GetResult();
|
||||
}, token).ConfigureAwait(false);
|
||||
@@ -510,6 +512,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
||||
_framesSinceNotVisible++;
|
||||
if (_framesSinceNotVisible > 30)
|
||||
{
|
||||
_framesSinceNotVisible = 30;
|
||||
IsVisible = false;
|
||||
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
||||
}
|
||||
@@ -627,7 +630,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
||||
if (minionOrMount != IntPtr.Zero)
|
||||
{
|
||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, false).ConfigureAwait(false);
|
||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false);
|
||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.Pet)
|
||||
@@ -636,7 +639,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
||||
if (pet != IntPtr.Zero)
|
||||
{
|
||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, false).ConfigureAwait(false);
|
||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false);
|
||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (objectKind == ObjectKind.Companion)
|
||||
@@ -645,7 +648,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
|
||||
if (companion != IntPtr.Zero)
|
||||
{
|
||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, false).ConfigureAwait(false);
|
||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token, fireAndForget: false).ConfigureAwait(false);
|
||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public class OnlinePlayerManager : DisposableMediatorSubscriberBase
|
||||
}
|
||||
});
|
||||
Mediator.Subscribe<CachedPlayerVisibleMessage>(this, (msg) => _newVisiblePlayers.Add(msg.Player));
|
||||
Mediator.Subscribe<ConnectedMessage>(this, (_) => PushCharacterData(_pairManager.GetVisibleUsers()));
|
||||
}
|
||||
|
||||
private void FrameworkOnUpdate()
|
||||
|
||||
@@ -18,6 +18,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
||||
private readonly Dictionary<ObjectKind, GameObjectHandler> _playerRelatedObjects = new();
|
||||
private Task? _cacheCreationTask;
|
||||
private CancellationTokenSource _honorificCts = new();
|
||||
private bool _isZoning = false;
|
||||
private CancellationTokenSource _palettePlusCts = new();
|
||||
|
||||
public CacheCreationService(ILogger<CacheCreationService> logger, MareMediator mediator, GameObjectHandlerFactory gameObjectHandlerFactory,
|
||||
@@ -27,7 +28,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
||||
|
||||
Mediator.Subscribe<CreateCacheForObjectMessage>(this, (msg) =>
|
||||
{
|
||||
Logger.LogDebug("Received CreateCacheForObject for {handler}, updating player", msg.ObjectToCreateFor);
|
||||
Logger.LogDebug("Received CreateCacheForObject for {handler}, updating", msg.ObjectToCreateFor);
|
||||
_cacheCreateLock.Wait();
|
||||
_cachesToCreate[msg.ObjectToCreateFor.ObjectKind] = msg.ObjectToCreateFor;
|
||||
_cacheCreateLock.Release();
|
||||
@@ -37,25 +38,32 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logger.LogTrace("Clearing cache for {obj}", msg.ObjectToCreateFor);
|
||||
_playerData.FileReplacements.Remove(msg.ObjectToCreateFor.ObjectKind);
|
||||
_playerData.GlamourerString.Remove(msg.ObjectToCreateFor.ObjectKind);
|
||||
Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI()));
|
||||
});
|
||||
});
|
||||
|
||||
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (msg) => _isZoning = true);
|
||||
Mediator.Subscribe<ZoneSwitchEndMessage>(this, (msg) => _isZoning = false);
|
||||
|
||||
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (msg) => ProcessCacheCreation());
|
||||
Mediator.Subscribe<CustomizePlusMessage>(this, async (_) =>
|
||||
{
|
||||
if (_isZoning) return;
|
||||
Logger.LogDebug("Received CustomizePlus change, updating player");
|
||||
await AddPlayerCacheToCreate().ConfigureAwait(false);
|
||||
});
|
||||
Mediator.Subscribe<HeelsOffsetMessage>(this, async (_) =>
|
||||
{
|
||||
if (_isZoning) return;
|
||||
Logger.LogDebug("Received Heels Offset change, updating player");
|
||||
await AddPlayerCacheToCreate().ConfigureAwait(false);
|
||||
});
|
||||
Mediator.Subscribe<PalettePlusMessage>(this, (msg) =>
|
||||
{
|
||||
if (_isZoning) return;
|
||||
if (msg.Character.Address == _playerRelatedObjects[ObjectKind.Player].Address)
|
||||
{
|
||||
Logger.LogDebug("Received PalettePlus change, updating player");
|
||||
@@ -64,6 +72,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
||||
});
|
||||
Mediator.Subscribe<HonorificMessage>(this, (msg) =>
|
||||
{
|
||||
if (_isZoning) return;
|
||||
if (!string.Equals(msg.NewHonorificTitle, _playerData.HonorificData, StringComparison.Ordinal))
|
||||
{
|
||||
Logger.LogDebug("Received Honorific change, updating player");
|
||||
@@ -76,7 +85,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
||||
await AddPlayerCacheToCreate().ConfigureAwait(false);
|
||||
});
|
||||
|
||||
_playerRelatedObjects[ObjectKind.Player] = gameObjectHandlerFactory.Create(ObjectKind.Player, () => dalamudUtil.GetPlayerPointer(), true)
|
||||
_playerRelatedObjects[ObjectKind.Player] = gameObjectHandlerFactory.Create(ObjectKind.Player, dalamudUtil.GetPlayerPointer, true)
|
||||
.GetAwaiter().GetResult();
|
||||
_playerRelatedObjects[ObjectKind.MinionOrMount] = gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => dalamudUtil.GetMinionOrMount(), true)
|
||||
.GetAwaiter().GetResult();
|
||||
@@ -131,6 +140,8 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
|
||||
|
||||
private void ProcessCacheCreation()
|
||||
{
|
||||
if (_isZoning) return;
|
||||
|
||||
if (_cachesToCreate.Any() && (_cacheCreationTask?.IsCompleted ?? true))
|
||||
{
|
||||
_cacheCreateLock.Wait();
|
||||
|
||||
@@ -6,12 +6,14 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.Gui;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using MareSynchronos.PlayerData.Handlers;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Utils;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
|
||||
|
||||
namespace MareSynchronos.Services;
|
||||
@@ -53,6 +55,7 @@ public class DalamudUtilService : IHostedService
|
||||
|
||||
public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget;
|
||||
public unsafe Dalamud.Game.ClientState.Objects.Types.GameObject? GposeTargetGameObject => GposeTarget == null ? null : _objectTable[GposeTarget->ObjectIndex];
|
||||
public bool IsAnythingDrawing { get; private set; } = false;
|
||||
public bool IsInCutscene { get; private set; } = false;
|
||||
public bool IsInGpose { get; private set; } = false;
|
||||
public bool IsLoggedIn { get; private set; }
|
||||
@@ -171,6 +174,11 @@ public class DalamudUtilService : IHostedService
|
||||
return _clientState.LocalPlayer?.Address ?? IntPtr.Zero;
|
||||
}
|
||||
|
||||
public async Task<IntPtr> GetPlayerPointerAsync()
|
||||
{
|
||||
return await RunOnFrameworkThread(GetPlayerPointer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public uint GetWorldId()
|
||||
{
|
||||
EnsureIsOnFramework();
|
||||
@@ -198,7 +206,10 @@ public class DalamudUtilService : IHostedService
|
||||
return await RunOnFrameworkThread(() => IsObjectPresent(obj)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task RunOnFrameworkThread(Action act)
|
||||
public async Task RunOnFrameworkThread(Action act, [CallerMemberName] string callerMember = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(callerFilePath);
|
||||
await _performanceCollector.LogPerformance(this, "RunOnFramework:Act/" + fileName + ">" + callerMember + ":" + callerLineNumber, async () =>
|
||||
{
|
||||
if (!_framework.IsInFrameworkUpdateThread)
|
||||
{
|
||||
@@ -211,9 +222,13 @@ public class DalamudUtilService : IHostedService
|
||||
}
|
||||
else
|
||||
act();
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<T> RunOnFrameworkThread<T>(Func<T> func)
|
||||
public async Task<T> RunOnFrameworkThread<T>(Func<T> func, [CallerMemberName] string callerMember = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(callerFilePath);
|
||||
return await _performanceCollector.LogPerformance(this, "RunOnFramework:Func<" + typeof(T) + ">/" + fileName + ">" + callerMember + ":" + callerLineNumber, async () =>
|
||||
{
|
||||
if (!_framework.IsInFrameworkUpdateThread)
|
||||
{
|
||||
@@ -227,6 +242,7 @@ public class DalamudUtilService : IHostedService
|
||||
}
|
||||
|
||||
return func.Invoke();
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
@@ -318,9 +334,35 @@ public class DalamudUtilService : IHostedService
|
||||
{
|
||||
if (_clientState.LocalPlayer?.IsDead ?? false) return;
|
||||
|
||||
IsAnythingDrawing = false;
|
||||
_playerCharas = _performanceCollector.LogPerformance(this, "ObjTableToCharas",
|
||||
() => _objectTable.OfType<PlayerCharacter>().Where(o => o.ObjectIndex < 240)
|
||||
.ToDictionary(p => p.GetHash256(), p => (p.Name.ToString(), p.Address), StringComparer.Ordinal));
|
||||
.ToDictionary(p => p.GetHash256(), p =>
|
||||
{
|
||||
if (!IsAnythingDrawing)
|
||||
{
|
||||
var gameObj = (GameObject*)p.Address;
|
||||
bool isDrawing = gameObj->RenderFlags == 0b100000000000;
|
||||
if (!isDrawing)
|
||||
{
|
||||
var drawObj = gameObj->DrawObject;
|
||||
if ((nint)drawObj != IntPtr.Zero)
|
||||
{
|
||||
isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0;
|
||||
if (!isDrawing)
|
||||
{
|
||||
isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isDrawing)
|
||||
{
|
||||
IsAnythingDrawing = true;
|
||||
}
|
||||
}
|
||||
return (p.Name.ToString(), p.Address);
|
||||
}, StringComparer.Ordinal));
|
||||
|
||||
if (GposeTarget != null && !IsInGpose)
|
||||
{
|
||||
|
||||
@@ -168,7 +168,8 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
||||
|
||||
await _mareHub.StartAsync(token).ConfigureAwait(false);
|
||||
|
||||
await InitializeData().ConfigureAwait(false);
|
||||
InitializeApiHooks();
|
||||
await LoadIninitialPairs().ConfigureAwait(false);
|
||||
|
||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||
|
||||
@@ -198,6 +199,8 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
||||
$"Please keep your Mare Synchronos client up-to-date.",
|
||||
Dalamud.Interface.Internal.Notifications.NotificationType.Error));
|
||||
}
|
||||
|
||||
await LoadOnlinePairs().ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
@@ -283,7 +286,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
||||
ServerState = ServerState.Offline;
|
||||
}
|
||||
|
||||
private async Task InitializeData()
|
||||
private void InitializeApiHooks()
|
||||
{
|
||||
if (_mareHub == null) return;
|
||||
|
||||
@@ -311,6 +314,16 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
||||
OnGroupSendFullInfo((dto) => Client_GroupSendFullInfo(dto));
|
||||
OnGroupSendInfo((dto) => Client_GroupSendInfo(dto));
|
||||
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
_healthCheckTokenSource?.Dispose();
|
||||
_healthCheckTokenSource = new CancellationTokenSource();
|
||||
_ = ClientHealthCheck(_healthCheckTokenSource.Token);
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private async Task LoadIninitialPairs()
|
||||
{
|
||||
foreach (var userPair in await UserGetPairedClients().ConfigureAwait(false))
|
||||
{
|
||||
Logger.LogDebug("Individual Pair: {userPair}", userPair);
|
||||
@@ -330,18 +343,15 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
||||
_pairManager.AddGroupPair(user);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false))
|
||||
{
|
||||
_pairManager.MarkPairOnline(entry, sendNotif: false);
|
||||
}
|
||||
|
||||
_healthCheckTokenSource?.Cancel();
|
||||
_healthCheckTokenSource?.Dispose();
|
||||
_healthCheckTokenSource = new CancellationTokenSource();
|
||||
_ = ClientHealthCheck(_healthCheckTokenSource.Token);
|
||||
|
||||
_initialized = true;
|
||||
private async Task LoadOnlinePairs()
|
||||
{
|
||||
foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false))
|
||||
{
|
||||
Logger.LogDebug("Pair online: {pair}", entry);
|
||||
_pairManager.MarkPairOnline(entry, sendNotif: false);
|
||||
}
|
||||
}
|
||||
|
||||
private void MareHubOnClosed(Exception? arg)
|
||||
@@ -364,13 +374,15 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
||||
ServerState = ServerState.Connecting;
|
||||
try
|
||||
{
|
||||
await InitializeData().ConfigureAwait(false);
|
||||
InitializeApiHooks();
|
||||
await LoadIninitialPairs().ConfigureAwait(false);
|
||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||
if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
|
||||
{
|
||||
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
await LoadOnlinePairs().ConfigureAwait(false);
|
||||
ServerState = ServerState.Connected;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Reference in New Issue
Block a user