diff --git a/Glamourer/Api/StateApi.cs b/Glamourer/Api/StateApi.cs
index cb7fe51..41f7650 100644
--- a/Glamourer/Api/StateApi.cs
+++ b/Glamourer/Api/StateApi.cs
@@ -14,7 +14,6 @@ using StateChanged = Glamourer.Events.StateChanged;
using StateUpdated = Glamourer.Events.StateUpdated;
namespace Glamourer.Api;
-
public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
{
private readonly ApiHelpers _helpers;
@@ -340,7 +339,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private void OnStateChanged(StateChangeType type, StateSource _2, ActorState _3, ActorData actors, ITransaction? _5)
{
- // Glamourer.Log.Verbose($"[OnStateChanged] Sending out OnStateChanged with type {type}.");
+ Glamourer.Log.Excessive($"[OnStateChanged] State Changed with Type {type} [Affecting {actors.ToLazyString("nothing")}.]");
if (StateChanged != null)
foreach (var actor in actors.Objects)
StateChanged.Invoke(actor.Address);
@@ -352,7 +351,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private void OnStateUpdated(StateUpdateType type, ActorData actors)
{
- // Glamourer.Log.Verbose($"[OnStateUpdated] Sending out OnStateUpdated with type {type}.");
+ Glamourer.Log.Verbose($"[OnStateUpdated] State Updated with Type {type}. [Affecting {actors.ToLazyString("nothing")}.]");
if (StateUpdated != null)
foreach (var actor in actors.Objects)
StateUpdated.Invoke(actor.Address, type);
diff --git a/Glamourer/Events/GearsetDataLoaded.cs b/Glamourer/Events/GearsetDataLoaded.cs
index dd12bc1..4750939 100644
--- a/Glamourer/Events/GearsetDataLoaded.cs
+++ b/Glamourer/Events/GearsetDataLoaded.cs
@@ -4,10 +4,10 @@ using Penumbra.GameData.Interop;
namespace Glamourer.Events;
///
-/// Triggers when the equipped gearset finished running all of its LoadEquipment, LoadWeapon, and crest calls.
-/// This defines a universal endpoint of base game state application to monitor.
+/// Triggers when the equipped gearset finished all LoadEquipment, LoadWeapon, and LoadCrest calls. (All Non-MetaData)
+/// This defines an endpoint for when the gameState is updated.
///
-/// - The model drawobject associated with the finished load (Also fired by other players on render)
+/// - The model draw object associated with the finished load (Also fired by other players on render)
///
///
public sealed class GearsetDataLoaded()
@@ -18,4 +18,4 @@ public sealed class GearsetDataLoaded()
///
StateListener = 0,
}
-}
\ No newline at end of file
+}
diff --git a/Glamourer/Interop/InventoryService.cs b/Glamourer/Interop/InventoryService.cs
index f0ed6b5..4b98d46 100644
--- a/Glamourer/Interop/InventoryService.cs
+++ b/Glamourer/Interop/InventoryService.cs
@@ -23,34 +23,26 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
_gearsetEvent = gearsetEvent;
_moveItemHook = interop.HookFromAddress((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
- // This can be uncommented after ClientStructs Updates with EquipGearsetInternal after merged PR. (See comment below)
- //_equipGearsetInternalHook = interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetInternalDetour);
-
- // Can be removed after ClientStructs Update since this is only needed for current EquipGearsetInternal [Signature]
- interop.InitializeFromAttributes(this);
+ _equipGearsetHook = interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearsetInternal, EquipGearSetDetour);
_moveItemHook.Enable();
- _equipGearsetInternalHook.Enable();
+ _equipGearsetHook.Enable();
}
public void Dispose()
{
_moveItemHook.Dispose();
- _equipGearsetInternalHook.Dispose();
+ _equipGearsetHook.Dispose();
}
- // This is the internal function processed by all sources of Equipping a gearset, such as hotbar gearset application and command gearset application.
- // Currently is pending to ClientStructs for integration. See: https://github.com/aers/FFXIVClientStructs/pull/1277
- public const string EquipGearsetInternal = "40 55 53 56 57 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 4C 63 FA";
private delegate nint EquipGearsetInternalDelegate(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId);
- [Signature(EquipGearsetInternal, DetourName = nameof(EquipGearSetInternalDetour))]
- private readonly Hook _equipGearsetInternalHook = null!;
+ private readonly Hook _equipGearsetHook = null!;
- private nint EquipGearSetInternalDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId)
+ private nint EquipGearSetDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId)
{
var prior = module->CurrentGearsetIndex;
- var ret = _equipGearsetInternalHook.Original(module, gearsetId, glamourPlateId);
+ var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
var set = module->GetGearset((int)gearsetId);
_gearsetEvent.Invoke(new ByteString(set->Name).ToString(), (int)gearsetId, prior, glamourPlateId, set->ClassJob);
Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
diff --git a/Glamourer/Interop/UpdateSlotService.cs b/Glamourer/Interop/UpdateSlotService.cs
index e453c6e..466f1ae 100644
--- a/Glamourer/Interop/UpdateSlotService.cs
+++ b/Glamourer/Interop/UpdateSlotService.cs
@@ -2,6 +2,7 @@
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
+using FFXIVClientStructs.FFXIV.Client.Game.Network;
using Glamourer.Events;
using Penumbra.GameData;
using Penumbra.GameData.DataContainers;
@@ -13,22 +14,20 @@ namespace Glamourer.Interop;
public unsafe class UpdateSlotService : IDisposable
{
- public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
- public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
- public readonly GearsetDataLoaded GearsetDataLoadedEvent;
- private readonly DictBonusItems _bonusItems;
+ public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
+ public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
+ public readonly GearsetDataLoaded GearsetDataLoadedEvent;
+ private readonly DictBonusItems _bonusItems;
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
IGameInteropProvider interop, DictBonusItems bonusItems)
{
EquipSlotUpdatingEvent = equipSlotUpdating;
BonusSlotUpdatingEvent = bonusSlotUpdating;
GearsetDataLoadedEvent = gearsetDataLoaded;
- _bonusItems = bonusItems;
+ _bonusItems = bonusItems;
- // Usable after the merge with client structs.
- //_loadGearsetDataHook = interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
+ _loadGearsetDataHook = interop.HookFromAddress((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
interop.InitializeFromAttributes(this);
-
_flagSlotForUpdateHook.Enable();
_flagBonusSlotForUpdateHook.Enable();
_loadGearsetDataHook.Enable();
@@ -87,25 +86,15 @@ public unsafe class UpdateSlotService : IDisposable
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
private readonly Hook _flagBonusSlotForUpdateHook = null!;
- // This signature is what calls the weapon/equipment/crest load functions in the drawData container inherited from a human/characterBase.
- //
- // Contrary to assumption, this is not frequently fired when any slot changes, and is instead only called when another player
- // initially loads, or when the client player changes gearsets. (Does not fire when another player or self is redrawn)
- //
- // This functions purpose is to iterate all Equipment/Weapon/Crest data on gearset change / initial player load, and determine which slots need to fire FlagSlotForUpdate.
- //
- // Because Glamourer processes GameState changes by detouring this method, this means by returning original after detour, any logic performed after will occur
- // AFTER Glamourer finishes applying all changes to the game State, providing a gearset endpoint. (MetaData not included)
- // Currently pending a merge to clientStructs, after which it can be removed, along with the explicit struct. See: https://github.com/aers/FFXIVClientStructs/pull/1277/files
- public const string LoadGearsetDataSig = "48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 44 0F B6 B9";
- private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, GearsetDataStruct* gearsetData);
-
- [Signature(LoadGearsetDataSig, DetourName = nameof(LoadGearsetDataDetour))]
+ /// Detours the func that makes all FlagSlotForUpdate calls on a gearset change or initial render of a given actor (Only Cases this is Called).
+ /// Logic done after returning the original hook executes After all equipment/weapon/crest data is loaded into the Actors BaseData.
+ ///
+ private delegate Int64 LoadGearsetDataDelegate(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData);
private readonly Hook _loadGearsetDataHook = null!;
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
{
- var slot = slotIdx.ToEquipSlot();
+ var slot = slotIdx.ToEquipSlot();
var returnValue = ulong.MaxValue;
EquipSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
Glamourer.Log.Excessive($"[FlagSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
@@ -114,7 +103,7 @@ public unsafe class UpdateSlotService : IDisposable
private ulong FlagBonusSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
{
- var slot = slotIdx.ToBonusSlot();
+ var slot = slotIdx.ToBonusSlot();
var returnValue = ulong.MaxValue;
BonusSlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
@@ -123,29 +112,27 @@ public unsafe class UpdateSlotService : IDisposable
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
{
- Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Invoked by Glamourer on 0x{drawObject.Address:X} on {slot} with itemdata {armor}.");
+ 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, GearsetDataStruct* gearsetData)
+ private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, PacketPlayerGearsetData* gearsetData)
{
- // Let the gearset data process all of its loads and slot flag update calls first.
var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
Model drawObject = drawDataContainer->OwnerObject->DrawObject;
- Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] Owner: 0x{drawObject.Address:X} Finished Applying its GameState!");
GearsetDataLoadedEvent.Invoke(drawObject);
- // Glamourer.Log.Verbose($"[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(GearsetDataStruct gearsetData)
+ private string FormatGearsetItemDataStruct(PacketPlayerGearsetData gearsetData)
{
string 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} | UNK_18: {gearsetData.UNK_18} | UNK_19: {gearsetData.UNK_19}";
+ $"\nCrestBitField: {gearsetData.CrestBitField} | JobId: {gearsetData.JobId}";
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
{
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
@@ -156,43 +143,3 @@ public unsafe class UpdateSlotService : IDisposable
return ret;
}
}
-
-// Can be removed once merged with client structs and referenced directly. See: https://github.com/aers/FFXIVClientStructs/pull/1277/files
-[StructLayout(LayoutKind.Explicit)]
-public readonly struct GearsetDataStruct
-{
- // Stores the weapon data. Includes both dyes in the data.
- [FieldOffset(0)] public readonly WeaponModelId MainhandWeaponData;
- [FieldOffset(8)] public readonly WeaponModelId OffhandWeaponData;
-
- [FieldOffset(16)] public readonly byte CrestBitField; // A Bitfield:: ShieldCrest == 1, HeadCrest == 2, Chest Crest == 4
- [FieldOffset(17)] public readonly byte JobId; // Job ID associated with the gearset change.
-
- // Flicks from 0 to 127 (anywhere inbetween), have yet to associate what it is linked to. Remains the same when flicking between gearsets of the same job.
- [FieldOffset(18)] public readonly byte UNK_18;
- [FieldOffset(19)] public readonly byte UNK_19; // I have never seen this be anything other than 0.
-
- // Legacy helmet equip slot armor for a character.
- [FieldOffset(20)] public readonly LegacyCharacterArmor HeadSlotArmor;
- [FieldOffset(24)] public readonly LegacyCharacterArmor TopSlotArmor;
- [FieldOffset(28)] public readonly LegacyCharacterArmor ArmsSlotArmor;
- [FieldOffset(32)] public readonly LegacyCharacterArmor LegsSlotArmor;
- [FieldOffset(26)] public readonly LegacyCharacterArmor FeetSlotArmor;
- [FieldOffset(40)] public readonly LegacyCharacterArmor EarSlotArmor;
- [FieldOffset(44)] public readonly LegacyCharacterArmor NeckSlotArmor;
- [FieldOffset(48)] public readonly LegacyCharacterArmor WristSlotArmor;
- [FieldOffset(52)] public readonly LegacyCharacterArmor RFingerSlotArmor;
- [FieldOffset(56)] public readonly LegacyCharacterArmor LFingerSlotArmor;
-
- // Byte array of all slot's secondary dyes.
- [FieldOffset(60)] public readonly byte HeadSlotSecondaryDye;
- [FieldOffset(61)] public readonly byte TopSlotSecondaryDye;
- [FieldOffset(62)] public readonly byte ArmsSlotSecondaryDye;
- [FieldOffset(63)] public readonly byte LegsSlotSecondaryDye;
- [FieldOffset(64)] public readonly byte FeetSlotSecondaryDye;
- [FieldOffset(65)] public readonly byte EarSlotSecondaryDye;
- [FieldOffset(66)] public readonly byte NeckSlotSecondaryDye;
- [FieldOffset(67)] public readonly byte WristSlotSecondaryDye;
- [FieldOffset(68)] public readonly byte RFingerSlotSecondaryDye;
- [FieldOffset(69)] public readonly byte LFingerSlotSecondaryDye;
-}
diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs
index b122352..13b0706 100644
--- a/Glamourer/State/StateEditor.cs
+++ b/Glamourer/State/StateEditor.cs
@@ -416,7 +416,7 @@ public class StateEditor(
var actors = settings.Source.RequiresChange()
? Applier.ApplyAll(state, requiresRedraw, false)
: ActorData.Invalid;
-
+
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
diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs
index d570805..c4c16b5 100644
--- a/Glamourer/State/StateListener.cs
+++ b/Glamourer/State/StateListener.cs
@@ -225,7 +225,8 @@ public class StateListener : IDisposable
// then we do not want to use our restricted gear protection
// since we assume the player has that gear modded to availability.
var locked = false;
- if (actor.Identifier(_actors, out var identifier) && _manager.TryGetValue(identifier, out var state))
+ if (actor.Identifier(_actors, out var identifier)
+ && _manager.TryGetValue(identifier, out var state))
{
HandleEquipSlot(actor, state, slot, ref armor);
locked = state.Sources[slot, false] is StateSource.IpcFixed;