From 9155f68753203db9d3aea9f76987e685b53b850d Mon Sep 17 00:00:00 2001 From: pohky Date: Thu, 9 Dec 2021 16:13:11 +0100 Subject: [PATCH 1/5] add list of available aetherytes --- .../ClientState/Aetherytes/AetheryteEntry.cs | 35 ++++++ .../ClientState/Aetherytes/AetheryteList.cs | 117 ++++++++++++++++++ Dalamud/Game/ClientState/ClientState.cs | 3 + .../ClientState/ClientStateAddressResolver.cs | 14 +++ .../Interface/Internal/Windows/DataWindow.cs | 69 +++++++++++ 5 files changed, 238 insertions(+) create mode 100644 Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs create mode 100644 Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs new file mode 100644 index 000000000..59e78831f --- /dev/null +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -0,0 +1,35 @@ +using Dalamud.Game.ClientState.Resolvers; +using FFXIVClientStructs.FFXIV.Client.Game.UI; + +namespace Dalamud.Game.ClientState.Aetherytes +{ + public class AetheryteEntry + { + private readonly TeleportInfo data; + + internal AetheryteEntry(TeleportInfo data) + { + this.data = data; + } + + public uint AetheryteId => this.data.AetheryteId; + + public uint TerritoryId => this.data.TerritoryId; + + public byte SubIndex => this.data.SubIndex; + + public byte Ward => this.data.Ward; + + public byte Plot => this.data.Plot; + + public uint GilCost => this.data.GilCost; + + public bool IsFavourite => this.data.IsFavourite != 0; + + public bool IsSharedHouse => this.data.IsSharedHouse; + + public bool IsAppartment => this.data.IsAppartment; + + public ExcelResolver AetheryteData => new(this.AetheryteId); + } +} diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs new file mode 100644 index 000000000..994b8371e --- /dev/null +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Dalamud.IoC; +using Dalamud.IoC.Internal; +using Serilog; + +namespace Dalamud.Game.ClientState.Aetherytes +{ + /// + /// This collection represents the list of available Aetherytes in the Teleport window. + /// + [PluginInterface] + [InterfaceVersion("1.0")] + public sealed partial class AetheryteList + { + private delegate void UpdateAetheryteListDelegate(IntPtr telepo, byte arg1); + + private readonly ClientStateAddressResolver address; + private readonly UpdateAetheryteListDelegate updateAetheryteListFunc; + + /// + /// Initializes a new instance of the class. + /// + /// Client state address resolver. + internal AetheryteList(ClientStateAddressResolver addressResolver) + { + this.address = addressResolver; + this.updateAetheryteListFunc = Marshal.GetDelegateForFunctionPointer(this.address.UpdateAetheryteList); + + Log.Verbose($"Teleport address 0x{this.address.Telepo.ToInt64():X}"); + } + + /// + /// Gets the amount of Aetherytes the local player has unlocked. + /// + public unsafe int Length + { + get + { + var clientState = Service.Get(); + + if (clientState.LocalPlayer == null) + return 0; + + this.Update(); + + if (TelepoStruct->TeleportList.First == TelepoStruct->TeleportList.Last) + return 0; + + return (int)TelepoStruct->TeleportList.Size(); + } + } + + private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Telepo* TelepoStruct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Telepo*)this.address.Telepo; + + /// + /// Gets a Aetheryte Entry at the specified index. + /// + /// Index. + /// A at the specified index. + public unsafe AetheryteEntry? this[int index] + { + get + { + if (index < 0 || index >= Length) + { + return null; + } + + var clientState = Service.Get(); + + if (clientState.LocalPlayer == null) + return null; + + return new AetheryteEntry(TelepoStruct->TeleportList.Get((ulong)index)); + } + } + + private void Update() + { + var clientState = Service.Get(); + + // this is very very important as otherwise it crashes + if (clientState.LocalPlayer == null) + return; + + this.updateAetheryteListFunc(this.address.Telepo, 0); + } + } + + /// + /// This collection represents the list of available Aetherytes in the Teleport window. + /// + public sealed partial class AetheryteList : IReadOnlyCollection + { + + /// + public int Count => this.Length; + + /// + public IEnumerator GetEnumerator() + { + for (var i = 0; i < this.Length; i++) + { + yield return this[i]; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 52b63ed9e..cda6d7c9b 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -10,6 +10,7 @@ using Dalamud.Game.ClientState.Keys; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Party; +using Dalamud.Game.ClientState.Aetherytes; using Dalamud.Game.Network.Internal; using Dalamud.Hooking; using Dalamud.IoC; @@ -61,6 +62,8 @@ namespace Dalamud.Game.ClientState Service.Set(this.address); + Service.Set(this.address); + Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}"); this.setupTerritoryTypeHook = new Hook(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour); diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 64b7f5d4c..4fa7feb79 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -62,6 +62,11 @@ namespace Dalamud.Game.ClientState /// public IntPtr ConditionFlags { get; private set; } + /// + /// Gets the address of the Telepo instance. + /// + public IntPtr Telepo { get; private set; } + // Functions /// @@ -75,6 +80,11 @@ namespace Dalamud.Game.ClientState /// public IntPtr GamepadPoll { get; private set; } + /// + /// Gets the address of the method which updates the list of available teleport locations. + /// + public IntPtr UpdateAetheryteList { get; private set; } + /// /// Scan for and setup any configured address pointers. /// @@ -109,6 +119,10 @@ namespace Dalamud.Game.ClientState this.TargetManager = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? FF 50 ?? 48 85 DB"); this.GamepadPoll = sig.ScanText("40 ?? 57 41 ?? 48 81 EC ?? ?? ?? ?? 44 0F ?? ?? ?? ?? ?? ?? ?? 48 8B"); + + this.Telepo = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 48 8B 12"); + + this.UpdateAetheryteList = sig.ScanText("E8 ?? ?? ?? ?? 48 89 46 68 4C 8D 45 50"); } } } diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs index 48febaa5a..b6e9db296 100644 --- a/Dalamud/Interface/Internal/Windows/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs @@ -23,6 +23,7 @@ using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Party; +using Dalamud.Game.ClientState.Aetherytes; using Dalamud.Game.Command; using Dalamud.Game.Gui; using Dalamud.Game.Gui.FlyText; @@ -156,6 +157,7 @@ namespace Dalamud.Interface.Internal.Windows Configuration, TaskSched, Hook, + Aetherytes, } /// @@ -335,6 +337,9 @@ namespace Dalamud.Interface.Internal.Windows case DataKind.Hook: this.DrawHook(); break; + case DataKind.Aetherytes: + this.DrawAetherytes(); + break; } } else @@ -1510,6 +1515,70 @@ namespace Dalamud.Interface.Internal.Windows } } + private void DrawAetherytes() + { + if (!ImGui.BeginTable("##aetheryteTable", 11, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders)) + return; + + ImGui.TableSetupScrollFreeze(0, 1); + ImGui.TableSetupColumn("Idx", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("ID", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Zone", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Ward", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Plot", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Sub", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Gil", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Fav", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Shared", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableSetupColumn("Appartment", ImGuiTableColumnFlags.WidthFixed); + ImGui.TableHeadersRow(); + + var tpList = Service.Get(); + + for (var i = 0; i < tpList.Length; i++) + { + var info = tpList[i]; + if (info == null) + continue; + + ImGui.TableNextColumn(); // Idx + ImGui.TextUnformatted($"{i}"); + + ImGui.TableNextColumn(); // Name + ImGui.TextUnformatted($"{info.AetheryteData.GameData.PlaceName.Value?.Name}"); + + ImGui.TableNextColumn(); // ID + ImGui.TextUnformatted($"{info.AetheryteId}"); + + ImGui.TableNextColumn(); // Zone + ImGui.TextUnformatted($"{info.TerritoryId}"); + + ImGui.TableNextColumn(); // Ward + ImGui.TextUnformatted($"{info.Ward}"); + + ImGui.TableNextColumn(); // Plot + ImGui.TextUnformatted($"{info.Plot}"); + + ImGui.TableNextColumn(); // Sub + ImGui.TextUnformatted($"{info.SubIndex}"); + + ImGui.TableNextColumn(); // Gil + ImGui.TextUnformatted($"{info.GilCost}"); + + ImGui.TableNextColumn(); // Favourite + ImGui.TextUnformatted($"{info.IsFavourite}"); + + ImGui.TableNextColumn(); // Shared + ImGui.TextUnformatted($"{info.IsSharedHouse}"); + + ImGui.TableNextColumn(); // Appartment + ImGui.TextUnformatted($"{info.IsAppartment}"); + } + + ImGui.EndTable(); + } + private async Task TestTaskInTaskDelay() { await Task.Delay(5000); From 6158b488a98210d8b420148c7306df90041f715e Mon Sep 17 00:00:00 2001 From: pohky Date: Thu, 9 Dec 2021 16:30:43 +0100 Subject: [PATCH 2/5] Update AetheryteEntry.cs --- .../ClientState/Aetherytes/AetheryteEntry.cs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs index 59e78831f..3020c6ba0 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -3,33 +3,70 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI; namespace Dalamud.Game.ClientState.Aetherytes { - public class AetheryteEntry + /// + /// This class represents an entry in the Aetheryte list. + /// + public sealed class AetheryteEntry { private readonly TeleportInfo data; + /// + /// Initializes a new instance of the class. + /// + /// Data read from the Aetheryte List. internal AetheryteEntry(TeleportInfo data) { this.data = data; } + /// + ///Gets the Aetheryte ID. + /// public uint AetheryteId => this.data.AetheryteId; + /// + ///Gets the Territory ID. + /// public uint TerritoryId => this.data.TerritoryId; + /// + ///Gets the SubIndex used when there can be multiple Aetherytes with the same ID (Private/Shared Estates etc.). + /// public byte SubIndex => this.data.SubIndex; + /// + ///Gets the Ward. Zero if not a Shared Estate. + /// public byte Ward => this.data.Ward; + /// + ///Gets the Plot. Zero if not a Shared Estate. + /// public byte Plot => this.data.Plot; + /// + ///Gets the Cost in Gil to Teleport to this location. + /// public uint GilCost => this.data.GilCost; + /// + ///Gets if the LocalPlayer has set this Aetheryte as Favorite. + /// public bool IsFavourite => this.data.IsFavourite != 0; - + + /// + ///Gets if this Aetheryte is a Shared Estate. + /// public bool IsSharedHouse => this.data.IsSharedHouse; + /// + ///Gets if this Aetheryte is an Appartment. + /// public bool IsAppartment => this.data.IsAppartment; + /// + /// Gets the Aetheryte data related to this aetheryte. + /// public ExcelResolver AetheryteData => new(this.AetheryteId); } } From d78d88e089b7c54a63674016098de2f86a2bc2f3 Mon Sep 17 00:00:00 2001 From: pohky Date: Thu, 9 Dec 2021 16:34:35 +0100 Subject: [PATCH 3/5] fix formatting --- .../ClientState/Aetherytes/AetheryteEntry.cs | 18 +++++++++--------- .../ClientState/Aetherytes/AetheryteList.cs | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs index 3020c6ba0..9ada955f2 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -20,47 +20,47 @@ namespace Dalamud.Game.ClientState.Aetherytes } /// - ///Gets the Aetheryte ID. + /// Gets the Aetheryte ID. /// public uint AetheryteId => this.data.AetheryteId; /// - ///Gets the Territory ID. + /// Gets the Territory ID. /// public uint TerritoryId => this.data.TerritoryId; /// - ///Gets the SubIndex used when there can be multiple Aetherytes with the same ID (Private/Shared Estates etc.). + /// Gets the SubIndex used when there can be multiple Aetherytes with the same ID (Private/Shared Estates etc.). /// public byte SubIndex => this.data.SubIndex; /// - ///Gets the Ward. Zero if not a Shared Estate. + /// Gets the Ward. Zero if not a Shared Estate. /// public byte Ward => this.data.Ward; /// - ///Gets the Plot. Zero if not a Shared Estate. + /// Gets the Plot. Zero if not a Shared Estate. /// public byte Plot => this.data.Plot; /// - ///Gets the Cost in Gil to Teleport to this location. + /// Gets the Cost in Gil to Teleport to this location. /// public uint GilCost => this.data.GilCost; /// - ///Gets if the LocalPlayer has set this Aetheryte as Favorite. + /// Gets a value indicating whether the LocalPlayer has set this Aetheryte as Favorite or not. /// public bool IsFavourite => this.data.IsFavourite != 0; /// - ///Gets if this Aetheryte is a Shared Estate. + /// Gets a value indicating whether this Aetheryte is a Shared Estate or not. /// public bool IsSharedHouse => this.data.IsSharedHouse; /// - ///Gets if this Aetheryte is an Appartment. + /// Gets a value indicating whether this Aetheryte is an Appartment or not. /// public bool IsAppartment => this.data.IsAppartment; diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs index 994b8371e..0f03338d2 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; + using Dalamud.IoC; using Dalamud.IoC.Internal; using Serilog; @@ -15,8 +16,6 @@ namespace Dalamud.Game.ClientState.Aetherytes [InterfaceVersion("1.0")] public sealed partial class AetheryteList { - private delegate void UpdateAetheryteListDelegate(IntPtr telepo, byte arg1); - private readonly ClientStateAddressResolver address; private readonly UpdateAetheryteListDelegate updateAetheryteListFunc; @@ -32,6 +31,8 @@ namespace Dalamud.Game.ClientState.Aetherytes Log.Verbose($"Teleport address 0x{this.address.Telepo.ToInt64():X}"); } + private delegate void UpdateAetheryteListDelegate(IntPtr telepo, byte arg1); + /// /// Gets the amount of Aetherytes the local player has unlocked. /// @@ -64,7 +65,7 @@ namespace Dalamud.Game.ClientState.Aetherytes { get { - if (index < 0 || index >= Length) + if (index < 0 || index >= this.Length) { return null; } @@ -95,7 +96,6 @@ namespace Dalamud.Game.ClientState.Aetherytes /// public sealed partial class AetheryteList : IReadOnlyCollection { - /// public int Count => this.Length; @@ -111,7 +111,7 @@ namespace Dalamud.Game.ClientState.Aetherytes /// IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); + return this.GetEnumerator(); } } } From 10962c897417da263a2c0a5bbb0bb21765732604 Mon Sep 17 00:00:00 2001 From: pohky Date: Thu, 9 Dec 2021 17:25:41 +0100 Subject: [PATCH 4/5] add selftest --- .../AgingSteps/AetheryteListAgingStep.cs | 48 +++++++++++++++++++ .../Windows/SelfTest/SelfTestWindow.cs | 1 + 2 files changed, 49 insertions(+) create mode 100644 Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs new file mode 100644 index 000000000..b9a9beaf9 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs @@ -0,0 +1,48 @@ +using Dalamud.Game.ClientState.Aetherytes; +using Dalamud.Utility; +using ImGuiNET; + +namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps +{ + /// + /// Test setup for the Aetheryte List. + /// + internal class AetheryteListAgingStep : IAgingStep + { + private int index = 0; + + /// + public string Name => "Test AetheryteList"; + + /// + public SelfTestStepResult RunStep() + { + var list = Service.Get(); + + ImGui.Text("Checking aetheryte list..."); + + if (this.index == list.Length - 1) + { + return SelfTestStepResult.Pass; + } + + var aetheryte = list[this.index]; + this.index++; + + if (aetheryte == null) + { + return SelfTestStepResult.Waiting; + } + + Util.ShowObject(aetheryte); + + return SelfTestStepResult.Waiting; + } + + /// + public void CleanUp() + { + // ignored + } + } +} diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs index d97fad7bc..4fd2762fd 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs @@ -28,6 +28,7 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest new EnterTerritoryAgingStep(148, "Central Shroud"), new ActorTableAgingStep(), new FateTableAgingStep(), + new AetheryteListAgingStep(), new ConditionAgingStep(), new ToastAgingStep(), new TargetAgingStep(), From f40665d2b1653dcf2f7ef6a5a9a3ed6888100fa6 Mon Sep 17 00:00:00 2001 From: pohky Date: Thu, 9 Dec 2021 17:28:02 +0100 Subject: [PATCH 5/5] Update AetheryteListAgingStep.cs fix copy pasta --- .../Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs index b9a9beaf9..4035f860b 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AetheryteListAgingStep.cs @@ -34,6 +34,11 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps return SelfTestStepResult.Waiting; } + if (aetheryte.AetheryteId == 0) + { + return SelfTestStepResult.Fail; + } + Util.ShowObject(aetheryte); return SelfTestStepResult.Waiting;