temporary workaround for actor table crash on logout; switch reading memory to use ReadProcessMemory, and just return null if it fails. Ugly but functional until we find a better way

This commit is contained in:
meli 2020-03-31 14:34:19 -07:00
parent fb42419b5d
commit 218bad90b1

View file

@ -1,5 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using Dalamud.Game.ClientState.Actors.Types; using Dalamud.Game.ClientState.Actors.Types;
@ -12,6 +14,17 @@ namespace Dalamud.Game.ClientState.Actors {
/// This collection represents the currently spawned FFXIV actors. /// This collection represents the currently spawned FFXIV actors.
/// </summary> /// </summary>
public class ActorTable : ICollection, IDisposable { public class ActorTable : ICollection, IDisposable {
#region temporary imports for crash workaround
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
IntPtr lpBuffer,
int dwSize,
out IntPtr lpNumberOfBytesRead);
#endregion
private ClientStateAddressResolver Address { get; } private ClientStateAddressResolver Address { get; }
private Dalamud dalamud; private Dalamud dalamud;
@ -43,6 +56,8 @@ namespace Dalamud.Game.ClientState.Actors {
public void Dispose() { public void Dispose() {
if (!this.isReady) if (!this.isReady)
this.someActorTableAccessHook.Dispose(); this.someActorTableAccessHook.Dispose();
this.isReady = false;
} }
private IntPtr SomeActorTableAccessDetour(IntPtr manager, IntPtr offset) { private IntPtr SomeActorTableAccessDetour(IntPtr manager, IntPtr offset) {
@ -66,7 +81,7 @@ namespace Dalamud.Game.ClientState.Actors {
this.someActorTableAccessHook = null; this.someActorTableAccessHook = null;
} }
if (index > Length) if (index >= Length)
return null; return null;
//Log.Information("Trying to get actor at {0}", index); //Log.Information("Trying to get actor at {0}", index);
@ -79,21 +94,27 @@ namespace Dalamud.Game.ClientState.Actors {
if (offset == IntPtr.Zero) if (offset == IntPtr.Zero)
return null; return null;
try { // FIXME: hack workaround for trying to access the player on logout, after the main object has been deleted
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(offset); var sz = Marshal.SizeOf(typeof(Structs.Actor));
var actorMem = Marshal.AllocHGlobal(sz); // we arguably could just reuse this
//Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"), if (!ReadProcessMemory(Process.GetCurrentProcess().Handle, offset, actorMem, sz, out _))
// actorStruct.ObjectKind.ToString()); {
Log.Debug("ActorTable - ReadProcessMemory failed: likely player deletion during logout");
switch (actorStruct.ObjectKind)
{
case ObjectKind.Player: return new PlayerCharacter(actorStruct, this.dalamud);
case ObjectKind.BattleNpc: return new BattleNpc(actorStruct, this.dalamud);
default: return new Actor(actorStruct, this.dalamud);
}
} catch (AccessViolationException) {
return null; return null;
} }
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(actorMem);
Marshal.FreeHGlobal(actorMem);
//Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"),
// actorStruct.ObjectKind.ToString());
switch (actorStruct.ObjectKind)
{
case ObjectKind.Player: return new PlayerCharacter(actorStruct, this.dalamud);
case ObjectKind.BattleNpc: return new BattleNpc(actorStruct, this.dalamud);
default: return new Actor(actorStruct, this.dalamud);
}
} }
} }