@@ -1,6 +1,5 @@
using Dalamud.Utility ;
using FFXIVClientStructs.FFXIV.Client.Game.Character ;
using LightlessSync.API.Data.Enum ;
using FFXIVClientStructs.FFXIV.Client.Game.Object ;
using LightlessSync.FileCache ;
using LightlessSync.Interop.Ipc ;
using LightlessSync.LightlessConfiguration ;
@@ -14,6 +13,7 @@ using Microsoft.Extensions.Logging;
using System.Collections.Concurrent ;
using System.Diagnostics ;
using System.Runtime.InteropServices ;
using ObjectKind = LightlessSync . API . Data . Enum . ObjectKind ;
namespace LightlessSync.PlayerData.Factories ;
@@ -119,39 +119,48 @@ public class PlayerDataFactory
return null ;
}
private static readonly int _drawObjectOffset =
( int ) Marshal . OffsetOf < GameObject > ( nameof ( GameObject . DrawObject ) ) ;
private async Task < bool > CheckForNullDrawObject ( IntPtr playerPointer )
= > await _dalamudUtil . RunOnFrameworkThread ( ( ) = > CheckForNullDrawObjectUnsafe ( playerPointer ) ) . ConfigureAwait ( false ) ;
= > await _dalamudUtil . RunOnFrameworkThread ( ( ) = >
{
nint basePtr = playerPointer ;
private unsafe static bool CheckForNullDrawObjectUnsafe ( IntPtr playerPointer )
if ( ! LooksLikeUserPtr ( basePtr ) )
return true ;
nint drawObjAddr = basePtr + _drawObjectOffset ;
if ( ! TryReadIntPtr ( drawObjAddr , out var drawObj ) )
return true ;
return drawObj = = 0 ;
} ) . ConfigureAwait ( false ) ;
private static bool LooksLikeUserPtr ( nint p )
{
if ( playerPointer = = IntPtr . Zero )
return true ;
if ( p = = 0 ) return false ;
if ( ! IsPointerValid ( playerPointer ) )
return true ;
ulong u = ( ulong ) p ;
var character = ( Character * ) playerPointer ;
if ( character = = null )
return tru e;
if ( u < 0x0000 _0001_0000UL ) return false ;
if ( u > 0x0000 _7FFF_FFFF_FFFFUL ) return false ;
if ( ( u & 0x7 UL ) ! = 0 ) return fals e;
var gameObject = & character - > GameObject ;
if ( gameObject = = null )
return true ;
if ( ! IsPointerValid ( ( IntPtr ) gameObject ) )
return true ;
return gameObject - > DrawObject = = null ;
return true ;
}
private static bool IsPointerValid ( IntPtr ptr )
private static bool TryReadIntPtr ( nint addr , out nint value )
{
if ( ptr = = IntPtr . Zero )
value = 0 ;
if ( ! VirtualReadable ( addr ) )
return false ;
try
{
_ = Marshal . ReadByte ( pt r) ;
value = Marshal . ReadIntPtr ( add r) ;
return true ;
}
catch
@@ -160,6 +169,37 @@ public class PlayerDataFactory
}
}
private static bool VirtualReadable ( nint addr )
{
if ( VirtualQuery ( addr , out var mbi , ( nuint ) Marshal . SizeOf < MEMORY_BASIC_INFORMATION > ( ) ) = = 0 )
return false ;
const uint MEM_COMMIT = 0x1000 ;
const uint PAGE_NOACCESS = 0x01 ;
const uint PAGE_GUARD = 0x100 ;
if ( mbi . State ! = MEM_COMMIT ) return false ;
if ( ( mbi . Protect & PAGE_GUARD ) ! = 0 ) return false ;
if ( mbi . Protect = = PAGE_NOACCESS ) return false ;
return true ;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern nuint VirtualQuery ( nint lpAddress , out MEMORY_BASIC_INFORMATION lpBuffer , nuint dwLength ) ;
[StructLayout(LayoutKind.Sequential)]
private struct MEMORY_BASIC_INFORMATION
{
public nint BaseAddress ;
public nint AllocationBase ;
public uint AllocationProtect ;
public nuint RegionSize ;
public uint State ;
public uint Protect ;
public uint Type ;
}
private static bool IsCacheFresh ( CacheEntry entry )
= > ( DateTime . UtcNow - entry . CreatedUtc ) < = _characterCacheTtl ;