This commit is contained in:
Cordelia 2025-12-06 22:56:09 +09:00 committed by GitHub
commit b41eca9ad2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 138 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 = 7; public const int CurrentApiVersionMinor = 7;
@ -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),
@ -50,6 +51,8 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.GetStateBase64Name.Provider(pi, api.State), IpcSubscribers.GetStateBase64Name.Provider(pi, api.State),
IpcSubscribers.ApplyState.Provider(pi, api.State), IpcSubscribers.ApplyState.Provider(pi, api.State),
IpcSubscribers.ApplyStateName.Provider(pi, api.State), IpcSubscribers.ApplyStateName.Provider(pi, api.State),
IpcSubscribers.ReapplyState.Provider(pi, api.State),
IpcSubscribers.ReapplyStateName.Provider(pi, api.State),
IpcSubscribers.RevertState.Provider(pi, api.State), IpcSubscribers.RevertState.Provider(pi, api.State),
IpcSubscribers.RevertStateName.Provider(pi, api.State), IpcSubscribers.RevertStateName.Provider(pi, api.State),
IpcSubscribers.UnlockState.Provider(pi, api.State), IpcSubscribers.UnlockState.Provider(pi, api.State),
@ -58,6 +61,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 DesignConverter _converter; private readonly DesignConverter _converter;
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;
@ -29,6 +30,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
DesignConverter converter, DesignConverter converter,
AutoDesignApplier autoDesigns, AutoDesignApplier autoDesigns,
ActorObjectManager objects, ActorObjectManager objects,
AutoRedrawChanged autoRedraw,
StateChanged stateChanged, StateChanged stateChanged,
StateFinalized stateFinalized, StateFinalized stateFinalized,
GPoseService gPose) GPoseService gPose)
@ -38,9 +40,11 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_converter = converter; _converter = converter;
_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);
@ -48,6 +52,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);
@ -121,6 +126,48 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args); return ApiHelpers.Return(GlamourerApiEc.Success, args);
} }
public GlamourerApiEc ReapplyState(int objectIndex, uint key, ApplyFlag flags)
{
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
if (_helpers.FindExistingState(objectIndex, out var state) != GlamourerApiEc.Success)
return ApiHelpers.Return(GlamourerApiEc.ActorNotFound, args);
if (state == null)
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
if (!state.CanUnlock(key))
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
Reapply(_objects.Objects[objectIndex], state, key, flags);
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public GlamourerApiEc ReapplyStateName(string playerName, uint key, ApplyFlag flags)
{
var args = ApiHelpers.Args("Name", playerName, "Key", key, "Flags", flags);
var states = _helpers.FindExistingStates(playerName);
var any = false;
var anyReapplied = false;
foreach (var state in states)
{
any = true;
if (!state.CanUnlock(key))
continue;
anyReapplied = true;
anyReapplied |= Reapply(state, key, flags) is GlamourerApiEc.Success;
}
if (any)
ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
if (!anyReapplied)
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags) public GlamourerApiEc RevertState(int objectIndex, uint key, ApplyFlag flags)
{ {
var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags); var args = ApiHelpers.Args("Index", objectIndex, "Key", key, "Flags", flags);
@ -270,6 +317,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;
@ -284,6 +332,24 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
ApiHelpers.Lock(state, key, flags); ApiHelpers.Lock(state, key, flags);
} }
private GlamourerApiEc Reapply(ActorState state, uint key, ApplyFlag flags)
{
if (!_objects.TryGetValue(state.Identifier, out var actors) || !actors.Valid)
return GlamourerApiEc.ActorNotFound;
foreach (var actor in actors.Objects)
Reapply(actor, state, key, flags);
return GlamourerApiEc.Success;
}
private void Reapply(Actor actor, ActorState state, uint key, ApplyFlag flags)
{
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
_stateManager.ReapplyState(actor, state, false, source, true);
ApiHelpers.Lock(state, key, flags);
}
private void Revert(ActorState state, uint key, ApplyFlag flags) private void Revert(ActorState state, uint key, ApplyFlag flags)
{ {
var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed; var source = (flags & ApplyFlag.Once) != 0 ? StateSource.IpcManual : StateSource.IpcFixed;
@ -344,8 +410,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)
{ {
@ -366,4 +432,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");
@ -138,6 +147,14 @@ public class StateIpcTester : IUiService, IDisposable
if (ImUtf8.Button("Apply Base64##Name"u8)) if (ImUtf8.Button("Apply Base64##Name"u8))
_lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags); _lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(ReapplyState.Label);
if (ImUtf8.Button("Reapply##Idx"u8))
_lastError = new ReapplyState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(ReapplyStateName.Label);
if (ImUtf8.Button("Reapply##Name"u8))
_lastError = new ReapplyStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertState.Label); IpcTesterHelpers.DrawIntro(RevertState.Label);
if (ImUtf8.Button("Revert##Idx"u8)) if (ImUtf8.Button("Revert##Idx"u8))
_lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags); _lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
@ -225,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);