MEOW MEOW MEOW

This commit is contained in:
2026-01-06 11:57:01 +09:00
parent 9b9010ab8e
commit 0e24da75d5
3 changed files with 176 additions and 194 deletions

View File

@@ -2826,7 +2826,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
HandleVisibilityLoss(logChange: false); HandleVisibilityLoss(logChange: false);
} }
private static bool TryResolveDescriptorHash(ActorObjectService.ActorDescriptor descriptor, out string hashedCid) private bool TryResolveDescriptorHash(ActorObjectService.ActorDescriptor descriptor, out string hashedCid)
{ {
hashedCid = descriptor.HashedContentId ?? string.Empty; hashedCid = descriptor.HashedContentId ?? string.Empty;
if (!string.IsNullOrEmpty(hashedCid)) if (!string.IsNullOrEmpty(hashedCid))
@@ -2835,8 +2835,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
if (descriptor.ObjectKind != DalamudObjectKind.Player || descriptor.Address == nint.Zero) if (descriptor.ObjectKind != DalamudObjectKind.Player || descriptor.Address == nint.Zero)
return false; return false;
hashedCid = DalamudUtilService.GetHashedCIDFromPlayerPointer(descriptor.Address); return _dalamudUtil.TryGetHashedCIDFromAddress(descriptor.Address, out hashedCid)
return !string.IsNullOrEmpty(hashedCid); && !string.IsNullOrEmpty(hashedCid);
} }
private void UpdateLastKnownActor(ActorObjectService.ActorDescriptor descriptor) private void UpdateLastKnownActor(ActorObjectService.ActorDescriptor descriptor)

View File

@@ -93,6 +93,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
} }
RefreshTrackedActors(force: true); RefreshTrackedActors(force: true);
}); });
_mediator.Subscribe<DalamudLogoutMessage>(this, _ => ClearTrackingState());
} }
private bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51]; private bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51];
@@ -342,18 +343,8 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
{ {
DisposeHooks(); DisposeHooks();
_activePlayers.Clear(); ClearTrackingState();
_gposePlayers.Clear();
_actorsByHash.Clear();
_actorsByName.Clear();
_pendingHashResolutions.Clear();
_mediator.UnsubscribeAll(this); _mediator.UnsubscribeAll(this);
lock (_playerRelatedHandlerLock)
{
_playerRelatedHandlers.Clear();
}
Volatile.Write(ref _snapshot, ActorSnapshot.Empty);
Volatile.Write(ref _gposeSnapshot, GposeSnapshot.Empty);
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -1077,6 +1068,22 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
} }
} }
private void ClearTrackingState()
{
_activePlayers.Clear();
_gposePlayers.Clear();
_actorsByHash.Clear();
_actorsByName.Clear();
_pendingHashResolutions.Clear();
lock (_playerRelatedHandlerLock)
{
_playerRelatedHandlers.Clear();
}
Volatile.Write(ref _snapshot, ActorSnapshot.Empty);
Volatile.Write(ref _gposeSnapshot, GposeSnapshot.Empty);
_nextRefreshAllowed = DateTime.MinValue;
}
public void Dispose() public void Dispose()
{ {
DisposeHooks(); DisposeHooks();

View File

@@ -22,10 +22,8 @@ using LightlessSync.Utils;
using Lumina.Excel.Sheets; using Lumina.Excel.Sheets;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using BattleNpcSubKind = FFXIVClientStructs.FFXIV.Client.Game.Object.BattleNpcSubKind; using BattleNpcSubKind = FFXIVClientStructs.FFXIV.Client.Game.Object.BattleNpcSubKind;
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind; using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
@@ -229,6 +227,28 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
_ = RunOnFrameworkThread(ReleaseFocusUnsafe); _ = RunOnFrameworkThread(ReleaseFocusUnsafe);
} }
public void TargetPlayerByAddress(nint address)
{
if (address == nint.Zero) return;
if (_clientState.IsPvP) return;
_ = RunOnFrameworkThread(() =>
{
var gameObject = CreateGameObject(address);
if (gameObject is null) return;
var useFocusTarget = _configService.Current.UseFocusTarget;
if (useFocusTarget)
{
_targetManager.FocusTarget = gameObject;
}
else
{
_targetManager.Target = gameObject;
}
});
}
private void FocusPairUnsafe(nint address, PairUniqueIdentifier pairIdent) private void FocusPairUnsafe(nint address, PairUniqueIdentifier pairIdent)
{ {
var target = CreateGameObject(address); var target = CreateGameObject(address);
@@ -634,6 +654,37 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
return true; return true;
} }
public bool TryGetHashedCIDFromAddress(nint address, out string hashedCid)
{
hashedCid = string.Empty;
if (address == nint.Zero)
return false;
if (_framework.IsInFrameworkUpdateThread)
{
return TryGetHashedCIDFromAddressInternal(address, out hashedCid);
}
var result = _framework.RunOnFrameworkThread(() =>
{
var success = TryGetHashedCIDFromAddressInternal(address, out var resolved);
return (success, resolved);
}).GetAwaiter().GetResult();
hashedCid = result.resolved;
return result.success;
}
private bool TryGetHashedCIDFromAddressInternal(nint address, out string hashedCid)
{
hashedCid = string.Empty;
var player = _objectTable.CreateObjectReference(address) as IPlayerCharacter;
if (player == null || player.Address != address)
return false;
return TryGetHashedCID(player, out hashedCid);
}
public unsafe static string GetHashedCIDFromPlayerPointer(nint ptr) public unsafe static string GetHashedCIDFromPlayerPointer(nint ptr)
{ {
return ((BattleChara*)ptr)->Character.ContentId.ToString().GetHash256(); return ((BattleChara*)ptr)->Character.ContentId.ToString().GetHash256();
@@ -845,43 +896,33 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task WaitWhileCharacterIsDrawing( public async Task WaitWhileCharacterIsDrawing(ILogger logger, GameObjectHandler handler, Guid redrawId, int timeOut = 5000, CancellationToken? ct = null)
ILogger logger,
GameObjectHandler handler,
Guid redrawId,
int timeOut = 5000,
CancellationToken? ct = null)
{ {
if (!_clientState.IsLoggedIn) return; if (!_clientState.IsLoggedIn) return;
var token = ct ?? CancellationToken.None; if (ct == null)
ct = CancellationToken.None;
const int tick = 250; const int tick = 250;
const int initialSettle = 50; int curWaitTime = 0;
var sw = Stopwatch.StartNew();
try try
{ {
logger.LogTrace("[{redrawId}] Starting wait for {handler} to draw", redrawId, handler); logger.LogTrace("[{redrawId}] Starting wait for {handler} to draw", redrawId, handler);
await Task.Delay(tick, ct.Value).ConfigureAwait(true);
curWaitTime += tick;
await Task.Delay(initialSettle, token).ConfigureAwait(false); while ((!ct.Value.IsCancellationRequested)
&& curWaitTime < timeOut
while (!token.IsCancellationRequested && await handler.IsBeingDrawnRunOnFrameworkAsync().ConfigureAwait(false)) // 0b100000000000 is "still rendering" or something
&& sw.ElapsedMilliseconds < timeOut
&& await handler.IsBeingDrawnRunOnFrameworkAsync().ConfigureAwait(false))
{ {
logger.LogTrace("[{redrawId}] Waiting for {handler} to finish drawing", redrawId, handler); logger.LogTrace("[{redrawId}] Waiting for {handler} to finish drawing", redrawId, handler);
await Task.Delay(tick, token).ConfigureAwait(false); curWaitTime += tick;
await Task.Delay(tick, ct.Value).ConfigureAwait(true);
} }
logger.LogTrace("[{redrawId}] Finished drawing after {ms}ms", redrawId, sw.ElapsedMilliseconds); logger.LogTrace("[{redrawId}] Finished drawing after {curWaitTime}ms", redrawId, curWaitTime);
} }
catch (OperationCanceledException) catch (AccessViolationException ex)
{
// ignore
}
catch (Exception ex)
{ {
logger.LogWarning(ex, "Error accessing {handler}, object does not exist anymore?", handler); logger.LogWarning(ex, "Error accessing {handler}, object does not exist anymore?", handler);
} }
@@ -931,109 +972,37 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
return WorldData.Value.TryGetValue(worldId, out var worldName) ? worldName : null; return WorldData.Value.TryGetValue(worldId, out var worldName) ? worldName : null;
} }
public void TargetPlayerByAddress(nint address)
{
if (address == nint.Zero) return;
if (_clientState.IsPvP) return;
_ = RunOnFrameworkThread(() =>
{
var gameObject = CreateGameObject(address);
if (gameObject is null) return;
var useFocusTarget = _configService.Current.UseFocusTarget;
if (useFocusTarget)
{
_targetManager.FocusTarget = gameObject;
}
else
{
_targetManager.Target = gameObject;
}
});
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool IsBadReadPtr(IntPtr ptr, UIntPtr size);
private static bool IsValidPointer(nint ptr, int size = 8)
{
if (ptr == nint.Zero)
return false;
try
{
if (!Util.IsWine())
{
return !IsBadReadPtr(ptr, (UIntPtr)size);
}
return ptr != nint.Zero && (ptr % IntPtr.Size) == 0;
}
catch
{
return false;
}
}
private unsafe void CheckCharacterForDrawing(nint address, string characterName) private unsafe void CheckCharacterForDrawing(nint address, string characterName)
{ {
if (address == nint.Zero)
return;
if (!IsValidPointer(address))
{
_logger.LogDebug("Invalid pointer for character {name} at {addr}", characterName, address.ToString("X"));
return;
}
var gameObj = (GameObject*)address; var gameObj = (GameObject*)address;
if (gameObj == null)
return;
if (!_objectTable.Any(o => o?.Address == address))
{
_logger.LogDebug("Character {name} at {addr} no longer in object table", characterName, address.ToString("X"));
return;
}
if (gameObj->ObjectKind == 0)
return;
var drawObj = gameObj->DrawObject; var drawObj = gameObj->DrawObject;
bool isDrawing = false; bool isDrawing = false;
bool isDrawingChanged = false; bool isDrawingChanged = false;
if ((nint)drawObj != IntPtr.Zero)
if ((nint)drawObj != IntPtr.Zero && IsValidPointer((nint)drawObj))
{ {
isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000; isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000;
if (!isDrawing) if (!isDrawing)
{ {
var charBase = (CharacterBase*)drawObj; isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0;
if (charBase != null && IsValidPointer((nint)charBase)) if (!isDrawing)
{ {
isDrawing = charBase->HasModelInSlotLoaded != 0; isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0;
if (!isDrawing) if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
{ {
isDrawing = charBase->HasModelFilesInSlotLoaded != 0; _lastGlobalBlockPlayer = characterName;
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) _lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) isDrawingChanged = true;
{
_lastGlobalBlockPlayer = characterName;
_lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
isDrawingChanged = true;
}
} }
else }
else
{
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal))
{ {
if (!string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal) _lastGlobalBlockPlayer = characterName;
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) _lastGlobalBlockReason = "HasModelInSlotLoaded";
{ isDrawingChanged = true;
_lastGlobalBlockPlayer = characterName;
_lastGlobalBlockReason = "HasModelInSlotLoaded";
isDrawingChanged = true;
}
} }
} }
} }
@@ -1064,12 +1033,8 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
private unsafe void FrameworkOnUpdateInternal() private unsafe void FrameworkOnUpdateInternal()
{ {
if (!_clientState.IsLoggedIn || _objectTable.LocalPlayer == null) var localPlayer = _objectTable.LocalPlayer;
{ if ((localPlayer?.IsDead ?? false) && _condition[ConditionFlag.BoundByDuty])
return;
}
if ((_objectTable.LocalPlayer?.IsDead ?? false) && _condition[ConditionFlag.BoundByDuty])
{ {
return; return;
} }
@@ -1079,6 +1044,44 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
_performanceCollector.LogPerformance(this, $"FrameworkOnUpdateInternal+{(isNormalFrameworkUpdate ? "Regular" : "Delayed")}", () => _performanceCollector.LogPerformance(this, $"FrameworkOnUpdateInternal+{(isNormalFrameworkUpdate ? "Regular" : "Delayed")}", () =>
{ {
IsAnythingDrawing = false; IsAnythingDrawing = false;
if (!isNormalFrameworkUpdate)
{
if (localPlayer != null && !IsLoggedIn)
{
_logger.LogDebug("Logged in");
IsLoggedIn = true;
_lastZone = _clientState.TerritoryType;
_lastWorldId = (ushort)localPlayer.CurrentWorld.RowId;
_cid = RebuildCID();
Mediator.Publish(new DalamudLoginMessage());
}
else if (localPlayer == null && IsLoggedIn)
{
_logger.LogDebug("Logged out");
IsLoggedIn = false;
_lastWorldId = 0;
Mediator.Publish(new DalamudLogoutMessage());
}
if (_gameConfig != null
&& _gameConfig.TryGet(Dalamud.Game.Config.SystemConfigOption.LodType_DX11, out bool lodEnabled))
{
IsLodEnabled = lodEnabled;
}
if (IsInCombat || IsPerforming || IsInInstance)
Mediator.Publish(new FrameworkUpdateMessage());
Mediator.Publish(new DelayedFrameworkUpdateMessage());
_delayedFrameworkUpdateCheck = DateTime.UtcNow;
}
if (!_clientState.IsLoggedIn || localPlayer == null)
{
return;
}
_performanceCollector.LogPerformance(this, $"TrackedActorsToState", _performanceCollector.LogPerformance(this, $"TrackedActorsToState",
() => () =>
{ {
@@ -1087,40 +1090,46 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
_actorObjectService.RefreshTrackedActors(); _actorObjectService.RefreshTrackedActors();
} }
var playerDescriptors = _actorObjectService.PlayerDescriptors; if (_clientState.IsLoggedIn && localPlayer != null)
var descriptorCount = playerDescriptors.Count;
for (var i = 0; i < descriptorCount; i++)
{ {
if (i >= playerDescriptors.Count) var playerDescriptors = _actorObjectService.PlayerDescriptors;
break; for (var i = 0; i < playerDescriptors.Count; i++)
var actor = playerDescriptors[i];
var playerAddress = actor.Address;
if (playerAddress == nint.Zero || !IsValidPointer(playerAddress))
continue;
if (actor.ObjectIndex >= 200)
continue;
if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime)
{ {
_logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X")); var actor = playerDescriptors[i];
continue;
}
if (!IsAnythingDrawing) var playerAddress = actor.Address;
{ if (playerAddress == nint.Zero)
if (!_objectTable.Any(o => o?.Address == playerAddress)) continue;
if (actor.ObjectIndex >= 200)
continue;
var obj = _objectTable[actor.ObjectIndex];
if (obj is not IPlayerCharacter player || player.Address != playerAddress)
continue;
if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime)
{ {
_logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X"));
continue; continue;
} }
CheckCharacterForDrawing(playerAddress, actor.Name); if (!IsAnythingDrawing)
{
var charaName = player.Name.TextValue;
if (string.IsNullOrEmpty(charaName))
{
charaName = actor.Name;
}
if (IsAnythingDrawing) CheckCharacterForDrawing(playerAddress, charaName);
if (IsAnythingDrawing)
break;
}
else
{
break; break;
}
} }
} }
}); });
@@ -1246,7 +1255,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
} }
var localPlayer = _objectTable.LocalPlayer;
if (localPlayer != null) if (localPlayer != null)
{ {
_classJobId = localPlayer.ClassJob.RowId; _classJobId = localPlayer.ClassJob.RowId;
@@ -1268,39 +1276,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
Mediator.Publish(new FrameworkUpdateMessage()); Mediator.Publish(new FrameworkUpdateMessage());
Mediator.Publish(new PriorityFrameworkUpdateMessage()); Mediator.Publish(new PriorityFrameworkUpdateMessage());
if (isNormalFrameworkUpdate)
return;
if (localPlayer != null && !IsLoggedIn)
{
_logger.LogDebug("Logged in");
IsLoggedIn = true;
_lastZone = _clientState.TerritoryType;
_lastWorldId = (ushort)localPlayer.CurrentWorld.RowId;
_cid = RebuildCID();
Mediator.Publish(new DalamudLoginMessage());
}
else if (localPlayer == null && IsLoggedIn)
{
_logger.LogDebug("Logged out");
IsLoggedIn = false;
_lastWorldId = 0;
Mediator.Publish(new DalamudLogoutMessage());
}
if (_gameConfig != null
&& _gameConfig.TryGet(Dalamud.Game.Config.SystemConfigOption.LodType_DX11, out bool lodEnabled))
{
IsLodEnabled = lodEnabled;
}
if (IsInCombat || IsPerforming || IsInInstance)
Mediator.Publish(new FrameworkUpdateMessage());
Mediator.Publish(new DelayedFrameworkUpdateMessage());
_delayedFrameworkUpdateCheck = DateTime.UtcNow;
}); });
} }