various fixes and improvements for drawing checks and data sending

This commit is contained in:
rootdarkarchon
2023-05-03 21:59:44 +02:00
parent ae5f1e9527
commit 3448601b6f
10 changed files with 242 additions and 147 deletions

View File

@@ -185,9 +185,9 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
Task.Run(() => Task.Run(() =>
{ {
Logger.LogDebug("Penumbra Mod Settings changed, verifying SemiTransientResources"); 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));
} }
}); });
} }

View File

@@ -170,24 +170,30 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
public async Task CustomizePlusRevertAsync(IntPtr character) public async Task CustomizePlusRevertAsync(IntPtr character)
{ {
if (!CheckCustomizePlusApi()) return; if (!CheckCustomizePlusApi()) return;
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is Character c)
{ {
Logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X")); var gameObj = _dalamudUtil.CreateGameObject(character);
await _dalamudUtil.RunOnFrameworkThread(() => _customizePlusRevert!.InvokeAction(c)).ConfigureAwait(false); if (gameObj is Character c)
} {
Logger.LogTrace("CustomizePlus reverting for {chara}", c.Address.ToString("X"));
_customizePlusRevert!.InvokeAction(c);
}
}).ConfigureAwait(false);
} }
public async Task CustomizePlusSetBodyScaleAsync(IntPtr character, string scale) public async Task CustomizePlusSetBodyScaleAsync(IntPtr character, string scale)
{ {
if (!CheckCustomizePlusApi() || string.IsNullOrEmpty(scale)) return; if (!CheckCustomizePlusApi() || string.IsNullOrEmpty(scale)) return;
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is Character c)
{ {
string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale)); var gameObj = _dalamudUtil.CreateGameObject(character);
Logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X")); if (gameObj is Character c)
await _dalamudUtil.RunOnFrameworkThread(() => _customizePlusSetBodyScaleToCharacter!.InvokeAction(decodedScale, c)).ConfigureAwait(false); {
} string decodedScale = Encoding.UTF8.GetString(Convert.FromBase64String(scale));
Logger.LogTrace("CustomizePlus applying for {chara}", c.Address.ToString("X"));
_customizePlusSetBodyScaleToCharacter!.InvokeAction(decodedScale, c);
}
}).ConfigureAwait(false);
} }
public async Task<string> GetCustomizePlusScaleAsync() public async Task<string> GetCustomizePlusScaleAsync()
@@ -198,10 +204,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
return Convert.ToBase64String(Encoding.UTF8.GetBytes(scale)); return Convert.ToBase64String(Encoding.UTF8.GetBytes(scale));
} }
public float GetHeelsOffset() public async Task<float> GetHeelsOffsetAsync()
{ {
if (!CheckHeelsApi()) return 0.0f; 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) public async Task GlamourerApplyAllAsync(ILogger logger, GameObjectHandler handler, string? customization, Guid applicationId, CancellationToken token, bool fireAndForget = false)
@@ -210,15 +216,12 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
try try
{ {
await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false); await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
var gameObj = await _dalamudUtil.CreateGameObjectAsync(handler.Address).ConfigureAwait(false);
if (gameObj is Character c) await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
{ {
await PenumbraRedrawAsync(logger, handler, applicationId, () => logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyAll", applicationId);
{ _glamourerApplyAll!.InvokeAction(customization, chara);
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyAll", applicationId); }).ConfigureAwait(false);
_glamourerApplyAll!.InvokeAction(customization, c);
}).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -232,20 +235,16 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
try try
{ {
await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false); await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
var gameObj = await _dalamudUtil.CreateGameObjectAsync(handler.Address).ConfigureAwait(false); await PenumbraRedrawInternalAsync(logger, handler, applicationid, (chara) =>
if (gameObj is Character c)
{ {
await PenumbraRedrawAsync(logger, handler, applicationid, () => logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyCustomization", applicationid);
{ _glamourerApplyOnlyCustomization!.InvokeAction(customization, chara);
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyCustomization", applicationid); }).ConfigureAwait(false);
_glamourerApplyOnlyCustomization!.InvokeAction(customization, c); await PenumbraRedrawInternalAsync(logger, handler, applicationid, (chara) =>
}).ConfigureAwait(false); {
await PenumbraRedrawAsync(logger, handler, applicationid, () => logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyEquipment", applicationid);
{ _glamourerApplyOnlyEquipment!.InvokeAction(equipment, chara);
logger.LogDebug("[{appid}] Calling on IPC: GlamourerApplyOnlyEquipment", applicationid); }).ConfigureAwait(false);
_glamourerApplyOnlyEquipment!.InvokeAction(equipment, c);
}).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -258,17 +257,20 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
if (!CheckGlamourerApi()) return string.Empty; if (!CheckGlamourerApi()) return string.Empty;
try try
{ {
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); return await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is Character c)
{ {
var glamourerString = await _dalamudUtil.RunOnFrameworkThread(() => _glamourerGetAllCustomization!.InvokeFunc(c)).ConfigureAwait(false); var gameObj = _dalamudUtil.CreateGameObject(character);
byte[] bytes = Convert.FromBase64String(glamourerString); if (gameObj is Character c)
// ignore transparency {
bytes[88] = 128; var glamourerString = _glamourerGetAllCustomization!.InvokeFunc(c);
bytes[89] = 63; byte[] bytes = Convert.FromBase64String(glamourerString);
return Convert.ToBase64String(bytes); // ignore transparency
} bytes[88] = 128;
return string.Empty; bytes[89] = 63;
return Convert.ToBase64String(bytes);
}
return string.Empty;
}).ConfigureAwait(false);
} }
catch catch
{ {
@@ -279,34 +281,43 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
public async Task HeelsRestoreOffsetForPlayerAsync(IntPtr character) public async Task HeelsRestoreOffsetForPlayerAsync(IntPtr character)
{ {
if (!CheckHeelsApi()) return; if (!CheckHeelsApi()) return;
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj != null)
{ {
Logger.LogTrace("Restoring Heels data to {chara}", character.ToString("X")); var gameObj = _dalamudUtil.CreateGameObject(character);
await _dalamudUtil.RunOnFrameworkThread(() => _heelsUnregisterPlayer.InvokeAction(gameObj)).ConfigureAwait(false); if (gameObj != null)
} {
Logger.LogTrace("Restoring Heels data to {chara}", character.ToString("X"));
_heelsUnregisterPlayer.InvokeAction(gameObj);
}
}).ConfigureAwait(false);
} }
public async Task HeelsSetOffsetForPlayerAsync(IntPtr character, float offset) public async Task HeelsSetOffsetForPlayerAsync(IntPtr character, float offset)
{ {
if (!CheckHeelsApi()) return; if (!CheckHeelsApi()) return;
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj != null)
{ {
Logger.LogTrace("Applying Heels data to {chara}", character.ToString("X")); var gameObj = _dalamudUtil.CreateGameObject(character);
await _dalamudUtil.RunOnFrameworkThread(() => _heelsRegisterPlayer.InvokeAction(gameObj, offset)).ConfigureAwait(false); if (gameObj != null)
} {
Logger.LogTrace("Applying Heels data to {chara}", character.ToString("X"));
_heelsRegisterPlayer.InvokeAction(gameObj, offset);
}
}).ConfigureAwait(false);
} }
public async Task HonorificClearTitleAsync(nint character) public async Task HonorificClearTitleAsync(nint character)
{ {
if (!CheckHonorificApi()) return; if (!CheckHonorificApi()) return;
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is PlayerCharacter c)
{ {
Logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X")); var gameObj = _dalamudUtil.CreateGameObject(character);
await _dalamudUtil.RunOnFrameworkThread(() => _honorificClearCharacterTitle!.InvokeAction(c)).ConfigureAwait(false); if (gameObj is PlayerCharacter c)
} {
Logger.LogTrace("Honorific removing for {addr}", c.Address.ToString("X"));
_honorificClearCharacterTitle!.InvokeAction(c);
}
}).ConfigureAwait(false);
} }
public string HonorificGetTitle() public string HonorificGetTitle()
@@ -320,10 +331,10 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
{ {
if (!CheckHonorificApi()) return; if (!CheckHonorificApi()) return;
Logger.LogTrace("Applying Honorific data to {chara}", character.ToString("X")); Logger.LogTrace("Applying Honorific data to {chara}", character.ToString("X"));
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is PlayerCharacter pc)
{ {
await _dalamudUtil.RunOnFrameworkThread(() => var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is PlayerCharacter pc)
{ {
if (string.IsNullOrEmpty(honorificData)) if (string.IsNullOrEmpty(honorificData))
{ {
@@ -333,8 +344,8 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
{ {
_honorificSetCharacterTitle!.InvokeAction(pc, honorificData[1..], honorificData[0] == '1'); _honorificSetCharacterTitle!.InvokeAction(pc, honorificData[1..], honorificData[0] == '1');
} }
}).ConfigureAwait(false); }
} }).ConfigureAwait(false);
} }
public async Task<string> PalettePlusBuildPaletteAsync() public async Task<string> PalettePlusBuildPaletteAsync()
@@ -348,22 +359,25 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
public async Task PalettePlusRemovePaletteAsync(IntPtr character) public async Task PalettePlusRemovePaletteAsync(IntPtr character)
{ {
if (!CheckPalettePlusApi()) return; if (!CheckPalettePlusApi()) return;
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is Character c)
{ {
Logger.LogTrace("PalettePlus removing for {addr}", c.Address.ToString("X")); var gameObj = _dalamudUtil.CreateGameObject(character);
await _dalamudUtil.RunOnFrameworkThread(() => _palettePlusRemoveCharaPalette!.InvokeAction(c)).ConfigureAwait(false); if (gameObj is Character c)
} {
Logger.LogTrace("PalettePlus removing for {addr}", c.Address.ToString("X"));
_palettePlusRemoveCharaPalette!.InvokeAction(c);
}
}).ConfigureAwait(false);
} }
public async Task PalettePlusSetPaletteAsync(IntPtr character, string palette) public async Task PalettePlusSetPaletteAsync(IntPtr character, string palette)
{ {
if (!CheckPalettePlusApi()) return; if (!CheckPalettePlusApi()) return;
string decodedPalette = Encoding.UTF8.GetString(Convert.FromBase64String(palette)); string decodedPalette = Encoding.UTF8.GetString(Convert.FromBase64String(palette));
var gameObj = await _dalamudUtil.CreateGameObjectAsync(character).ConfigureAwait(false); await _dalamudUtil.RunOnFrameworkThread(() =>
if (gameObj is Character c)
{ {
await _dalamudUtil.RunOnFrameworkThread(() => var gameObj = _dalamudUtil.CreateGameObject(character);
if (gameObj is Character c)
{ {
if (string.IsNullOrEmpty(decodedPalette)) if (string.IsNullOrEmpty(decodedPalette))
{ {
@@ -375,8 +389,8 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
Logger.LogTrace("PalettePlus applying for {addr}", c.Address.ToString("X")); Logger.LogTrace("PalettePlus applying for {addr}", c.Address.ToString("X"));
_palettePlusSetCharaPalette!.InvokeAction(c, decodedPalette); _palettePlusSetCharaPalette!.InvokeAction(c, decodedPalette);
} }
}).ConfigureAwait(false); }
} }).ConfigureAwait(false);
} }
public string PenumbraGetMetaManipulations() public string PenumbraGetMetaManipulations()
@@ -385,21 +399,17 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
return _penumbraGetMetaManipulations.Invoke(); 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; if (!CheckPenumbraApi() || _dalamudUtil.IsZoning) return;
try try
{ {
await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false); await _redrawSemaphore.WaitAsync(token).ConfigureAwait(false);
var gameObj = await _dalamudUtil.CreateGameObjectAsync(handler.Address).ConfigureAwait(false); await PenumbraRedrawInternalAsync(logger, handler, applicationId, (chara) =>
if (gameObj is Character c)
{ {
await PenumbraRedrawAsync(logger, handler, applicationId, () => logger.LogDebug("[{appid}] Calling on IPC: PenumbraRedraw", applicationId);
{ _penumbraRedrawObject!.Invoke(chara, RedrawType.Redraw);
logger.LogDebug("[{appid}] Calling on IPC: PenumbraRedraw", applicationId); }).ConfigureAwait(false);
_penumbraRedrawObject!.Invoke(c, RedrawType.Redraw);
}).ConfigureAwait(false);
}
} }
finally finally
{ {
@@ -442,6 +452,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
logger.LogTrace("[{applicationId}] Change: {from} => {to}", applicationId, mod.Key, mod.Value); 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); var ret2 = _penumbraAddTemporaryMod.Invoke("MareChara", collName, modPaths, manipulationData, 0);
logger.LogTrace("[{applicationId}] Setting temp mods for {collName}, Success: {ret2}", applicationId, collName, ret2); logger.LogTrace("[{applicationId}] Setting temp mods for {collName}, Success: {ret2}", applicationId, collName, ret2);
}).ConfigureAwait(false); }).ConfigureAwait(false);
@@ -650,29 +661,27 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
_penumbraRedraw!.Invoke("self", RedrawType.Redraw); _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 try
{ {
CancellationTokenSource cancelToken = new CancellationTokenSource(); CancellationTokenSource cancelToken = new CancellationTokenSource();
cancelToken.CancelAfter(TimeSpan.FromSeconds(15)); cancelToken.CancelAfter(TimeSpan.FromSeconds(15));
await obj.ActOnFrameworkAfterEnsureNoDrawAsync(action, cancelToken.Token); await handler.ActOnFrameworkAfterEnsureNoDrawAsync(action, cancelToken.Token).ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(1), _disposalCts.Token).ConfigureAwait(false);
if (!_disposalCts.Token.IsCancellationRequested) 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 finally
{ {
_penumbraRedrawRequests[obj.Address] = false; _penumbraRedrawRequests[handler.Address] = false;
} }
Mediator.Publish(new PenumbraEndRedrawMessage(obj.Address)); Mediator.Publish(new PenumbraEndRedrawMessage(handler.Address));
} }
private void PeriodicApiStateCheck() private void PeriodicApiStateCheck()

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Authors></Authors> <Authors></Authors>
<Company></Company> <Company></Company>
<Version>0.8.32</Version> <Version>0.8.33</Version>
<Description></Description> <Description></Description>
<Copyright></Copyright> <Copyright></Copyright>
<PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl> <PackageProjectUrl>https://github.com/Penumbra-Sync/client</PackageProjectUrl>

View File

@@ -375,7 +375,7 @@ public class PlayerDataFactory
// gather up data from ipc // gather up data from ipc
previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations(); previousData.ManipulationString = _ipcManager.PenumbraGetMetaManipulations();
previousData.HeelsOffset = _ipcManager.GetHeelsOffset(); Task<float> getHeelsOffset = _ipcManager.GetHeelsOffsetAsync();
Task<string> getGlamourerData = _ipcManager.GlamourerGetCharacterCustomizationAsync(playerRelatedObject.Address); Task<string> getGlamourerData = _ipcManager.GlamourerGetCharacterCustomizationAsync(playerRelatedObject.Address);
Task<string> getCustomizeData = _ipcManager.GetCustomizePlusScaleAsync(); Task<string> getCustomizeData = _ipcManager.GetCustomizePlusScaleAsync();
Task<string> getPalettePlusData = _ipcManager.PalettePlusBuildPaletteAsync(); Task<string> getPalettePlusData = _ipcManager.PalettePlusBuildPaletteAsync();
@@ -387,6 +387,8 @@ public class PlayerDataFactory
_logger.LogDebug("Palette is now: {data}", previousData.PalettePlusPalette); _logger.LogDebug("Palette is now: {data}", previousData.PalettePlusPalette);
previousData.HonorificData = _ipcManager.HonorificGetTitle(); previousData.HonorificData = _ipcManager.HonorificGetTitle();
_logger.LogDebug("Honorific is now: {data}", previousData.HonorificData); _logger.LogDebug("Honorific is now: {data}", previousData.HonorificData);
previousData.HeelsOffset = await getHeelsOffset.ConfigureAwait(false);
_logger.LogDebug("Heels is now: {heels}", previousData.HeelsOffset);
st.Stop(); st.Stop();
_logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(st.ElapsedTicks).TotalMilliseconds); _logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(st.ElapsedTicks).TotalMilliseconds);

View File

@@ -1,6 +1,4 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -61,6 +59,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
Mediator.Subscribe<CutsceneEndMessage>(this, (_) => Mediator.Subscribe<CutsceneEndMessage>(this, (_) =>
{ {
_haltProcessing = false; _haltProcessing = false;
ZoneSwitchEnd();
}); });
Mediator.Subscribe<PenumbraStartRedrawMessage>(this, (msg) => Mediator.Subscribe<PenumbraStartRedrawMessage>(this, (msg) =>
{ {
@@ -93,12 +92,16 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
private IntPtr DrawObjectAddress { get; set; } private IntPtr DrawObjectAddress { get; set; }
private byte[] EquipSlotData { get; set; } = new byte[40]; 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(() => while (await _dalamudUtil.RunOnFrameworkThread(() =>
{ {
if (IsBeingDrawn()) return true; 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; return false;
}).ConfigureAwait(false)) }).ConfigureAwait(false))
{ {
@@ -117,6 +120,12 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
return _dalamudUtil.CreateGameObject(Address); return _dalamudUtil.CreateGameObject(Address);
} }
public void Invalidate()
{
Address = IntPtr.Zero;
DrawObjectAddress = IntPtr.Zero;
}
public async Task<bool> IsBeingDrawnRunOnFrameworkAsync() public async Task<bool> IsBeingDrawnRunOnFrameworkAsync()
{ {
return await _dalamudUtil.RunOnFrameworkThread(IsBeingDrawn).ConfigureAwait(false); return await _dalamudUtil.RunOnFrameworkThread(IsBeingDrawn).ConfigureAwait(false);
@@ -146,7 +155,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
Address = _getAddress(); Address = _getAddress();
if (Address != IntPtr.Zero) if (Address != IntPtr.Zero)
{ {
var drawObjAddr = (IntPtr)((GameObject*)Address)->DrawObject; var drawObjAddr = (IntPtr)((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address)->DrawObject;
DrawObjectAddress = drawObjAddr; DrawObjectAddress = drawObjAddr;
} }
else else
@@ -165,7 +174,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
_clearCts?.Cancel(); _clearCts?.Cancel();
_clearCts = null; _clearCts = null;
} }
var chara = (Character*)Address; var chara = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)Address;
var name = new ByteString(chara->GameObject.Name).ToString(); var name = new ByteString(chara->GameObject.Name).ToString();
bool nameChange = !string.Equals(name, Name, StringComparison.Ordinal); bool nameChange = !string.Equals(name, Name, StringComparison.Ordinal);
Name = name; Name = name;
@@ -259,11 +268,17 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
private unsafe IntPtr GetDrawObj(nint curPtr) private unsafe IntPtr GetDrawObj(nint curPtr)
{ {
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Getting new DrawObject", this); 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() private bool IsBeingDrawn()
{ {
if (_dalamudUtil.IsAnythingDrawing)
{
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Global draw block", this);
return true;
}
var curPtr = _getAddress(); var curPtr = _getAddress();
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, CurPtr: {ptr}", this, curPtr.ToString("X")); Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, CurPtr: {ptr}", this, curPtr.ToString("X"));
@@ -289,7 +304,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
var drawObjZero = drawObj == IntPtr.Zero; var drawObjZero = drawObj == IntPtr.Zero;
Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Condition IsDrawObjZero: {cond}", this, drawObjZero); Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Condition IsDrawObjZero: {cond}", this, drawObjZero);
if (drawObjZero) return true; 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); Logger.LogTrace("[{this}] IsBeingDrawnRunOnFramework, Condition RenderFlags: {cond}", this, renderFlags);
if (renderFlags) return true; if (renderFlags) return true;
var modelInSlotLoaded = (((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0); var modelInSlotLoaded = (((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0);
@@ -303,7 +318,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
} }
return drawObj == IntPtr.Zero return drawObj == IntPtr.Zero
|| ((GameObject*)curPtr)->RenderFlags != 0x0; || ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)curPtr)->RenderFlags != 0x0;
} }
private void ZoneSwitchEnd() private void ZoneSwitchEnd()

View File

@@ -14,10 +14,8 @@ using MareSynchronos.Utils;
using MareSynchronos.WebAPI.Files; using MareSynchronos.WebAPI.Files;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using static System.Net.Mime.MediaTypeNames;
using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind; using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind;
namespace MareSynchronos.PlayerData.Pairs; namespace MareSynchronos.PlayerData.Pairs;
@@ -55,6 +53,12 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
_lifetime = lifetime; _lifetime = lifetime;
_fileDbManager = fileDbManager; _fileDbManager = fileDbManager;
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate()); Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
{
_charaHandler?.Invalidate();
IsVisible = false;
}
);
_pluginWarnings ??= new() _pluginWarnings ??= new()
{ {
ShownCustomizePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings, ShownCustomizePlusWarning = mareConfigService.Current.DisableOptionalPluginWarnings,
@@ -445,16 +449,14 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData))) if (updateModdedPaths && (moddedPaths.Any() || !string.IsNullOrEmpty(charaData.ManipulationData)))
{ {
await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync(() => _ipcManager await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync((_) => _ipcManager
.PenumbraRemoveTemporaryCollectionAsync(Logger, _applicationId, PlayerName!) .PenumbraRemoveTemporaryCollectionAsync(Logger, _applicationId, PlayerName!)
.ConfigureAwait(true).GetAwaiter().GetResult(), token).ConfigureAwait(false); .ConfigureAwait(true).GetAwaiter().GetResult(), token).ConfigureAwait(false);
token.ThrowIfCancellationRequested(); token.ThrowIfCancellationRequested();
await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync(() => await _charaHandler!.ActOnFrameworkAfterEnsureNoDrawAsync((chara) =>
{ {
var gameObj = _charaHandler!.GetGameObject(); var objTableIndex = chara.ObjectTableIndex();
if (gameObj == null) return;
var objTableIndex = gameObj.ObjectTableIndex();
_ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, PlayerName!, objTableIndex, moddedPaths, charaData.ManipulationData) _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, PlayerName!, objTableIndex, moddedPaths, charaData.ManipulationData)
.ConfigureAwait(true).GetAwaiter().GetResult(); .ConfigureAwait(true).GetAwaiter().GetResult();
}, token).ConfigureAwait(false); }, token).ConfigureAwait(false);
@@ -510,6 +512,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
_framesSinceNotVisible++; _framesSinceNotVisible++;
if (_framesSinceNotVisible > 30) if (_framesSinceNotVisible > 30)
{ {
_framesSinceNotVisible = 30;
IsVisible = false; IsVisible = false;
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible); Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
} }
@@ -627,7 +630,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
if (minionOrMount != IntPtr.Zero) if (minionOrMount != IntPtr.Zero)
{ {
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, false).ConfigureAwait(false); 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) else if (objectKind == ObjectKind.Pet)
@@ -636,7 +639,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
if (pet != IntPtr.Zero) if (pet != IntPtr.Zero)
{ {
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, false).ConfigureAwait(false); 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) else if (objectKind == ObjectKind.Companion)
@@ -645,7 +648,7 @@ public sealed class CachedPlayer : DisposableMediatorSubscriberBase
if (companion != IntPtr.Zero) if (companion != IntPtr.Zero)
{ {
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, false).ConfigureAwait(false); 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);
} }
} }
} }

View File

@@ -41,6 +41,7 @@ public class OnlinePlayerManager : DisposableMediatorSubscriberBase
} }
}); });
Mediator.Subscribe<CachedPlayerVisibleMessage>(this, (msg) => _newVisiblePlayers.Add(msg.Player)); Mediator.Subscribe<CachedPlayerVisibleMessage>(this, (msg) => _newVisiblePlayers.Add(msg.Player));
Mediator.Subscribe<ConnectedMessage>(this, (_) => PushCharacterData(_pairManager.GetVisibleUsers()));
} }
private void FrameworkOnUpdate() private void FrameworkOnUpdate()

View File

@@ -18,6 +18,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
private readonly Dictionary<ObjectKind, GameObjectHandler> _playerRelatedObjects = new(); private readonly Dictionary<ObjectKind, GameObjectHandler> _playerRelatedObjects = new();
private Task? _cacheCreationTask; private Task? _cacheCreationTask;
private CancellationTokenSource _honorificCts = new(); private CancellationTokenSource _honorificCts = new();
private bool _isZoning = false;
private CancellationTokenSource _palettePlusCts = new(); private CancellationTokenSource _palettePlusCts = new();
public CacheCreationService(ILogger<CacheCreationService> logger, MareMediator mediator, GameObjectHandlerFactory gameObjectHandlerFactory, public CacheCreationService(ILogger<CacheCreationService> logger, MareMediator mediator, GameObjectHandlerFactory gameObjectHandlerFactory,
@@ -27,7 +28,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
Mediator.Subscribe<CreateCacheForObjectMessage>(this, (msg) => 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(); _cacheCreateLock.Wait();
_cachesToCreate[msg.ObjectToCreateFor.ObjectKind] = msg.ObjectToCreateFor; _cachesToCreate[msg.ObjectToCreateFor.ObjectKind] = msg.ObjectToCreateFor;
_cacheCreateLock.Release(); _cacheCreateLock.Release();
@@ -37,25 +38,32 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
{ {
Task.Run(() => Task.Run(() =>
{ {
Logger.LogTrace("Clearing cache for {obj}", msg.ObjectToCreateFor);
_playerData.FileReplacements.Remove(msg.ObjectToCreateFor.ObjectKind); _playerData.FileReplacements.Remove(msg.ObjectToCreateFor.ObjectKind);
_playerData.GlamourerString.Remove(msg.ObjectToCreateFor.ObjectKind); _playerData.GlamourerString.Remove(msg.ObjectToCreateFor.ObjectKind);
Mediator.Publish(new CharacterDataCreatedMessage(_playerData.ToAPI())); 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<DelayedFrameworkUpdateMessage>(this, (msg) => ProcessCacheCreation());
Mediator.Subscribe<CustomizePlusMessage>(this, async (_) => Mediator.Subscribe<CustomizePlusMessage>(this, async (_) =>
{ {
if (_isZoning) return;
Logger.LogDebug("Received CustomizePlus change, updating player"); Logger.LogDebug("Received CustomizePlus change, updating player");
await AddPlayerCacheToCreate().ConfigureAwait(false); await AddPlayerCacheToCreate().ConfigureAwait(false);
}); });
Mediator.Subscribe<HeelsOffsetMessage>(this, async (_) => Mediator.Subscribe<HeelsOffsetMessage>(this, async (_) =>
{ {
if (_isZoning) return;
Logger.LogDebug("Received Heels Offset change, updating player"); Logger.LogDebug("Received Heels Offset change, updating player");
await AddPlayerCacheToCreate().ConfigureAwait(false); await AddPlayerCacheToCreate().ConfigureAwait(false);
}); });
Mediator.Subscribe<PalettePlusMessage>(this, (msg) => Mediator.Subscribe<PalettePlusMessage>(this, (msg) =>
{ {
if (_isZoning) return;
if (msg.Character.Address == _playerRelatedObjects[ObjectKind.Player].Address) if (msg.Character.Address == _playerRelatedObjects[ObjectKind.Player].Address)
{ {
Logger.LogDebug("Received PalettePlus change, updating player"); Logger.LogDebug("Received PalettePlus change, updating player");
@@ -64,6 +72,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
}); });
Mediator.Subscribe<HonorificMessage>(this, (msg) => Mediator.Subscribe<HonorificMessage>(this, (msg) =>
{ {
if (_isZoning) return;
if (!string.Equals(msg.NewHonorificTitle, _playerData.HonorificData, StringComparison.Ordinal)) if (!string.Equals(msg.NewHonorificTitle, _playerData.HonorificData, StringComparison.Ordinal))
{ {
Logger.LogDebug("Received Honorific change, updating player"); Logger.LogDebug("Received Honorific change, updating player");
@@ -76,7 +85,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
await AddPlayerCacheToCreate().ConfigureAwait(false); 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(); .GetAwaiter().GetResult();
_playerRelatedObjects[ObjectKind.MinionOrMount] = gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => dalamudUtil.GetMinionOrMount(), true) _playerRelatedObjects[ObjectKind.MinionOrMount] = gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => dalamudUtil.GetMinionOrMount(), true)
.GetAwaiter().GetResult(); .GetAwaiter().GetResult();
@@ -131,6 +140,8 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase
private void ProcessCacheCreation() private void ProcessCacheCreation()
{ {
if (_isZoning) return;
if (_cachesToCreate.Any() && (_cacheCreationTask?.IsCompleted ?? true)) if (_cachesToCreate.Any() && (_cacheCreationTask?.IsCompleted ?? true))
{ {
_cacheCreateLock.Wait(); _cacheCreateLock.Wait();

View File

@@ -6,12 +6,14 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
namespace MareSynchronos.Services; namespace MareSynchronos.Services;
@@ -53,6 +55,7 @@ public class DalamudUtilService : IHostedService
public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget; public unsafe GameObject* GposeTarget => TargetSystem.Instance()->GPoseTarget;
public unsafe Dalamud.Game.ClientState.Objects.Types.GameObject? GposeTargetGameObject => GposeTarget == null ? null : _objectTable[GposeTarget->ObjectIndex]; 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 IsInCutscene { get; private set; } = false;
public bool IsInGpose { get; private set; } = false; public bool IsInGpose { get; private set; } = false;
public bool IsLoggedIn { get; private set; } public bool IsLoggedIn { get; private set; }
@@ -171,6 +174,11 @@ public class DalamudUtilService : IHostedService
return _clientState.LocalPlayer?.Address ?? IntPtr.Zero; return _clientState.LocalPlayer?.Address ?? IntPtr.Zero;
} }
public async Task<IntPtr> GetPlayerPointerAsync()
{
return await RunOnFrameworkThread(GetPlayerPointer).ConfigureAwait(false);
}
public uint GetWorldId() public uint GetWorldId()
{ {
EnsureIsOnFramework(); EnsureIsOnFramework();
@@ -198,35 +206,43 @@ public class DalamudUtilService : IHostedService
return await RunOnFrameworkThread(() => IsObjectPresent(obj)).ConfigureAwait(false); 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)
{ {
if (!_framework.IsInFrameworkUpdateThread) var fileName = Path.GetFileNameWithoutExtension(callerFilePath);
await _performanceCollector.LogPerformance(this, "RunOnFramework:Act/" + fileName + ">" + callerMember + ":" + callerLineNumber, async () =>
{ {
await _framework.RunOnFrameworkThread(act).ContinueWith((_) => Task.CompletedTask).ConfigureAwait(false); if (!_framework.IsInFrameworkUpdateThread)
while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
{ {
_logger.LogTrace("Still on framework"); await _framework.RunOnFrameworkThread(act).ContinueWith((_) => Task.CompletedTask).ConfigureAwait(false);
await Task.Delay(1).ConfigureAwait(false); while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
{
_logger.LogTrace("Still on framework");
await Task.Delay(1).ConfigureAwait(false);
}
} }
} else
else act();
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)
{ {
if (!_framework.IsInFrameworkUpdateThread) var fileName = Path.GetFileNameWithoutExtension(callerFilePath);
return await _performanceCollector.LogPerformance(this, "RunOnFramework:Func<" + typeof(T) + ">/" + fileName + ">" + callerMember + ":" + callerLineNumber, async () =>
{ {
var result = await _framework.RunOnFrameworkThread(func).ContinueWith((task) => task.Result).ConfigureAwait(false); if (!_framework.IsInFrameworkUpdateThread)
while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
{ {
_logger.LogTrace("Still on framework"); var result = await _framework.RunOnFrameworkThread(func).ContinueWith((task) => task.Result).ConfigureAwait(false);
await Task.Delay(1).ConfigureAwait(false); while (_framework.IsInFrameworkUpdateThread) // yield the thread again, should technically never be triggered
{
_logger.LogTrace("Still on framework");
await Task.Delay(1).ConfigureAwait(false);
}
return result;
} }
return result;
}
return func.Invoke(); return func.Invoke();
}).ConfigureAwait(false);
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
@@ -318,9 +334,35 @@ public class DalamudUtilService : IHostedService
{ {
if (_clientState.LocalPlayer?.IsDead ?? false) return; if (_clientState.LocalPlayer?.IsDead ?? false) return;
IsAnythingDrawing = false;
_playerCharas = _performanceCollector.LogPerformance(this, "ObjTableToCharas", _playerCharas = _performanceCollector.LogPerformance(this, "ObjTableToCharas",
() => _objectTable.OfType<PlayerCharacter>().Where(o => o.ObjectIndex < 240) () => _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) if (GposeTarget != null && !IsInGpose)
{ {

View File

@@ -168,7 +168,8 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
await _mareHub.StartAsync(token).ConfigureAwait(false); await _mareHub.StartAsync(token).ConfigureAwait(false);
await InitializeData().ConfigureAwait(false); InitializeApiHooks();
await LoadIninitialPairs().ConfigureAwait(false);
_connectionDto = await GetConnectionDto().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.", $"Please keep your Mare Synchronos client up-to-date.",
Dalamud.Interface.Internal.Notifications.NotificationType.Error)); Dalamud.Interface.Internal.Notifications.NotificationType.Error));
} }
await LoadOnlinePairs().ConfigureAwait(false);
} }
catch (HttpRequestException ex) catch (HttpRequestException ex)
{ {
@@ -283,7 +286,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
ServerState = ServerState.Offline; ServerState = ServerState.Offline;
} }
private async Task InitializeData() private void InitializeApiHooks()
{ {
if (_mareHub == null) return; if (_mareHub == null) return;
@@ -311,6 +314,16 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
OnGroupSendFullInfo((dto) => Client_GroupSendFullInfo(dto)); OnGroupSendFullInfo((dto) => Client_GroupSendFullInfo(dto));
OnGroupSendInfo((dto) => Client_GroupSendInfo(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)) foreach (var userPair in await UserGetPairedClients().ConfigureAwait(false))
{ {
Logger.LogDebug("Individual Pair: {userPair}", userPair); Logger.LogDebug("Individual Pair: {userPair}", userPair);
@@ -330,18 +343,15 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
_pairManager.AddGroupPair(user); _pairManager.AddGroupPair(user);
} }
} }
}
private async Task LoadOnlinePairs()
{
foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false)) foreach (var entry in await UserGetOnlinePairs().ConfigureAwait(false))
{ {
Logger.LogDebug("Pair online: {pair}", entry);
_pairManager.MarkPairOnline(entry, sendNotif: false); _pairManager.MarkPairOnline(entry, sendNotif: false);
} }
_healthCheckTokenSource?.Cancel();
_healthCheckTokenSource?.Dispose();
_healthCheckTokenSource = new CancellationTokenSource();
_ = ClientHealthCheck(_healthCheckTokenSource.Token);
_initialized = true;
} }
private void MareHubOnClosed(Exception? arg) private void MareHubOnClosed(Exception? arg)
@@ -364,13 +374,15 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
ServerState = ServerState.Connecting; ServerState = ServerState.Connecting;
try try
{ {
await InitializeData().ConfigureAwait(false); InitializeApiHooks();
await LoadIninitialPairs().ConfigureAwait(false);
_connectionDto = await GetConnectionDto().ConfigureAwait(false); _connectionDto = await GetConnectionDto().ConfigureAwait(false);
if (_connectionDto.ServerVersion != IMareHub.ApiVersion) if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
{ {
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false); await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
return; return;
} }
await LoadOnlinePairs().ConfigureAwait(false);
ServerState = ServerState.Connected; ServerState = ServerState.Connected;
} }
catch (Exception ex) catch (Exception ex)