Add MaterialUI IPC and increase API Version to 6. No breaking changes, only additions.

This commit is contained in:
Ottermandias 2022-07-01 16:21:41 +02:00
parent 53ff9a8ab3
commit 653e21e237
6 changed files with 242 additions and 25 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Data;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.Mods;
@ -16,7 +17,7 @@ public interface IPenumbraApiBase
public delegate void ChangedItemHover( object? item );
public delegate void ChangedItemClick( MouseButton button, object? item );
public delegate void GameObjectRedrawn( IntPtr objectPtr, int objectTableIndex );
public delegate void ModSettingChanged( ModSettingChange type, string collectionName, string modDirectory, bool inherited );
public enum PenumbraApiEc
{
Success = 0,
@ -47,6 +48,11 @@ public interface IPenumbraApi : IPenumbraApiBase
// Can be used to append tooltips.
public event ChangedItemHover? ChangedItemTooltip;
// Events that are fired before and after the content of a mod settings panel are drawn.
// Both are fired inside the child window of the settings panel itself.
public event Action<string>? PreSettingsPanelDraw;
public event Action<string>? PostSettingsPanelDraw;
// Triggered when the user clicks a listed changed object in a mod tab.
public event ChangedItemClick? ChangedItemClicked;
public event GameObjectRedrawn? GameObjectRedrawn;
@ -101,6 +107,16 @@ public interface IPenumbraApi : IPenumbraApiBase
// Obtain a list of all installed mods. The first string is their directory name, the second string is their mod name.
public IList< (string, string) > GetModList();
// Try to reload an existing mod by its directory name or mod name.
// Can return ModMissing or success.
// Reload is the same as if triggered by button press and might delete the mod if it is not valid anymore.
public PenumbraApiEc ReloadMod( string modDirectory, string modName );
// Try to add a new mod inside the mod root directory (modDirectory should only be the name, not the full name).
// Returns FileMissing if the directory does not exist or success otherwise.
// Note that success does only imply a successful call, not a successful mod load.
public PenumbraApiEc AddMod( string modDirectory );
// Obtain a base64 encoded, zipped json-string with a prepended version-byte of the current manipulations
// for the given collection associated with the character name, or the default collection.
public string GetMetaManipulations( string characterName );
@ -139,6 +155,8 @@ public interface IPenumbraApi : IPenumbraApiBase
public PenumbraApiEc TrySetModSettings( string collectionName, string modDirectory, string modName, string optionGroupName,
IReadOnlyList< string > options );
// This event gets fired when any setting in any collection changes.
public event ModSettingChanged? ModSettingChanged;
// Create a temporary collection without actual settings but with a cache.
// If no character collection for this character exists or forceOverwriteCharacter is true,

View file

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -12,6 +13,7 @@ using Dalamud.Plugin.Ipc;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.Mods;
@ -25,7 +27,10 @@ public class IpcTester : IDisposable
private readonly ICallGateSubscriber< object? > _initialized;
private readonly ICallGateSubscriber< object? > _disposed;
private readonly ICallGateSubscriber< string, object? > _preSettingsDraw;
private readonly ICallGateSubscriber< string, object? > _postSettingsDraw;
private readonly ICallGateSubscriber< IntPtr, int, object? > _redrawn;
private readonly ICallGateSubscriber< ModSettingChange, string, string, bool, object? > _settingChanged;
private readonly List< DateTimeOffset > _initializedList = new();
private readonly List< DateTimeOffset > _disposedList = new();
@ -37,9 +42,15 @@ public class IpcTester : IDisposable
_initialized = _pi.GetIpcSubscriber< object? >( PenumbraIpc.LabelProviderInitialized );
_disposed = _pi.GetIpcSubscriber< object? >( PenumbraIpc.LabelProviderDisposed );
_redrawn = _pi.GetIpcSubscriber< IntPtr, int, object? >( PenumbraIpc.LabelProviderGameObjectRedrawn );
_preSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPreSettingsDraw );
_postSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPostSettingsDraw );
_settingChanged = _pi.GetIpcSubscriber< ModSettingChange, string, string, bool, object? >( PenumbraIpc.LabelProviderModSettingChanged );
_initialized.Subscribe( AddInitialized );
_disposed.Subscribe( AddDisposed );
_redrawn.Subscribe( SetLastRedrawn );
_preSettingsDraw.Subscribe( UpdateLastDrawnMod );
_postSettingsDraw.Subscribe( UpdateLastDrawnMod );
_settingChanged.Subscribe( UpdateLastModSetting );
}
public void Dispose()
@ -49,6 +60,9 @@ public class IpcTester : IDisposable
_redrawn.Subscribe( SetLastRedrawn );
_tooltip?.Unsubscribe( AddedTooltip );
_click?.Unsubscribe( AddedClick );
_preSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_postSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_settingChanged.Unsubscribe( UpdateLastModSetting );
}
private void AddInitialized()
@ -112,6 +126,11 @@ public class IpcTester : IDisposable
}
private string _currentConfiguration = string.Empty;
private string _lastDrawnMod = string.Empty;
private DateTimeOffset _lastDrawnModTime;
private void UpdateLastDrawnMod( string name )
=> ( _lastDrawnMod, _lastDrawnModTime ) = ( name, DateTimeOffset.Now );
private void DrawGeneral()
{
@ -143,6 +162,8 @@ public class IpcTester : IDisposable
DrawList( PenumbraIpc.LabelProviderInitialized, "Last Initialized", _initializedList );
DrawList( PenumbraIpc.LabelProviderDisposed, "Last Disposed", _disposedList );
DrawIntro( PenumbraIpc.LabelProviderPostSettingsDraw, "Last Drawn Mod" );
ImGui.TextUnformatted( _lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None" );
DrawIntro( PenumbraIpc.LabelProviderApiVersion, "Current Version" );
ImGui.TextUnformatted( _pi.GetIpcSubscriber< int >( PenumbraIpc.LabelProviderApiVersion ).InvokeFunc().ToString() );
DrawIntro( PenumbraIpc.LabelProviderGetModDirectory, "Current Mod Directory" );
@ -500,8 +521,23 @@ public class IpcTester : IDisposable
private IDictionary< string, (IList< string >, SelectType) >? _availableSettings;
private IDictionary< string, IList< string > >? _currentSettings = null;
private PenumbraApiEc _lastSettingsError = PenumbraApiEc.Success;
private ModSettingChange _lastSettingChangeType;
private string _lastSettingChangeCollection = string.Empty;
private string _lastSettingChangeMod = string.Empty;
private bool _lastSettingChangeInherited;
private DateTimeOffset _lastSettingChange;
private PenumbraApiEc _lastReloadEc = PenumbraApiEc.Success;
private void UpdateLastModSetting( ModSettingChange type, string collection, string mod, bool inherited )
{
_lastSettingChangeType = type;
_lastSettingChangeCollection = collection;
_lastSettingChangeMod = mod;
_lastSettingChangeInherited = inherited;
_lastSettingChange = DateTimeOffset.Now;
}
private void DrawSetting()
{
using var _ = ImRaii.TreeNode( "Settings IPC" );
@ -522,7 +558,10 @@ public class IpcTester : IDisposable
}
DrawIntro( "Last Error", _lastSettingsError.ToString() );
DrawIntro( PenumbraIpc.LabelProviderModSettingChanged, "Last Mod Setting Changed" );
ImGui.TextUnformatted( _lastSettingChangeMod.Length > 0
? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{( _lastSettingChangeInherited ? " (Inherited)" : string.Empty )} at {_lastSettingChange}"
: "None" );
DrawIntro( PenumbraIpc.LabelProviderGetAvailableModSettings, "Get Available Settings" );
if( ImGui.Button( "Get##Available" ) )
{
@ -532,6 +571,16 @@ public class IpcTester : IDisposable
_lastSettingsError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success;
}
DrawIntro( PenumbraIpc.LabelProviderReloadMod, "Reload Mod" );
if( ImGui.Button( "Reload" ) )
{
_lastReloadEc = _pi.GetIpcSubscriber< string, string, PenumbraApiEc >( PenumbraIpc.LabelProviderReloadMod )
.InvokeFunc( _settingsModDirectory, _settingsModName );
}
ImGui.SameLine();
ImGui.TextUnformatted( _lastReloadEc.ToString() );
DrawIntro( PenumbraIpc.LabelProviderGetCurrentModSettings, "Get Current Settings" );
if( ImGui.Button( "Get##Current" ) )
{

View file

@ -21,11 +21,17 @@ namespace Penumbra.Api;
public class PenumbraApi : IDisposable, IPenumbraApi
{
public int ApiVersion
=> 5;
=> 6;
private Penumbra? _penumbra;
private Lumina.GameData? _lumina;
private readonly Dictionary< ModCollection, ModCollection.ModSettingChangeDelegate > _delegates = new();
public event Action<string>? PreSettingsPanelDraw;
public event Action<string>? PostSettingsPanelDraw;
public event GameObjectRedrawn? GameObjectRedrawn;
public event ModSettingChanged? ModSettingChanged;
public bool Valid
=> _penumbra != null;
@ -37,13 +43,27 @@ public class PenumbraApi : IDisposable, IPenumbraApi
.GetField( "gameData", BindingFlags.Instance | BindingFlags.NonPublic )
?.GetValue( Dalamud.GameData );
_penumbra.ObjectReloader.GameObjectRedrawn += OnGameObjectRedrawn;
foreach( var collection in Penumbra.CollectionManager )
{
SubscribeToCollection( collection );
}
Penumbra.CollectionManager.CollectionChanged += SubscribeToNewCollections;
}
public void Dispose()
{
Penumbra.CollectionManager.CollectionChanged -= SubscribeToNewCollections;
_penumbra!.ObjectReloader.GameObjectRedrawn -= OnGameObjectRedrawn;
_penumbra = null;
_lumina = null;
foreach( var collection in Penumbra.CollectionManager )
{
if( _delegates.TryGetValue( collection, out var del ) )
{
collection.ModSettingChanged -= del;
}
}
}
public event ChangedItemClick? ChangedItemClicked;
@ -214,6 +234,29 @@ public class PenumbraApi : IDisposable, IPenumbraApi
( shareSettings.Enabled, shareSettings.Priority, shareSettings.Settings, collection.Settings[ mod.Index ] != null ) );
}
public PenumbraApiEc ReloadMod( string modDirectory, string modName )
{
CheckInitialized();
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
{
return PenumbraApiEc.ModMissing;
}
Penumbra.ModManager.ReloadMod( mod.Index );
return PenumbraApiEc.Success;
}
public PenumbraApiEc AddMod( string modDirectory )
{
CheckInitialized();
var dir = new DirectoryInfo( Path.Combine(Penumbra.ModManager.BasePath.FullName, modDirectory) );
if( !dir.Exists )
return PenumbraApiEc.FileMissing;
Penumbra.ModManager.AddMod( dir );
return PenumbraApiEc.Success;
}
public PenumbraApiEc TryInheritMod( string collectionName, string modDirectory, string modName, bool inherit )
{
CheckInitialized();
@ -572,4 +615,39 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return true;
}
private void SubscribeToCollection( ModCollection c )
{
var name = c.Name;
void Del( ModSettingChange type, int idx, int _, int _2, bool inherited )
=> ModSettingChanged?.Invoke( type, name, Penumbra.ModManager[ idx ].ModPath.Name, inherited );
_delegates[ c ] = Del;
c.ModSettingChanged += Del;
}
private void SubscribeToNewCollections( CollectionType type, ModCollection? oldCollection, ModCollection? newCollection, string? _ )
{
if( type != CollectionType.Inactive )
{
return;
}
if( oldCollection != null && _delegates.TryGetValue( oldCollection, out var del ) )
{
oldCollection.ModSettingChanged -= del;
}
if( newCollection != null )
{
SubscribeToCollection( newCollection );
}
}
public void InvokePreSettingsPanel(string modDirectory)
=> PreSettingsPanelDraw?.Invoke(modDirectory);
public void InvokePostSettingsPanel(string modDirectory)
=> PostSettingsPanelDraw?.Invoke(modDirectory);
}

View file

@ -4,6 +4,7 @@ using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
namespace Penumbra.Api;
@ -48,12 +49,16 @@ public partial class PenumbraIpc
public const string LabelProviderApiVersion = "Penumbra.ApiVersion";
public const string LabelProviderGetModDirectory = "Penumbra.GetModDirectory";
public const string LabelProviderGetConfiguration = "Penumbra.GetConfiguration";
public const string LabelProviderPreSettingsDraw = "Penumbra.PreSettingsDraw";
public const string LabelProviderPostSettingsDraw = "Penumbra.PostSettingsDraw";
internal ICallGateProvider< object? >? ProviderInitialized;
internal ICallGateProvider< object? >? ProviderDisposed;
internal ICallGateProvider< int >? ProviderApiVersion;
internal ICallGateProvider< string >? ProviderGetModDirectory;
internal ICallGateProvider< string >? ProviderGetConfiguration;
internal ICallGateProvider<string, object?>? ProviderPreSettingsDraw;
internal ICallGateProvider<string, object?>? ProviderPostSettingsDraw;
private void InitializeGeneralProviders( DalamudPluginInterface pi )
{
@ -104,6 +109,26 @@ public partial class PenumbraIpc
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetConfiguration}:\n{e}" );
}
try
{
ProviderPreSettingsDraw = pi.GetIpcProvider<string, object?>( LabelProviderPreSettingsDraw );
Api.PreSettingsPanelDraw += InvokeSettingsPreDraw;
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderPreSettingsDraw}:\n{e}" );
}
try
{
ProviderPostSettingsDraw = pi.GetIpcProvider<string, object?>( LabelProviderPostSettingsDraw );
Api.PostSettingsPanelDraw += InvokeSettingsPostDraw;
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderPostSettingsDraw}:\n{e}" );
}
}
private void DisposeGeneralProviders()
@ -195,6 +220,12 @@ public partial class PenumbraIpc
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
=> ProviderGameObjectRedrawn?.SendMessage( objectAddress, objectTableIndex );
private void InvokeSettingsPreDraw( string modDirectory )
=> ProviderPreSettingsDraw!.SendMessage( modDirectory );
private void InvokeSettingsPostDraw( string modDirectory )
=> ProviderPostSettingsDraw!.SendMessage( modDirectory );
private void DisposeRedrawProviders()
{
ProviderRedrawName?.UnregisterAction();
@ -425,14 +456,21 @@ public partial class PenumbraIpc
public partial class PenumbraIpc
{
public const string LabelProviderGetAvailableModSettings = "Penumbra.GetAvailableModSettings";
public const string LabelProviderReloadMod = "Penumbra.ReloadMod";
public const string LabelProviderAddMod = "Penumbra.AddMod";
public const string LabelProviderGetCurrentModSettings = "Penumbra.GetCurrentModSettings";
public const string LabelProviderTryInheritMod = "Penumbra.TryInheritMod";
public const string LabelProviderTrySetMod = "Penumbra.TrySetMod";
public const string LabelProviderTrySetModPriority = "Penumbra.TrySetModPriority";
public const string LabelProviderTrySetModSetting = "Penumbra.TrySetModSetting";
public const string LabelProviderTrySetModSettings = "Penumbra.TrySetModSettings";
public const string LabelProviderModSettingChanged = "Penumbra.ModSettingChanged";
internal ICallGateProvider< ModSettingChange, string, string, bool, object? >? ProviderModSettingChanged;
internal ICallGateProvider< string, string, IDictionary< string, (IList< string >, Mods.SelectType) >? >? ProviderGetAvailableModSettings;
internal ICallGateProvider< string, string, PenumbraApiEc >? ProviderReloadMod;
internal ICallGateProvider< string, PenumbraApiEc >? ProviderAddMod;
internal ICallGateProvider< string, string, string, bool, (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) >?
ProviderGetCurrentModSettings;
@ -445,6 +483,16 @@ public partial class PenumbraIpc
private void InitializeSettingProviders( DalamudPluginInterface pi )
{
try
{
ProviderModSettingChanged = pi.GetIpcProvider< ModSettingChange, string, string, bool, object? >( LabelProviderModSettingChanged );
Api.ModSettingChanged += InvokeModSettingChanged;
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderModSettingChanged}:\n{e}" );
}
try
{
ProviderGetAvailableModSettings =
@ -457,6 +505,26 @@ public partial class PenumbraIpc
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetAvailableModSettings}:\n{e}" );
}
try
{
ProviderReloadMod = pi.GetIpcProvider< string, string, PenumbraApiEc >( LabelProviderReloadMod );
ProviderReloadMod.RegisterFunc( Api.ReloadMod );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderReloadMod}:\n{e}" );
}
try
{
ProviderAddMod = pi.GetIpcProvider< string, PenumbraApiEc >( LabelProviderAddMod );
ProviderAddMod.RegisterFunc( Api.AddMod );
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
try
{
ProviderGetCurrentModSettings =
@ -524,7 +592,10 @@ public partial class PenumbraIpc
private void DisposeSettingProviders()
{
Api.ModSettingChanged -= InvokeModSettingChanged;
ProviderGetAvailableModSettings?.UnregisterFunc();
ProviderReloadMod?.UnregisterFunc();
ProviderAddMod?.UnregisterFunc();
ProviderGetCurrentModSettings?.UnregisterFunc();
ProviderTryInheritMod?.UnregisterFunc();
ProviderTrySetMod?.UnregisterFunc();
@ -532,6 +603,9 @@ public partial class PenumbraIpc
ProviderTrySetModSetting?.UnregisterFunc();
ProviderTrySetModSettings?.UnregisterFunc();
}
private void InvokeModSettingChanged( ModSettingChange type, string collection, string mod, bool inherited )
=> ProviderModSettingChanged?.SendMessage( type, collection, mod, inherited );
}
public partial class PenumbraIpc

View file

@ -1,20 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Api;
using Penumbra.Collections;
using Penumbra.Interop.Loader;
using Penumbra.Interop.Structs;
using Penumbra.Mods;
using CharacterUtility = Penumbra.Interop.CharacterUtility;
namespace Penumbra.UI;

View file

@ -49,6 +49,7 @@ public partial class ConfigWindow
DrawInheritedWarning();
ImGui.Dummy( _window._defaultSpace );
_window._penumbra.Api.InvokePreSettingsPanel( _mod.ModPath.Name );
DrawEnabledInput();
ImGui.SameLine();
DrawPriorityInput();
@ -64,6 +65,8 @@ public partial class ConfigWindow
{
DrawMultiGroup( _mod.Groups[ idx ], idx );
}
_window._penumbra.Api.InvokePostSettingsPanel(_mod.ModPath.Name);
}