mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Change actor redrawing to be frame based and allow for placeholders for redrawing.
This commit is contained in:
parent
d29049ca21
commit
e617d0c1ea
4 changed files with 275 additions and 65 deletions
266
Penumbra/Game/ActorRefresher.cs
Normal file
266
Penumbra/Game/ActorRefresher.cs
Normal file
|
|
@ -0,0 +1,266 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Game.ClientState.Actors.Types;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using Penumbra.Mods;
|
||||||
|
|
||||||
|
namespace Penumbra.Game
|
||||||
|
{
|
||||||
|
public enum Redraw
|
||||||
|
{
|
||||||
|
WithoutSettings,
|
||||||
|
WithSettings,
|
||||||
|
OnlyWithSettings,
|
||||||
|
Unload,
|
||||||
|
RedrawWithoutSettings,
|
||||||
|
RedrawWithSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActorRefresher : IDisposable
|
||||||
|
{
|
||||||
|
private const int RenderModeOffset = 0x0104;
|
||||||
|
private const int ModelInvisibilityFlag = 0b10;
|
||||||
|
private const int UnloadAllRedrawDelay = 250;
|
||||||
|
private const int NpcActorId = -536870912;
|
||||||
|
|
||||||
|
private readonly DalamudPluginInterface _pi;
|
||||||
|
private readonly ModManager _mods;
|
||||||
|
private readonly Queue< (int actorId, string name, Redraw s) > _actorIds = new();
|
||||||
|
|
||||||
|
private int _currentFrame = 0;
|
||||||
|
private bool _changedSettings = false;
|
||||||
|
private int _currentActorId = -1;
|
||||||
|
private string? _currentActorName = null;
|
||||||
|
|
||||||
|
public ActorRefresher( DalamudPluginInterface pi, ModManager mods )
|
||||||
|
{
|
||||||
|
_pi = pi;
|
||||||
|
_mods = mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeSettings()
|
||||||
|
=> _changedSettings = true;
|
||||||
|
|
||||||
|
private void RestoreSettings()
|
||||||
|
=> _changedSettings = false;
|
||||||
|
|
||||||
|
private static unsafe void WriteInvisible( IntPtr renderPtr )
|
||||||
|
{
|
||||||
|
if( renderPtr != IntPtr.Zero )
|
||||||
|
{
|
||||||
|
*( int* )renderPtr |= ModelInvisibilityFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void WriteVisible( IntPtr renderPtr )
|
||||||
|
{
|
||||||
|
if( renderPtr != IntPtr.Zero )
|
||||||
|
{
|
||||||
|
*( int* )renderPtr &= ~ModelInvisibilityFlag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckActor( Actor actor )
|
||||||
|
{
|
||||||
|
if( _currentActorId != actor.ActorId )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _currentActorId != NpcActorId )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _currentActorName == actor.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Actor? FindCurrentActor()
|
||||||
|
=> _pi.ClientState.Actors.FirstOrDefault( CheckActor );
|
||||||
|
|
||||||
|
private void ChangeSettingsAndUndraw()
|
||||||
|
{
|
||||||
|
if( _actorIds.Count > 0 )
|
||||||
|
{
|
||||||
|
var (id, name, s) = _actorIds.Dequeue();
|
||||||
|
_currentActorName = name;
|
||||||
|
_currentActorId = id;
|
||||||
|
var actor = FindCurrentActor();
|
||||||
|
if( actor == null )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( s )
|
||||||
|
{
|
||||||
|
case Redraw.Unload:
|
||||||
|
WriteInvisible( actor.Address + RenderModeOffset );
|
||||||
|
_currentFrame = 0;
|
||||||
|
break;
|
||||||
|
case Redraw.RedrawWithSettings:
|
||||||
|
ChangeSettings();
|
||||||
|
++_currentFrame;
|
||||||
|
break;
|
||||||
|
case Redraw.RedrawWithoutSettings:
|
||||||
|
WriteVisible( actor.Address + RenderModeOffset );
|
||||||
|
_currentFrame = 0;
|
||||||
|
break;
|
||||||
|
case Redraw.WithoutSettings:
|
||||||
|
WriteInvisible( actor.Address + RenderModeOffset );
|
||||||
|
++_currentFrame;
|
||||||
|
break;
|
||||||
|
case Redraw.WithSettings:
|
||||||
|
ChangeSettings();
|
||||||
|
WriteInvisible( actor.Address + RenderModeOffset );
|
||||||
|
++_currentFrame;
|
||||||
|
break;
|
||||||
|
case Redraw.OnlyWithSettings:
|
||||||
|
ChangeSettings();
|
||||||
|
if( !_changedSettings )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteInvisible( actor.Address + RenderModeOffset );
|
||||||
|
++_currentFrame;
|
||||||
|
break;
|
||||||
|
default: throw new InvalidEnumArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pi.Framework.OnUpdateEvent -= OnUpdateEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartRedraw()
|
||||||
|
{
|
||||||
|
var actor = FindCurrentActor();
|
||||||
|
if( actor == null )
|
||||||
|
{
|
||||||
|
RevertSettings();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteVisible( actor.Address + RenderModeOffset );
|
||||||
|
_currentFrame = _changedSettings ? _currentFrame + 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RevertSettings()
|
||||||
|
{
|
||||||
|
RestoreSettings();
|
||||||
|
_currentFrame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdateEvent( object framework )
|
||||||
|
{
|
||||||
|
switch( _currentFrame )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
ChangeSettingsAndUndraw();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
StartRedraw();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
RevertSettings();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_currentFrame = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RedrawActorIntern( int actorId, string actorName, Redraw settings )
|
||||||
|
{
|
||||||
|
if( _actorIds.Contains( ( actorId, actorName, settings ) ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_actorIds.Enqueue( ( actorId, actorName, settings ) );
|
||||||
|
if( _actorIds.Count == 1 )
|
||||||
|
{
|
||||||
|
_pi.Framework.OnUpdateEvent += OnUpdateEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RedrawActor( Actor? actor, Redraw settings = Redraw.WithSettings )
|
||||||
|
{
|
||||||
|
if( actor != null )
|
||||||
|
{
|
||||||
|
RedrawActorIntern( actor.ActorId, actor.Name, settings );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Actor? GetName( string name )
|
||||||
|
{
|
||||||
|
var lowerName = name.ToLowerInvariant();
|
||||||
|
return lowerName switch
|
||||||
|
{
|
||||||
|
"" => null,
|
||||||
|
"<me>" => _pi.ClientState.Actors[ 0 ],
|
||||||
|
"self" => _pi.ClientState.Actors[ 0 ],
|
||||||
|
"<t>" => _pi.ClientState.Targets.CurrentTarget,
|
||||||
|
"target" => _pi.ClientState.Targets.CurrentTarget,
|
||||||
|
"<f>" => _pi.ClientState.Targets.FocusTarget,
|
||||||
|
"focus" => _pi.ClientState.Targets.FocusTarget,
|
||||||
|
"<mo>" => _pi.ClientState.Targets.MouseOverTarget,
|
||||||
|
"mouseover" => _pi.ClientState.Targets.MouseOverTarget,
|
||||||
|
_ => _pi.ClientState.Actors.FirstOrDefault(
|
||||||
|
a => string.Equals( a.Name, lowerName, StringComparison.InvariantCultureIgnoreCase ) ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RedrawActor( string name, Redraw settings = Redraw.WithSettings )
|
||||||
|
=> RedrawActor( GetName( name ), settings );
|
||||||
|
|
||||||
|
public void RedrawAll( Redraw settings = Redraw.WithSettings )
|
||||||
|
{
|
||||||
|
foreach( var actor in _pi.ClientState.Actors )
|
||||||
|
{
|
||||||
|
RedrawActor( actor, settings );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadAll()
|
||||||
|
{
|
||||||
|
foreach( var a in _pi.ClientState.Actors )
|
||||||
|
{
|
||||||
|
WriteInvisible( a.Address + RenderModeOffset );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RedrawAllWithoutSettings()
|
||||||
|
{
|
||||||
|
foreach( var a in _pi.ClientState.Actors )
|
||||||
|
{
|
||||||
|
WriteVisible( a.Address + RenderModeOffset );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void UnloadAtOnceRedrawWithSettings()
|
||||||
|
{
|
||||||
|
UnloadAll();
|
||||||
|
await Task.Delay( UnloadAllRedrawDelay );
|
||||||
|
RedrawAll( Redraw.RedrawWithSettings );
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void UnloadAtOnceRedrawWithoutSettings()
|
||||||
|
{
|
||||||
|
UnloadAll();
|
||||||
|
await Task.Delay( UnloadAllRedrawDelay );
|
||||||
|
RedrawAllWithoutSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
RevertSettings();
|
||||||
|
_actorIds.Clear();
|
||||||
|
_pi.Framework.OnUpdateEvent -= OnUpdateEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Dalamud.Game.ClientState.Actors;
|
|
||||||
using Dalamud.Game.ClientState.Actors.Types;
|
|
||||||
|
|
||||||
namespace Penumbra.Game
|
|
||||||
{
|
|
||||||
public static class RefreshActors
|
|
||||||
{
|
|
||||||
private const int RenderModeOffset = 0x0104;
|
|
||||||
private const int RenderTaskPlayerDelay = 75;
|
|
||||||
private const int RenderTaskOtherDelay = 25;
|
|
||||||
private const int ModelInvisibilityFlag = 0b10;
|
|
||||||
|
|
||||||
private static async void Redraw( Actor actor )
|
|
||||||
{
|
|
||||||
var ptr = actor.Address;
|
|
||||||
var renderModePtr = ptr + RenderModeOffset;
|
|
||||||
var renderStatus = Marshal.ReadInt32( renderModePtr );
|
|
||||||
|
|
||||||
async void DrawObject( int delay )
|
|
||||||
{
|
|
||||||
Marshal.WriteInt32( renderModePtr, renderStatus | ModelInvisibilityFlag );
|
|
||||||
await Task.Delay( delay );
|
|
||||||
Marshal.WriteInt32( renderModePtr, renderStatus & ~ModelInvisibilityFlag );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( actor.ObjectKind == ObjectKind.Player )
|
|
||||||
{
|
|
||||||
DrawObject( RenderTaskPlayerDelay );
|
|
||||||
await Task.Delay( RenderTaskPlayerDelay );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DrawObject( RenderTaskOtherDelay );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RedrawSpecific( ActorTable actors, string name )
|
|
||||||
{
|
|
||||||
if( name?.Length == 0 )
|
|
||||||
{
|
|
||||||
RedrawAll( actors );
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach( var actor in actors.Where( a => a.Name == name ) )
|
|
||||||
{
|
|
||||||
Redraw( actor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RedrawAll( ActorTable actors )
|
|
||||||
{
|
|
||||||
foreach( var actor in actors )
|
|
||||||
{
|
|
||||||
Redraw( actor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Game.ClientState.Actors.Types;
|
||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using EmbedIO;
|
using EmbedIO;
|
||||||
|
|
@ -31,6 +32,7 @@ namespace Penumbra
|
||||||
public ResourceLoader ResourceLoader { get; set; } = null!;
|
public ResourceLoader ResourceLoader { get; set; } = null!;
|
||||||
public SettingsInterface SettingsInterface { get; set; } = null!;
|
public SettingsInterface SettingsInterface { get; set; } = null!;
|
||||||
public MusicManager SoundShit { get; set; } = null!;
|
public MusicManager SoundShit { get; set; } = null!;
|
||||||
|
public ActorRefresher ActorRefresher { get; set; } = null!;
|
||||||
|
|
||||||
private WebServer? _webServer;
|
private WebServer? _webServer;
|
||||||
|
|
||||||
|
|
@ -50,6 +52,8 @@ namespace Penumbra
|
||||||
|
|
||||||
modManager.DiscoverMods();
|
modManager.DiscoverMods();
|
||||||
|
|
||||||
|
ActorRefresher = new ActorRefresher( PluginInterface, modManager );
|
||||||
|
|
||||||
ResourceLoader = new ResourceLoader( this );
|
ResourceLoader = new ResourceLoader( this );
|
||||||
|
|
||||||
PluginInterface.CommandManager.AddHandler( CommandName, new CommandInfo( OnCommand )
|
PluginInterface.CommandManager.AddHandler( CommandName, new CommandInfo( OnCommand )
|
||||||
|
|
@ -98,6 +102,7 @@ namespace Penumbra
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
ActorRefresher.Dispose();
|
||||||
PluginInterface.UiBuilder.OnBuildUi -= SettingsInterface.Draw;
|
PluginInterface.UiBuilder.OnBuildUi -= SettingsInterface.Draw;
|
||||||
|
|
||||||
PluginInterface.CommandManager.RemoveHandler( CommandName );
|
PluginInterface.CommandManager.RemoveHandler( CommandName );
|
||||||
|
|
@ -110,7 +115,7 @@ namespace Penumbra
|
||||||
|
|
||||||
private void OnCommand( string command, string rawArgs )
|
private void OnCommand( string command, string rawArgs )
|
||||||
{
|
{
|
||||||
var args = rawArgs.Split( ' ' );
|
var args = rawArgs.Split( new[] { ' ' }, 2 );
|
||||||
if( args.Length > 0 && args[ 0 ].Length > 0 )
|
if( args.Length > 0 && args[ 0 ].Length > 0 )
|
||||||
{
|
{
|
||||||
switch( args[ 0 ] )
|
switch( args[ 0 ] )
|
||||||
|
|
@ -127,11 +132,11 @@ namespace Penumbra
|
||||||
{
|
{
|
||||||
if( args.Length > 1 )
|
if( args.Length > 1 )
|
||||||
{
|
{
|
||||||
RefreshActors.RedrawSpecific( PluginInterface.ClientState.Actors, string.Join( " ", args.Skip( 1 ) ) );
|
ActorRefresher.RedrawActor( args[ 1 ] );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RefreshActors.RedrawAll( PluginInterface.ClientState.Actors );
|
ActorRefresher.RedrawAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ namespace Penumbra.UI
|
||||||
{
|
{
|
||||||
_config.IsEnabled = enabled;
|
_config.IsEnabled = enabled;
|
||||||
_configChanged = true;
|
_configChanged = true;
|
||||||
Game.RefreshActors.RedrawAll( _base._plugin!.PluginInterface!.ClientState.Actors );
|
_base._plugin.ActorRefresher.RedrawAll( );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue