Added an event when a newly created draw object finishes CharacterBase.Create.

This commit is contained in:
Ottermandias 2022-08-16 15:30:05 +02:00
parent 5b5a1e2fd8
commit 80edfe7804
6 changed files with 54 additions and 153 deletions

View file

@ -26,6 +26,8 @@ public delegate void ModSettingChanged( ModSettingChange type, string collection
public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize,
IntPtr equipData ); IntPtr equipData );
public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr drawObject );
public enum PenumbraApiEc public enum PenumbraApiEc
{ {
Success = 0, Success = 0,
@ -73,6 +75,10 @@ public interface IPenumbraApi : IPenumbraApiBase
// before the Draw Object is actually created, so customize and equipdata can be manipulated beforehand. // before the Draw Object is actually created, so customize and equipdata can be manipulated beforehand.
public event CreatingCharacterBaseDelegate? CreatingCharacterBase; public event CreatingCharacterBaseDelegate? CreatingCharacterBase;
// Triggered after a character base was created if a corresponding gameObject could be found,
// so you can apply flag changes after finishing.
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
// Queue redrawing of all actors of the given name with the given RedrawType. // Queue redrawing of all actors of the given name with the given RedrawType.
public void RedrawObject( string name, RedrawType setting ); public void RedrawObject( string name, RedrawType setting );

View file

@ -31,7 +31,8 @@ public class IpcTester : IDisposable
private readonly ICallGateSubscriber< string, bool, object? > _modDirectoryChanged; private readonly ICallGateSubscriber< string, bool, object? > _modDirectoryChanged;
private readonly ICallGateSubscriber< IntPtr, int, object? > _redrawn; private readonly ICallGateSubscriber< IntPtr, int, object? > _redrawn;
private readonly ICallGateSubscriber< ModSettingChange, string, string, bool, object? > _settingChanged; private readonly ICallGateSubscriber< ModSettingChange, string, string, bool, object? > _settingChanged;
private readonly ICallGateSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? > _characterBaseCreated; private readonly ICallGateSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? > _characterBaseCreating;
private readonly ICallGateSubscriber< IntPtr, string, IntPtr, object? > _characterBaseCreated;
private readonly List< DateTimeOffset > _initializedList = new(); private readonly List< DateTimeOffset > _initializedList = new();
private readonly List< DateTimeOffset > _disposedList = new(); private readonly List< DateTimeOffset > _disposedList = new();
@ -47,15 +48,17 @@ public class IpcTester : IDisposable
_postSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPostSettingsDraw ); _postSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPostSettingsDraw );
_settingChanged = _pi.GetIpcSubscriber< ModSettingChange, string, string, bool, object? >( PenumbraIpc.LabelProviderModSettingChanged ); _settingChanged = _pi.GetIpcSubscriber< ModSettingChange, string, string, bool, object? >( PenumbraIpc.LabelProviderModSettingChanged );
_modDirectoryChanged = _pi.GetIpcSubscriber< string, bool, object? >( PenumbraIpc.LabelProviderModDirectoryChanged ); _modDirectoryChanged = _pi.GetIpcSubscriber< string, bool, object? >( PenumbraIpc.LabelProviderModDirectoryChanged );
_characterBaseCreated = _characterBaseCreating =
_pi.GetIpcSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( PenumbraIpc.LabelProviderCreatingCharacterBase ); _pi.GetIpcSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( PenumbraIpc.LabelProviderCreatingCharacterBase );
_characterBaseCreated = _pi.GetIpcSubscriber< IntPtr, string, IntPtr, object? >( PenumbraIpc.LabelProviderCreatedCharacterBase );
_initialized.Subscribe( AddInitialized ); _initialized.Subscribe( AddInitialized );
_disposed.Subscribe( AddDisposed ); _disposed.Subscribe( AddDisposed );
_redrawn.Subscribe( SetLastRedrawn ); _redrawn.Subscribe( SetLastRedrawn );
_preSettingsDraw.Subscribe( UpdateLastDrawnMod ); _preSettingsDraw.Subscribe( UpdateLastDrawnMod );
_postSettingsDraw.Subscribe( UpdateLastDrawnMod ); _postSettingsDraw.Subscribe( UpdateLastDrawnMod );
_settingChanged.Subscribe( UpdateLastModSetting ); _settingChanged.Subscribe( UpdateLastModSetting );
_characterBaseCreated.Subscribe( UpdateLastCreated ); _characterBaseCreating.Subscribe( UpdateLastCreated );
_characterBaseCreated.Subscribe( UpdateLastCreated2 );
_modDirectoryChanged.Subscribe( UpdateModDirectoryChanged ); _modDirectoryChanged.Subscribe( UpdateModDirectoryChanged );
} }
@ -69,7 +72,8 @@ public class IpcTester : IDisposable
_preSettingsDraw.Unsubscribe( UpdateLastDrawnMod ); _preSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_postSettingsDraw.Unsubscribe( UpdateLastDrawnMod ); _postSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_settingChanged.Unsubscribe( UpdateLastModSetting ); _settingChanged.Unsubscribe( UpdateLastModSetting );
_characterBaseCreated.Unsubscribe( UpdateLastCreated ); _characterBaseCreating.Unsubscribe( UpdateLastCreated );
_characterBaseCreated.Unsubscribe( UpdateLastCreated2 );
_modDirectoryChanged.Unsubscribe( UpdateModDirectoryChanged ); _modDirectoryChanged.Unsubscribe( UpdateModDirectoryChanged );
} }
@ -218,6 +222,7 @@ public class IpcTester : IDisposable
private IntPtr _currentDrawObject = IntPtr.Zero; private IntPtr _currentDrawObject = IntPtr.Zero;
private int _currentCutsceneActor = 0; private int _currentCutsceneActor = 0;
private string _lastCreatedGameObjectName = string.Empty; private string _lastCreatedGameObjectName = string.Empty;
private IntPtr _lastCreatedDrawObject = IntPtr.Zero;
private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue; private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue;
private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 ) private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 )
@ -225,6 +230,15 @@ public class IpcTester : IDisposable
var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject;
_lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString(); _lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString();
_lastCreatedGameObjectTime = DateTimeOffset.Now; _lastCreatedGameObjectTime = DateTimeOffset.Now;
_lastCreatedDrawObject = IntPtr.Zero;
}
private unsafe void UpdateLastCreated2( IntPtr gameObject, string _, IntPtr drawObject )
{
var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject;
_lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString();
_lastCreatedGameObjectTime = DateTimeOffset.Now;
_lastCreatedDrawObject = drawObject;
} }
private void DrawResolve() private void DrawResolve()
@ -318,7 +332,9 @@ public class IpcTester : IDisposable
DrawIntro( PenumbraIpc.LabelProviderCreatingCharacterBase, "Last Drawobject created" ); DrawIntro( PenumbraIpc.LabelProviderCreatingCharacterBase, "Last Drawobject created" );
if( _lastCreatedGameObjectTime < DateTimeOffset.Now ) if( _lastCreatedGameObjectTime < DateTimeOffset.Now )
{ {
ImGui.TextUnformatted( $"for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" ); ImGui.TextUnformatted( _lastCreatedDrawObject != IntPtr.Zero
? $"0x{_lastCreatedDrawObject:X} for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}"
: $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" );
} }
} }

View file

@ -45,6 +45,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
remove => PathResolver.DrawObjectState.CreatingCharacterBase -= value; remove => PathResolver.DrawObjectState.CreatingCharacterBase -= value;
} }
public event CreatedCharacterBaseDelegate? CreatedCharacterBase
{
add => PathResolver.DrawObjectState.CreatedCharacterBase += value;
remove => PathResolver.DrawObjectState.CreatedCharacterBase -= value;
}
public bool Valid public bool Valid
=> _penumbra != null; => _penumbra != null;

View file

@ -282,6 +282,7 @@ public partial class PenumbraIpc
public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath"; public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath";
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath"; public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase"; public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
internal ICallGateProvider< string, string >? ProviderResolveDefault; internal ICallGateProvider< string, string >? ProviderResolveDefault;
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter; internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
@ -291,6 +292,7 @@ public partial class PenumbraIpc
internal ICallGateProvider< string, string, string[] >? ProviderReverseResolvePath; internal ICallGateProvider< string, string, string[] >? ProviderReverseResolvePath;
internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer; internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer;
internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase; internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase;
internal ICallGateProvider< IntPtr, string, IntPtr, object? >? ProviderCreatedCharacterBase;
private void InitializeResolveProviders( DalamudPluginInterface pi ) private void InitializeResolveProviders( DalamudPluginInterface pi )
{ {
@ -374,6 +376,17 @@ public partial class PenumbraIpc
{ {
PluginLog.Error( $"Error registering IPC provider for {LabelProviderCreatingCharacterBase}:\n{e}" ); PluginLog.Error( $"Error registering IPC provider for {LabelProviderCreatingCharacterBase}:\n{e}" );
} }
try
{
ProviderCreatedCharacterBase =
pi.GetIpcProvider< IntPtr, string, IntPtr, object? >( LabelProviderCreatedCharacterBase );
Api.CreatedCharacterBase += CreatedCharacterBaseEvent;
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderCreatedCharacterBase}:\n{e}" );
}
} }
private void DisposeResolveProviders() private void DisposeResolveProviders()
@ -385,12 +398,18 @@ public partial class PenumbraIpc
ProviderReverseResolvePath?.UnregisterFunc(); ProviderReverseResolvePath?.UnregisterFunc();
ProviderReverseResolvePathPlayer?.UnregisterFunc(); ProviderReverseResolvePathPlayer?.UnregisterFunc();
Api.CreatingCharacterBase -= CreatingCharacterBaseEvent; Api.CreatingCharacterBase -= CreatingCharacterBaseEvent;
Api.CreatedCharacterBase -= CreatedCharacterBaseEvent;
} }
private void CreatingCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, IntPtr equipData ) private void CreatingCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, IntPtr equipData )
{ {
ProviderCreatingCharacterBase?.SendMessage( gameObject, collection.Name, modelId, customize, equipData ); ProviderCreatingCharacterBase?.SendMessage( gameObject, collection.Name, modelId, customize, equipData );
} }
private void CreatedCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr drawObject )
{
ProviderCreatedCharacterBase?.SendMessage( gameObject, collection.Name, drawObject );
}
} }
public partial class PenumbraIpc public partial class PenumbraIpc

View file

@ -1,148 +0,0 @@
using System;
using System.Collections.Generic;
using OtterGui.Classes;
using Penumbra.GameData.ByteString;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Collections;
public struct ConflictCache
{
// A conflict stores all data about a mod conflict.
public readonly struct Conflict : IComparable< Conflict >
{
public readonly object Data;
public readonly int Mod1;
public readonly int Mod2;
public readonly bool Mod1Priority;
public readonly bool Solved;
public Conflict( int modIdx1, int modIdx2, bool priority, bool solved, object data )
{
Mod1 = modIdx1;
Mod2 = modIdx2;
Data = data;
Mod1Priority = priority;
Solved = solved;
}
// Order: Mod1 -> Mod1 overwritten -> Mod2 -> File > MetaManipulation
public int CompareTo( Conflict other )
{
var idxComp = Mod1.CompareTo( other.Mod1 );
if( idxComp != 0 )
{
return idxComp;
}
if( Mod1Priority != other.Mod1Priority )
{
return Mod1Priority ? 1 : -1;
}
idxComp = Mod2.CompareTo( other.Mod2 );
if( idxComp != 0 )
{
return idxComp;
}
return Data switch
{
Utf8GamePath p when other.Data is Utf8GamePath q => p.CompareTo( q ),
Utf8GamePath => -1,
MetaManipulation m when other.Data is MetaManipulation n => m.CompareTo( n ),
MetaManipulation => 1,
_ => 0,
};
}
public override string ToString()
=> ( Mod1Priority, Solved ) switch
{
(true, true) => $"{Penumbra.ModManager[ Mod1 ].Name} > {Penumbra.ModManager[ Mod2 ].Name} ({Data})",
(true, false) => $"{Penumbra.ModManager[ Mod1 ].Name} >= {Penumbra.ModManager[ Mod2 ].Name} ({Data})",
(false, true) => $"{Penumbra.ModManager[ Mod1 ].Name} < {Penumbra.ModManager[ Mod2 ].Name} ({Data})",
(false, false) => $"{Penumbra.ModManager[ Mod1 ].Name} <= {Penumbra.ModManager[ Mod2 ].Name} ({Data})",
};
}
private readonly List< Conflict > _conflicts = new();
private bool _isSorted = true;
public ConflictCache()
{ }
public IReadOnlyList< Conflict > Conflicts
{
get
{
Sort();
return _conflicts;
}
}
// Find all mod conflicts concerning the specified mod (in both directions).
public SubList< Conflict > ModConflicts( int modIdx )
{
Sort();
var start = _conflicts.FindIndex( c => c.Mod1 == modIdx );
if( start < 0 )
{
return SubList< Conflict >.Empty;
}
var end = _conflicts.FindIndex( start, c => c.Mod1 != modIdx );
return new SubList< Conflict >( _conflicts, start, end - start );
}
private void Sort()
{
if( !_isSorted )
{
_conflicts?.Sort();
_isSorted = true;
}
}
// Add both directions for the mod.
// On same priority, it is assumed that mod1 is the earlier one.
// Also update older conflicts to refer to the highest-prioritized conflict.
private void AddConflict( int modIdx1, int modIdx2, int priority1, int priority2, object data )
{
var solved = priority1 != priority2;
var priority = priority1 >= priority2;
var prioritizedMod = priority ? modIdx1 : modIdx2;
_conflicts.Add( new Conflict( modIdx1, modIdx2, priority, solved, data ) );
_conflicts.Add( new Conflict( modIdx2, modIdx1, !priority, solved, data ) );
for( var i = 0; i < _conflicts.Count; ++i )
{
var c = _conflicts[ i ];
if( data.Equals( c.Data ) )
{
_conflicts[ i ] = c.Mod1Priority
? new Conflict( prioritizedMod, c.Mod2, true, c.Solved || solved, data )
: new Conflict( c.Mod1, prioritizedMod, false, c.Solved || solved, data );
}
}
_isSorted = false;
}
public void AddConflict( int modIdx1, int modIdx2, int priority1, int priority2, Utf8GamePath gamePath )
=> AddConflict( modIdx1, modIdx2, priority1, priority2, ( object )gamePath );
public void AddConflict( int modIdx1, int modIdx2, int priority1, int priority2, MetaManipulation manipulation )
=> AddConflict( modIdx1, modIdx2, priority1, priority2, ( object )manipulation );
public void ClearConflicts()
=> _conflicts?.Clear();
public void ClearFileConflicts()
=> _conflicts?.RemoveAll( m => m.Data is Utf8GamePath );
public void ClearMetaConflicts()
=> _conflicts?.RemoveAll( m => m.Data is MetaManipulation );
public void ClearConflictsWithMod( int modIdx )
=> _conflicts?.RemoveAll( m => m.Mod1 == modIdx || m.Mod2 == modIdx );
}

View file

@ -18,6 +18,7 @@ public unsafe partial class PathResolver
public class DrawObjectState public class DrawObjectState
{ {
public static event CreatingCharacterBaseDelegate? CreatingCharacterBase; public static event CreatingCharacterBaseDelegate? CreatingCharacterBase;
public static event CreatedCharacterBaseDelegate? CreatedCharacterBase;
public IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjects public IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjects
=> _drawObjectToObject; => _drawObjectToObject;
@ -147,6 +148,7 @@ public unsafe partial class PathResolver
if( LastGameObject != null ) if( LastGameObject != null )
{ {
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex ); _drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ret );
} }
return ret; return ret;