Continue renames, some cleanup.

This commit is contained in:
Ottermandias 2025-01-24 17:50:58 +01:00
parent 7be283ca30
commit d9f9937d41
18 changed files with 175 additions and 134 deletions

View file

@ -33,7 +33,7 @@ public class DesignsApi(ApiHelpers helpers, DesignManager designs, StateManager
{
var once = (flags & ApplyFlag.Once) != 0;
var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true,
ResetMaterials: !once && key != 0, SendStateUpdate: true);
ResetMaterials: !once && key != 0, IsFinal: true);
using var restrict = ApiHelpers.Restrict(design, flags);
stateManager.ApplyDesign(state, design, settings);

View file

@ -2,7 +2,6 @@ using Dalamud.Plugin;
using Glamourer.Api.Api;
using Glamourer.Api.Helpers;
using OtterGui.Services;
using System.Reflection.Emit;
using Glamourer.Api.Enums;
namespace Glamourer.Api;

View file

@ -11,9 +11,9 @@ using OtterGui.Services;
using Penumbra.GameData.Interop;
using ObjectManager = Glamourer.Interop.ObjectManager;
using StateChanged = Glamourer.Events.StateChanged;
using StateUpdated = Glamourer.Events.StateUpdated;
namespace Glamourer.Api;
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
{
private readonly ApiHelpers _helpers;
@ -23,7 +23,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private readonly AutoDesignApplier _autoDesigns;
private readonly ObjectManager _objects;
private readonly StateChanged _stateChanged;
private readonly StateUpdated _stateUpdated;
private readonly StateFinalized _stateFinalized;
private readonly GPoseService _gPose;
public StateApi(ApiHelpers helpers,
@ -33,27 +33,27 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
AutoDesignApplier autoDesigns,
ObjectManager objects,
StateChanged stateChanged,
StateUpdated stateUpdated,
StateFinalized stateFinalized,
GPoseService gPose)
{
_helpers = helpers;
_stateManager = stateManager;
_converter = converter;
_config = config;
_autoDesigns = autoDesigns;
_objects = objects;
_stateChanged = stateChanged;
_stateUpdated = stateUpdated;
_gPose = gPose;
_helpers = helpers;
_stateManager = stateManager;
_converter = converter;
_config = config;
_autoDesigns = autoDesigns;
_objects = objects;
_stateChanged = stateChanged;
_stateFinalized = stateFinalized;
_gPose = gPose;
_stateChanged.Subscribe(OnStateChanged, Events.StateChanged.Priority.GlamourerIpc);
_stateUpdated.Subscribe(OnStateUpdated, Events.StateUpdated.Priority.GlamourerIpc);
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.GlamourerIpc);
_stateFinalized.Subscribe(OnStateFinalized, Events.StateFinalized.Priority.StateApi);
_gPose.Subscribe(OnGPoseChange, GPoseService.Priority.StateApi);
}
public void Dispose()
{
_stateChanged.Unsubscribe(OnStateChanged);
_stateUpdated.Unsubscribe(OnStateUpdated);
_stateFinalized.Unsubscribe(OnStateFinalized);
_gPose.Unsubscribe(OnGPoseChange);
}
@ -253,16 +253,16 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public event Action<nint>? StateChanged;
public event Action<IntPtr, StateChangeType>? StateChangedWithType;
public event Action<nint>? StateChanged;
public event Action<IntPtr, StateChangeType>? StateChangedWithType;
public event Action<IntPtr, StateFinalizationType>? StateFinalized;
public event Action<bool>? GPoseChanged;
public event Action<bool>? GPoseChanged;
private void ApplyDesign(ActorState state, DesignBase design, uint key, ApplyFlag flags)
{
var once = (flags & ApplyFlag.Once) != 0;
var settings = new ApplySettings(Source: once ? StateSource.IpcManual : StateSource.IpcFixed, Key: key, MergeLinks: true,
ResetMaterials: !once && key != 0, SendStateUpdate: true);
ResetMaterials: !once && key != 0, IsFinal: true);
_stateManager.ApplyDesign(state, design, settings);
ApiHelpers.Lock(state, key, flags);
}
@ -349,7 +349,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
StateChangedWithType.Invoke(actor.Address, type);
}
private void OnStateUpdated(StateFinalizationType type, ActorData actors)
private void OnStateFinalized(StateFinalizationType type, ActorData actors)
{
Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]");
if (StateFinalized != null)

View file

@ -14,7 +14,7 @@ public readonly record struct ApplySettings(
bool UseSingleSource = false,
bool MergeLinks = false,
bool ResetMaterials = false,
bool SendStateUpdate = false)
bool IsFinal = false)
{
public static readonly ApplySettings Manual = new()
{
@ -25,7 +25,7 @@ public readonly record struct ApplySettings(
UseSingleSource = false,
MergeLinks = false,
ResetMaterials = false,
SendStateUpdate = false,
IsFinal = false,
};
public static readonly ApplySettings ManualWithLinks = new()
@ -37,7 +37,7 @@ public readonly record struct ApplySettings(
UseSingleSource = false,
MergeLinks = true,
ResetMaterials = false,
SendStateUpdate = false,
IsFinal = false,
};
public static readonly ApplySettings Game = new()
@ -49,7 +49,7 @@ public readonly record struct ApplySettings(
UseSingleSource = false,
MergeLinks = false,
ResetMaterials = true,
SendStateUpdate = false,
IsFinal = false,
};
}

View file

@ -13,8 +13,8 @@ public sealed class GPoseService : EventWrapper<bool, GPoseService.Priority>
public enum Priority
{
/// <seealso cref="Api.GlamourerIpc.OnGPoseChanged"/>
GlamourerIpc = int.MinValue,
/// <seealso cref="Api.StateApi.OnGPoseChange"/>
StateApi = int.MinValue,
}
public bool InGPose { get; private set; }

View file

@ -1,24 +1,23 @@
using Glamourer.Api;
using Glamourer.Api.Enums;
using Glamourer.Designs.History;
using Glamourer.Interop.Structs;
using Glamourer.State;
using OtterGui.Classes;
namespace Glamourer.Events;
/// <summary>
/// Triggered when a Design is edited in any way.
/// Triggered when a set of grouped changes finishes being applied to a Glamourer state.
/// <list type="number">
/// <item>Parameter is the operation that finished updating the saved state. </item>
/// <item>Parameter is the existing actors using this saved state. </item>
/// </list>
/// </summary>
public sealed class StateUpdated()
: EventWrapper<StateFinalizationType, ActorData, StateUpdated.Priority>(nameof(StateUpdated))
public sealed class StateFinalized()
: EventWrapper<StateFinalizationType, ActorData, StateFinalized.Priority>(nameof(StateFinalized))
{
public enum Priority
{
/// <seealso cref="Api.GlamourerIpc.OnStateUpdated"/>
GlamourerIpc = int.MinValue,
/// <seealso cref="StateApi.OnStateFinalized"/>
StateApi = int.MinValue,
}
}

View file

@ -183,7 +183,7 @@ public sealed class DesignQuickBar : Window, IDisposable
}
using var _ = design!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true });
}
private void DrawRevertButton(Vector2 buttonSize)
@ -213,7 +213,7 @@ public sealed class DesignQuickBar : Window, IDisposable
var (clicked, _, _, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available);
ImGui.SameLine();
if (clicked)
_stateManager.ResetState(state!, StateSource.Manual, stateUpdate: true);
_stateManager.ResetState(state!, StateSource.Manual, isFinal: true);
}
private void DrawRevertAutomationButton(Vector2 buttonSize)

View file

@ -385,7 +385,7 @@ public class ActorPanel
{
if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.",
_state!.IsLocked))
_stateManager.ResetState(_state!, StateSource.Manual, stateUpdate: true);
_stateManager.ResetState(_state!, StateSource.Manual, isFinal: true);
ImGui.SameLine();
@ -423,7 +423,7 @@ public class ActorPanel
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
ApplySettings.Manual with { SendStateUpdate = true });
ApplySettings.Manual with { IsFinal = true });
}
private void DrawApplyToTarget()
@ -440,7 +440,7 @@ public class ActorPanel
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
_stateManager.ApplyDesign(state, _converter.Convert(_state!, ApplicationRules.FromModifiers(_state!)),
ApplySettings.Manual with { SendStateUpdate = true });
ApplySettings.Manual with { IsFinal = true });
}
@ -467,7 +467,7 @@ public class ActorPanel
var text = ImGui.GetClipboardText();
var design = panel._converter.FromBase64(text, applyCustomize, applyGear, out _)
?? throw new Exception("The clipboard did not contain valid data.");
panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
panel._stateManager.ApplyDesign(panel._state!, design, ApplySettings.ManualWithLinks with { IsFinal = true });
}
catch (Exception ex)
{

View file

@ -79,7 +79,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled))
{
var design = CreateDesign(plate);
_state.ApplyDesign(state!, design, ApplySettings.Manual with { SendStateUpdate = true });
_state.ApplyDesign(state!, design, ApplySettings.Manual with { IsFinal = true });
}
using (ImRaii.Group())

View file

@ -51,6 +51,7 @@ public class IpcTesterPanel(
Glamourer.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester.");
state.GPoseChanged.Enable();
state.StateChanged.Enable();
state.StateFinalized.Enable();
framework.Update += CheckUnsubscribe;
_subscribed = true;
}
@ -73,5 +74,6 @@ public class IpcTesterPanel(
_subscribed = false;
state.GPoseChanged.Disable();
state.StateChanged.Disable();
state.StateFinalized.Disable();
}
}

View file

@ -11,6 +11,7 @@ using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Interop;
using Penumbra.String;
@ -31,9 +32,16 @@ public class StateIpcTester : IUiService, IDisposable
private string? _getStateString;
public readonly EventSubscriber<nint, StateChangeType> StateChanged;
private nint _lastStateChangeActor;
private ByteString _lastStateChangeName = ByteString.Empty;
private DateTime _lastStateChangeTime;
private nint _lastStateChangeActor;
private ByteString _lastStateChangeName = ByteString.Empty;
private DateTime _lastStateChangeTime;
private StateChangeType _lastStateChangeType;
public readonly EventSubscriber<nint, StateFinalizationType> StateFinalized;
private nint _lastStateFinalizeActor;
private ByteString _lastStateFinalizeName = ByteString.Empty;
private DateTime _lastStateFinalizeTime;
private StateFinalizationType _lastStateFinalizeType;
public readonly EventSubscriber<bool> GPoseChanged;
private bool _lastGPoseChangeValue;
@ -44,15 +52,18 @@ public class StateIpcTester : IUiService, IDisposable
public StateIpcTester(IDalamudPluginInterface pluginInterface)
{
_pluginInterface = pluginInterface;
StateChanged = Api.IpcSubscribers.StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
StateChanged = StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
StateFinalized = Api.IpcSubscribers.StateFinalized.Subscriber(_pluginInterface, OnStateFinalized);
GPoseChanged = Api.IpcSubscribers.GPoseChanged.Subscriber(_pluginInterface, OnGPoseChange);
StateChanged.Disable();
StateFinalized.Disable();
GPoseChanged.Disable();
}
public void Dispose()
{
StateChanged.Dispose();
StateFinalized.Dispose();
GPoseChanged.Dispose();
}
@ -73,86 +84,88 @@ public class StateIpcTester : IUiService, IDisposable
IpcTesterHelpers.DrawIntro("Last Error");
ImGui.TextUnformatted(_lastError.ToString());
IpcTesterHelpers.DrawIntro("Last State Change");
PrintName();
PrintChangeName();
IpcTesterHelpers.DrawIntro("Last State Finalization");
PrintFinalizeName();
IpcTesterHelpers.DrawIntro("Last GPose Change");
ImGui.TextUnformatted($"{_lastGPoseChangeValue} at {_lastGPoseChangeTime.ToLocalTime().TimeOfDay}");
IpcTesterHelpers.DrawIntro(GetState.Label);
DrawStatePopup();
if (ImGui.Button("Get##Idx"))
if (ImUtf8.Button("Get##Idx"u8))
{
(_lastError, _state) = new GetState(_pluginInterface).Invoke(_gameObjectIndex, _key);
_stateString = _state?.ToString(Formatting.Indented) ?? "No State Available";
ImGui.OpenPopup("State");
ImUtf8.OpenPopup("State"u8);
}
IpcTesterHelpers.DrawIntro(GetStateName.Label);
if (ImGui.Button("Get##Name"))
if (ImUtf8.Button("Get##Name"u8))
{
(_lastError, _state) = new GetStateName(_pluginInterface).Invoke(_gameObjectName, _key);
_stateString = _state?.ToString(Formatting.Indented) ?? "No State Available";
ImGui.OpenPopup("State");
ImUtf8.OpenPopup("State"u8);
}
IpcTesterHelpers.DrawIntro(GetStateBase64.Label);
if (ImGui.Button("Get##Base64Idx"))
if (ImUtf8.Button("Get##Base64Idx"u8))
{
(_lastError, _getStateString) = new GetStateBase64(_pluginInterface).Invoke(_gameObjectIndex, _key);
_stateString = _getStateString ?? "No State Available";
ImGui.OpenPopup("State");
ImUtf8.OpenPopup("State"u8);
}
IpcTesterHelpers.DrawIntro(GetStateBase64Name.Label);
if (ImGui.Button("Get##Base64Idx"))
if (ImUtf8.Button("Get##Base64Idx"u8))
{
(_lastError, _getStateString) = new GetStateBase64Name(_pluginInterface).Invoke(_gameObjectName, _key);
_stateString = _getStateString ?? "No State Available";
ImGui.OpenPopup("State");
ImUtf8.OpenPopup("State"u8);
}
IpcTesterHelpers.DrawIntro(ApplyState.Label);
if (ImGuiUtil.DrawDisabledButton("Apply Last##Idx", Vector2.Zero, string.Empty, _state == null))
_lastError = new ApplyState(_pluginInterface).Invoke(_state!, _gameObjectIndex, _key, _flags);
ImGui.SameLine();
if (ImGui.Button("Apply Base64##Idx"))
if (ImUtf8.Button("Apply Base64##Idx"u8))
_lastError = new ApplyState(_pluginInterface).Invoke(_base64State, _gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(ApplyStateName.Label);
if (ImGuiUtil.DrawDisabledButton("Apply Last##Name", Vector2.Zero, string.Empty, _state == null))
_lastError = new ApplyStateName(_pluginInterface).Invoke(_state!, _gameObjectName, _key, _flags);
ImGui.SameLine();
if (ImGui.Button("Apply Base64##Name"))
if (ImUtf8.Button("Apply Base64##Name"u8))
_lastError = new ApplyStateName(_pluginInterface).Invoke(_base64State, _gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertState.Label);
if (ImGui.Button("Revert##Idx"))
if (ImUtf8.Button("Revert##Idx"u8))
_lastError = new RevertState(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertStateName.Label);
if (ImGui.Button("Revert##Name"))
if (ImUtf8.Button("Revert##Name"u8))
_lastError = new RevertStateName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
IpcTesterHelpers.DrawIntro(UnlockState.Label);
if (ImGui.Button("Unlock##Idx"))
if (ImUtf8.Button("Unlock##Idx"u8))
_lastError = new UnlockState(_pluginInterface).Invoke(_gameObjectIndex, _key);
IpcTesterHelpers.DrawIntro(UnlockStateName.Label);
if (ImGui.Button("Unlock##Name"))
if (ImUtf8.Button("Unlock##Name"u8))
_lastError = new UnlockStateName(_pluginInterface).Invoke(_gameObjectName, _key);
IpcTesterHelpers.DrawIntro(UnlockAll.Label);
if (ImGui.Button("Unlock##All"))
if (ImUtf8.Button("Unlock##All"u8))
_numUnlocked = new UnlockAll(_pluginInterface).Invoke(_key);
ImGui.SameLine();
ImGui.TextUnformatted($"Unlocked {_numUnlocked}");
IpcTesterHelpers.DrawIntro(RevertToAutomation.Label);
if (ImGui.Button("Revert##AutomationIdx"))
if (ImUtf8.Button("Revert##AutomationIdx"u8))
_lastError = new RevertToAutomation(_pluginInterface).Invoke(_gameObjectIndex, _key, _flags);
IpcTesterHelpers.DrawIntro(RevertToAutomationName.Label);
if (ImGui.Button("Revert##AutomationName"))
if (ImUtf8.Button("Revert##AutomationName"u8))
_lastError = new RevertToAutomationName(_pluginInterface).Invoke(_gameObjectName, _key, _flags);
}
@ -162,44 +175,70 @@ public class StateIpcTester : IUiService, IDisposable
if (_stateString == null)
return;
using var p = ImRaii.Popup("State");
using var p = ImUtf8.Popup("State"u8);
if (!p)
return;
if (ImGui.Button("Copy to Clipboard"))
ImGui.SetClipboardText(_stateString);
if (ImUtf8.Button("Copy to Clipboard"u8))
ImUtf8.SetClipboardText(_stateString);
if (_stateString[0] is '{')
{
ImGui.SameLine();
if (ImGui.Button("Copy as Base64") && _state != null)
ImGui.SetClipboardText(DesignConverter.ToBase64(_state));
if (ImUtf8.Button("Copy as Base64"u8) && _state != null)
ImUtf8.SetClipboardText(DesignConverter.ToBase64(_state));
}
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(_stateString ?? string.Empty);
ImUtf8.TextWrapped(_stateString ?? string.Empty);
if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused())
if (ImUtf8.Button("Close"u8, -Vector2.UnitX) || !ImGui.IsWindowFocused())
ImGui.CloseCurrentPopup();
}
private unsafe void PrintName()
private unsafe void PrintChangeName()
{
ImGuiNative.igTextUnformatted(_lastStateChangeName.Path, _lastStateChangeName.Path + _lastStateChangeName.Length);
ImUtf8.Text(_lastStateChangeName.Span);
ImGui.SameLine(0, 0);
ImUtf8.Text($" ({_lastStateChangeType})");
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.MonoFont))
{
ImGuiUtil.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}");
ImUtf8.CopyOnClickSelectable($"0x{_lastStateChangeActor:X}");
}
ImGui.SameLine();
ImGui.TextUnformatted($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}");
ImUtf8.Text($"at {_lastStateChangeTime.ToLocalTime().TimeOfDay}");
}
private void OnStateChanged(nint actor, StateChangeType _)
private unsafe void PrintFinalizeName()
{
ImUtf8.Text(_lastStateFinalizeName.Span);
ImGui.SameLine(0, 0);
ImUtf8.Text($" ({_lastStateFinalizeType})");
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.MonoFont))
{
ImUtf8.CopyOnClickSelectable($"0x{_lastStateFinalizeActor:X}");
}
ImGui.SameLine();
ImUtf8.Text($"at {_lastStateFinalizeTime.ToLocalTime().TimeOfDay}");
}
private void OnStateChanged(nint actor, StateChangeType type)
{
_lastStateChangeActor = actor;
_lastStateChangeTime = DateTime.UtcNow;
_lastStateChangeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty;
_lastStateChangeType = type;
}
private void OnStateFinalized(nint actor, StateFinalizationType type)
{
_lastStateFinalizeActor = actor;
_lastStateFinalizeTime = DateTime.UtcNow;
_lastStateFinalizeName = actor != nint.Zero ? ((Actor)actor).Utf8Name.Clone() : ByteString.Empty;
_lastStateFinalizeType = type;
}
private void OnGPoseChange(bool value)

View file

@ -460,7 +460,7 @@ public class DesignPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state))
{
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true });
}
}
@ -478,7 +478,7 @@ public class DesignPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state))
{
using var _ = _selector.Selected!.TemporarilyRestrictApplication(ApplicationCollection.FromKeys());
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
_state.ApplyDesign(state, _selector.Selected!, ApplySettings.ManualWithLinks with { IsFinal = true });
}
}

View file

@ -196,7 +196,7 @@ public class NpcPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state))
{
var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
_state.ApplyDesign(state, design, ApplySettings.Manual with { SendStateUpdate = true });
_state.ApplyDesign(state, design, ApplySettings.Manual with { IsFinal = true });
}
}
@ -214,7 +214,7 @@ public class NpcPanel
if (_state.GetOrCreate(id, data.Objects[0], out var state))
{
var design = _converter.Convert(ToDesignData(), new StateMaterialManager(), ApplicationRules.NpcFromModifiers());
_state.ApplyDesign(state, design, ApplySettings.Manual with { SendStateUpdate = true });
_state.ApplyDesign(state, design, ApplySettings.Manual with { IsFinal = true });
}
}

View file

@ -18,6 +18,7 @@ public unsafe class UpdateSlotService : IDisposable
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
public readonly GearsetDataLoaded GearsetDataLoadedEvent;
private readonly DictBonusItems _bonusItems;
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
IGameInteropProvider interop, DictBonusItems bonusItems)
{
@ -26,8 +27,8 @@ public unsafe class UpdateSlotService : IDisposable
GearsetDataLoadedEvent = gearsetDataLoaded;
_bonusItems = bonusItems;
_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
interop.InitializeFromAttributes(this);
_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
_flagSlotForUpdateHook.Enable();
_flagBonusSlotForUpdateHook.Enable();
_loadGearsetDataHook.Enable();
@ -89,8 +90,8 @@ public unsafe class UpdateSlotService : IDisposable
/// <summary> Detours the func that makes all FlagSlotForUpdate calls on a gearset change or initial render of a given actor (Only Cases this is Called).
/// <para> Logic done after returning the original hook executes <b>After</b> all equipment/weapon/crest data is loaded into the Actors BaseData. </para>
/// </summary>
private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData);
private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook = null!;
private delegate ulong LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData);
private readonly Hook<LoadGearsetDataDelegate> _loadGearsetDataHook;
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
{
@ -115,30 +116,30 @@ public unsafe class UpdateSlotService : IDisposable
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Glamourer-Invoked on 0x{drawObject.Address:X} on {slot} with item data {armor}.");
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
}
private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData)
private ulong LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData)
{
var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
Model drawObject = drawDataContainer->OwnerObject->DrawObject;
var drawObject = drawDataContainer->OwnerObject->DrawObject;
GearsetDataLoadedEvent.Invoke(drawObject);
// Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
Glamourer.Log.Excessive($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
return ret;
}
// If you ever care to debug this, here is a formatted string output of this new gearsetData struct.
private string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData)
private static string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData)
{
string ret =
var ret =
$"\nMainhandWeaponData: Id: {gearsetData.MainhandWeaponData.Id}, Type: {gearsetData.MainhandWeaponData.Type}, " +
$"Variant: {gearsetData.MainhandWeaponData.Variant}, Stain0: {gearsetData.MainhandWeaponData.Stain0}, Stain1: {gearsetData.MainhandWeaponData.Stain1}" +
$"\nOffhandWeaponData: Id: {gearsetData.OffhandWeaponData.Id}, Type: {gearsetData.OffhandWeaponData.Type}, " +
$"Variant: {gearsetData.OffhandWeaponData.Variant}, Stain0: {gearsetData.OffhandWeaponData.Stain0}, Stain1: {gearsetData.OffhandWeaponData.Stain1}" +
$"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId}";
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
for (var offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
{
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
int dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset
byte* dyePtr = (byte*)&gearsetData + dyeOffset;
ret += $"\nEquipSlot {((EquipSlot)(dyeOffset - 60)).ToString()}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}";
var equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
var dyeOffset = (offset - 20) / sizeof(LegacyCharacterArmor) + 60; // Calculate the corresponding dye offset
var dyePtr = (byte*)&gearsetData + dyeOffset;
ret += $"\nEquipSlot {(EquipSlot)(dyeOffset - 60)}:: Id: {(*equipSlotPtr).Set}, Variant: {(*equipSlotPtr).Variant}, Stain0: {(*equipSlotPtr).Stain.Id}, Stain1: {*dyePtr}";
}
return ret;
}

View file

@ -668,7 +668,7 @@ public class CommandService : IDisposable, IApiService
if (!_objects.TryGetValue(identifier, out var actors))
{
if (_stateManager.TryGetValue(identifier, out var state))
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true });
}
else
{
@ -677,7 +677,7 @@ public class CommandService : IDisposable, IApiService
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
{
ApplyModSettings(design, actor, applyMods);
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { SendStateUpdate = true });
_stateManager.ApplyDesign(state, design, ApplySettings.ManualWithLinks with { IsFinal = true });
}
}
}

View file

@ -17,7 +17,7 @@ public class StateEditor(
InternalStateEditor editor,
StateApplier applier,
StateChanged stateChanged,
StateUpdated stateUpdated,
StateFinalized stateFinalized,
JobChangeState jobChange,
Configuration config,
ItemManager items,
@ -25,12 +25,12 @@ public class StateEditor(
ModSettingApplier modApplier,
GPoseService gPose) : IDesignEditor
{
protected readonly InternalStateEditor Editor = editor;
protected readonly StateApplier Applier = applier;
protected readonly StateChanged StateChanged = stateChanged;
protected readonly StateUpdated StateUpdated = stateUpdated;
protected readonly Configuration Config = config;
protected readonly ItemManager Items = items;
protected readonly InternalStateEditor Editor = editor;
protected readonly StateApplier Applier = applier;
protected readonly StateChanged StateChanged = stateChanged;
protected readonly StateFinalized StateFinalized = stateFinalized;
protected readonly Configuration Config = config;
protected readonly ItemManager Items = items;
/// <summary> Turn an actor to. </summary>
public void ChangeModelId(ActorState state, uint modelId, CustomizeArray customize, nint equipData, StateSource source,
@ -43,7 +43,7 @@ public class StateEditor(
Glamourer.Log.Verbose(
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Model, source, state, actors, null);
StateUpdated.Invoke(StateFinalizationType.ModelChange, actors);
StateFinalized.Invoke(StateFinalizationType.ModelChange, actors);
}
/// <inheritdoc/>
@ -383,7 +383,7 @@ public class StateEditor(
Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key);
}
if (settings.ResetMaterials || (!settings.RespectManual && mergedDesign.ResetAdvancedDyes))
if (settings.ResetMaterials || !settings.RespectManual && mergedDesign.ResetAdvancedDyes)
state.Materials.Clear();
foreach (var (key, value) in mergedDesign.Design.Materials)
@ -420,8 +420,8 @@ public class StateEditor(
Glamourer.Log.Verbose(
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Design, state.Sources[MetaIndex.Wetness], state, actors, null); // FIXME: maybe later
if(settings.SendStateUpdate)
StateUpdated.Invoke(StateFinalizationType.DesignApplied, actors);
if (settings.IsFinal)
StateFinalized.Invoke(StateFinalizationType.DesignApplied, actors);
return;
@ -442,7 +442,8 @@ public class StateEditor(
if (!settings.MergeLinks || design is not Design d)
merged = new MergedDesign(design);
else
merged = merger.Merge(d.AllLinks(true), state.ModelData.IsHuman ? state.ModelData.Customize : CustomizeArray.Default, state.BaseData,
merged = merger.Merge(d.AllLinks(true), state.ModelData.IsHuman ? state.ModelData.Customize : CustomizeArray.Default,
state.BaseData,
false, Config.AlwaysApplyAssociatedMods);
ApplyDesign(data, merged, settings with
@ -460,7 +461,7 @@ public class StateEditor(
if (!Config.ChangeEntireItem || !settings.Source.IsManual())
return;
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);
// Do not change Shields to nothing.
if (mh.Type is FullEquipType.Sword)
return;

View file

@ -41,7 +41,7 @@ public class StateListener : IDisposable
private readonly HeadGearVisibilityChanged _headGearVisibility;
private readonly VisorStateChanged _visorState;
private readonly WeaponVisibilityChanged _weaponVisibility;
private readonly StateUpdated _stateUpdated;
private readonly StateFinalized _stateFinalized;
private readonly AutoDesignApplier _autoDesignApplier;
private readonly FunModule _funModule;
private readonly HumanModelList _humans;
@ -63,7 +63,7 @@ public class StateListener : IDisposable
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects,
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateUpdated stateUpdated)
CrestService crestService, BonusSlotUpdating bonusSlotUpdating, StateFinalized stateFinalized)
{
_manager = manager;
_items = items;
@ -88,7 +88,7 @@ public class StateListener : IDisposable
_condition = condition;
_crestService = crestService;
_bonusSlotUpdating = bonusSlotUpdating;
_stateUpdated = stateUpdated;
_stateFinalized = stateFinalized;
Subscribe();
}
@ -281,7 +281,7 @@ public class StateListener : IDisposable
_objects.Update();
if (_objects.TryGetValue(identifier, out var actors) && actors.Valid)
_stateUpdated.Invoke(StateFinalizationType.Gearset, actors);
_stateFinalized.Invoke(StateFinalizationType.Gearset, actors);
}

View file

@ -18,20 +18,20 @@ using Penumbra.GameData.Interop;
namespace Glamourer.State;
public sealed class StateManager(
ActorManager _actors,
ActorManager actors,
ItemManager items,
StateChanged @event,
StateUpdated @event2,
StateChanged changeEvent,
StateFinalized finalizeEvent,
StateApplier applier,
InternalStateEditor editor,
HumanModelList _humans,
IClientState _clientState,
HumanModelList humans,
IClientState clientState,
Configuration config,
JobChangeState jobChange,
DesignMerger merger,
ModSettingApplier modApplier,
GPoseService gPose)
: StateEditor(editor, applier, @event, @event2, jobChange, config, items, merger, modApplier, gPose),
: StateEditor(editor, applier, changeEvent, finalizeEvent, jobChange, config, items, merger, modApplier, gPose),
IReadOnlyDictionary<ActorIdentifier, ActorState>
{
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
@ -62,7 +62,7 @@ public sealed class StateManager(
/// <inheritdoc cref="GetOrCreate(ActorIdentifier, Actor, out ActorState?)"/>
public bool GetOrCreate(Actor actor, [NotNullWhen(true)] out ActorState? state)
=> GetOrCreate(actor.GetIdentifier(_actors), actor, out state);
=> GetOrCreate(actor.GetIdentifier(actors), actor, out state);
/// <summary> Try to obtain or create a new state for an existing actor. Returns false if no state could be created. </summary>
public unsafe bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state)
@ -82,7 +82,7 @@ public sealed class StateManager(
ModelData = FromActor(actor, true, false),
BaseData = FromActor(actor, false, false),
LastJob = (byte)(actor.IsCharacter ? actor.AsCharacter->CharacterData.ClassJob : 0),
LastTerritory = _clientState.TerritoryType,
LastTerritory = clientState.TerritoryType,
};
// state.Identifier is owned.
_states.Add(state.Identifier, state);
@ -115,7 +115,7 @@ public sealed class StateManager(
// Model ID is only unambiguously contained in the game object.
// The draw object only has the object type.
// TODO reverse search model data to get model id from model.
if (!_humans.IsHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId))
if (!humans.IsHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId))
{
ret.LoadNonHuman((uint)actor.AsCharacter->ModelContainer.ModelCharaId, *(CustomizeArray*)&actor.AsCharacter->DrawData.CustomizeData,
(nint)Unsafe.AsPointer(ref actor.AsCharacter->DrawData.EquipmentModelIds[0]));
@ -236,7 +236,7 @@ public sealed class StateManager(
public void TurnHuman(ActorState state, StateSource source, uint key = 0)
=> ChangeModelId(state, 0, CustomizeArray.Default, nint.Zero, source, key);
public void ResetState(ActorState state, StateSource source, uint key = 0, bool stateUpdate = false)
public void ResetState(ActorState state, StateSource source, uint key = 0, bool isFinal = false)
{
if (!state.Unlock(key))
return;
@ -278,8 +278,8 @@ public sealed class StateManager(
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null);
// only invoke if we define this reset call as the final call in our state update.
if(stateUpdate)
StateUpdated.Invoke(StateFinalizationType.Revert, actors);
if(isFinal)
StateFinalized.Invoke(StateFinalizationType.Revert, actors);
}
public void ResetAdvancedState(ActorState state, StateSource source, uint key = 0)
@ -306,7 +306,7 @@ public sealed class StateManager(
$"Reset advanced customization and dye state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Reset, source, state, actors, null);
// Update that we have completed a full operation. (We can do this directly as nothing else is linked)
StateUpdated.Invoke(StateFinalizationType.RevertAdvanced, actors);
StateFinalized.Invoke(StateFinalizationType.RevertAdvanced, actors);
}
public void ResetCustomize(ActorState state, StateSource source, uint key = 0)
@ -325,7 +325,7 @@ public sealed class StateManager(
Glamourer.Log.Verbose(
$"Reset customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
// Update that we have completed a full operation. (We can do this directly as nothing else is linked)
StateUpdated.Invoke(StateFinalizationType.RevertCustomize, actors);
StateFinalized.Invoke(StateFinalizationType.RevertCustomize, actors);
}
public void ResetEquip(ActorState state, StateSource source, uint key = 0)
@ -376,7 +376,7 @@ public sealed class StateManager(
Glamourer.Log.Verbose(
$"Reset equipment state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
// Update that we have completed a full operation. (We can do this directly as nothing else is linked)
StateUpdated.Invoke(StateFinalizationType.RevertEquipment, actors);
StateFinalized.Invoke(StateFinalizationType.RevertEquipment, actors);
}
public void ResetStateFixed(ActorState state, bool respectManualPalettes, uint key = 0)
@ -453,23 +453,23 @@ public sealed class StateManager(
}
}
public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool stateUpdate = false)
public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool isFinal = false)
{
if (!GetOrCreate(actor, out var state))
return;
ReapplyState(actor, state, forceRedraw, source, stateUpdate);
ReapplyState(actor, state, forceRedraw, source, isFinal);
}
public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool stateUpdate)
public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool isFinal)
{
var data = Applier.ApplyAll(state,
forceRedraw
|| !actor.Model.IsHuman
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null);
if(stateUpdate)
StateUpdated.Invoke(StateFinalizationType.Reapply, data);
if(isFinal)
StateFinalized.Invoke(StateFinalizationType.Reapply, data);
}
/// <summary> Automation variant for reapply, to fire the correct StateUpdateType once reapplied. </summary>
@ -490,7 +490,7 @@ public sealed class StateManager(
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null);
// invoke the automation update based on what reset is.
StateUpdated.Invoke(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data);
StateFinalized.Invoke(wasReset ? StateFinalizationType.RevertAutomation : StateFinalizationType.ReapplyAutomation, data);
}
public void DeleteState(ActorIdentifier identifier)