mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +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 Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Plugin;
|
||||
using EmbedIO;
|
||||
|
|
@ -31,6 +32,7 @@ namespace Penumbra
|
|||
public ResourceLoader ResourceLoader { get; set; } = null!;
|
||||
public SettingsInterface SettingsInterface { get; set; } = null!;
|
||||
public MusicManager SoundShit { get; set; } = null!;
|
||||
public ActorRefresher ActorRefresher { get; set; } = null!;
|
||||
|
||||
private WebServer? _webServer;
|
||||
|
||||
|
|
@ -50,6 +52,8 @@ namespace Penumbra
|
|||
|
||||
modManager.DiscoverMods();
|
||||
|
||||
ActorRefresher = new ActorRefresher( PluginInterface, modManager );
|
||||
|
||||
ResourceLoader = new ResourceLoader( this );
|
||||
|
||||
PluginInterface.CommandManager.AddHandler( CommandName, new CommandInfo( OnCommand )
|
||||
|
|
@ -98,6 +102,7 @@ namespace Penumbra
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
ActorRefresher.Dispose();
|
||||
PluginInterface.UiBuilder.OnBuildUi -= SettingsInterface.Draw;
|
||||
|
||||
PluginInterface.CommandManager.RemoveHandler( CommandName );
|
||||
|
|
@ -110,7 +115,7 @@ namespace Penumbra
|
|||
|
||||
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 )
|
||||
{
|
||||
switch( args[ 0 ] )
|
||||
|
|
@ -127,11 +132,11 @@ namespace Penumbra
|
|||
{
|
||||
if( args.Length > 1 )
|
||||
{
|
||||
RefreshActors.RedrawSpecific( PluginInterface.ClientState.Actors, string.Join( " ", args.Skip( 1 ) ) );
|
||||
ActorRefresher.RedrawActor( args[ 1 ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
RefreshActors.RedrawAll( PluginInterface.ClientState.Actors );
|
||||
ActorRefresher.RedrawAll();
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ namespace Penumbra.UI
|
|||
{
|
||||
_config.IsEnabled = enabled;
|
||||
_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