Fix issue with Penumbra load order and visor state.

This commit is contained in:
Ottermandias 2024-06-15 11:52:38 +02:00
parent 205a95b254
commit 5cf6b0eb75
3 changed files with 31 additions and 6 deletions

View file

@ -12,5 +12,8 @@ public sealed class PenumbraReloaded()
{ {
/// <seealso cref="Interop.ChangeCustomizeService.Restore"/> /// <seealso cref="Interop.ChangeCustomizeService.Restore"/>
ChangeCustomizeService = 0, ChangeCustomizeService = 0,
/// <seealso cref="Interop.VisorService.Restore"/>
VisorService = 0,
} }
} }

View file

@ -9,17 +9,24 @@ namespace Glamourer.Interop;
public class VisorService : IDisposable public class VisorService : IDisposable
{ {
public readonly VisorStateChanged Event; private readonly PenumbraReloaded _penumbra;
private readonly IGameInteropProvider _interop;
public readonly VisorStateChanged Event;
public unsafe VisorService(VisorStateChanged visorStateChanged, IGameInteropProvider interop) public VisorService(VisorStateChanged visorStateChanged, IGameInteropProvider interop, PenumbraReloaded penumbra)
{ {
_interop = interop;
_penumbra = penumbra;
Event = visorStateChanged; Event = visorStateChanged;
_setupVisorHook = interop.HookFromAddress<UpdateVisorDelegateInternal>((nint)Human.MemberFunctionPointers.SetupVisor, SetupVisorDetour); _setupVisorHook = Create();
_setupVisorHook.Enable(); _penumbra.Subscribe(Restore, PenumbraReloaded.Priority.VisorService);
} }
public void Dispose() public void Dispose()
=> _setupVisorHook.Dispose(); {
_setupVisorHook.Dispose();
_penumbra.Unsubscribe(Restore);
}
/// <summary> Obtain the current state of the Visor for the given draw object (true: toggled). </summary> /// <summary> Obtain the current state of the Visor for the given draw object (true: toggled). </summary>
public static unsafe bool GetVisorState(Model characterBase) public static unsafe bool GetVisorState(Model characterBase)
@ -45,7 +52,7 @@ public class VisorService : IDisposable
private delegate void UpdateVisorDelegateInternal(nint humanPtr, ushort modelId, byte on); private delegate void UpdateVisorDelegateInternal(nint humanPtr, ushort modelId, byte on);
private readonly Hook<UpdateVisorDelegateInternal> _setupVisorHook; private Hook<UpdateVisorDelegateInternal> _setupVisorHook;
private void SetupVisorDetour(nint human, ushort modelId, byte value) private void SetupVisorDetour(nint human, ushort modelId, byte value)
{ {
@ -72,4 +79,17 @@ public class VisorService : IDisposable
human.AsCharacterBase->VisorToggled = on; human.AsCharacterBase->VisorToggled = on;
_setupVisorHook.Original(human.Address, modelId, on ? (byte)1 : (byte)0); _setupVisorHook.Original(human.Address, modelId, on ? (byte)1 : (byte)0);
} }
private unsafe Hook<UpdateVisorDelegateInternal> Create()
{
var hook = _interop.HookFromAddress<UpdateVisorDelegateInternal>((nint)Human.MemberFunctionPointers.SetupVisor, SetupVisorDetour);
hook.Enable();
return hook;
}
private void Restore()
{
_setupVisorHook.Dispose();
_setupVisorHook = Create();
}
} }

View file

@ -578,6 +578,8 @@ public class StateListener : IDisposable
// We do not need to handle fixed designs, // We do not need to handle fixed designs,
// since a fixed design would already have established state-tracking. // since a fixed design would already have established state-tracking.
var actor = _penumbra.GameObjectFromDrawObject(model); var actor = _penumbra.GameObjectFromDrawObject(model);
if (!actor.IsCharacter)
return;
// Only actually change anything if the actor state changed, // Only actually change anything if the actor state changed,
// when equipping headgear the method is called with the current draw object state, // when equipping headgear the method is called with the current draw object state,