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.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Dalamud.Game.ClientState.Actors.Types;
using Dalamud.Plugin;
@ -21,6 +22,8 @@ namespace Penumbra.Interop
public class ActorRefresher : IDisposable
{
private delegate void ManipulateDraw( IntPtr actor );
[Flags]
public enum LoadingFlags : int
{
@ -35,6 +38,7 @@ namespace Penumbra.Interop
private const int RenderModeOffset = 0x0104;
private const int UnloadAllRedrawDelay = 250;
private const int NpcActorId = -536870912;
public const int GPosePlayerActorIdx = 201;
private readonly DalamudPluginInterface _pi;
private readonly ModManager _mods;
@ -71,12 +75,22 @@ namespace Penumbra.Interop
_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;
*( 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;
}
private static unsafe void WriteVisible( IntPtr renderPtr )
{
if( renderPtr != IntPtr.Zero )
private static unsafe void WriteVisible( Actor actor, int actorIdx )
{
var renderPtr = RenderPtr( actor );
*( 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;
}
private Actor? FindCurrentActor()
=> _pi.ClientState.Actors.FirstOrDefault( CheckActor );
private (Actor?, int) FindCurrentActor()
{
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()
{
@ -135,7 +165,7 @@ namespace Penumbra.Interop
_currentActorName = name;
_currentActorId = id;
_currentActorRedraw = s;
var actor = FindCurrentActor();
var (actor, idx) = FindCurrentActor();
if( actor == null )
{
return;
@ -151,7 +181,7 @@ namespace Penumbra.Interop
private void ApplySettingsOrRedraw()
{
var actor = FindCurrentActor();
var (actor, idx) = FindCurrentActor();
if( actor == null )
{
_currentFrame = 0;
@ -161,7 +191,8 @@ namespace Penumbra.Interop
switch( _currentActorRedraw )
{
case Redraw.Unload:
WriteInvisible( RenderPtr( actor ) );
WriteInvisible( actor, idx );
_currentFrame = 0;
break;
case Redraw.RedrawWithSettings:
@ -169,16 +200,16 @@ namespace Penumbra.Interop
++_currentFrame;
break;
case Redraw.RedrawWithoutSettings:
WriteVisible( RenderPtr( actor ) );
WriteVisible( actor, idx );
_currentFrame = 0;
break;
case Redraw.WithoutSettings:
WriteInvisible( RenderPtr( actor ) );
WriteInvisible( actor, idx );
++_currentFrame;
break;
case Redraw.WithSettings:
ChangeSettings();
WriteInvisible( RenderPtr( actor ) );
WriteInvisible( actor, idx );
++_currentFrame;
break;
case Redraw.OnlyWithSettings:
@ -188,7 +219,7 @@ namespace Penumbra.Interop
return;
}
WriteInvisible( RenderPtr( actor ) );
WriteInvisible( actor, idx );
++_currentFrame;
break;
default: throw new InvalidEnumArgumentException();
@ -197,20 +228,20 @@ namespace Penumbra.Interop
private void StartRedrawAndWait()
{
var actor = FindCurrentActor();
var (actor, idx) = FindCurrentActor();
if( actor == null )
{
RevertSettings();
return;
}
WriteVisible( RenderPtr( actor ) );
WriteVisible( actor, idx );
_currentFrame = _changedSettings ? _currentFrame + 1 : 0;
}
private void RevertSettings()
{
var actor = FindCurrentActor();
var (actor, idx) = FindCurrentActor();
if( actor != null )
{
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 )
{
var lowerName = name.ToLowerInvariant();
return lowerName switch
{
"" => null,
"<me>" => _pi.ClientState.Actors[ 0 ],
"self" => _pi.ClientState.Actors[ 0 ],
"<me>" => GetLocalPlayer(),
"self" => GetLocalPlayer(),
"<t>" => _pi.ClientState.Targets.CurrentTarget,
"target" => _pi.ClientState.Targets.CurrentTarget,
"<f>" => _pi.ClientState.Targets.FocusTarget,
@ -303,18 +340,18 @@ namespace Penumbra.Interop
private void UnloadAll()
{
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()
{
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 Dictionary< string, CharEquipment > _equip = new();
private int _frameTicker;
private IntPtr _lastGPoseAddress = IntPtr.Zero;
public PlayerWatcher( DalamudPluginInterface pi )
=> _pi = pi;
@ -82,6 +83,17 @@ namespace Penumbra.Interop
private void OnFrameworkUpdate( object framework )
{
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 )
{
_frameTicker = _frameTicker < actors.Length - 2

View file

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

View file

@ -181,9 +181,9 @@ namespace Penumbra.UI
.GetField( "_currentActorRedraw", BindingFlags.Instance | BindingFlags.NonPublic )
?.GetValue( _plugin.ActorRefresher );
var currentActor = ( Actor? )_plugin.ActorRefresher.GetType()
var (currentActor, currentActorIdx) = ( (Actor?, int) )_plugin.ActorRefresher.GetType()
.GetMethod( "FindCurrentActor", BindingFlags.NonPublic | BindingFlags.Instance )?
.Invoke( _plugin.ActorRefresher, Array.Empty< object >() );
.Invoke( _plugin.ActorRefresher, Array.Empty< object >() )!;
var currentRender = currentActor != null
? ( 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 Redraw", currentActorRedraw?.ToString() ?? "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" );
ImGui.EndTable();
}