Optional Addition: Include IPC to get the current state of Auto-Reload Gear, and an IPC Event call for when the option changes.

- Should help clear up ambiguity with any external plugins intending to call ReapplyState on a mod-change to themselves, to know if Glamourer has it handled for them.
This commit is contained in:
Cordelia Mist 2025-10-05 10:31:41 -07:00
parent d6c36ca4f7
commit 48bef12555
8 changed files with 68 additions and 13 deletions

View file

@ -3,7 +3,7 @@ using OtterGui.Services;
namespace Glamourer.Api; namespace Glamourer.Api;
public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService public class GlamourerApi(Configuration config, DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService
{ {
public const int CurrentApiVersionMajor = 1; public const int CurrentApiVersionMajor = 1;
public const int CurrentApiVersionMinor = 6; public const int CurrentApiVersionMinor = 6;
@ -11,6 +11,9 @@ public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) :
public (int Major, int Minor) ApiVersion public (int Major, int Minor) ApiVersion
=> (CurrentApiVersionMajor, CurrentApiVersionMinor); => (CurrentApiVersionMajor, CurrentApiVersionMinor);
public bool AutoReloadGearEnabled
=> config.AutoRedrawEquipOnChanges;
public IGlamourerApiDesigns Designs public IGlamourerApiDesigns Designs
=> designs; => designs;

View file

@ -22,6 +22,7 @@ public sealed class IpcProviders : IDisposable, IApiService
new FuncProvider<(int Major, int Minor)>(pi, "Glamourer.ApiVersions", () => api.ApiVersion), // backward compatibility new FuncProvider<(int Major, int Minor)>(pi, "Glamourer.ApiVersions", () => api.ApiVersion), // backward compatibility
new FuncProvider<int>(pi, "Glamourer.ApiVersion", () => api.ApiVersion.Major), // backward compatibility new FuncProvider<int>(pi, "Glamourer.ApiVersion", () => api.ApiVersion.Major), // backward compatibility
IpcSubscribers.ApiVersion.Provider(pi, api), IpcSubscribers.ApiVersion.Provider(pi, api),
IpcSubscribers.AutoReloadGearEnabled.Provider(pi, api),
IpcSubscribers.GetDesignList.Provider(pi, api.Designs), IpcSubscribers.GetDesignList.Provider(pi, api.Designs),
IpcSubscribers.GetDesignListExtended.Provider(pi, api.Designs), IpcSubscribers.GetDesignListExtended.Provider(pi, api.Designs),
@ -59,6 +60,7 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.UnlockAll.Provider(pi, api.State), IpcSubscribers.UnlockAll.Provider(pi, api.State),
IpcSubscribers.RevertToAutomation.Provider(pi, api.State), IpcSubscribers.RevertToAutomation.Provider(pi, api.State),
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State), IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),
IpcSubscribers.AutoReloadGearChanged.Provider(pi, api.State),
IpcSubscribers.StateChanged.Provider(pi, api.State), IpcSubscribers.StateChanged.Provider(pi, api.State),
IpcSubscribers.StateChangedWithType.Provider(pi, api.State), IpcSubscribers.StateChangedWithType.Provider(pi, api.State),
IpcSubscribers.StateFinalized.Provider(pi, api.State), IpcSubscribers.StateFinalized.Provider(pi, api.State),

View file

@ -20,6 +20,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private readonly Configuration _config; private readonly Configuration _config;
private readonly AutoDesignApplier _autoDesigns; private readonly AutoDesignApplier _autoDesigns;
private readonly ActorObjectManager _objects; private readonly ActorObjectManager _objects;
private readonly AutoRedrawChanged _autoRedraw;
private readonly StateChanged _stateChanged; private readonly StateChanged _stateChanged;
private readonly StateFinalized _stateFinalized; private readonly StateFinalized _stateFinalized;
private readonly GPoseService _gPose; private readonly GPoseService _gPose;
@ -30,6 +31,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
Configuration config, Configuration config,
AutoDesignApplier autoDesigns, AutoDesignApplier autoDesigns,
ActorObjectManager objects, ActorObjectManager objects,
AutoRedrawChanged autoRedraw,
StateChanged stateChanged, StateChanged stateChanged,
StateFinalized stateFinalized, StateFinalized stateFinalized,
GPoseService gPose) GPoseService gPose)
@ -40,9 +42,11 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_config = config; _config = config;
_autoDesigns = autoDesigns; _autoDesigns = autoDesigns;
_objects = objects; _objects = objects;
_autoRedraw = autoRedraw;
_stateChanged = stateChanged; _stateChanged = stateChanged;
_stateFinalized = stateFinalized; _stateFinalized = stateFinalized;
_gPose = gPose; _gPose = gPose;
_autoRedraw.Subscribe(OnAutoRedrawChange, AutoRedrawChanged.Priority.StateApi);
_stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc); _stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc);
_stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi); _stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi);
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi); _gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi);
@ -50,6 +54,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public void Dispose() public void Dispose()
{ {
_autoRedraw.Unsubscribe(OnAutoRedrawChange);
_stateChanged.Unsubscribe(OnStateChanged); _stateChanged.Unsubscribe(OnStateChanged);
_stateFinalized.Unsubscribe(OnStateFinalized); _stateFinalized.Unsubscribe(OnStateFinalized);
_gPose.Unsubscribe(OnGPoseChange); _gPose.Unsubscribe(OnGPoseChange);
@ -293,6 +298,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public event Action<bool>? AutoReloadGearChanged;
public event Action<nint>? StateChanged; public event Action<nint>? StateChanged;
public event Action<IntPtr, StateChangeType>? StateChangedWithType; public event Action<IntPtr, StateChangeType>? StateChangedWithType;
public event Action<IntPtr, StateFinalizationType>? StateFinalized; public event Action<IntPtr, StateFinalizationType>? StateFinalized;
@ -385,8 +391,8 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
}; };
} }
private void OnGPoseChange(bool gPose) private void OnAutoRedrawChange(bool autoReload)
=> GPoseChanged?.Invoke(gPose); => AutoReloadGearChanged?.Invoke(autoReload);
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5) private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
{ {
@ -407,4 +413,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
foreach (var actor in actors.Objects) foreach (var actor in actors.Objects)
StateFinalized.Invoke(actor.Address, type); StateFinalized.Invoke(actor.Address, type);
} }
private void OnGPoseChange(bool gPose)
=> GPoseChanged?.Invoke(gPose);
} }

View file

@ -0,0 +1,16 @@
using OtterGui.Classes;
namespace Glamourer.Events;
/// <summary>
/// Triggered when the auto-reload gear setting is changed in glamourer configuration.
/// </summary>
public sealed class AutoRedrawChanged()
: EventWrapper<bool, AutoRedrawChanged.Priority>(nameof(AutoRedrawChanged))
{
public enum Priority
{
/// <seealso cref="Api.StateApi.OnGPoseChange"/>
StateApi = int.MinValue,
}
}

View file

@ -1,8 +1,5 @@
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Designs;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using OtterGui;
using static Penumbra.GameData.Files.ShpkFile;
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester; namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;

View file

@ -33,6 +33,11 @@ public class IpcTesterPanel(
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextUnformatted($"({major}.{minor:D4})"); ImGui.TextUnformatted($"({major}.{minor:D4})");
ImGui.TextUnformatted(AutoReloadGearEnabled.Label);
var autoRedraw = new AutoReloadGearEnabled(pluginInterface).Invoke();
ImGui.SameLine();
ImGui.TextUnformatted(autoRedraw ? "Enabled" : "Disabled");
designs.Draw(); designs.Draw();
items.Draw(); items.Draw();
state.Draw(); state.Draw();
@ -49,6 +54,7 @@ public class IpcTesterPanel(
return; return;
Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester."); Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
state.AutoRedrawChanged.Enable();
state.GPoseChanged.Enable(); state.GPoseChanged.Enable();
state.StateChanged.Enable(); state.StateChanged.Enable();
state.StateFinalized.Enable(); state.StateFinalized.Enable();
@ -72,6 +78,7 @@ public class IpcTesterPanel(
Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester."); Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester.");
_subscribed = false; _subscribed = false;
state.AutoRedrawChanged.Disable();
state.GPoseChanged.Disable(); state.GPoseChanged.Disable();
state.StateChanged.Disable(); state.StateChanged.Disable();
state.StateFinalized.Disable(); state.StateFinalized.Disable();

View file

@ -1,11 +1,11 @@
using Dalamud.Interface; using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Plugin; using Dalamud.Plugin;
using Glamourer.Api.Enums; using Glamourer.Api.Enums;
using Glamourer.Api.Helpers; using Glamourer.Api.Helpers;
using Glamourer.Api.IpcSubscribers; using Glamourer.Api.IpcSubscribers;
using Glamourer.Designs; using Glamourer.Designs;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
@ -31,6 +31,10 @@ public class StateIpcTester : IUiService, IDisposable
private string _base64State = string.Empty; private string _base64State = string.Empty;
private string? _getStateString; private string? _getStateString;
public readonly EventSubscriber<bool> AutoRedrawChanged;
private bool _lastAutoRedrawChangeValue;
private DateTime _lastAutoRedrawChangeTime;
public readonly EventSubscriber<nint, StateChangeType> StateChanged; public readonly EventSubscriber<nint, StateChangeType> StateChanged;
private nint _lastStateChangeActor; private nint _lastStateChangeActor;
private ByteString _lastStateChangeName = ByteString.Empty; private ByteString _lastStateChangeName = ByteString.Empty;
@ -51,10 +55,12 @@ public class StateIpcTester : IUiService, IDisposable
public StateIpcTester(IDalamudPluginInterface pluginInterface) public StateIpcTester(IDalamudPluginInterface pluginInterface)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged); AutoRedrawChanged = AutoReloadGearChanged.Subscriber(_pluginInterface, OnAutoRedrawChanged);
StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized); StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange); StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized);
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange);
AutoRedrawChanged.Disable();
StateChanged.Disable(); StateChanged.Disable();
StateFinalized.Disable(); StateFinalized.Disable();
GPoseChanged.Disable(); GPoseChanged.Disable();
@ -62,6 +68,7 @@ public class StateIpcTester : IUiService, IDisposable
public void Dispose() public void Dispose()
{ {
AutoRedrawChanged.Dispose();
StateChanged.Dispose(); StateChanged.Dispose();
StateFinalized.Dispose(); StateFinalized.Dispose();
GPoseChanged.Dispose(); GPoseChanged.Dispose();
@ -83,6 +90,8 @@ public class StateIpcTester : IUiService, IDisposable
IpcTesterHelpers.DrawIntro("Last Error"); IpcTesterHelpers.DrawIntro("Last Error");
ImGui.TextUnformatted(_lastError.ToString()); ImGui.TextUnformatted(_lastError.ToString());
IpcTesterHelpers.DrawIntro("Last Auto Redraw Change");
ImGui.TextUnformatted($"{_lastAutoRedrawChangeValue} at {_lastAutoRedrawChangeTime.ToLocalTime().TimeOfDay}");
IpcTesterHelpers.DrawIntro("Last State Change"); IpcTesterHelpers.DrawIntro("Last State Change");
PrintChangeName(); PrintChangeName();
IpcTesterHelpers.DrawIntro("Last State Finalization"); IpcTesterHelpers.DrawIntro("Last State Finalization");
@ -233,6 +242,12 @@ public class StateIpcTester : IUiService, IDisposable
ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}"); ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}");
} }
private void OnAutoRedrawChanged(bool value)
{
_lastAutoRedrawChangeValue = value;
_lastAutoRedrawChangeTime = DateTime.UtcNow;
}
private void OnStateChanged(nint actor, StateChangeType type) private void OnStateChanged(nint actor, StateChangeType type)
{ {
_lastStateChangeActor = actor; _lastStateChangeActor = actor;

View file

@ -6,6 +6,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Glamourer.Automation; using Glamourer.Automation;
using Glamourer.Designs; using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Gui.Tabs.DesignTab; using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Interop.PalettePlus; using Glamourer.Interop.PalettePlus;
@ -30,6 +31,7 @@ public class SettingsTab(
CodeDrawer codeDrawer, CodeDrawer codeDrawer,
Glamourer glamourer, Glamourer glamourer,
AutoDesignApplier autoDesignApplier, AutoDesignApplier autoDesignApplier,
AutoRedrawChanged autoRedraw,
PcpService pcpService) PcpService pcpService)
: ITab : ITab
{ {
@ -91,7 +93,11 @@ public class SettingsTab(
config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2); config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2);
Checkbox("Auto-Reload Gear"u8, Checkbox("Auto-Reload Gear"u8,
"Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection."u8, "Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection."u8,
config.AutoRedrawEquipOnChanges, v => config.AutoRedrawEquipOnChanges = v); config.AutoRedrawEquipOnChanges, v =>
{
config.AutoRedrawEquipOnChanges = v;
autoRedraw.Invoke(v);
});
Checkbox("Attach to PCP-Handling"u8, Checkbox("Attach to PCP-Handling"u8,
"Add the actor's glamourer state when a PCP is created by Penumbra, and create a design and apply it if possible when a PCP is installed by Penumbra."u8, "Add the actor's glamourer state when a PCP is created by Penumbra, and create a design and apply it if possible when a PCP is installed by Penumbra."u8,
config.AttachToPcp, pcpService.Set); config.AttachToPcp, pcpService.Set);