Merge branch '2.0.3' into dev
Some checks failed
Tag and Release Lightless / tag-and-release (push) Has been cancelled

This commit is contained in:
defnotken
2026-01-19 12:35:29 -06:00

View File

@@ -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 true;
if (u < 0x0000_0001_0000UL) return false;
if (u > 0x0000_7FFF_FFFF_FFFFUL) return false;
if ((u & 0x7UL) != 0) return false;
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(ptr);
value = Marshal.ReadIntPtr(addr);
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;