diff --git a/Glamourer/Automation/AutoDesignApplier.cs b/Glamourer/Automation/AutoDesignApplier.cs
index bcc907c..52956cc 100644
--- a/Glamourer/Automation/AutoDesignApplier.cs
+++ b/Glamourer/Automation/AutoDesignApplier.cs
@@ -202,17 +202,8 @@ public sealed class AutoDesignApplier : IDisposable
}
}
- ///
- /// JOB CHANGE IS CALLED UPON HERE.
- ///
private void OnJobChange(Actor actor, Job oldJob, Job newJob)
{
- unsafe
- {
- var drawObject = actor.AsCharacter->DrawObject;
- Glamourer.Log.Information($"[AutoDesignApplier][OnJobChange] 0x{(nint)drawObject:X} changed job from {oldJob} ({oldJob.Id}) to {newJob} ({newJob.Id}).");
- }
-
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors, out var id))
return;
diff --git a/Glamourer/Events/GearsetDataLoaded.cs b/Glamourer/Events/GearsetDataLoaded.cs
index 47d0108..680ae3f 100644
--- a/Glamourer/Events/GearsetDataLoaded.cs
+++ b/Glamourer/Events/GearsetDataLoaded.cs
@@ -1,7 +1,5 @@
using OtterGui.Classes;
-using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
-using Penumbra.GameData.Structs;
namespace Glamourer.Events;
@@ -9,7 +7,7 @@ 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.
///
-/// - The model drawobject associated with the finished load (should always be ClientPlayer)
+/// - The model drawobject associated with the finished load (Also fired by other players on render)
///
///
public sealed class GearsetDataLoaded()
diff --git a/Glamourer/Events/StateUpdated.cs b/Glamourer/Events/StateUpdated.cs
index 82d737f..f18a69a 100644
--- a/Glamourer/Events/StateUpdated.cs
+++ b/Glamourer/Events/StateUpdated.cs
@@ -9,10 +9,8 @@ namespace Glamourer.Events;
///
/// Triggered when a Design is edited in any way.
///
-/// - Parameter is the type of the change
-/// - Parameter is the changed saved state.
+/// - Parameter is the operation that finished updating the saved state.
/// - Parameter is the existing actors using this saved state.
-/// - Parameter is any additional data depending on the type of change.
///
///
public sealed class StateUpdated()
diff --git a/Glamourer/Interop/ChangeCustomizeService.cs b/Glamourer/Interop/ChangeCustomizeService.cs
index 10e3a12..495d69c 100644
--- a/Glamourer/Interop/ChangeCustomizeService.cs
+++ b/Glamourer/Interop/ChangeCustomizeService.cs
@@ -64,7 +64,6 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2 _changeCustomizeHook;
- // manual invoke by calling the detours _original call to `execute to` instead of `listen to`.
public bool UpdateCustomize(Model model, CustomizeArray customize)
{
if (!model.IsHuman)
@@ -79,7 +78,6 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2 UpdateCustomize(actor.Model, customize);
- // detoured method.
private bool ChangeCustomizeDetour(Human* human, byte* data, byte skipEquipment)
{
if (!InUpdate.InMethod)
@@ -90,7 +88,6 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2 action, Post.Priority priority)
=> _postEvent.Subscribe(action, priority);
diff --git a/Glamourer/Interop/InventoryService.cs b/Glamourer/Interop/InventoryService.cs
index 743bea1..33ba5cf 100644
--- a/Glamourer/Interop/InventoryService.cs
+++ b/Glamourer/Interop/InventoryService.cs
@@ -5,7 +5,6 @@ using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using Glamourer.Events;
using OtterGui.Services;
-using Penumbra.GameData;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
@@ -14,10 +13,6 @@ namespace Glamourer.Interop;
public sealed unsafe class InventoryService : IDisposable, IRequiredService
{
- private readonly MovedEquipment _movedItemsEvent;
- private readonly EquippedGearset _gearsetEvent;
- private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12);
-
// Called by EquipGearset, but returns a pointer instead of an int.
// This is the internal function processed by all sources of Equipping a gearset,
// such as hotbar gearset application and command gearset application
@@ -27,10 +22,16 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
[Signature(EquipGearsetInternal, DetourName = nameof(EquipGearSetInternalDetour))]
private readonly Hook _equipGearsetInternalHook = null!;
+ // The following above is currently pending for an accepted PR in FFXIVCLientStructs.
+ // Once accepted, remove everything above this comment and replace EquipGearset with EquipGearsetInternal.
+
+ private readonly MovedEquipment _movedItemsEvent;
+ private readonly EquippedGearset _gearsetEvent;
+ private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12);
public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent)
{
_movedItemsEvent = movedItemsEvent;
- _gearsetEvent = gearsetEvent;
+ _gearsetEvent = gearsetEvent;
_moveItemHook = interop.HookFromAddress((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
_equipGearsetHook = interop.HookFromAddress((nint)RaptureGearsetModule.MemberFunctionPointers.EquipGearset, EquipGearSetDetour);
@@ -58,7 +59,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
var ret = _equipGearsetInternalHook.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] [EquipInternal] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
+ Glamourer.Log.Verbose($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
if (ret == 0)
{
var entry = module->GetGearset((int)gearsetId);
@@ -131,9 +132,10 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
return ret;
}
+ // Remove once internal is added. This no longer serves any purpose.
private int EquipGearSetDetour(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId)
{
- var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
+ var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId);
Glamourer.Log.Excessive($"[InventoryService] (old) Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})");
return ret;
}
@@ -216,18 +218,18 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
private static EquipSlot GetSlot(uint slot)
=> slot switch
{
- 0 => EquipSlot.MainHand,
- 1 => EquipSlot.OffHand,
- 2 => EquipSlot.Head,
- 3 => EquipSlot.Body,
- 4 => EquipSlot.Hands,
- 6 => EquipSlot.Legs,
- 7 => EquipSlot.Feet,
- 8 => EquipSlot.Ears,
- 9 => EquipSlot.Neck,
+ 0 => EquipSlot.MainHand,
+ 1 => EquipSlot.OffHand,
+ 2 => EquipSlot.Head,
+ 3 => EquipSlot.Body,
+ 4 => EquipSlot.Hands,
+ 6 => EquipSlot.Legs,
+ 7 => EquipSlot.Feet,
+ 8 => EquipSlot.Ears,
+ 9 => EquipSlot.Neck,
10 => EquipSlot.Wrists,
11 => EquipSlot.RFinger,
12 => EquipSlot.LFinger,
- _ => EquipSlot.Unknown,
+ _ => EquipSlot.Unknown,
};
}
diff --git a/Glamourer/Interop/UpdateSlotService.cs b/Glamourer/Interop/UpdateSlotService.cs
index 7e5cf59..9ee8d8f 100644
--- a/Glamourer/Interop/UpdateSlotService.cs
+++ b/Glamourer/Interop/UpdateSlotService.cs
@@ -8,12 +8,11 @@ using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
+
namespace Glamourer.Interop;
-///
-/// This struct is the struct that loadallequipment passes in as its gearsetData container.
-///
-[StructLayout(LayoutKind.Explicit)] // Size of 70 bytes maybe?
+// This struct is implemented into a PR for FFXIVClientStructs. Once merged, remove this struct and reference the data in ClientStructs instead.
+[StructLayout(LayoutKind.Explicit)]
public readonly struct GearsetItemDataStruct
{
// Stores the weapon data. Includes both dyes in the data.
@@ -54,30 +53,15 @@ public readonly struct GearsetItemDataStruct
public unsafe class UpdateSlotService : IDisposable
{
+ // This function is what calls the weapon/equipment/crest loads, which call FlagSlotForUpdate if different. (MetaData not included)
+ 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, GearsetItemDataStruct* gearsetData);
+ // The above can be removed after the FFXIVClientStruct Merge is made!
+
public readonly EquipSlotUpdating EquipSlotUpdatingEvent;
public readonly BonusSlotUpdating BonusSlotUpdatingEvent;
public readonly GearsetDataLoaded GearsetDataLoadedEvent;
private readonly DictBonusItems _bonusItems;
-
- // This function is what calls the weapon/equipment/crest loads, which call FlagSlotForUpdate if different. (MetaData not included)
- 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, GearsetItemDataStruct* gearsetData);
- private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData)
- {
- // Let the gearset data process all of its loads and slot flag update calls first.
- var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
- // Ensure that the owner of the drawdata container is a character base.
- Model ownerDrawObject = drawDataContainer->OwnerObject->DrawObject;
- if (!ownerDrawObject.IsCharacterBase)
- return ret;
-
- // invoke the changed event for the state listener and return.
- Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] Owner: 0x{ownerDrawObject.Address:X} Finished Applying its GameState!");
- // Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
- GearsetDataLoadedEvent.Invoke(drawDataContainer->OwnerObject->DrawObject);
- return ret;
- }
-
public UpdateSlotService(EquipSlotUpdating equipSlotUpdating, BonusSlotUpdating bonusSlotUpdating, GearsetDataLoaded gearsetDataLoaded,
IGameInteropProvider interop, DictBonusItems bonusItems)
{
@@ -150,7 +134,7 @@ public unsafe class UpdateSlotService : IDisposable
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}).");
@@ -160,7 +144,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}).");
@@ -173,6 +157,20 @@ public unsafe class UpdateSlotService : IDisposable
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Invoked by Glamourer on 0x{drawObject.Address:X} on {slot} with itemdata {armor}.");
return _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
}
+ private Int64 LoadGearsetDataDetour(DrawDataContainer* drawDataContainer, GearsetItemDataStruct* gearsetData)
+ {
+ // Let the gearset data process all of its loads and slot flag update calls first.
+ var ret = _loadGearsetDataHook.Original(drawDataContainer, gearsetData);
+ Model ownerDrawObject = drawDataContainer->OwnerObject->DrawObject;
+ if (!ownerDrawObject.IsCharacterBase)
+ return ret;
+
+ // invoke the changed event for the state listener and return.
+ Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] Owner: 0x{ownerDrawObject.Address:X} Finished Applying its GameState!");
+ // Glamourer.Log.Verbose($"[LoadAllEquipmentDetour] GearsetItemData: {FormatGearsetItemDataStruct(*gearsetData)}");
+ GearsetDataLoadedEvent.Invoke(drawDataContainer->OwnerObject->DrawObject);
+ return ret;
+ }
// If you ever care to debug this, here is a formatted string output of this new gearsetDataPacket struct.
private string FormatGearsetItemDataStruct(GearsetItemDataStruct gearsetData)
@@ -183,7 +181,6 @@ public unsafe class UpdateSlotService : IDisposable
$"\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}";
- // Iterate through offsets from 20 to 60 and format the CharacterArmor data
for (int offset = 20; offset <= 56; offset += sizeof(LegacyCharacterArmor))
{
LegacyCharacterArmor* equipSlotPtr = (LegacyCharacterArmor*)((byte*)&gearsetData + offset);
diff --git a/Glamourer/State/StateEditor.cs b/Glamourer/State/StateEditor.cs
index e1bd6a4..891c61d 100644
--- a/Glamourer/State/StateEditor.cs
+++ b/Glamourer/State/StateEditor.cs
@@ -54,7 +54,7 @@ public class StateEditor(
return;
var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
- Glamourer.Log.Information(
+ Glamourer.Log.Verbose(
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Customize, settings.Source, state, actors, new CustomizeTransaction(idx, old, value));
}
@@ -67,7 +67,7 @@ public class StateEditor(
return;
var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
- Glamourer.Log.Information(
+ Glamourer.Log.Verbose(
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.EntireCustomize, settings.Source, state, actors,
new EntireCustomizeTransaction(applied, old, customizeInput));
@@ -78,10 +78,7 @@ public class StateEditor(
{
var state = (ActorState)data;
if (!Editor.ChangeItem(state, slot, item, settings.Source, out var old, settings.Key))
- {
- Glamourer.Log.Information("Not Setting State or invoking, Editor requested us not to change it!");
return;
- }
var type = slot.ToIndex() < 10 ? StateChangeType.Equip : StateChangeType.Weapon;
var actors = type is StateChangeType.Equip
@@ -92,8 +89,8 @@ public class StateEditor(
if (slot is EquipSlot.MainHand)
ApplyMainhandPeriphery(state, item, null, settings);
- Glamourer.Log.Debug(
- $"[ChangeItem] Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
+ Glamourer.Log.Verbose(
+ $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
if (type is StateChangeType.Equip)
{
@@ -122,8 +119,8 @@ public class StateEditor(
return;
var actors = Applier.ChangeBonusItem(state, slot, settings.Source.RequiresChange());
- Glamourer.Log.Debug(
- $"[ChangeBonus] Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]");
+ Glamourer.Log.Verbose(
+ $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.BonusItem, settings.Source, state, actors, new BonusItemTransaction(slot, old, item));
}
@@ -155,8 +152,8 @@ public class StateEditor(
if (slot is EquipSlot.MainHand)
ApplyMainhandPeriphery(state, item, stains, settings);
- Glamourer.Log.Debug(
- $"[ChangeEquip] Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStains} to {stains!.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
+ Glamourer.Log.Verbose(
+ $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStains} to {stains!.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
if (type is StateChangeType.Equip)
{
StateChanged.Invoke(type, settings.Source, state, actors, new EquipTransaction(slot, old, item!.Value));
@@ -187,7 +184,7 @@ public class StateEditor(
return;
var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange());
- Glamourer.Log.Debug(
+ Glamourer.Log.Verbose(
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old} to {stains}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, new StainTransaction(slot, old, stains));
}
@@ -419,8 +416,8 @@ public class StateEditor(
var actors = settings.Source.RequiresChange()
? Applier.ApplyAll(state, requiresRedraw, false)
: ActorData.Invalid;
-
- Glamourer.Log.Debug($"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
+
+ 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(StateUpdateType.DesignApplied, actors);
diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs
index 4d10c49..d312815 100644
--- a/Glamourer/State/StateListener.cs
+++ b/Glamourer/State/StateListener.cs
@@ -13,8 +13,8 @@ using Glamourer.GameData;
using Penumbra.GameData.DataContainers;
using Glamourer.Designs;
using Penumbra.GameData.Interop;
-using ObjectManager = Glamourer.Interop.ObjectManager;
using Glamourer.Api.Enums;
+using ObjectManager = Glamourer.Interop.ObjectManager;
namespace Glamourer.State;
diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs
index 129f8bb..0348148 100644
--- a/Glamourer/State/StateManager.cs
+++ b/Glamourer/State/StateManager.cs
@@ -274,7 +274,7 @@ public sealed class StateManager(
if (source is not StateSource.Game)
actors = Applier.ApplyAll(state, redraw, true);
- Glamourer.Log.Debug(
+ Glamourer.Log.Verbose(
$"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.
@@ -302,7 +302,7 @@ public sealed class StateManager(
state.Materials.Clear();
- Glamourer.Log.Debug(
+ Glamourer.Log.Verbose(
$"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)
@@ -453,22 +453,22 @@ public sealed class StateManager(
}
}
- public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool isUpdate = false)
+ public void ReapplyState(Actor actor, bool forceRedraw, StateSource source, bool stateUpdate = false)
{
if (!GetOrCreate(actor, out var state))
return;
- ReapplyState(actor, state, forceRedraw, source, isUpdate);
+ ReapplyState(actor, state, forceRedraw, source, stateUpdate);
}
- public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool isUpdate)
+ public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source, bool stateUpdate)
{
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(isUpdate)
+ if(stateUpdate)
StateUpdated.Invoke(StateUpdateType.Reapply, data);
}