From 79960fa993cf90e8c3982bc5ca5824b2b61c7d55 Mon Sep 17 00:00:00 2001 From: attickdoor Date: Wed, 15 Apr 2020 17:55:48 -0400 Subject: [PATCH] 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; + } +}