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;
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 CurrentApiVersionMinor = 7;
@ -11,6 +11,9 @@ public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) :
public (int Major, int Minor) ApiVersion
=> (CurrentApiVersionMajor, CurrentApiVersionMinor);
public bool AutoReloadGearEnabled
=> config.AutoRedrawEquipOnChanges;
public IGlamourerApiDesigns 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>(pi, "Glamourer.ApiVersion", () => api.ApiVersion.Major), // backward compatibility
IpcSubscribers.ApiVersion.Provider(pi, api),
IpcSubscribers.AutoReloadGearEnabled.Provider(pi, api),
IpcSubscribers.GetDesignList.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.ApplyState.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.RevertStateName.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.RevertToAutomation.Provider(pi, api.State),
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),
IpcSubscribers.AutoReloadGearChanged.Provider(pi, api.State),
IpcSubscribers.StateChanged.Provider(pi, api.State),
IpcSubscribers.StateChangedWithType.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 AutoDesignApplier _autoDesigns;
private readonly ActorObjectManager _objects;
private readonly AutoRedrawChanged _autoRedraw;
private readonly StateChanged _stateChanged;
private readonly StateFinalized _stateFinalized;
private readonly GPoseService _gPose;
@ -29,6 +30,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
DesignConverter converter,
AutoDesignApplier autoDesigns,
ActorObjectManager objects,
AutoRedrawChanged autoRedraw,
StateChanged stateChanged,
StateFinalized stateFinalized,
GPoseService gPose)
@ -38,9 +40,11 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_converter = converter;
_autoDesigns = autoDesigns;
_objects = objects;
_autoRedraw = autoRedraw;
_stateChanged = stateChanged;
_stateFinalized = stateFinalized;
_gPose = gPose;
_autoRedraw.Subscribe(OnAutoRedrawChange, AutoRedrawChanged.Priority.StateApi);
_stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc);
_stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi);
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi);
@ -48,6 +52,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public void Dispose()
{
_autoRedraw.Unsubscribe(OnAutoRedrawChange);
_stateChanged.Unsubscribe(OnStateChanged);
_stateFinalized.Unsubscribe(OnStateFinalized);
_gPose.Unsubscribe(OnGPoseChange);
@ -121,6 +126,48 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
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)
{
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);
}
public event Action<bool>? AutoReloadGearChanged;
public event Action<nint>? StateChanged;
public event Action<IntPtr, StateChangeType>? StateChangedWithType;
public event Action<IntPtr, StateFinalizationType>? StateFinalized;
@ -284,6 +332,24 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
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)
{
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)
=> GPoseChanged?.Invoke(gPose);
private void OnAutoRedrawChange(bool autoReload)
=> AutoReloadGearChanged?.Invoke(autoReload);
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)
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.Designs;
using Dalamud.Bindings.ImGui;
using OtterGui;
using static Penumbra.GameData.Files.ShpkFile;
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;

View file

@ -33,6 +33,11 @@ public class IpcTesterPanel(
ImGui.SameLine();
ImGui.TextUnformatted($"({major}.{minor:D4})");
ImGui.TextUnformatted(AutoReloadGearEnabled.Label);
var autoRedraw = new AutoReloadGearEnabled(pluginInterface).Invoke();
ImGui.SameLine();
ImGui.TextUnformatted(autoRedraw ? "Enabled" : "Disabled");
designs.Draw();
items.Draw();
state.Draw();
@ -49,6 +54,7 @@ public class IpcTesterPanel(
return;
Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
state.AutoRedrawChanged.Enable();
state.GPoseChanged.Enable();
state.StateChanged.Enable();
state.StateFinalized.Enable();
@ -72,6 +78,7 @@ public class IpcTesterPanel(
Glamourer.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester.");
_subscribed = false;
state.AutoRedrawChanged.Disable();
state.GPoseChanged.Disable();
state.StateChanged.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.Plugin;
using Glamourer.Api.Enums;
using Glamourer.Api.Helpers;
using Glamourer.Api.IpcSubscribers;
using Glamourer.Designs;
using Dalamud.Bindings.ImGui;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;
@ -31,6 +31,10 @@ public class StateIpcTester : IUiService, IDisposable
private string _base64State = string.Empty;
private string? _getStateString;
public readonly EventSubscriber<bool> AutoRedrawChanged;
private bool _lastAutoRedrawChangeValue;
private DateTime _lastAutoRedrawChangeTime;
public readonly EventSubscriber<nint, StateChangeType> StateChanged;
private nint _lastStateChangeActor;
private ByteString _lastStateChangeName = ByteString.Empty;
@ -52,9 +56,11 @@ public class StateIpcTester : IUiService, IDisposable
public StateIpcTester(IDalamudPluginInterface pluginInterface)
{
_pluginInterface = pluginInterface;
AutoRedrawChanged = AutoReloadGearChanged.Subscriber(_pluginInterface, OnAutoRedrawChanged);
StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized);
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange);
AutoRedrawChanged.Disable();
StateChanged.Disable();
StateFinalized.Disable();
GPoseChanged.Disable();
@ -62,6 +68,7 @@ public class StateIpcTester : IUiService, IDisposable
public void Dispose()
{
AutoRedrawChanged.Dispose();
StateChanged.Dispose();
StateFinalized.Dispose();
GPoseChanged.Dispose();
@ -83,6 +90,8 @@ public class StateIpcTester : IUiService, IDisposable
IpcTesterHelpers.DrawIntro("Last Error");
ImGui.TextUnformatted(_lastError.ToString());
IpcTesterHelpers.DrawIntro("Last Auto Redraw Change");
ImGui.TextUnformatted($"{_lastAutoRedrawChangeValue} at {_lastAutoRedrawChangeTime.ToLocalTime().TimeOfDay}");
IpcTesterHelpers.DrawIntro("Last State Change");
PrintChangeName();
IpcTesterHelpers.DrawIntro("Last State Finalization");
@ -138,6 +147,14 @@ public class StateIpcTester : IUiService, IDisposable
if (ImUtf8.Button("Apply Base64##Name"u8))
_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);
if (ImUtf8.Button("Revert##Idx"u8))
_lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
@ -225,6 +242,12 @@ public class StateIpcTester : IUiService, IDisposable
ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}");
}
private void OnAutoRedrawChanged(bool value)
{
_lastAutoRedrawChangeValue = value;
_lastAutoRedrawChangeTime = DateTime.UtcNow;
}
private void OnStateChanged(nint actor, StateChangeType type)
{
_lastStateChangeActor = actor;

View file

@ -6,6 +6,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services;
using Glamourer.Automation;
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop;
using Glamourer.Interop.PalettePlus;
@ -30,6 +31,7 @@ public class SettingsTab(
CodeDrawer codeDrawer,
Glamourer glamourer,
AutoDesignApplier autoDesignApplier,
AutoRedrawChanged autoRedraw,
PcpService pcpService)
: ITab
{
@ -91,7 +93,11 @@ public class SettingsTab(
config.DisableFestivals == 0, v => config.DisableFestivals = v ? (byte)0 : (byte)2);
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,
config.AutoRedrawEquipOnChanges, v => config.AutoRedrawEquipOnChanges = v);
config.AutoRedrawEquipOnChanges, v =>
{
config.AutoRedrawEquipOnChanges = v;
autoRedraw.Invoke(v);
});
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,
config.AttachToPcp, pcpService.Set);