Merge abel stuff

This commit is contained in:
cake
2026-01-05 01:41:03 +01:00
17 changed files with 431 additions and 69 deletions

View File

@@ -6,6 +6,7 @@ using FFXIVClientStructs.Interop;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using LightlessSync.PlayerData.Handlers;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -16,7 +17,7 @@ using LightlessObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
namespace LightlessSync.Services.ActorTracking;
public sealed class ActorObjectService : IHostedService, IDisposable
public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorSubscriber
{
public readonly record struct ActorDescriptor(
string Name,
@@ -36,6 +37,8 @@ public sealed class ActorObjectService : IHostedService, IDisposable
private readonly IClientState _clientState;
private readonly ICondition _condition;
private readonly LightlessMediator _mediator;
private readonly object _playerRelatedHandlerLock = new();
private readonly HashSet<GameObjectHandler> _playerRelatedHandlers = [];
private readonly ConcurrentDictionary<nint, ActorDescriptor> _activePlayers = new();
private readonly ConcurrentDictionary<nint, ActorDescriptor> _gposePlayers = new();
@@ -71,6 +74,25 @@ public sealed class ActorObjectService : IHostedService, IDisposable
_clientState = clientState;
_condition = condition;
_mediator = mediator;
_mediator.Subscribe<GameObjectHandlerCreatedMessage>(this, (msg) =>
{
if (!msg.OwnedObject) return;
lock (_playerRelatedHandlerLock)
{
_playerRelatedHandlers.Add(msg.GameObjectHandler);
}
RefreshTrackedActors(force: true);
});
_mediator.Subscribe<GameObjectHandlerDestroyedMessage>(this, (msg) =>
{
if (!msg.OwnedObject) return;
lock (_playerRelatedHandlerLock)
{
_playerRelatedHandlers.Remove(msg.GameObjectHandler);
}
RefreshTrackedActors(force: true);
});
}
private bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51];
@@ -84,6 +106,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable
public IReadOnlyList<ActorDescriptor> PlayerDescriptors => Snapshot.PlayerDescriptors;
public IReadOnlyList<ActorDescriptor> OwnedDescriptors => Snapshot.OwnedDescriptors;
public IReadOnlyList<ActorDescriptor> GposeDescriptors => CurrentGposeSnapshot.GposeDescriptors;
public LightlessMediator Mediator => _mediator;
public bool TryGetActorByHash(string hash, out ActorDescriptor descriptor) => _actorsByHash.TryGetValue(hash, out descriptor);
public bool TryGetValidatedActorByHash(string hash, out ActorDescriptor descriptor)
@@ -324,6 +347,11 @@ public sealed class ActorObjectService : IHostedService, IDisposable
_actorsByHash.Clear();
_actorsByName.Clear();
_pendingHashResolutions.Clear();
_mediator.UnsubscribeAll(this);
lock (_playerRelatedHandlerLock)
{
_playerRelatedHandlers.Clear();
}
Volatile.Write(ref _snapshot, ActorSnapshot.Empty);
Volatile.Write(ref _gposeSnapshot, GposeSnapshot.Empty);
return Task.CompletedTask;
@@ -500,7 +528,9 @@ public sealed class ActorObjectService : IHostedService, IDisposable
if (objectKind is DalamudObjectKind.MountType or DalamudObjectKind.Companion)
{
var expectedMinionOrMount = GetMinionOrMountAddress(localPlayerAddress, localEntityId);
if (expectedMinionOrMount != nint.Zero && (nint)gameObject == expectedMinionOrMount)
if (expectedMinionOrMount != nint.Zero
&& (nint)gameObject == expectedMinionOrMount
&& IsPlayerRelatedOwnedAddress(expectedMinionOrMount, LightlessObjectKind.MinionOrMount))
{
var resolvedOwner = ownerId != 0 ? ownerId : localEntityId;
return (LightlessObjectKind.MinionOrMount, resolvedOwner);
@@ -514,16 +544,37 @@ public sealed class ActorObjectService : IHostedService, IDisposable
return (null, ownerId);
var expectedPet = GetPetAddress(localPlayerAddress, localEntityId);
if (expectedPet != nint.Zero && (nint)gameObject == expectedPet)
if (expectedPet != nint.Zero
&& (nint)gameObject == expectedPet
&& IsPlayerRelatedOwnedAddress(expectedPet, LightlessObjectKind.Pet))
return (LightlessObjectKind.Pet, ownerId);
var expectedCompanion = GetCompanionAddress(localPlayerAddress, localEntityId);
if (expectedCompanion != nint.Zero && (nint)gameObject == expectedCompanion)
if (expectedCompanion != nint.Zero
&& (nint)gameObject == expectedCompanion
&& IsPlayerRelatedOwnedAddress(expectedCompanion, LightlessObjectKind.Companion))
return (LightlessObjectKind.Companion, ownerId);
return (null, ownerId);
}
private bool IsPlayerRelatedOwnedAddress(nint address, LightlessObjectKind expectedKind)
{
if (address == nint.Zero)
return false;
lock (_playerRelatedHandlerLock)
{
foreach (var handler in _playerRelatedHandlers)
{
if (handler.Address == address && handler.ObjectKind == expectedKind)
return true;
}
}
return false;
}
private unsafe nint GetMinionOrMountAddress(nint localPlayerAddress, uint ownerEntityId)
{
if (localPlayerAddress == nint.Zero)
@@ -531,20 +582,20 @@ public sealed class ActorObjectService : IHostedService, IDisposable
var playerObject = (GameObject*)localPlayerAddress;
var candidateAddress = _objectTable.GetObjectAddress(playerObject->ObjectIndex + 1);
if (ownerEntityId == 0)
return nint.Zero;
if (candidateAddress != nint.Zero)
{
var candidate = (GameObject*)candidateAddress;
var candidateKind = (DalamudObjectKind)candidate->ObjectKind;
if (candidateKind is DalamudObjectKind.MountType or DalamudObjectKind.Companion)
{
if (ownerEntityId == 0 || ResolveOwnerId(candidate) == ownerEntityId)
if (ResolveOwnerId(candidate) == ownerEntityId)
return candidateAddress;
}
}
if (ownerEntityId == 0)
return candidateAddress;
foreach (var obj in _objectTable)
{
if (obj is null || obj.Address == nint.Zero || obj.Address == localPlayerAddress)
@@ -558,7 +609,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable
return obj.Address;
}
return candidateAddress;
return nint.Zero;
}
private unsafe nint GetPetAddress(nint localPlayerAddress, uint ownerEntityId)
@@ -1029,6 +1080,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable
public void Dispose()
{
DisposeHooks();
_mediator.UnsubscribeAll(this);
GC.SuppressFinalize(this);
}

View File

@@ -1022,7 +1022,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
if (actor.ObjectIndex >= 200)
continue;
if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, out bool firstTime) && firstTime)
if (_blockedCharacterHandler.IsCharacterBlocked(playerAddress, actor.ObjectIndex, out bool firstTime) && firstTime)
{
_logger.LogTrace("Skipping character {addr}, blocked/muted", playerAddress.ToString("X"));
continue;

View File

@@ -1,6 +1,7 @@
using Lumina.Data.Parsing;
using Lumina.Extensions;
using MeshDecimator;
using MeshDecimator.Algorithms;
using MeshDecimator.Math;
using Microsoft.Extensions.Logging;
using Penumbra.GameData.Files.ModelStructs;
@@ -94,6 +95,8 @@ internal static class MdlDecimator
var newVertexBuffer = new List<byte>(mdl.VertexBufferSize[lodIndex] > 0 ? (int)mdl.VertexBufferSize[lodIndex] : 0);
var newIndexBuffer = new List<ushort>(mdl.IndexBufferSize[lodIndex] > 0 ? (int)(mdl.IndexBufferSize[lodIndex] / sizeof(ushort)) : 0);
var subMeshCursor = 0;
DecimationAlgorithm? decimationAlgorithm = null;
int? decimationUvChannelCount = null;
for (var meshIndex = 0; meshIndex < meshes.Length; meshIndex++)
{
@@ -119,6 +122,8 @@ internal static class MdlDecimator
out vertexStreams,
out indices,
out decimated,
ref decimationAlgorithm,
ref decimationUvChannelCount,
logger))
{
updatedSubMeshes = OffsetSubMeshes(updatedSubMeshes, meshIndexBase);
@@ -309,6 +314,8 @@ internal static class MdlDecimator
out byte[][] vertexStreams,
out int[] indices,
out bool decimated,
ref DecimationAlgorithm? decimationAlgorithm,
ref int? decimationUvChannelCount,
MsLogger logger)
{
updatedMesh = mesh;
@@ -352,8 +359,7 @@ internal static class MdlDecimator
}
var meshDecimatorMesh = BuildMesh(decoded, subMeshIndices);
var algorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default);
algorithm.Logger = logger;
var algorithm = GetOrCreateAlgorithm(format, ref decimationAlgorithm, ref decimationUvChannelCount, logger);
algorithm.Initialize(meshDecimatorMesh);
algorithm.DecimateMesh(targetTriangles);
var decimatedMesh = algorithm.ToMesh();
@@ -374,6 +380,23 @@ internal static class MdlDecimator
return true;
}
private static DecimationAlgorithm GetOrCreateAlgorithm(
VertexFormat format,
ref DecimationAlgorithm? decimationAlgorithm,
ref int? decimationUvChannelCount,
MsLogger logger)
{
var uvChannelCount = format.UvChannelCount;
if (decimationAlgorithm == null || decimationUvChannelCount != uvChannelCount)
{
decimationAlgorithm = MeshDecimation.CreateAlgorithm(Algorithm.Default);
decimationAlgorithm.Logger = logger;
decimationUvChannelCount = uvChannelCount;
}
return decimationAlgorithm;
}
private static Mesh BuildMesh(DecodedMeshData decoded, int[][] subMeshIndices)
{
var mesh = new Mesh(decoded.Positions, subMeshIndices);

View File

@@ -129,6 +129,8 @@ public class PlayerPerformanceService
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var skipDecimation = config.SkipModelDecimationForPreferredPairs && pairHandler.IsDirectlyPaired && pairHandler.HasStickyPermissions;
foreach (var hash in moddedModelHashes)
{
var tris = await _xivDataAnalyzer.GetTrianglesByHash(hash).ConfigureAwait(false);
@@ -138,7 +140,12 @@ public class PlayerPerformanceService
var fileEntry = _fileCacheManager.GetFileCacheByHash(hash);
if (fileEntry != null)
{
var preferredPath = _modelDecimationService.GetPreferredPath(hash, fileEntry.ResolvedFilepath);
var preferredPath = fileEntry.ResolvedFilepath;
if (!skipDecimation)
{
preferredPath = _modelDecimationService.GetPreferredPath(hash, fileEntry.ResolvedFilepath);
}
if (!string.Equals(preferredPath, fileEntry.ResolvedFilepath, StringComparison.OrdinalIgnoreCase))
{
var decimatedTris = await _xivDataAnalyzer.GetEffectiveTrianglesByHash(hash, preferredPath).ConfigureAwait(false);
@@ -192,7 +199,9 @@ public class PlayerPerformanceService
public bool ComputeAndAutoPauseOnVRAMUsageThresholds(IPairPerformanceSubject pairHandler, CharacterData charaData, List<DownloadFileTransfer> toDownloadFiles)
{
var config = _playerPerformanceConfigService.Current;
bool skipDownscale = pairHandler.IsDirectlyPaired && pairHandler.HasStickyPermissions;
bool skipDownscale = config.SkipTextureDownscaleForPreferredPairs
&& pairHandler.IsDirectlyPaired
&& pairHandler.HasStickyPermissions;
long vramUsage = 0;
long effectiveVramUsage = 0;