Maybe fix crash issue in AtchHook1 / issue with kept draw object links.

This commit is contained in:
Ottermandias 2025-04-02 23:36:56 +02:00
parent abb47751c8
commit c3be151d40
3 changed files with 38 additions and 2 deletions

View file

@ -15,6 +15,9 @@ public sealed unsafe class CharacterDestructor : EventWrapperPtr<Character, Char
/// <seealso cref="PathResolving.IdentifiedCollectionCache"/> /// <seealso cref="PathResolving.IdentifiedCollectionCache"/>
IdentifiedCollectionCache = 0, IdentifiedCollectionCache = 0,
/// <seealso cref="PathResolving.DrawObjectState.OnCharacterDestructor"/>
DrawObjectState = 0,
} }
public CharacterDestructor(HookManager hooks) public CharacterDestructor(HookManager hooks)

View file

@ -15,6 +15,7 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<nint, (ni
private readonly CreateCharacterBase _createCharacterBase; private readonly CreateCharacterBase _createCharacterBase;
private readonly WeaponReload _weaponReload; private readonly WeaponReload _weaponReload;
private readonly CharacterBaseDestructor _characterBaseDestructor; private readonly CharacterBaseDestructor _characterBaseDestructor;
private readonly CharacterDestructor _characterDestructor;
private readonly GameState _gameState; private readonly GameState _gameState;
private readonly Dictionary<nint, (nint GameObject, bool IsChild)> _drawObjectToGameObject = []; private readonly Dictionary<nint, (nint GameObject, bool IsChild)> _drawObjectToGameObject = [];
@ -23,21 +24,24 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<nint, (ni
=> _gameState.LastGameObject; => _gameState.LastGameObject;
public unsafe DrawObjectState(ObjectManager objects, CreateCharacterBase createCharacterBase, WeaponReload weaponReload, public unsafe DrawObjectState(ObjectManager objects, CreateCharacterBase createCharacterBase, WeaponReload weaponReload,
CharacterBaseDestructor characterBaseDestructor, GameState gameState, IFramework framework) CharacterBaseDestructor characterBaseDestructor, GameState gameState, IFramework framework, CharacterDestructor characterDestructor)
{ {
_objects = objects; _objects = objects;
_createCharacterBase = createCharacterBase; _createCharacterBase = createCharacterBase;
_weaponReload = weaponReload; _weaponReload = weaponReload;
_characterBaseDestructor = characterBaseDestructor; _characterBaseDestructor = characterBaseDestructor;
_gameState = gameState; _gameState = gameState;
_characterDestructor = characterDestructor;
framework.RunOnFrameworkThread(InitializeDrawObjects); framework.RunOnFrameworkThread(InitializeDrawObjects);
_weaponReload.Subscribe(OnWeaponReloading, WeaponReload.Priority.DrawObjectState); _weaponReload.Subscribe(OnWeaponReloading, WeaponReload.Priority.DrawObjectState);
_weaponReload.Subscribe(OnWeaponReloaded, WeaponReload.PostEvent.Priority.DrawObjectState); _weaponReload.Subscribe(OnWeaponReloaded, WeaponReload.PostEvent.Priority.DrawObjectState);
_createCharacterBase.Subscribe(OnCharacterBaseCreated, CreateCharacterBase.PostEvent.Priority.DrawObjectState); _createCharacterBase.Subscribe(OnCharacterBaseCreated, CreateCharacterBase.PostEvent.Priority.DrawObjectState);
_characterBaseDestructor.Subscribe(OnCharacterBaseDestructor, CharacterBaseDestructor.Priority.DrawObjectState); _characterBaseDestructor.Subscribe(OnCharacterBaseDestructor, CharacterBaseDestructor.Priority.DrawObjectState);
_characterDestructor.Subscribe(OnCharacterDestructor, CharacterDestructor.Priority.DrawObjectState);
} }
public bool ContainsKey(nint key) public bool ContainsKey(nint key)
=> _drawObjectToGameObject.ContainsKey(key); => _drawObjectToGameObject.ContainsKey(key);
@ -68,6 +72,36 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<nint, (ni
_weaponReload.Unsubscribe(OnWeaponReloaded); _weaponReload.Unsubscribe(OnWeaponReloaded);
_createCharacterBase.Unsubscribe(OnCharacterBaseCreated); _createCharacterBase.Unsubscribe(OnCharacterBaseCreated);
_characterBaseDestructor.Unsubscribe(OnCharacterBaseDestructor); _characterBaseDestructor.Unsubscribe(OnCharacterBaseDestructor);
_characterDestructor.Unsubscribe(OnCharacterDestructor);
}
/// <remarks>
/// Seems like sometimes the draw object of a game object is destroyed in frames after the original game object is already destroyed.
/// So protect against outdated game object pointers in the dictionary.
/// </remarks>
private unsafe void OnCharacterDestructor(Character* a)
{
if (a is null)
return;
var character = (nint)a;
var delete = stackalloc nint[5];
var current = 0;
foreach (var (drawObject, (gameObject, _)) in _drawObjectToGameObject)
{
if (gameObject != character)
continue;
delete[current++] = drawObject;
if (current is 4)
break;
}
for (var ptr = delete; *ptr != nint.Zero; ++ptr)
{
_drawObjectToGameObject.Remove(*ptr, out var pair);
Penumbra.Log.Excessive($"[DrawObjectState] Removed draw object 0x{*ptr:X} -> 0x{(nint)a:X} (actual: 0x{pair.GameObject:X}, {pair.IsChild}).");
}
} }
private unsafe void OnWeaponReloading(DrawDataContainer* _, Character* character, CharacterWeapon* _2) private unsafe void OnWeaponReloading(DrawDataContainer* _, Character* character, CharacterWeapon* _2)

View file

@ -90,7 +90,6 @@ public class PenumbraChangelog : IUiService
.RegisterEntry("The EQP entry previously named Unknown 4 was renamed to 'Hide Glove Cuffs'.") .RegisterEntry("The EQP entry previously named Unknown 4 was renamed to 'Hide Glove Cuffs'.")
.RegisterEntry("Fixed the changed item identification for EST changes.") .RegisterEntry("Fixed the changed item identification for EST changes.")
.RegisterEntry("Fixed clipping issues in the changed items panel when no grouping was active."); .RegisterEntry("Fixed clipping issues in the changed items panel when no grouping was active.");
private static void Add1_3_5_0(Changelog log) private static void Add1_3_5_0(Changelog log)