fix some shit, add triangle count

check for invalid animations

I hate animations

ignore broken bones from god knows what

fix more idiotic mod things

fully ignore garbage skeletons that fail to process properly

fix my own mistakes

fix more bullshit

check for filename length and continue

idk some cleanup

fix spoopy skellingtons

change loglevel of tris
This commit is contained in:
rootdarkarchon
2024-03-20 15:12:19 +01:00
committed by Loporrit
parent 5642d354e0
commit 324288652d
24 changed files with 432 additions and 40 deletions

View File

@@ -20,13 +20,14 @@ public class PairHandlerFactory
private readonly IpcManager _ipcManager;
private readonly ILoggerFactory _loggerFactory;
private readonly MareMediator _mareMediator;
private readonly XivDataAnalyzer _xivDataAnalyzer;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly ServerConfigurationManager _serverConfigurationManager;
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
PluginWarningNotificationService pluginWarningNotificationManager, ServerConfigurationManager serverConfigurationManager,
CancellationToken dalamudLifetime, FileCacheManager fileCacheManager, MareMediator mareMediator)
CancellationToken dalamudLifetime, FileCacheManager fileCacheManager, MareMediator mareMediator, XivDataAnalyzer modelAnalyzer)
{
_loggerFactory = loggerFactory;
_gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -38,12 +39,13 @@ public class PairHandlerFactory
_dalamudLifetimeToken = dalamudLifetime;
_fileCacheManager = fileCacheManager;
_mareMediator = mareMediator;
_xivDataAnalyzer = modelAnalyzer;
}
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
{
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _serverConfigurationManager, _dalamudUtilService,
_dalamudLifetimeToken, _fileCacheManager, _mareMediator);
_dalamudLifetimeToken, _fileCacheManager, _mareMediator, _xivDataAnalyzer);
}
}

View File

@@ -2,9 +2,11 @@
using MareSynchronos.API.Data.Enum;
using MareSynchronos.FileCache;
using MareSynchronos.Interop.Ipc;
using MareSynchronos.MareConfiguration.Models;
using MareSynchronos.PlayerData.Data;
using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using CharacterData = MareSynchronos.PlayerData.Data.CharacterData;
@@ -18,11 +20,13 @@ public class PlayerDataFactory
private readonly IpcManager _ipcManager;
private readonly ILogger<PlayerDataFactory> _logger;
private readonly PerformanceCollectorService _performanceCollector;
private readonly XivDataAnalyzer _modelAnalyzer;
private readonly MareMediator _mareMediator;
private readonly TransientResourceManager _transientResourceManager;
public PlayerDataFactory(ILogger<PlayerDataFactory> logger, DalamudUtilService dalamudUtil, IpcManager ipcManager,
TransientResourceManager transientResourceManager, FileCacheManager fileReplacementFactory,
PerformanceCollectorService performanceCollector)
PerformanceCollectorService performanceCollector, XivDataAnalyzer modelAnalyzer, MareMediator mareMediator)
{
_logger = logger;
_dalamudUtil = dalamudUtil;
@@ -30,7 +34,9 @@ public class PlayerDataFactory
_transientResourceManager = transientResourceManager;
_fileCacheManager = fileReplacementFactory;
_performanceCollector = performanceCollector;
_logger.LogTrace("Creating " + nameof(PlayerDataFactory));
_modelAnalyzer = modelAnalyzer;
_mareMediator = mareMediator;
_logger.LogTrace("Creating {this}", nameof(PlayerDataFactory));
}
public async Task BuildCharacterData(CharacterData previousData, GameObjectHandler playerRelatedObject, CancellationToken token)
@@ -128,12 +134,16 @@ public class PlayerDataFactory
// wait until chara is not drawing and present so nothing spontaneously explodes
await _dalamudUtil.WaitWhileCharacterIsDrawing(_logger, playerRelatedObject, Guid.NewGuid(), 30000, ct: token).ConfigureAwait(false);
int totalWaitTime = 10000;
while (!await _dalamudUtil.IsObjectPresentAsync(await _dalamudUtil.CreateGameObjectAsync(charaPointer).ConfigureAwait(false)).ConfigureAwait(false) && totalWaitTime > 0)
while (!await _dalamudUtil.IsObjectPresentAsync(await _dalamudUtil.CreateGameObjectAsync(playerRelatedObject.Address).ConfigureAwait(false)).ConfigureAwait(false) && totalWaitTime > 0)
{
_logger.LogTrace("Character is null but it shouldn't be, waiting");
await Task.Delay(50, token).ConfigureAwait(false);
totalWaitTime -= 50;
}
Dictionary<string, List<ushort>>? boneIndices =
objectKind != ObjectKind.Player
? null
: await _dalamudUtil.RunOnFrameworkThread(() => _modelAnalyzer.GetSkeletonBoneIndices(playerRelatedObject)).ConfigureAwait(false);
DateTime start = DateTime.UtcNow;
@@ -226,11 +236,84 @@ public class PlayerDataFactory
}
}
if (objectKind == ObjectKind.Player)
{
try
{
await VerifyPlayerAnimationBones(boneIndices, previousData, objectKind).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to verify player animations, continuing without further verification");
}
}
_logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(DateTime.UtcNow.Ticks - start.Ticks).TotalMilliseconds);
return previousData;
}
private async Task VerifyPlayerAnimationBones(Dictionary<string, List<ushort>>? boneIndices, CharacterData previousData, ObjectKind objectKind)
{
if (boneIndices == null) return;
foreach (var kvp in boneIndices)
{
_logger.LogDebug("Found {skellyname} ({idx} bone indices) on player: {bones}", kvp.Key, kvp.Value.Any() ? kvp.Value.Max() : 0, string.Join(',', kvp.Value));
}
if (boneIndices.All(u => u.Value.Count == 0)) return;
int noValidationFailed = 0;
foreach (var file in previousData.FileReplacements[objectKind].Where(f => !f.IsFileSwap && f.GamePaths.First().EndsWith("pap", StringComparison.OrdinalIgnoreCase)).ToList())
{
var skeletonIndices = await _dalamudUtil.RunOnFrameworkThread(() => _modelAnalyzer.GetBoneIndicesFromPap(file.Hash)).ConfigureAwait(false);
bool validationFailed = false;
if (skeletonIndices != null)
{
// 105 is the maximum vanilla skellington spoopy bone index
if (skeletonIndices.All(k => k.Value.Max() <= 105))
{
_logger.LogTrace("All indices of {path} are <= 105, ignoring", file.ResolvedPath);
continue;
}
_logger.LogDebug("Verifying bone indices for {path}, found {x} skeletons", file.ResolvedPath, skeletonIndices.Count);
foreach (var boneCount in skeletonIndices.Select(k => k).ToList())
{
if (boneCount.Value.Max() > boneIndices.SelectMany(b => b.Value).Max())
{
_logger.LogWarning("Found more bone indices on the animation {path} skeleton {skl} (max indice {idx}) than on any player related skeleton (max indice {idx2})",
file.ResolvedPath, boneCount.Key, boneCount.Value.Max(), boneIndices.SelectMany(b => b.Value).Max());
validationFailed = true;
break;
}
}
}
if (validationFailed)
{
noValidationFailed++;
_logger.LogDebug("Removing {file} from sent file replacements and transient data", file.ResolvedPath);
previousData.FileReplacements[objectKind].Remove(file);
foreach (var gamePath in file.GamePaths)
{
_transientResourceManager.RemoveTransientResource(objectKind, gamePath);
}
}
}
if (noValidationFailed > 0)
{
_mareMediator.Publish(new NotificationMessage("Invalid Skeleton Setup",
$"Your client is attempting to send {noValidationFailed} animation files with invalid bone data. Those animation files have been removed from your sent data. " +
$"Verify that you are using the correct skeleton for those animation files (Check /xllog for more information).",
NotificationType.Warning, TimeSpan.FromSeconds(10)));
}
}
private async Task<IReadOnlyDictionary<string, string[]>> GetFileReplacementsFromPaths(HashSet<string> forwardResolve, HashSet<string> reverseResolve)
{
var forwardPaths = forwardResolve.ToArray();

View File

@@ -24,6 +24,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private readonly DalamudUtilService _dalamudUtil;
private readonly FileDownloadManager _downloadManager;
private readonly FileCacheManager _fileDbManager;
private readonly XivDataAnalyzer _xivDataAnalyzer;
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
private readonly IpcManager _ipcManager;
private readonly CancellationToken _lifetime;
@@ -42,13 +43,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private bool _redrawOnNextApplication = false;
private CombatData? _dataReceivedInCombat;
public long LastAppliedDataSize { get; private set; }
public long LastAppliedDataTris { get; private set; }
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
GameObjectHandlerFactory gameObjectHandlerFactory,
IpcManager ipcManager, FileDownloadManager transferManager,
PluginWarningNotificationService pluginWarningNotificationManager, ServerConfigurationManager serverConfigurationManager,
DalamudUtilService dalamudUtil, CancellationToken lifetime,
FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
FileCacheManager fileDbManager, MareMediator mediator,
XivDataAnalyzer modelAnalyzer) : base(logger, mediator)
{
OnlineUser = onlineUser;
_gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -59,6 +62,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_dalamudUtil = dalamudUtil;
_lifetime = lifetime;
_fileDbManager = fileDbManager;
_xivDataAnalyzer = modelAnalyzer;
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
@@ -100,6 +104,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
});
LastAppliedDataSize = -1;
LastAppliedDataTris = -1;
}
public bool IsVisible
@@ -377,7 +382,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_ = Task.Run(async () =>
{
Dictionary<string, string> moddedPaths = new(StringComparer.Ordinal);
Dictionary<(string GamePath, string? Hash), string> moddedPaths = new();
if (updateModdedPaths)
{
@@ -444,12 +449,20 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (updateModdedPaths)
{
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false);
await _ipcManager.Penumbra.SetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection,
moddedPaths.ToDictionary(k => k.Key.GamePath, k => k.Value, StringComparer.Ordinal)).ConfigureAwait(false);
LastAppliedDataSize = -1;
LastAppliedDataTris = -1;
foreach (var path in moddedPaths.Values.Distinct(StringComparer.OrdinalIgnoreCase).Select(v => new FileInfo(v)).Where(p => p.Exists))
{
if (LastAppliedDataSize == -1) LastAppliedDataSize = 0;
LastAppliedDataSize += path.Length;
}
foreach (var key in moddedPaths.Keys.Where(k => !string.IsNullOrEmpty(k.Hash)))
{
if (LastAppliedDataTris == -1) LastAppliedDataTris = 0;
LastAppliedDataTris += await _xivDataAnalyzer.GetTrianglesByHash(key.Hash!).ConfigureAwait(false);
}
}
if (updateManip)
@@ -619,12 +632,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
}
}
private List<FileReplacementData> TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary<string, string> moddedDictionary, CancellationToken token)
private List<FileReplacementData> TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary<(string GamePath, string? Hash), string> moddedDictionary, CancellationToken token)
{
Stopwatch st = Stopwatch.StartNew();
ConcurrentBag<FileReplacementData> missingFiles = [];
moddedDictionary = new Dictionary<string, string>(StringComparer.Ordinal);
ConcurrentDictionary<string, string> outputDict = new(StringComparer.Ordinal);
moddedDictionary = new Dictionary<(string GamePath, string? Hash), string>();
ConcurrentDictionary<(string GamePath, string? Hash), string> outputDict = new();
bool hasMigrationChanges = false;
try
@@ -649,7 +662,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
foreach (var gamePath in item.GamePaths)
{
outputDict[gamePath] = fileCache.ResolvedFilepath;
outputDict[(gamePath, item.Hash)] = fileCache.ResolvedFilepath;
}
}
else
@@ -659,14 +672,14 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
}
});
moddedDictionary = outputDict.ToDictionary(k => k.Key, k => k.Value, StringComparer.Ordinal);
moddedDictionary = outputDict.ToDictionary(k => k.Key, k => k.Value);
foreach (var item in charaData.FileReplacements.SelectMany(k => k.Value.Where(v => !string.IsNullOrEmpty(v.FileSwapPath))).ToList())
{
foreach (var gamePath in item.GamePaths)
{
Logger.LogTrace("[BASE-{appBase}] Adding file swap for {path}: {fileSwap}", applicationBase, gamePath, item.FileSwapPath);
moddedDictionary[gamePath] = item.FileSwapPath;
moddedDictionary[(gamePath, null)] = item.FileSwapPath;
}
}
}

View File

@@ -50,6 +50,7 @@ public class Pair
public string? PlayerName => GetPlayerName();
public uint PlayerCharacterId => GetPlayerCharacterId();
public long LastAppliedDataSize => CachedPlayer?.LastAppliedDataSize ?? -1;
public long LastAppliedDataTris => CachedPlayer?.LastAppliedDataTris ?? -1;
public UserData UserData => UserPair?.User ?? GroupPair.First().Value.User;