Change actor redrawing to be frame based and allow for placeholders for redrawing.

This commit is contained in:
Ottermandias 2021-06-25 16:08:29 +02:00
parent d29049ca21
commit e617d0c1ea
4 changed files with 275 additions and 65 deletions

View 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;
}
}
}

View file

@ -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 );
}
}
}
}

View file

@ -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;

View file

@ -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( );
} }
} }