From 79960fa993cf90e8c3982bc5ca5824b2b61c7d55 Mon Sep 17 00:00:00 2001 From: attickdoor Date: Wed, 15 Apr 2020 17:55:48 -0400 Subject: [PATCH 1/3] Add PartyList and PartyMember for ClientState use. --- .../ClientState/Actors/Types/PartyMember.cs | 34 ++++++ Dalamud/Game/ClientState/ClientState.cs | 9 ++ .../ClientState/ClientStateAddressResolver.cs | 5 +- Dalamud/Game/ClientState/PartyList.cs | 113 ++++++++++++++++++ .../Game/ClientState/Structs/PartyMember.cs | 19 +++ 5 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Game/ClientState/Actors/Types/PartyMember.cs create mode 100644 Dalamud/Game/ClientState/PartyList.cs create mode 100644 Dalamud/Game/ClientState/Structs/PartyMember.cs diff --git a/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs new file mode 100644 index 000000000..07fef0e75 --- /dev/null +++ b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs @@ -0,0 +1,34 @@ +using Dalamud.Game.ClientState.Structs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Dalamud.Game.ClientState.Actors.Types +{ + public class PartyMember + { + public string CharacterName; + public long Unknown; + public Actor Actor; + public ObjectKind ObjectKind; + + public PartyMember(ActorTable table, Structs.PartyMember rawData) + { + CharacterName = Marshal.PtrToStringAnsi(rawData.namePtr); + Unknown = rawData.unknown; + Actor = null; + for (var i = 0; i < table.Length; i++) + { + if (table[i].ActorId == rawData.actorId) + { + Actor = table[i]; + break; + } + } + ObjectKind = rawData.objectKind; + } + } +} diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 6215be83b..aff9b2ec8 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -80,6 +80,11 @@ namespace Dalamud.Game.ClientState /// public JobGauges JobGauges; + /// + /// The class facilitating party list data access + /// + public PartyList PartyList; + /// /// Provides access to the keypress state of keyboard keys in game. /// @@ -101,6 +106,8 @@ namespace Dalamud.Game.ClientState this.Actors = new ActorTable(dalamud, Address); + this.PartyList = new PartyList(dalamud, Address); + this.JobGauges = new JobGauges(Address); this.KeyState = new KeyState(Address, scanner.Module.BaseAddress); @@ -116,11 +123,13 @@ namespace Dalamud.Game.ClientState public void Enable() { this.Actors.Enable(); + this.PartyList.Enable(); this.setupTerritoryTypeHook.Enable(); } public void Dispose() { this.Actors.Dispose(); + this.PartyList.Dispose(); this.setupTerritoryTypeHook.Dispose(); } diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 27717981a..45ed5577a 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -13,7 +13,8 @@ namespace Dalamud.Game.ClientState // Functions public IntPtr SetupTerritoryType { get; private set; } public IntPtr SomeActorTableAccess { get; private set; } - + public IntPtr PartyListUpdate { get; private set; } + protected override void Setup64Bit(SigScanner sig) { ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148; SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??"); @@ -25,6 +26,8 @@ namespace Dalamud.Game.ClientState // This resolves to a fixed offset only, without the base address added in, so GetStaticAddressFromSig() can't be used KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; + + PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D4 4C 8D 87 ?? ?? ?? ??"); } } } diff --git a/Dalamud/Game/ClientState/PartyList.cs b/Dalamud/Game/ClientState/PartyList.cs new file mode 100644 index 000000000..edb6206e7 --- /dev/null +++ b/Dalamud/Game/ClientState/PartyList.cs @@ -0,0 +1,113 @@ +using Dalamud.Game.ClientState.Actors.Types; +using Dalamud.Hooking; +using Dalamud.Plugin; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Dalamud.Game.ClientState + +{ + public class PartyList : ICollection, IDisposable + { + private ClientStateAddressResolver Address { get; } + private Dalamud dalamud; + + private delegate long PartyListUpdateDelegate(IntPtr structBegin, long param2, char param3); + + private Hook partyListUpdateHook; + private IntPtr partyListBegin; + private bool isReady = false; + + public PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver) + { + Address = addressResolver; + this.dalamud = dalamud; + partyListUpdateHook = new Hook(Address.PartyListUpdate, new PartyListUpdateDelegate(PartyListUpdateDetour), this); + } + + public void Enable() + { + partyListUpdateHook.Enable(); + } + + public void Dispose() + { + if (!this.isReady) + partyListUpdateHook.Dispose(); + isReady = false; + } + + private long PartyListUpdateDetour(IntPtr structBegin, long param2, char param3) + { + var result = partyListUpdateHook.Original(structBegin, param2, param3); + partyListBegin = structBegin + 0xB48; + partyListUpdateHook.Dispose(); + isReady = true; + return result; + } + public PartyMember this[int index] + { + get + { + if (!this.isReady) + return null; + if (index >= Length) + return null; + var tblIndex = partyListBegin + index * 24; + var memberStruct = Marshal.PtrToStructure(tblIndex); + return new PartyMember(dalamud.ClientState.Actors, memberStruct); + } + } + + public void CopyTo(Array array, int index) + { + for (var i = 0; i < Length; i++) + { + array.SetValue(this[i], index); + index++; + } + } + + private class PartyListEnumerator : IEnumerator + { + private readonly PartyList party; + private int currentIndex; + + public PartyListEnumerator(PartyList list) + { + party = list; + } + + public object Current => party[currentIndex]; + + public bool MoveNext() + { + currentIndex++; + return currentIndex != party.Length; + } + + public void Reset() + { + currentIndex = 0; + } + } + + public IEnumerator GetEnumerator() + { + return new PartyListEnumerator(this); + } + + public int Length => !this.isReady ? 0 : Marshal.ReadByte(partyListBegin + 0xF0); + + public int Count => Length; + + public object SyncRoot => this; + + public bool IsSynchronized => false; + } +} diff --git a/Dalamud/Game/ClientState/Structs/PartyMember.cs b/Dalamud/Game/ClientState/Structs/PartyMember.cs new file mode 100644 index 000000000..71dbf0d91 --- /dev/null +++ b/Dalamud/Game/ClientState/Structs/PartyMember.cs @@ -0,0 +1,19 @@ +using Dalamud.Game.ClientState.Actors; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Dalamud.Game.ClientState.Structs +{ + [StructLayout(LayoutKind.Explicit)] + public struct PartyMember + { + [FieldOffset(0x0)] public IntPtr namePtr; + [FieldOffset(0x8)] public long unknown; + [FieldOffset(0x10)] public int actorId; + [FieldOffset(0x14)] public ObjectKind objectKind; + } +} From a87d7484b60ee18d8d63a4cc8dc959d1aceea3c4 Mon Sep 17 00:00:00 2001 From: attickdoor Date: Wed, 15 Apr 2020 19:14:09 -0400 Subject: [PATCH 2/3] Add TargetActorId --- Dalamud/Game/ClientState/Structs/Actor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs index d3089f8dd..356c62ca4 100644 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ b/Dalamud/Game/ClientState/Structs/Actor.cs @@ -24,7 +24,7 @@ namespace Dalamud.Game.ClientState.Structs // This field can't be correctly aligned, so we have to cut it manually. [FieldOffset(0x17d0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] public byte[] CompanyTag; - + [FieldOffset(0x17F8)] public int TargetActorId; [FieldOffset(0x1884)] public byte CurrentWorld; [FieldOffset(0x1886)] public byte HomeWorld; [FieldOffset(6328)] public int CurrentHp; @@ -33,5 +33,6 @@ namespace Dalamud.Game.ClientState.Structs [FieldOffset(6340)] public int MaxMp; [FieldOffset(6358)] public byte ClassJob; [FieldOffset(6360)] public byte Level; + } } From 00ab1847145a141b09b327f7dbbc2ba24fc663d6 Mon Sep 17 00:00:00 2001 From: attickdoor Date: Wed, 15 Apr 2020 19:25:59 -0400 Subject: [PATCH 3/3] distance and player target status --- Dalamud/Game/ClientState/Structs/Actor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dalamud/Game/ClientState/Structs/Actor.cs b/Dalamud/Game/ClientState/Structs/Actor.cs index 356c62ca4..cf82da97c 100644 --- a/Dalamud/Game/ClientState/Structs/Actor.cs +++ b/Dalamud/Game/ClientState/Structs/Actor.cs @@ -20,6 +20,9 @@ namespace Dalamud.Game.ClientState.Structs [FieldOffset(140)] public ObjectKind ObjectKind; [FieldOffset(141)] public byte SubKind; [FieldOffset(142)] public bool IsFriendly; + [FieldOffset(144)] public byte YalmDistanceFromPlayer1; // Demo says one of these is x distance + [FieldOffset(145)] public byte PlayerTargetStatus; // This is some kind of enum + [FieldOffset(146)] public byte YalmDistanceFromPlayer2; // and the other is z distance [FieldOffset(160)] public Position3 Position; // This field can't be correctly aligned, so we have to cut it manually.