mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Fix object reloading in GPose, also add index-based redraw to API/IPC.
This commit is contained in:
parent
19e7d1bf50
commit
1d935def58
5 changed files with 145 additions and 30 deletions
|
|
@ -34,6 +34,9 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
// Queue redrawing of all actors of the given name with the given RedrawType.
|
||||
public void RedrawObject( string name, RedrawType setting );
|
||||
|
||||
// Queue redrawing of the actor with the given object table index, if it exists, with the given RedrawType.
|
||||
public void RedrawObject( int tableIndex, RedrawType setting );
|
||||
|
||||
// Queue redrawing of the specific actor with the given RedrawType. Should only be used when the actor is sure to be valid.
|
||||
public void RedrawObject( GameObject gameObject, RedrawType setting );
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
}
|
||||
}
|
||||
|
||||
public void RedrawObject( int tableIndex, RedrawType setting )
|
||||
{
|
||||
CheckInitialized();
|
||||
_penumbra!.ObjectReloader.RedrawObject( tableIndex, setting );
|
||||
}
|
||||
|
||||
public void RedrawObject( string name, RedrawType setting )
|
||||
{
|
||||
CheckInitialized();
|
||||
|
|
|
|||
|
|
@ -112,10 +112,12 @@ public partial class PenumbraIpc
|
|||
public partial class PenumbraIpc
|
||||
{
|
||||
public const string LabelProviderRedrawName = "Penumbra.RedrawObjectByName";
|
||||
public const string LabelProviderRedrawIndex = "Penumbra.RedrawObjectByIndex";
|
||||
public const string LabelProviderRedrawObject = "Penumbra.RedrawObject";
|
||||
public const string LabelProviderRedrawAll = "Penumbra.RedrawAll";
|
||||
|
||||
internal ICallGateProvider< string, int, object >? ProviderRedrawName;
|
||||
internal ICallGateProvider< int, int, object >? ProviderRedrawIndex;
|
||||
internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject;
|
||||
internal ICallGateProvider< int, object >? ProviderRedrawAll;
|
||||
|
||||
|
|
@ -142,6 +144,16 @@ public partial class PenumbraIpc
|
|||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawName}:\n{e}" );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderRedrawIndex = pi.GetIpcProvider<int, int, object>( LabelProviderRedrawIndex );
|
||||
ProviderRedrawIndex.RegisterAction( ( idx, i ) => Api.RedrawObject( idx, CheckRedrawType( i ) ) );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawName}:\n{e}" );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderRedrawObject = pi.GetIpcProvider< GameObject, int, object >( LabelProviderRedrawObject );
|
||||
|
|
@ -166,6 +178,7 @@ public partial class PenumbraIpc
|
|||
private void DisposeRedrawProviders()
|
||||
{
|
||||
ProviderRedrawName?.UnregisterAction();
|
||||
ProviderRedrawIndex?.UnregisterAction();
|
||||
ProviderRedrawObject?.UnregisterAction();
|
||||
ProviderRedrawAll?.UnregisterAction();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,18 @@ public class RedrawController : WebApiController
|
|||
public async Task Redraw()
|
||||
{
|
||||
var data = await HttpContext.GetRequestDataAsync< RedrawData >();
|
||||
_penumbra.Api.RedrawObject( data.Name, data.Type );
|
||||
if( data.ObjectTableIndex >= 0 )
|
||||
{
|
||||
_penumbra.Api.RedrawObject( data.ObjectTableIndex, data.Type );
|
||||
}
|
||||
else if( data.Name.Length > 0 )
|
||||
{
|
||||
_penumbra.Api.RedrawObject( data.Name, data.Type );
|
||||
}
|
||||
else
|
||||
{
|
||||
_penumbra.Api.RedrawAll( data.Type );
|
||||
}
|
||||
}
|
||||
|
||||
[Route( HttpVerbs.Post, "/redrawAll" )]
|
||||
|
|
@ -30,5 +41,6 @@ public class RedrawController : WebApiController
|
|||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public RedrawType Type { get; set; } = RedrawType.Redraw;
|
||||
public int ObjectTableIndex { get; set; } = -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,12 +8,100 @@ using Penumbra.Interop.Structs;
|
|||
|
||||
namespace Penumbra.Interop;
|
||||
|
||||
public sealed unsafe class ObjectReloader : IDisposable
|
||||
public unsafe partial class ObjectReloader
|
||||
{
|
||||
public const int GPosePlayerIdx = 201;
|
||||
public const int GPoseSlots = 42;
|
||||
public const int GPoseEndIdx = GPosePlayerIdx + GPoseSlots;
|
||||
|
||||
private readonly string?[] _gPoseNames = new string?[GPoseSlots];
|
||||
private int _gPoseNameCounter = 0;
|
||||
private bool _inGPose = false;
|
||||
|
||||
// VFuncs that disable and enable draw, used only for GPose actors.
|
||||
private static void DisableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 17 ]( actor.Address );
|
||||
|
||||
private static void EnableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 16 ]( actor.Address );
|
||||
|
||||
|
||||
// Check whether we currently are in GPose.
|
||||
// Also clear the name list.
|
||||
private void SetGPose()
|
||||
{
|
||||
_inGPose = Dalamud.Objects[ GPosePlayerIdx ] != null;
|
||||
_gPoseNameCounter = 0;
|
||||
}
|
||||
|
||||
private static bool IsGPoseActor( int idx )
|
||||
=> idx is >= GPosePlayerIdx and < GPoseEndIdx;
|
||||
|
||||
// Return whether an object has to be replaced by a GPose object.
|
||||
// If the object does not exist, is already a GPose actor
|
||||
// or no actor of the same name is found in the GPose actor list,
|
||||
// obj will be the object itself (or null) and false will be returned.
|
||||
// If we are in GPose and a game object with the same name as the original actor is found,
|
||||
// this will be in obj and true will be returned.
|
||||
private bool FindCorrectActor( int idx, out GameObject? obj )
|
||||
{
|
||||
obj = Dalamud.Objects[ idx ];
|
||||
if( !_inGPose || obj == null || IsGPoseActor( idx ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var name = obj.Name.ToString();
|
||||
for( var i = 0; i < _gPoseNameCounter; ++i )
|
||||
{
|
||||
var gPoseName = _gPoseNames[ i ];
|
||||
if( gPoseName == null )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( name == gPoseName )
|
||||
{
|
||||
obj = Dalamud.Objects[ GPosePlayerIdx + i ];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for( ; _gPoseNameCounter < GPoseSlots; ++_gPoseNameCounter )
|
||||
{
|
||||
var gPoseName = Dalamud.Objects[ GPosePlayerIdx + _gPoseNameCounter ]?.Name.ToString();
|
||||
_gPoseNames[ _gPoseNameCounter ] = gPoseName;
|
||||
if( gPoseName == null )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( name == gPoseName )
|
||||
{
|
||||
obj = Dalamud.Objects[ GPosePlayerIdx + _gPoseNameCounter ];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Do not ever redraw any of the five UI Window actors.
|
||||
private static bool BadRedrawIndices( GameObject? actor, out int tableIndex )
|
||||
{
|
||||
if( actor == null )
|
||||
{
|
||||
tableIndex = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
tableIndex = ObjectTableIndex( actor );
|
||||
return tableIndex is >= 240 and < 245;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed unsafe partial class ObjectReloader : IDisposable
|
||||
{
|
||||
private readonly List< int > _queue = new(100);
|
||||
private readonly List< int > _afterGPoseQueue = new(GPoseSlots);
|
||||
private int _target = -1;
|
||||
|
|
@ -27,27 +115,9 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
public static DrawState* ActorDrawState( GameObject actor )
|
||||
=> ( DrawState* )( actor.Address + 0x0104 );
|
||||
|
||||
private static void DisableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 17 ]( actor.Address );
|
||||
|
||||
private static void EnableDraw( GameObject actor )
|
||||
=> ( ( delegate* unmanaged< IntPtr, void >** )actor.Address )[ 0 ][ 16 ]( actor.Address );
|
||||
|
||||
private static int ObjectTableIndex( GameObject actor )
|
||||
=> ( ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )actor.Address )->ObjectIndex;
|
||||
|
||||
private static bool BadRedrawIndices( GameObject? actor, out int tableIndex )
|
||||
{
|
||||
if( actor == null )
|
||||
{
|
||||
tableIndex = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
tableIndex = ObjectTableIndex( actor );
|
||||
return tableIndex is >= 240 and < 245;
|
||||
}
|
||||
|
||||
private static void WriteInvisible( GameObject? actor )
|
||||
{
|
||||
if( BadRedrawIndices( actor, out var tableIndex ) )
|
||||
|
|
@ -57,7 +127,7 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
|
||||
*ActorDrawState( actor! ) |= DrawState.Invisibility;
|
||||
|
||||
if( tableIndex is >= GPosePlayerIdx and < GPoseEndIdx )
|
||||
if( IsGPoseActor( tableIndex ) )
|
||||
{
|
||||
DisableDraw( actor! );
|
||||
}
|
||||
|
|
@ -72,7 +142,7 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
|
||||
*ActorDrawState( actor! ) &= ~DrawState.Invisibility;
|
||||
|
||||
if( tableIndex is >= GPosePlayerIdx and < GPoseEndIdx )
|
||||
if( IsGPoseActor( tableIndex ) )
|
||||
{
|
||||
EnableDraw( actor! );
|
||||
}
|
||||
|
|
@ -136,15 +206,22 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
for( var i = 0; i < _queue.Count; ++i )
|
||||
{
|
||||
var idx = _queue[ i ];
|
||||
if( idx < 0 )
|
||||
if( FindCorrectActor( idx < 0 ? ~idx : idx, out var obj ) )
|
||||
{
|
||||
var newIdx = ~idx;
|
||||
WriteInvisible( Dalamud.Objects[ newIdx ] );
|
||||
_queue[ numKept++ ] = newIdx;
|
||||
_afterGPoseQueue.Add( idx < 0 ? idx : ~idx );
|
||||
}
|
||||
else
|
||||
|
||||
if( obj != null )
|
||||
{
|
||||
WriteVisible( Dalamud.Objects[ idx ] );
|
||||
if( idx < 0 )
|
||||
{
|
||||
WriteInvisible( obj );
|
||||
_queue[ numKept++ ] = ObjectTableIndex( obj );
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteVisible( obj );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +230,7 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
|
||||
private void HandleAfterGPose()
|
||||
{
|
||||
if( _afterGPoseQueue.Count == 0 || Dalamud.Objects[ GPosePlayerIdx ] != null )
|
||||
if( _afterGPoseQueue.Count == 0 || _inGPose )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -174,7 +251,7 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
_afterGPoseQueue.RemoveRange( numKept, _queue.Count - numKept );
|
||||
_afterGPoseQueue.RemoveRange( numKept, _afterGPoseQueue.Count - numKept );
|
||||
}
|
||||
|
||||
private void OnUpdateEvent( object framework )
|
||||
|
|
@ -186,6 +263,7 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
SetGPose();
|
||||
HandleRedraw();
|
||||
HandleAfterGPose();
|
||||
HandleTarget();
|
||||
|
|
@ -229,6 +307,9 @@ public sealed unsafe class ObjectReloader : IDisposable
|
|||
return ret;
|
||||
}
|
||||
|
||||
public void RedrawObject( int tableIndex, RedrawType settings )
|
||||
=> RedrawObject( Dalamud.Objects[tableIndex], settings );
|
||||
|
||||
public void RedrawObject( string name, RedrawType settings )
|
||||
{
|
||||
var lowerName = name.ToLowerInvariant();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue