mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Updated new functionality and internal gearset to use FFXIVClientStructs member functions and implemented structs, corrected remaining spacing issues.
This commit is contained in:
parent
2e11481276
commit
c43ce9d978
6 changed files with 33 additions and 94 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ using Penumbra.GameData.Interop;
|
|||
namespace Glamourer.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// <list type="number">
|
||||
/// <item>The model drawobject associated with the finished load (Also fired by other players on render) </item>
|
||||
/// <item>The model draw object associated with the finished load (Also fired by other players on render) </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class GearsetDataLoaded()
|
||||
|
|
|
|||
|
|
@ -23,34 +23,26 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
|||
_gearsetEvent = gearsetEvent;
|
||||
|
||||
_moveItemHook = interop.HookFromAddress<MoveItemDelegate>((nint)InventoryManager.MemberFunctionPointers.MoveItemSlot, MoveItemDetour);
|
||||
// This can be uncommented after ClientStructs Updates with EquipGearsetInternal after merged PR. (See comment below)
|
||||
//_equipGearsetInternalHook = interop.HookFromAddress<EquipGearsetDelegate>((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<EquipGearsetInternalDelegate>((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<EquipGearsetInternalDelegate> _equipGearsetInternalHook = null!;
|
||||
private readonly Hook<EquipGearsetInternalDelegate> _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})");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -25,10 +26,8 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
GearsetDataLoadedEvent = gearsetDataLoaded;
|
||||
_bonusItems = bonusItems;
|
||||
|
||||
// Usable after the merge with client structs.
|
||||
//_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
|
||||
_loadGearsetDataHook = interop.HookFromAddress<LoadGearsetDataDelegate>((nint)DrawDataContainer.MemberFunctionPointers.LoadGearsetData, LoadGearsetDataDetour);
|
||||
interop.InitializeFromAttributes(this);
|
||||
|
||||
_flagSlotForUpdateHook.Enable();
|
||||
_flagBonusSlotForUpdateHook.Enable();
|
||||
_loadGearsetDataHook.Enable();
|
||||
|
|
@ -87,20 +86,10 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
|
||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _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))]
|
||||
/// <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 ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||
|
|
@ -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. </summary>
|
||||
[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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue