MEOW MEOW MEOW
This commit is contained in:
@@ -2826,7 +2826,7 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
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;
|
||||
if (!string.IsNullOrEmpty(hashedCid))
|
||||
@@ -2835,8 +2835,8 @@ internal sealed class PairHandlerAdapter : DisposableMediatorSubscriberBase, IPa
|
||||
if (descriptor.ObjectKind != DalamudObjectKind.Player || descriptor.Address == nint.Zero)
|
||||
return false;
|
||||
|
||||
hashedCid = DalamudUtilService.GetHashedCIDFromPlayerPointer(descriptor.Address);
|
||||
return !string.IsNullOrEmpty(hashedCid);
|
||||
return _dalamudUtil.TryGetHashedCIDFromAddress(descriptor.Address, out hashedCid)
|
||||
&& !string.IsNullOrEmpty(hashedCid);
|
||||
}
|
||||
|
||||
private void UpdateLastKnownActor(ActorObjectService.ActorDescriptor descriptor)
|
||||
|
||||
@@ -93,6 +93,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
}
|
||||
RefreshTrackedActors(force: true);
|
||||
});
|
||||
_mediator.Subscribe<DalamudLogoutMessage>(this, _ => ClearTrackingState());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DisposeHooks();
|
||||
_activePlayers.Clear();
|
||||
_gposePlayers.Clear();
|
||||
_actorsByHash.Clear();
|
||||
_actorsByName.Clear();
|
||||
_pendingHashResolutions.Clear();
|
||||
ClearTrackingState();
|
||||
_mediator.UnsubscribeAll(this);
|
||||
lock (_playerRelatedHandlerLock)
|
||||
{
|
||||
_playerRelatedHandlers.Clear();
|
||||
}
|
||||
Volatile.Write(ref _snapshot, ActorSnapshot.Empty);
|
||||
Volatile.Write(ref _gposeSnapshot, GposeSnapshot.Empty);
|
||||
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()
|
||||
{
|
||||
DisposeHooks();
|
||||
|
||||
@@ -22,10 +22,8 @@ using LightlessSync.Utils;
|
||||
using Lumina.Excel.Sheets;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using BattleNpcSubKind = FFXIVClientStructs.FFXIV.Client.Game.Object.BattleNpcSubKind;
|
||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||
@@ -229,6 +227,28 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
_ = 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)
|
||||
{
|
||||
var target = CreateGameObject(address);
|
||||
@@ -634,6 +654,37 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
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)
|
||||
{
|
||||
return ((BattleChara*)ptr)->Character.ContentId.ToString().GetHash256();
|
||||
@@ -845,43 +896,33 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task WaitWhileCharacterIsDrawing(
|
||||
ILogger logger,
|
||||
GameObjectHandler handler,
|
||||
Guid redrawId,
|
||||
int timeOut = 5000,
|
||||
CancellationToken? ct = null)
|
||||
public async Task WaitWhileCharacterIsDrawing(ILogger logger, GameObjectHandler handler, Guid redrawId, int timeOut = 5000, CancellationToken? ct = null)
|
||||
{
|
||||
if (!_clientState.IsLoggedIn) return;
|
||||
|
||||
var token = ct ?? CancellationToken.None;
|
||||
if (ct == null)
|
||||
ct = CancellationToken.None;
|
||||
|
||||
const int tick = 250;
|
||||
const int initialSettle = 50;
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
||||
int curWaitTime = 0;
|
||||
try
|
||||
{
|
||||
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 (!token.IsCancellationRequested
|
||||
&& sw.ElapsedMilliseconds < timeOut
|
||||
&& await handler.IsBeingDrawnRunOnFrameworkAsync().ConfigureAwait(false))
|
||||
while ((!ct.Value.IsCancellationRequested)
|
||||
&& curWaitTime < timeOut
|
||||
&& await handler.IsBeingDrawnRunOnFrameworkAsync().ConfigureAwait(false)) // 0b100000000000 is "still rendering" or something
|
||||
{
|
||||
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)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (AccessViolationException ex)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
bool isDrawing = false;
|
||||
bool isDrawingChanged = false;
|
||||
|
||||
if ((nint)drawObj != IntPtr.Zero && IsValidPointer((nint)drawObj))
|
||||
if ((nint)drawObj != IntPtr.Zero)
|
||||
{
|
||||
isDrawing = gameObj->RenderFlags == (VisibilityFlags)0b100000000000;
|
||||
|
||||
if (!isDrawing)
|
||||
{
|
||||
var charBase = (CharacterBase*)drawObj;
|
||||
if (charBase != null && IsValidPointer((nint)charBase))
|
||||
isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0;
|
||||
if (!isDrawing)
|
||||
{
|
||||
isDrawing = charBase->HasModelInSlotLoaded != 0;
|
||||
if (!isDrawing)
|
||||
isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0;
|
||||
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
isDrawing = charBase->HasModelFilesInSlotLoaded != 0;
|
||||
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, characterName, StringComparison.Ordinal)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
|
||||
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)
|
||||
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal))
|
||||
{
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "HasModelInSlotLoaded";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
_lastGlobalBlockPlayer = characterName;
|
||||
_lastGlobalBlockReason = "HasModelInSlotLoaded";
|
||||
isDrawingChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1064,12 +1033,8 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
|
||||
private unsafe void FrameworkOnUpdateInternal()
|
||||
{
|
||||
if (!_clientState.IsLoggedIn || _objectTable.LocalPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_objectTable.LocalPlayer?.IsDead ?? false) && _condition[ConditionFlag.BoundByDuty])
|
||||
var localPlayer = _objectTable.LocalPlayer;
|
||||
if ((localPlayer?.IsDead ?? false) && _condition[ConditionFlag.BoundByDuty])
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1079,6 +1044,44 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
_performanceCollector.LogPerformance(this, $"FrameworkOnUpdateInternal+{(isNormalFrameworkUpdate ? "Regular" : "Delayed")}", () =>
|
||||
{
|
||||
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",
|
||||
() =>
|
||||
{
|
||||
@@ -1087,40 +1090,46 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
_actorObjectService.RefreshTrackedActors();
|
||||
}
|
||||
|
||||
var playerDescriptors = _actorObjectService.PlayerDescriptors;
|
||||
var descriptorCount = playerDescriptors.Count;
|
||||
|
||||
for (var i = 0; i < descriptorCount; i++)
|
||||
if (_clientState.IsLoggedIn && localPlayer != null)
|
||||
{
|
||||
if (i >= playerDescriptors.Count)
|
||||
break;
|
||||
|
||||
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)
|
||||
var playerDescriptors = _actorObjectService.PlayerDescriptors;
|
||||
for (var i = 0; i < playerDescriptors.Count; i++)
|
||||
{
|
||||
_logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X"));
|
||||
continue;
|
||||
}
|
||||
var actor = playerDescriptors[i];
|
||||
|
||||
if (!IsAnythingDrawing)
|
||||
{
|
||||
if (!_objectTable.Any(o => o?.Address == playerAddress))
|
||||
var playerAddress = actor.Address;
|
||||
if (playerAddress == nint.Zero)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1246,7 +1255,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
}
|
||||
|
||||
|
||||
var localPlayer = _objectTable.LocalPlayer;
|
||||
if (localPlayer != null)
|
||||
{
|
||||
_classJobId = localPlayer.ClassJob.RowId;
|
||||
@@ -1268,39 +1276,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
Mediator.Publish(new FrameworkUpdateMessage());
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1330,4 +1305,4 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user