@@ -15,6 +15,7 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary<nint, (ni
15
15
private readonly CreateCharacterBase _createCharacterBase ;
16
16
private readonly WeaponReload _weaponReload ;
17
17
private readonly CharacterBaseDestructor _characterBaseDestructor ;
18
+ private readonly CharacterDestructor _characterDestructor ;
18
19
private readonly GameState _gameState ;
19
20
20
21
private readonly Dictionary < nint , ( nint GameObject , bool IsChild ) > _drawObjectToGameObject = [ ] ;
@@ -23,21 +24,24 @@ public nint LastGameObject
23
24
=> _gameState . LastGameObject ;
24
25
25
26
public unsafe DrawObjectState ( ObjectManager objects , CreateCharacterBase createCharacterBase , WeaponReload weaponReload ,
26
- CharacterBaseDestructor characterBaseDestructor , GameState gameState , IFramework framework )
27
+ CharacterBaseDestructor characterBaseDestructor , GameState gameState , IFramework framework , CharacterDestructor characterDestructor )
27
28
{
28
29
_objects = objects ;
29
30
_createCharacterBase = createCharacterBase ;
30
31
_weaponReload = weaponReload ;
31
32
_characterBaseDestructor = characterBaseDestructor ;
32
33
_gameState = gameState ;
34
+ _characterDestructor = characterDestructor ;
33
35
framework . RunOnFrameworkThread ( InitializeDrawObjects ) ;
34
36
35
37
_weaponReload . Subscribe ( OnWeaponReloading , WeaponReload . Priority . DrawObjectState ) ;
36
38
_weaponReload . Subscribe ( OnWeaponReloaded , WeaponReload . PostEvent . Priority . DrawObjectState ) ;
37
39
_createCharacterBase . Subscribe ( OnCharacterBaseCreated , CreateCharacterBase . PostEvent . Priority . DrawObjectState ) ;
38
40
_characterBaseDestructor . Subscribe ( OnCharacterBaseDestructor , CharacterBaseDestructor . Priority . DrawObjectState ) ;
41
+ _characterDestructor . Subscribe ( OnCharacterDestructor , CharacterDestructor . Priority . DrawObjectState ) ;
39
42
}
40
43
44
+
41
45
public bool ContainsKey ( nint key )
42
46
=> _drawObjectToGameObject . ContainsKey ( key ) ;
43
47
@@ -68,6 +72,36 @@ public unsafe void Dispose()
68
72
_weaponReload . Unsubscribe ( OnWeaponReloaded ) ;
69
73
_createCharacterBase . Unsubscribe ( OnCharacterBaseCreated ) ;
70
74
_characterBaseDestructor . Unsubscribe ( OnCharacterBaseDestructor ) ;
75
+ _characterDestructor . Unsubscribe ( OnCharacterDestructor ) ;
76
+ }
77
+
78
+ /// <remarks>
79
+ /// Seems like sometimes the draw object of a game object is destroyed in frames after the original game object is already destroyed.
80
+ /// So protect against outdated game object pointers in the dictionary.
81
+ /// </remarks>
82
+ private unsafe void OnCharacterDestructor ( Character * a )
83
+ {
84
+ if ( a is null )
85
+ return ;
86
+
87
+ var character = ( nint ) a ;
88
+ var delete = stackalloc nint [ 5 ] ;
89
+ var current = 0 ;
90
+ foreach ( var ( drawObject , ( gameObject , _) ) in _drawObjectToGameObject )
91
+ {
92
+ if ( gameObject != character )
93
+ continue ;
94
+
95
+ delete [ current ++ ] = drawObject ;
96
+ if ( current is 4 )
97
+ break ;
98
+ }
99
+
100
+ for ( var ptr = delete ; * ptr != nint . Zero ; ++ ptr )
101
+ {
102
+ _drawObjectToGameObject . Remove ( * ptr , out var pair ) ;
103
+ Penumbra . Log . Excessive ( $ "[DrawObjectState] Removed draw object 0x{ * ptr : X} -> 0x{ ( nint ) a : X} (actual: 0x{ pair . GameObject : X} , { pair . IsChild } ).") ;
104
+ }
71
105
}
72
106
73
107
private unsafe void OnWeaponReloading ( DrawDataContainer * _ , Character * character , CharacterWeapon * _2 )
0 commit comments