@@ -1,7 +1,4 @@
using FFXIVClientStructs.FFXIV.Client.Game.Character ;
using FFXIVClientStructs.FFXIV.Client.Graphics.Render ;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene ;
using FFXIVClientStructs.FFXIV.Client.System.Resource ;
using MareSynchronos.API.Data.Enum ;
using MareSynchronos.FileCache ;
using MareSynchronos.Interop ;
@@ -11,10 +8,7 @@ using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services ;
using Microsoft.Extensions.Logging ;
using System.Diagnostics ;
using System.Globalization ;
using CharacterData = MareSynchronos . PlayerData . Data . CharacterData ;
using Object = FFXIVClientStructs . FFXIV . Client . Graphics . Scene . Object ;
using Weapon = MareSynchronos . Interop . Weapon ;
namespace MareSynchronos.PlayerData.Factories ;
@@ -26,12 +20,11 @@ public class PlayerDataFactory
private readonly IpcManager _ipcManager ;
private readonly ILogger < PlayerDataFactory > _logger ;
private readonly PerformanceCollectorService _performanceCollector ;
private readonly MareConfigService _mareConfigService ;
private readonly TransientResourceManager _transientResourceManager ;
public PlayerDataFactory ( ILogger < PlayerDataFactory > logger , DalamudUtilService dalamudUtil , IpcManager ipcManager ,
TransientResourceManager transientResourceManager , FileCacheManager fileReplacementFactory ,
PerformanceCollectorService performanceCollector , MareConfigService mareConfigService )
PerformanceCollectorService performanceCollector )
{
_logger = logger ;
_dalamudUtil = dalamudUtil ;
@@ -39,7 +32,6 @@ public class PlayerDataFactory
_transientResourceManager = transientResourceManager ;
_fileCacheManager = fileReplacementFactory ;
_performanceCollector = performanceCollector ;
_mareConfigService = mareConfigService ;
_logger . LogTrace ( "Creating " + nameof ( PlayerDataFactory ) ) ;
}
@@ -107,190 +99,6 @@ public class PlayerDataFactory
previousData . CustomizePlusScale = previousCustomize ;
}
private unsafe void AddPlayerSpecificReplacements ( Human * human , HashSet < string > forwardResolve , HashSet < string > reverseResolve )
{
var weaponObject = ( Weapon * ) ( ( Object * ) human ) - > ChildObject ;
if ( ( IntPtr ) weaponObject ! = IntPtr . Zero )
{
var mainHandWeapon = weaponObject - > WeaponRenderModel - > RenderModel ;
AddReplacementsFromRenderModel ( ( Model * ) mainHandWeapon , forwardResolve , reverseResolve ) ;
foreach ( var item in _transientResourceManager . GetTransientResources ( ( IntPtr ) weaponObject ) )
{
_logger . LogTrace ( "Found transient weapon resource: {item}" , item ) ;
forwardResolve . Add ( item ) ;
}
if ( weaponObject - > NextSibling ! = ( IntPtr ) weaponObject )
{
var offHandWeapon = ( ( Weapon * ) weaponObject - > NextSibling ) - > WeaponRenderModel - > RenderModel ;
AddReplacementsFromRenderModel ( ( Model * ) offHandWeapon , forwardResolve , reverseResolve ) ;
foreach ( var item in _transientResourceManager . GetTransientResources ( ( IntPtr ) offHandWeapon ) )
{
_logger . LogTrace ( "Found transient offhand weapon resource: {item}" , item ) ;
forwardResolve . Add ( item ) ;
}
}
}
AddReplacementSkeleton ( ( human ) - > RaceSexId , forwardResolve ) ;
try
{
AddReplacementsFromTexture ( ( human ) - > Decal - > ResourceHandle . FileName . ToString ( ) , forwardResolve , reverseResolve , doNotReverseResolve : false ) ;
}
catch
{
_logger . LogWarning ( "Could not get Decal data" ) ;
}
try
{
AddReplacementsFromTexture ( ( human ) - > LegacyBodyDecal - > ResourceHandle . FileName . ToString ( ) , forwardResolve , reverseResolve , doNotReverseResolve : false ) ;
}
catch
{
_logger . LogWarning ( "Could not get Legacy Body Decal Data" ) ;
}
}
private unsafe void AddReplacementsFromMaterial ( Material * mtrl , HashSet < string > forwardResolve , HashSet < string > reverseResolve )
{
string fileName ;
try
{
fileName = mtrl - > MaterialResourceHandle - > ResourceHandle . FileName . ToString ( ) ;
}
catch
{
_logger . LogWarning ( "Could not get material data" ) ;
return ;
}
_logger . LogTrace ( "Checking File Replacement for Material {file}" , fileName ) ;
var mtrlPath = fileName . Split ( "|" ) [ 2 ] ;
reverseResolve . Add ( mtrlPath ) ;
var mtrlResourceHandle = mtrl - > MaterialResourceHandle ;
for ( var resIdx = 0 ; resIdx < mtrlResourceHandle - > TextureCount ; resIdx + + )
{
string? texPath = null ;
try
{
texPath = mtrlResourceHandle - > TexturePathString ( resIdx ) ;
}
catch ( Exception e )
{
_logger . LogWarning ( e , "Could not get Texture data for Material {file}" , fileName ) ;
}
if ( string . IsNullOrEmpty ( texPath ) ) continue ;
AddReplacementsFromTexture ( texPath , forwardResolve , reverseResolve ) ;
}
try
{
var shpkPath = "shader/sm5/shpk/" + mtrlResourceHandle - > ShpkNameString ;
_logger . LogTrace ( "Checking File Replacement for Shader {path}" , shpkPath ) ;
forwardResolve . Add ( shpkPath ) ;
}
catch ( Exception ex )
{
_logger . LogWarning ( ex , "Could not find shpk for Material {path}" , fileName ) ;
}
}
private unsafe void AddReplacementsFromRenderModel ( Model * mdl , HashSet < string > forwardResolve , HashSet < string > reverseResolve )
{
if ( mdl = = null | | mdl - > ModelResourceHandle = = null | | ( ResourceCategory ) mdl - > ModelResourceHandle - > ResourceHandle . Type . Value ! = ResourceCategory . Chara )
{
return ;
}
string mdlPath ;
try
{
mdlPath = mdl - > ModelResourceHandle - > ResourceHandle . FileName . ToString ( ) ;
}
catch
{
_logger . LogWarning ( "Could not get model data" ) ;
return ;
}
_logger . LogTrace ( "Checking File Replacement for Model {path}" , mdlPath ) ;
reverseResolve . Add ( mdlPath ) ;
for ( var mtrlIdx = 0 ; mtrlIdx < mdl - > MaterialCount ; mtrlIdx + + )
{
var mtrl = mdl - > Materials [ mtrlIdx ] ;
if ( mtrl = = null ) continue ;
AddReplacementsFromMaterial ( mtrl , forwardResolve , reverseResolve ) ;
}
}
private void AddReplacementsFromTexture ( string texPath , HashSet < string > forwardResolve , HashSet < string > reverseResolve , bool doNotReverseResolve = true )
{
if ( string . IsNullOrEmpty ( texPath ) ) return ;
_logger . LogTrace ( "Checking File Replacement for Texture {path}" , texPath ) ;
if ( doNotReverseResolve )
forwardResolve . Add ( texPath ) ;
else
reverseResolve . Add ( texPath ) ;
if ( texPath . Contains ( "/--" , StringComparison . Ordinal ) ) return ;
var dx11Path = texPath . Insert ( texPath . LastIndexOf ( '/' ) + 1 , "--" ) ;
if ( doNotReverseResolve )
forwardResolve . Add ( dx11Path ) ;
else
reverseResolve . Add ( dx11Path ) ;
}
private void AddReplacementSkeleton ( ushort raceSexId , HashSet < string > forwardResolve )
{
string raceSexIdString = raceSexId . ToString ( "0000" , CultureInfo . InvariantCulture ) ;
string skeletonPath = $"chara/human/c{raceSexIdString}/skeleton/base/b0001/skl_c{raceSexIdString}b0001.sklb" ;
_logger . LogTrace ( "Checking skeleton {path}" , skeletonPath ) ;
forwardResolve . Add ( skeletonPath ) ;
}
private unsafe ( HashSet < string > forwardResolve , HashSet < string > reverseResolve ) BuildDataFromModel ( ObjectKind objectKind , nint charaPointer , CancellationToken token )
{
HashSet < string > forwardResolve = new ( StringComparer . Ordinal ) ;
HashSet < string > reverseResolve = new ( StringComparer . Ordinal ) ;
var human = ( Human * ) ( ( Character * ) charaPointer ) - > GameObject . GetDrawObject ( ) ;
for ( var mdlIdx = 0 ; mdlIdx < human - > CharacterBase . SlotCount ; + + mdlIdx )
{
var mdl = human - > CharacterBase . Models [ mdlIdx ] ;
if ( mdl = = null | | mdl - > ModelResourceHandle = = null | | ( ResourceCategory ) mdl - > ModelResourceHandle - > ResourceHandle . Type . Value ! = ResourceCategory . Chara )
{
continue ;
}
token . ThrowIfCancellationRequested ( ) ;
AddReplacementsFromRenderModel ( mdl , forwardResolve , reverseResolve ) ;
}
if ( objectKind = = ObjectKind . Player & & human - > CharacterBase . GetModelType ( ) = = CharacterBase . ModelType . Human )
{
AddPlayerSpecificReplacements ( human , forwardResolve , reverseResolve ) ;
}
return ( forwardResolve , reverseResolve ) ;
}
private async Task < bool > CheckForNullDrawObject ( IntPtr playerPointer )
{
return await _dalamudUtil . RunOnFrameworkThread ( ( ) = > CheckForNullDrawObjectUnsafe ( playerPointer ) ) . ConfigureAwait ( false ) ;
@@ -333,17 +141,10 @@ public class PlayerDataFactory
// penumbra call, it's currently broken
IReadOnlyDictionary < string , string [ ] > ? resolvedPaths ;
if ( _mareConfigService . Current . ExperimentalUsePenumbraResourceTree )
{
resolvedPaths = ( await _ipcManager . PenumbraGetCharacterData ( _logger , playerRelatedObject ) . ConfigureAwait ( false ) ) ! [ 0 ] ;
if ( resolvedPaths = = null ) throw new InvalidOperationException ( "Penumbra returned null data" ) ;
}
else
{
// gather static replacements from render model
var ( forwardResolve , reverseResolve ) = await _dalamudUtil . RunOnFrameworkThread ( ( ) = > BuildDataFromModel ( objectKind , charaPointer , token ) ) . ConfigureAwait ( false ) ;
resolvedPaths = await GetFileReplacementsFromPaths ( forwardResolve , reverseResolve ) . ConfigureAwait ( false ) ;
}
previousData . FileReplacements [ objectKind ] =
new HashSet < FileReplacement > ( resolvedPaths . Select ( c = > new FileReplacement ( [ . . c . Value ] , c . Key ) ) , FileReplacementComparer . Instance )
. Where ( p = > p . HasFileReplacement ) . ToHashSet ( ) ;