Allow /penumbra redraw self and redrawing all, as well as the actor watcher, to redraw the GPose character.

This commit is contained in:
Ottermandias 2021-07-19 13:30:37 +02:00
parent 3a7716717c
commit 3a7209109a
4 changed files with 82 additions and 30 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game.ClientState.Actors.Types; using Dalamud.Game.ClientState.Actors.Types;
using Dalamud.Plugin; using Dalamud.Plugin;
@ -21,6 +22,8 @@ namespace Penumbra.Interop
public class ActorRefresher : IDisposable public class ActorRefresher : IDisposable
{ {
private delegate void ManipulateDraw( IntPtr actor );
[Flags] [Flags]
public enum LoadingFlags : int public enum LoadingFlags : int
{ {
@ -35,6 +38,7 @@ namespace Penumbra.Interop
private const int RenderModeOffset = 0x0104; private const int RenderModeOffset = 0x0104;
private const int UnloadAllRedrawDelay = 250; private const int UnloadAllRedrawDelay = 250;
private const int NpcActorId = -536870912; private const int NpcActorId = -536870912;
public const int GPosePlayerActorIdx = 201;
private readonly DalamudPluginInterface _pi; private readonly DalamudPluginInterface _pi;
private readonly ModManager _mods; private readonly ModManager _mods;
@ -71,12 +75,22 @@ namespace Penumbra.Interop
_changedSettings = false; _changedSettings = false;
} }
private unsafe void WriteInvisible( IntPtr renderPtr ) private unsafe void WriteInvisible( Actor actor, int actorIdx )
{ {
if( renderPtr != IntPtr.Zero ) var renderPtr = RenderPtr( actor );
if( renderPtr == IntPtr.Zero )
{ {
return;
}
_currentActorStartState = *( LoadingFlags* )renderPtr; _currentActorStartState = *( LoadingFlags* )renderPtr;
*( LoadingFlags* )renderPtr |= LoadingFlags.Invisibility; *( LoadingFlags* )renderPtr |= LoadingFlags.Invisibility;
if( actorIdx == GPosePlayerActorIdx )
{
var ptr = ( void*** )actor.Address;
var disableDraw = Marshal.GetDelegateForFunctionPointer< ManipulateDraw >( new IntPtr( ptr[ 0 ][ 17 ] ) );
disableDraw( actor.Address );
} }
} }
@ -101,11 +115,16 @@ namespace Penumbra.Interop
return false; return false;
} }
private static unsafe void WriteVisible( IntPtr renderPtr ) private static unsafe void WriteVisible( Actor actor, int actorIdx )
{
if( renderPtr != IntPtr.Zero )
{ {
var renderPtr = RenderPtr( actor );
*( LoadingFlags* )renderPtr &= ~LoadingFlags.Invisibility; *( LoadingFlags* )renderPtr &= ~LoadingFlags.Invisibility;
if( actorIdx == GPosePlayerActorIdx )
{
var ptr = ( void*** )actor.Address;
var enableDraw = Marshal.GetDelegateForFunctionPointer< ManipulateDraw >( new IntPtr( ptr[ 0 ][ 16 ] ) );
enableDraw( actor.Address );
} }
} }
@ -124,8 +143,19 @@ namespace Penumbra.Interop
return _currentActorName == actor.Name; return _currentActorName == actor.Name;
} }
private Actor? FindCurrentActor() private (Actor?, int) FindCurrentActor()
=> _pi.ClientState.Actors.FirstOrDefault( CheckActor ); {
for( var i = 0; i < _pi.ClientState.Actors.Length; ++i )
{
var actor = _pi.ClientState.Actors[ i ];
if( actor != null && CheckActor( actor ) )
{
return ( actor, i );
}
}
return ( null, -1 );
}
private void PopActor() private void PopActor()
{ {
@ -135,7 +165,7 @@ namespace Penumbra.Interop
_currentActorName = name; _currentActorName = name;
_currentActorId = id; _currentActorId = id;
_currentActorRedraw = s; _currentActorRedraw = s;
var actor = FindCurrentActor(); var (actor, idx) = FindCurrentActor();
if( actor == null ) if( actor == null )
{ {
return; return;
@ -151,7 +181,7 @@ namespace Penumbra.Interop
private void ApplySettingsOrRedraw() private void ApplySettingsOrRedraw()
{ {
var actor = FindCurrentActor(); var (actor, idx) = FindCurrentActor();
if( actor == null ) if( actor == null )
{ {
_currentFrame = 0; _currentFrame = 0;
@ -161,7 +191,8 @@ namespace Penumbra.Interop
switch( _currentActorRedraw ) switch( _currentActorRedraw )
{ {
case Redraw.Unload: case Redraw.Unload:
WriteInvisible( RenderPtr( actor ) ); WriteInvisible( actor, idx );
_currentFrame = 0; _currentFrame = 0;
break; break;
case Redraw.RedrawWithSettings: case Redraw.RedrawWithSettings:
@ -169,16 +200,16 @@ namespace Penumbra.Interop
++_currentFrame; ++_currentFrame;
break; break;
case Redraw.RedrawWithoutSettings: case Redraw.RedrawWithoutSettings:
WriteVisible( RenderPtr( actor ) ); WriteVisible( actor, idx );
_currentFrame = 0; _currentFrame = 0;
break; break;
case Redraw.WithoutSettings: case Redraw.WithoutSettings:
WriteInvisible( RenderPtr( actor ) ); WriteInvisible( actor, idx );
++_currentFrame; ++_currentFrame;
break; break;
case Redraw.WithSettings: case Redraw.WithSettings:
ChangeSettings(); ChangeSettings();
WriteInvisible( RenderPtr( actor ) ); WriteInvisible( actor, idx );
++_currentFrame; ++_currentFrame;
break; break;
case Redraw.OnlyWithSettings: case Redraw.OnlyWithSettings:
@ -188,7 +219,7 @@ namespace Penumbra.Interop
return; return;
} }
WriteInvisible( RenderPtr( actor ) ); WriteInvisible( actor, idx );
++_currentFrame; ++_currentFrame;
break; break;
default: throw new InvalidEnumArgumentException(); default: throw new InvalidEnumArgumentException();
@ -197,20 +228,20 @@ namespace Penumbra.Interop
private void StartRedrawAndWait() private void StartRedrawAndWait()
{ {
var actor = FindCurrentActor(); var (actor, idx) = FindCurrentActor();
if( actor == null ) if( actor == null )
{ {
RevertSettings(); RevertSettings();
return; return;
} }
WriteVisible( RenderPtr( actor ) ); WriteVisible( actor, idx );
_currentFrame = _changedSettings ? _currentFrame + 1 : 0; _currentFrame = _changedSettings ? _currentFrame + 1 : 0;
} }
private void RevertSettings() private void RevertSettings()
{ {
var actor = FindCurrentActor(); var (actor, idx) = FindCurrentActor();
if( actor != null ) if( actor != null )
{ {
if( !StillLoading( RenderPtr( actor ) ) ) if( !StillLoading( RenderPtr( actor ) ) )
@ -269,14 +300,20 @@ namespace Penumbra.Interop
} }
} }
private Actor? GetLocalPlayer()
{
var gPoseActor = _pi.ClientState.Actors[ GPosePlayerActorIdx ];
return gPoseActor ?? _pi.ClientState.Actors[ 0 ];
}
private Actor? GetName( string name ) private Actor? GetName( string name )
{ {
var lowerName = name.ToLowerInvariant(); var lowerName = name.ToLowerInvariant();
return lowerName switch return lowerName switch
{ {
"" => null, "" => null,
"<me>" => _pi.ClientState.Actors[ 0 ], "<me>" => GetLocalPlayer(),
"self" => _pi.ClientState.Actors[ 0 ], "self" => GetLocalPlayer(),
"<t>" => _pi.ClientState.Targets.CurrentTarget, "<t>" => _pi.ClientState.Targets.CurrentTarget,
"target" => _pi.ClientState.Targets.CurrentTarget, "target" => _pi.ClientState.Targets.CurrentTarget,
"<f>" => _pi.ClientState.Targets.FocusTarget, "<f>" => _pi.ClientState.Targets.FocusTarget,
@ -303,18 +340,18 @@ namespace Penumbra.Interop
private void UnloadAll() private void UnloadAll()
{ {
Clear(); Clear();
foreach( var a in _pi.ClientState.Actors ) foreach( var (actor, index) in _pi.ClientState.Actors.Select( ( a, i ) => ( a, i ) ) )
{ {
WriteInvisible( RenderPtr( a ) ); WriteInvisible( actor, index );
} }
} }
private void RedrawAllWithoutSettings() private void RedrawAllWithoutSettings()
{ {
Clear(); Clear();
foreach( var a in _pi.ClientState.Actors ) foreach( var (actor, index) in _pi.ClientState.Actors.Select( ( a, i ) => ( a, i ) ) )
{ {
WriteVisible( RenderPtr( a ) ); WriteVisible( actor, index );
} }
} }

View file

@ -14,6 +14,7 @@ namespace Penumbra.Interop
private readonly DalamudPluginInterface _pi; private readonly DalamudPluginInterface _pi;
private readonly Dictionary< string, CharEquipment > _equip = new(); private readonly Dictionary< string, CharEquipment > _equip = new();
private int _frameTicker; private int _frameTicker;
private IntPtr _lastGPoseAddress = IntPtr.Zero;
public PlayerWatcher( DalamudPluginInterface pi ) public PlayerWatcher( DalamudPluginInterface pi )
=> _pi = pi; => _pi = pi;
@ -82,6 +83,17 @@ namespace Penumbra.Interop
private void OnFrameworkUpdate( object framework ) private void OnFrameworkUpdate( object framework )
{ {
var actors = _pi.ClientState.Actors; var actors = _pi.ClientState.Actors;
var gPoseActor = actors[ ActorRefresher.GPosePlayerActorIdx ];
if( gPoseActor == null )
{
_lastGPoseAddress = IntPtr.Zero;
}
else if( gPoseActor.Address != _lastGPoseAddress )
{
_lastGPoseAddress = gPoseActor.Address;
ActorChanged?.Invoke( gPoseActor );
}
for( var i = 0; i < ActorsPerFrame; ++i ) for( var i = 0; i < ActorsPerFrame; ++i )
{ {
_frameTicker = _frameTicker < actors.Length - 2 _frameTicker = _frameTicker < actors.Length - 2

View file

@ -1,3 +1,4 @@
using System;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Plugin; using Dalamud.Plugin;
using EmbedIO; using EmbedIO;
@ -70,6 +71,7 @@ namespace Penumbra
SettingsInterface = new SettingsInterface( this ); SettingsInterface = new SettingsInterface( this );
PluginInterface.UiBuilder.DisableGposeUiHide = true;
PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw; PluginInterface.UiBuilder.OnBuildUi += SettingsInterface.Draw;
if( Configuration.EnableHttpApi ) if( Configuration.EnableHttpApi )

View file

@ -181,9 +181,9 @@ namespace Penumbra.UI
.GetField( "_currentActorRedraw", BindingFlags.Instance | BindingFlags.NonPublic ) .GetField( "_currentActorRedraw", BindingFlags.Instance | BindingFlags.NonPublic )
?.GetValue( _plugin.ActorRefresher ); ?.GetValue( _plugin.ActorRefresher );
var currentActor = ( Actor? )_plugin.ActorRefresher.GetType() var (currentActor, currentActorIdx) = ( (Actor?, int) )_plugin.ActorRefresher.GetType()
.GetMethod( "FindCurrentActor", BindingFlags.NonPublic | BindingFlags.Instance )? .GetMethod( "FindCurrentActor", BindingFlags.NonPublic | BindingFlags.Instance )?
.Invoke( _plugin.ActorRefresher, Array.Empty< object >() ); .Invoke( _plugin.ActorRefresher, Array.Empty< object >() )!;
var currentRender = currentActor != null var currentRender = currentActor != null
? ( ActorRefresher.LoadingFlags? )Marshal.ReadInt32( ActorRefresher.RenderPtr( currentActor ) ) ? ( ActorRefresher.LoadingFlags? )Marshal.ReadInt32( ActorRefresher.RenderPtr( currentActor ) )
@ -199,6 +199,7 @@ namespace Penumbra.UI
PrintValue( "Current Actor Start State", ( ( int? )currentActorStartState )?.ToString( "X8" ) ?? "null" ); PrintValue( "Current Actor Start State", ( ( int? )currentActorStartState )?.ToString( "X8" ) ?? "null" );
PrintValue( "Current Actor Redraw", currentActorRedraw?.ToString() ?? "null" ); PrintValue( "Current Actor Redraw", currentActorRedraw?.ToString() ?? "null" );
PrintValue( "Current Actor Address", currentActor?.Address.ToString( "X16" ) ?? "null" ); PrintValue( "Current Actor Address", currentActor?.Address.ToString( "X16" ) ?? "null" );
PrintValue( "Current Actor Index", currentActorIdx >= 0 ? currentActorIdx.ToString() : "null" );
PrintValue( "Current Actor Render Flags", ( ( int? )currentRender )?.ToString( "X8" ) ?? "null" ); PrintValue( "Current Actor Render Flags", ( ( int? )currentRender )?.ToString( "X8" ) ?? "null" );
ImGui.EndTable(); ImGui.EndTable();
} }