From 8add6e5519c6998194ad4adc5f818704e5fa96d6 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 20 Jan 2025 15:37:21 +0100 Subject: [PATCH 1/5] Fix some .net update issues. --- Glamourer/Gui/Customization/CustomizeParameterDrawData.cs | 4 ++-- Glamourer/Gui/Equipment/BonusDrawData.cs | 4 ++-- Glamourer/Gui/Equipment/EquipDrawData.cs | 4 ++-- Penumbra.GameData | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Glamourer/Gui/Customization/CustomizeParameterDrawData.cs b/Glamourer/Gui/Customization/CustomizeParameterDrawData.cs index aa43b79..421caed 100644 --- a/Glamourer/Gui/Customization/CustomizeParameterDrawData.cs +++ b/Glamourer/Gui/Customization/CustomizeParameterDrawData.cs @@ -6,8 +6,8 @@ namespace Glamourer.Gui.Customization; public struct CustomizeParameterDrawData(CustomizeParameterFlag flag, in DesignData data) { - private IDesignEditor _editor; - private object _object; + private IDesignEditor _editor = null!; + private object _object = null!; public readonly CustomizeParameterFlag Flag = flag; public bool Locked; public bool DisplayApplication; diff --git a/Glamourer/Gui/Equipment/BonusDrawData.cs b/Glamourer/Gui/Equipment/BonusDrawData.cs index 9c023b8..e19287a 100644 --- a/Glamourer/Gui/Equipment/BonusDrawData.cs +++ b/Glamourer/Gui/Equipment/BonusDrawData.cs @@ -7,8 +7,8 @@ namespace Glamourer.Gui.Equipment; public struct BonusDrawData(BonusItemFlag slot, in DesignData designData) { - private IDesignEditor _editor; - private object _object; + private IDesignEditor _editor = null!; + private object _object = null!; public readonly BonusItemFlag Slot = slot; public bool Locked; public bool DisplayApplication; diff --git a/Glamourer/Gui/Equipment/EquipDrawData.cs b/Glamourer/Gui/Equipment/EquipDrawData.cs index 9a3142b..b72e234 100644 --- a/Glamourer/Gui/Equipment/EquipDrawData.cs +++ b/Glamourer/Gui/Equipment/EquipDrawData.cs @@ -7,8 +7,8 @@ namespace Glamourer.Gui.Equipment; public struct EquipDrawData(EquipSlot slot, in DesignData designData) { - private IDesignEditor _editor; - private object _object; + private IDesignEditor _editor = null!; + private object _object = null!; public readonly EquipSlot Slot = slot; public bool Locked; public bool DisplayApplication; diff --git a/Penumbra.GameData b/Penumbra.GameData index 63ffca0..c525072 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 63ffca0ff0ad626605120e58809c888d92053d64 +Subproject commit c525072299d5febd2bb638ab229060b0073ba6a6 From cdc67a035c5efdc04801b8630e799ad59ea742f7 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 20 Jan 2025 15:37:59 +0100 Subject: [PATCH 2/5] Check for ENPC player copies and treat them as transformations. --- Glamourer/State/StateListener.cs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index d054a25..651e63e 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -9,6 +9,7 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Object; using Glamourer.GameData; using Penumbra.GameData.DataContainers; using Glamourer.Designs; @@ -50,6 +51,7 @@ public class StateListener : IDisposable private readonly Dictionary _fistOffhands = []; private ActorIdentifier _creatingIdentifier = ActorIdentifier.Invalid; + private bool _isPlayerNpc; private ActorState? _creatingState; private ActorState? _customizeState; @@ -117,11 +119,13 @@ public class StateListener : IDisposable return; _creatingIdentifier = actor.GetIdentifier(_actors); - ref var modelId = ref *(uint*)modelPtr; ref var customize = ref *(CustomizeArray*)customizePtr; if (_autoDesignApplier.Reduce(actor, _creatingIdentifier, out _creatingState)) { + _isPlayerNpc = _creatingIdentifier.Type is IdentifierType.Player + && actor.IsCharacter + && actor.AsCharacter->GetObjectKind() is ObjectKind.EventNpc; switch (UpdateBaseData(actor, _creatingState, modelId, customizePtr, equipDataPtr)) { // TODO handle right @@ -327,7 +331,7 @@ public class StateListener : IDisposable && weapon.Weapon.Id != 0 && _fistOffhands.TryGetValue(actor, out var lastFistOffhand)) { - Glamourer.Log.Information($"Applying stored fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}."); + Glamourer.Log.Verbose($"Applying stored fist weapon offhand {lastFistOffhand} for 0x{actor.Address:X}."); weapon = lastFistOffhand; } @@ -420,15 +424,22 @@ public class StateListener : IDisposable } var baseData = state.BaseData.Armor(slot); - var change = UpdateState.NoChange; + + var change = UpdateState.NoChange; if (baseData.Stains != armor.Stains) { + if (_isPlayerNpc) + return UpdateState.Transformed; + state.BaseData.SetStain(slot, armor.Stains); change = UpdateState.Change; } if (baseData.Set.Id != armor.Set.Id || baseData.Variant != armor.Variant && !fistWeapon) { + if (_isPlayerNpc) + return UpdateState.Transformed; + var item = _items.Identify(slot, armor.Set, armor.Variant); state.BaseData.SetItem(slot, item); change = UpdateState.Change; @@ -460,6 +471,9 @@ public class StateListener : IDisposable var change = UpdateState.NoChange; if (baseData.Id != actorItem.Id || baseData.PrimaryId != item.Set || baseData.Variant != item.Variant) { + if (_isPlayerNpc) + return UpdateState.Transformed; + var identified = _items.Identify(slot, item.Set, item.Variant); state.BaseData.SetBonusItem(slot, identified); change = UpdateState.Change; @@ -576,6 +590,9 @@ public class StateListener : IDisposable if (baseData.Skeleton.Id != weapon.Skeleton.Id || baseData.Weapon.Id != weapon.Weapon.Id || baseData.Variant != weapon.Variant) { + if (_isPlayerNpc) + return UpdateState.Transformed; + var item = _items.Identify(slot, weapon.Skeleton, weapon.Weapon, weapon.Variant, slot is EquipSlot.OffHand ? state.BaseData.MainhandType : FullEquipType.Unknown); state.BaseData.SetItem(slot, item); @@ -623,6 +640,10 @@ public class StateListener : IDisposable if (checkTransform && !actor.Customize->Equals(customize)) return UpdateState.Transformed; + // Check for player NPCs with a different game state. + if (_isPlayerNpc && !actor.Customize->Equals(state.BaseData.Customize)) + return UpdateState.Transformed; + // Customize array did not change to stored state. if (state.BaseData.Customize.Equals(customize)) return UpdateState.NoChange; // TODO: handle wrong base data. From 4db08224430ad4405a67b03c83e168b46841e173 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 20 Jan 2025 15:40:08 +0100 Subject: [PATCH 3/5] Update GameData. --- Penumbra.GameData | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Penumbra.GameData b/Penumbra.GameData index c525072..5bac66e 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit c525072299d5febd2bb638ab229060b0073ba6a6 +Subproject commit 5bac66e5ad73e57919aff7f8b046606b75e191a2 From 96dca5dbe7f4a61086b0f192521a60c3949372d2 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 20 Jan 2025 17:47:36 +0100 Subject: [PATCH 4/5] Cherry-Pick from ebdfd3f8 to use EquipGearSetInternal. --- Glamourer/Interop/InventoryService.cs | 34 +++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Glamourer/Interop/InventoryService.cs b/Glamourer/Interop/InventoryService.cs index 6d8e58b..f0ed6b5 100644 --- a/Glamourer/Interop/InventoryService.cs +++ b/Glamourer/Interop/InventoryService.cs @@ -1,5 +1,6 @@ using Dalamud.Hooking; using Dalamud.Plugin.Services; +using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using Glamourer.Events; @@ -19,36 +20,43 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService 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); + // 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); _moveItemHook.Enable(); - _equipGearsetHook.Enable(); + _equipGearsetInternalHook.Enable(); } public void Dispose() { _moveItemHook.Dispose(); - _equipGearsetHook.Dispose(); + _equipGearsetInternalHook.Dispose(); } - private delegate int EquipGearsetDelegate(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId); + // 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); - private readonly Hook _equipGearsetHook; + [Signature(EquipGearsetInternal, DetourName = nameof(EquipGearSetInternalDetour))] + private readonly Hook _equipGearsetInternalHook = null!; - private int EquipGearSetDetour(RaptureGearsetModule* module, int gearsetId, byte glamourPlateId) + private nint EquipGearSetInternalDetour(RaptureGearsetModule* module, uint gearsetId, byte glamourPlateId) { var prior = module->CurrentGearsetIndex; - var ret = _equipGearsetHook.Original(module, gearsetId, glamourPlateId); - var set = module->GetGearset(gearsetId); - _gearsetEvent.Invoke(new ByteString(set->Name).ToString(), gearsetId, prior, glamourPlateId, set->ClassJob); - Glamourer.Log.Excessive($"[InventoryService] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})"); + 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] Applied gear set {gearsetId} with glamour plate {glamourPlateId} (Returned {ret})"); if (ret == 0) { - var entry = module->GetGearset(gearsetId); + var entry = module->GetGearset((int)gearsetId); if (entry == null) return ret; From f1b335e794fe8e4bd72f6b08ba9b492c2dac6efc Mon Sep 17 00:00:00 2001 From: Actions User Date: Mon, 20 Jan 2025 16:49:52 +0000 Subject: [PATCH 5/5] [CI] Updating repo.json for testing_1.3.5.3 --- repo.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repo.json b/repo.json index dab02ea..5f4ae31 100644 --- a/repo.json +++ b/repo.json @@ -18,7 +18,7 @@ ], "InternalName": "Glamourer", "AssemblyVersion": "1.3.5.1", - "TestingAssemblyVersion": "1.3.5.2", + "TestingAssemblyVersion": "1.3.5.3", "RepoUrl": "https://github.com/Ottermandias/Glamourer", "ApplicableVersion": "any", "DalamudApiLevel": 11, @@ -29,7 +29,7 @@ "LastUpdate": 1618608322, "DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.3.5.1/Glamourer.zip", "DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.3.5.1/Glamourer.zip", - "DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/testing_1.3.5.2/Glamourer.zip", + "DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/testing_1.3.5.3/Glamourer.zip", "IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/main/images/icon.png" } ]