rework cache creation conditions

clean up some stuff

revert pet clearing

fix initial cache creation not happening without changes/redraws

fix draw conditions when framework inactive
This commit is contained in:
Stanley Dimant
2025-02-23 03:04:08 +01:00
committed by Loporrit
parent c1940767bf
commit f033b1aa74
13 changed files with 299 additions and 321 deletions

View File

@@ -2,7 +2,6 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils;
using Microsoft.Extensions.Logging;
using System.Runtime.CompilerServices;
using static FFXIVClientStructs.FFXIV.Client.Game.Character.DrawDataContainer;
@@ -10,18 +9,15 @@ using ObjectKind = MareSynchronos.API.Data.Enum.ObjectKind;
namespace MareSynchronos.PlayerData.Handlers;
public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
public sealed class GameObjectHandler : DisposableMediatorSubscriberBase, IHighPriorityMediatorSubscriber
{
private readonly DalamudUtilService _dalamudUtil;
private readonly Func<IntPtr> _getAddress;
private readonly bool _isOwnedObject;
private readonly PerformanceCollectorService _performanceCollector;
private CancellationTokenSource? _clearCts = new();
private byte _classJob = 0;
private Task? _delayedZoningTask;
private bool _haltProcessing = false;
private bool _ignoreSendAfterRedraw = false;
private int _ptrNullCounter = 0;
private byte _classJob = 0;
private CancellationTokenSource _zoningCts = new();
public GameObjectHandler(ILogger<GameObjectHandler> logger, PerformanceCollectorService performanceCollector,
@@ -76,12 +72,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
if (msg.Address == Address)
{
_haltProcessing = false;
_ = Task.Run(async () =>
{
_ignoreSendAfterRedraw = true;
await Task.Delay(500).ConfigureAwait(false);
_ignoreSendAfterRedraw = false;
});
}
});
@@ -90,22 +80,23 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
_dalamudUtil.RunOnFrameworkThread(CheckAndUpdateObject).GetAwaiter().GetResult();
}
private enum DrawCondition
public enum DrawCondition
{
None,
ObjectZero,
DrawObjectZero,
RenderFlags,
ModelInSlotLoaded,
ModelFilesInSlotLoaded
}
public byte RaceId { get; private set; }
public byte Gender { get; private set; }
public byte TribeId { get; private set; }
public IntPtr Address { get; private set; }
public DrawCondition CurrentDrawCondition { get; set; } = DrawCondition.None;
public byte Gender { get; private set; }
public string Name { get; private set; }
public ObjectKind ObjectKind { get; }
public byte RaceId { get; private set; }
public byte TribeId { get; private set; }
private byte[] CustomizeData { get; set; } = new byte[26];
private IntPtr DrawObjectAddress { get; set; }
private byte[] EquipSlotData { get; set; } = new byte[40];
@@ -116,7 +107,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
{
while (await _dalamudUtil.RunOnFrameworkThread(() =>
{
if (IsBeingDrawn()) return true;
if (_haltProcessing) CheckAndUpdateObject();
if (CurrentDrawCondition != DrawCondition.None) return true;
var gameObj = _dalamudUtil.CreateGameObject(Address);
if (gameObj is Dalamud.Game.ClientState.Objects.Types.ICharacter chara)
{
@@ -141,12 +133,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
}
}
public IntPtr CurrentAddress()
{
_dalamudUtil.EnsureIsOnFramework();
return _getAddress.Invoke();
}
public Dalamud.Game.ClientState.Objects.Types.IGameObject? GetGameObject()
{
return _dalamudUtil.CreateGameObject(Address);
@@ -185,15 +171,18 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
Address = _getAddress();
if (Address != IntPtr.Zero)
{
_ptrNullCounter = 0;
var drawObjAddr = (IntPtr)((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address)->DrawObject;
DrawObjectAddress = drawObjAddr;
CurrentDrawCondition = DrawCondition.None;
}
else
{
DrawObjectAddress = IntPtr.Zero;
CurrentDrawCondition = DrawCondition.DrawObjectZero;
}
CurrentDrawCondition = IsBeingDrawnUnsafe();
if (_haltProcessing) return;
bool drawObjDiff = DrawObjectAddress != prevDrawObj;
@@ -201,12 +190,6 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
if (Address != IntPtr.Zero && DrawObjectAddress != IntPtr.Zero)
{
if (_clearCts != null)
{
Logger.LogDebug("[{this}] Cancelling Clear Task", this);
_clearCts.CancelDispose();
_clearCts = null;
}
var chara = (Character*)Address;
var name = chara->GameObject.NameString;
bool nameChange = !string.Equals(name, Name, StringComparison.Ordinal);
@@ -244,7 +227,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
Logger.LogTrace("Checking [{this}] equip data from game obj, result: {diff}", this, equipDiff);
}
if (equipDiff && !_isOwnedObject && !_ignoreSendAfterRedraw) // send the message out immediately and cancel out, no reason to continue if not self
if (equipDiff && !_isOwnedObject) // send the message out immediately and cancel out, no reason to continue if not self
{
Logger.LogTrace("[{this}] Changed", this);
Mediator.Publish(new CharacterChangedMessage(this));
@@ -288,26 +271,15 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
}
else if (addrDiff || drawObjDiff)
{
CurrentDrawCondition = DrawCondition.DrawObjectZero;
Logger.LogTrace("[{this}] Changed", this);
if (_isOwnedObject && ObjectKind != ObjectKind.Player)
{
_clearCts?.CancelDispose();
_clearCts = new();
var token = _clearCts.Token;
_ = Task.Run(() => ClearAsync(token), token);
Mediator.Publish(new ClearCacheForObjectMessage(this));
}
}
}
private async Task ClearAsync(CancellationToken token)
{
Logger.LogDebug("[{this}] Running Clear Task", this);
await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false);
Logger.LogDebug("[{this}] Sending ClearCachedForObjectMessage", this);
Mediator.Publish(new ClearCacheForObjectMessage(this));
_clearCts = null;
}
private unsafe bool CompareAndUpdateCustomizeData(Span<byte> customizeData)
{
bool hasChanges = false;
@@ -382,31 +354,9 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
}
}
private unsafe IntPtr GetDrawObjUnsafe(nint curPtr)
{
return (IntPtr)((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)curPtr)->DrawObject;
}
private bool IsBeingDrawn()
{
var curPtr = _getAddress();
Logger.LogTrace("[{this}] IsBeingDrawn, CurPtr: {ptr}", this, curPtr.ToString("X"));
if (curPtr == IntPtr.Zero && _ptrNullCounter < 2)
{
Logger.LogTrace("[{this}] IsBeingDrawn, CurPtr is ZERO, counter is {cnt}", this, _ptrNullCounter);
_ptrNullCounter++;
return true;
}
if (curPtr == IntPtr.Zero)
{
Logger.LogTrace("[{this}] IsBeingDrawn, CurPtr is ZERO, returning", this);
Address = IntPtr.Zero;
DrawObjectAddress = IntPtr.Zero;
throw new ArgumentNullException($"CurPtr for {this} turned ZERO");
}
if (_haltProcessing) CheckAndUpdateObject();
if (_dalamudUtil.IsAnythingDrawing)
{
@@ -414,27 +364,23 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
return true;
}
var drawObj = GetDrawObjUnsafe(curPtr);
Logger.LogTrace("[{this}] IsBeingDrawn, DrawObjPtr: {ptr}", this, drawObj.ToString("X"));
var isDrawn = IsBeingDrawnUnsafe(drawObj, curPtr);
Logger.LogTrace("[{this}] IsBeingDrawn, Condition: {cond}", this, isDrawn);
return isDrawn != DrawCondition.None;
Logger.LogTrace("[{this}] IsBeingDrawn, Condition: {cond}", this, CurrentDrawCondition);
return CurrentDrawCondition != DrawCondition.None;
}
private unsafe DrawCondition IsBeingDrawnUnsafe(IntPtr drawObj, IntPtr curPtr)
private unsafe DrawCondition IsBeingDrawnUnsafe()
{
var drawObjZero = drawObj == IntPtr.Zero;
if (drawObjZero) return DrawCondition.DrawObjectZero;
var renderFlags = (((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)curPtr)->RenderFlags) != 0x0;
if (Address == IntPtr.Zero) return DrawCondition.ObjectZero;
if (DrawObjectAddress == IntPtr.Zero) return DrawCondition.DrawObjectZero;
var renderFlags = (((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Address)->RenderFlags) != 0x0;
if (renderFlags) return DrawCondition.RenderFlags;
if (ObjectKind == ObjectKind.Player)
{
var modelInSlotLoaded = (((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0);
var modelInSlotLoaded = (((CharacterBase*)DrawObjectAddress)->HasModelInSlotLoaded != 0);
if (modelInSlotLoaded) return DrawCondition.ModelInSlotLoaded;
var modelFilesInSlotLoaded = (((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0);
var modelFilesInSlotLoaded = (((CharacterBase*)DrawObjectAddress)->HasModelFilesInSlotLoaded != 0);
if (modelFilesInSlotLoaded) return DrawCondition.ModelFilesInSlotLoaded;
return DrawCondition.None;
}
return DrawCondition.None;
@@ -442,11 +388,8 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
private void ZoneSwitchEnd()
{
if (!_isOwnedObject || _haltProcessing) return;
if (!_isOwnedObject) return;
_clearCts?.Cancel();
_clearCts?.Dispose();
_clearCts = null;
try
{
_zoningCts?.CancelAfter(2500);
@@ -463,7 +406,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
private void ZoneSwitchStart()
{
if (!_isOwnedObject || _haltProcessing) return;
if (!_isOwnedObject) return;
_zoningCts = new();
Logger.LogDebug("[{obj}] Starting Delay After Zoning", this);