From 3c18814a66e7f7165f079932ed078c2580eee7c8 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sun, 29 Sep 2024 10:44:06 -0700 Subject: [PATCH 01/50] Update to newest Serilog --- Dalamud/Dalamud.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index b0b130810..528145d59 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -79,10 +79,10 @@ - - - - + + + + all From 621fd17e1d053c9a29c1b8201eb4447986e0afba Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sun, 29 Sep 2024 10:45:43 -0700 Subject: [PATCH 02/50] Expose Serilog API to plugins --- Dalamud/Logging/ScopedPluginLogService.cs | 10 ++++------ Dalamud/Plugin/Services/IPluginLog.cs | 9 ++++++++- targets/Dalamud.Plugin.targets | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Dalamud/Logging/ScopedPluginLogService.cs b/Dalamud/Logging/ScopedPluginLogService.cs index 54f231e0d..7305aa87b 100644 --- a/Dalamud/Logging/ScopedPluginLogService.cs +++ b/Dalamud/Logging/ScopedPluginLogService.cs @@ -40,6 +40,9 @@ internal class ScopedPluginLogService : IServiceType, IPluginLog this.Logger = loggerConfiguration.CreateLogger(); } + + /// + public ILogger Logger { get; } /// public LogEventLevel MinimumLogLevel @@ -47,12 +50,7 @@ internal class ScopedPluginLogService : IServiceType, IPluginLog get => this.levelSwitch.MinimumLevel; set => this.levelSwitch.MinimumLevel = value; } - - /// - /// Gets a logger that may be exposed to plugins some day. - /// - public ILogger Logger { get; } - + /// public void Fatal(string messageTemplate, params object[] values) => this.Write(LogEventLevel.Fatal, null, messageTemplate, values); diff --git a/Dalamud/Plugin/Services/IPluginLog.cs b/Dalamud/Plugin/Services/IPluginLog.cs index dcba5bb29..38406fd91 100644 --- a/Dalamud/Plugin/Services/IPluginLog.cs +++ b/Dalamud/Plugin/Services/IPluginLog.cs @@ -1,4 +1,5 @@ -using Serilog.Events; +using Serilog; +using Serilog.Events; #pragma warning disable CS1573 // See https://github.com/dotnet/roslyn/issues/40325 @@ -9,6 +10,12 @@ namespace Dalamud.Plugin.Services; /// public interface IPluginLog { + /// + /// Gets a Serilog ILogger instance for this plugin. This is the entrypoint for plugins that wish to use more + /// advanced logging functionality. + /// + public ILogger Logger { get; } + /// /// Gets or sets the minimum log level that will be recorded from this plugin to Dalamud's logs. This may be set /// by either the plugin or by Dalamud itself. diff --git a/targets/Dalamud.Plugin.targets b/targets/Dalamud.Plugin.targets index dc5bec410..10b63c437 100644 --- a/targets/Dalamud.Plugin.targets +++ b/targets/Dalamud.Plugin.targets @@ -22,6 +22,7 @@ + From 8d535af4ade9e1d706390830bf4e6778cf2ee07f Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sun, 29 Sep 2024 10:47:26 -0700 Subject: [PATCH 03/50] remove excess declarations from IPC --- Dalamud/Plugin/Ipc/ICallGateProvider.cs | 74 +------------------------ 1 file changed, 1 insertion(+), 73 deletions(-) diff --git a/Dalamud/Plugin/Ipc/ICallGateProvider.cs b/Dalamud/Plugin/Ipc/ICallGateProvider.cs index 154ca88d3..f4e5c76d7 100644 --- a/Dalamud/Plugin/Ipc/ICallGateProvider.cs +++ b/Dalamud/Plugin/Ipc/ICallGateProvider.cs @@ -13,7 +13,7 @@ public interface ICallGateProvider { /// public int SubscriptionCount { get; } - + /// public void UnregisterAction(); @@ -29,14 +29,6 @@ public interface ICallGateProvider : ICallGateProvider /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(); @@ -50,14 +42,6 @@ public interface ICallGateProvider : ICallGateProvider /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1); @@ -71,14 +55,6 @@ public interface ICallGateProvider : ICallGateProvider /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2); @@ -92,14 +68,6 @@ public interface ICallGateProvider : ICallGateProvider /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2, T3 arg3); @@ -113,14 +81,6 @@ public interface ICallGateProvider : ICallGateProvider /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4); @@ -134,14 +94,6 @@ public interface ICallGateProvider : ICallGateProvider /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); @@ -155,14 +107,6 @@ public interface ICallGateProvider : ICallGateProv /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); @@ -176,14 +120,6 @@ public interface ICallGateProvider : ICallGate /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); @@ -197,14 +133,6 @@ public interface ICallGateProvider : ICall /// public void RegisterFunc(Func func); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterAction(); - - /// - [Api11ToDo("Remove, it's in the base interface")] - public new void UnregisterFunc(); /// public void SendMessage(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); From 4d6b8e59efc839206bbbb30deb659203d59cd9e8 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sun, 29 Sep 2024 10:51:00 -0700 Subject: [PATCH 04/50] apply low-hanging fruit for api11todos --- Dalamud/Game/Gui/Dtr/DtrBarEntry.cs | 3 +-- Dalamud/Utility/SeStringExtensions.cs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs index 651f59b1b..8ba46f999 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBarEntry.cs @@ -85,8 +85,7 @@ public interface IDtrBarEntry : IReadOnlyDtrBarEntry /// /// Class representing an entry in the server info bar. /// -[Api11ToDo(Api11ToDoAttribute.MakeInternal)] -public sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry +internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry { private readonly DalamudConfiguration configuration; diff --git a/Dalamud/Utility/SeStringExtensions.cs b/Dalamud/Utility/SeStringExtensions.cs index f78cf09ce..b89f815bc 100644 --- a/Dalamud/Utility/SeStringExtensions.cs +++ b/Dalamud/Utility/SeStringExtensions.cs @@ -24,7 +24,7 @@ public static class SeStringExtensions /// Target SeString builder. /// Macro string in UTF-8 to compile and append to . /// this for method chaining. - [Obsolete($"Use {nameof(LSeStringBuilder)}.{nameof(LSeStringBuilder.AppendMacroString)} directly instead.")] + [Obsolete($"Use {nameof(LSeStringBuilder)}.{nameof(LSeStringBuilder.AppendMacroString)} directly instead.", true)] [Api11ToDo("Remove")] public static LSeStringBuilder AppendMacroString(this LSeStringBuilder ssb, ReadOnlySpan macroString) => ssb.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }); @@ -33,7 +33,7 @@ public static class SeStringExtensions /// Target SeString builder. /// Macro string in UTF-16 to compile and append to . /// this for method chaining. - [Obsolete($"Use {nameof(LSeStringBuilder)}.{nameof(LSeStringBuilder.AppendMacroString)} directly instead.")] + [Obsolete($"Use {nameof(LSeStringBuilder)}.{nameof(LSeStringBuilder.AppendMacroString)} directly instead.", true)] [Api11ToDo("Remove")] public static LSeStringBuilder AppendMacroString(this LSeStringBuilder ssb, ReadOnlySpan macroString) => ssb.AppendMacroString(macroString, new() { ExceptionMode = MacroStringParseExceptionMode.EmbedError }); From 0b9af0e3f43c42dedd29c3d19371fbaacc762caa Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Sun, 20 Oct 2024 19:59:03 -0700 Subject: [PATCH 05/50] Update to Lumina 5 (new Excel parsing) (#2022) * Refactor and upgrade to new excel design * Obsolete ExcelResolver and use only RowRef * Better benchmarking for Lumina * Add custom game-supported RSV provider * Refactor and move Lazy and nullable/cached row objects to RowRefs * Convert IRSVProvider to delegate, resolve strings by default * Split IExcelRow into IExcelSubrow * Extra lumina documentation * Minor RSV CS fixes * Fix UIGlowPayload warning * Fix rebase * Update to Lumina 5 --- Dalamud.CorePlugin/Dalamud.CorePlugin.csproj | 4 +- Dalamud/Dalamud.csproj | 4 +- Dalamud/Data/DataManager.cs | 18 ++-- Dalamud/Data/LuminaUtils.cs | 22 +++++ Dalamud/Data/RsvResolver.cs | 51 +++++++++++ .../ClientState/Aetherytes/AetheryteEntry.cs | 9 +- Dalamud/Game/ClientState/Buddy/BuddyMember.cs | 16 ++-- Dalamud/Game/ClientState/ClientState.cs | 2 +- Dalamud/Game/ClientState/Fates/Fate.cs | 11 +-- .../ClientState/JobGauge/Types/SMNGauge.cs | 4 +- .../Objects/SubKinds/PlayerCharacter.cs | 22 ++--- .../ClientState/Objects/Types/Character.cs | 28 ++++--- Dalamud/Game/ClientState/Party/PartyMember.cs | 16 ++-- .../ClientState/Resolvers/ExcelResolver{T}.cs | 38 --------- Dalamud/Game/ClientState/Statuses/Status.cs | 6 +- Dalamud/Game/Gui/ContextMenu/MenuItem.cs | 2 +- .../Game/Gui/ContextMenu/MenuTargetDefault.cs | 7 +- .../PartyFinder/Types/JobFlagsExtensions.cs | 2 +- .../PartyFinder/Types/PartyFinderListing.cs | 36 ++++---- .../UniversalisMarketBoardUploader.cs | 6 +- .../Game/Network/Internal/NetworkHandlers.cs | 14 ++-- .../Structures/InfoProxy/CharacterData.cs | 22 ++--- Dalamud/Game/Text/SeStringHandling/Payload.cs | 7 -- .../Payloads/AutoTranslatePayload.cs | 84 +++++++++---------- .../SeStringHandling/Payloads/ItemPayload.cs | 34 +++----- .../Payloads/MapLinkPayload.cs | 34 +++----- .../Payloads/PlayerPayload.cs | 16 ++-- .../SeStringHandling/Payloads/QuestPayload.cs | 14 ++-- .../Payloads/StatusPayload.cs | 14 ++-- .../Payloads/UIForegroundPayload.cs | 22 ++--- .../Payloads/UIGlowPayload.cs | 17 ++-- .../Game/Text/SeStringHandling/SeString.cs | 19 ++--- .../Internal/SeStringColorStackSet.cs | 2 +- .../Internal/SeStringRenderer.cs | 2 +- .../Windows/Data/Widgets/AetherytesWidget.cs | 4 +- .../Windows/Data/Widgets/GaugeWidget.cs | 4 +- .../Windows/Data/Widgets/ObjectTableWidget.cs | 6 +- .../Widgets/SeStringRendererTestWidget.cs | 24 +++--- .../Windows/Data/Widgets/UIColorWidget.cs | 52 +++++++----- .../AgingSteps/ContextMenuAgingStep.cs | 41 +++++---- .../SelfTest/AgingSteps/LuminaAgingStep.cs | 38 ++++++--- .../Windows/SelfTest/SelfTestWindow.cs | 8 +- Dalamud/Plugin/Internal/Types/LocalPlugin.cs | 2 +- Dalamud/Plugin/Services/IClientState.cs | 2 +- Dalamud/Plugin/Services/IDataManager.cs | 33 ++++++-- Dalamud/Utility/MapUtil.cs | 10 +-- Dalamud/Utility/SeStringExtensions.cs | 20 ++++- Dalamud/Utility/Util.cs | 12 +-- lib/FFXIVClientStructs | 2 +- 49 files changed, 460 insertions(+), 403 deletions(-) create mode 100644 Dalamud/Data/LuminaUtils.cs create mode 100644 Dalamud/Data/RsvResolver.cs delete mode 100644 Dalamud/Game/ClientState/Resolvers/ExcelResolver{T}.cs diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index 5f91a6ac8..6cc92cbb5 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -27,8 +27,8 @@ - - + + all diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 528145d59..a577f130b 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -71,8 +71,8 @@ - - + + all diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index 78fa83b00..d017bf85a 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -11,6 +11,7 @@ using Dalamud.Utility.Timing; using Lumina; using Lumina.Data; using Lumina.Excel; + using Newtonsoft.Json; using Serilog; @@ -28,12 +29,15 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager { private readonly Thread luminaResourceThread; private readonly CancellationTokenSource luminaCancellationTokenSource; + private readonly RsvResolver rsvResolver; [ServiceManager.ServiceConstructor] private DataManager(Dalamud dalamud) { this.Language = (ClientLanguage)dalamud.StartInfo.Language; + this.rsvResolver = new(); + try { Log.Verbose("Starting data load..."); @@ -44,11 +48,8 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager { LoadMultithreaded = true, CacheFileResources = true, -#if NEVER // Lumina bug PanicOnSheetChecksumMismatch = true, -#else - PanicOnSheetChecksumMismatch = false, -#endif + RsvResolver = this.rsvResolver.TryResolve, DefaultExcelLanguage = this.Language.ToLumina(), }; @@ -129,12 +130,12 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager #region Lumina Wrappers /// - public ExcelSheet? GetExcelSheet() where T : ExcelRow - => this.Excel.GetSheet(); + public ExcelSheet GetExcelSheet(ClientLanguage? language = null, string? name = null) where T : struct, IExcelRow + => this.Excel.GetSheet(language?.ToLumina(), name); /// - public ExcelSheet? GetExcelSheet(ClientLanguage language) where T : ExcelRow - => this.Excel.GetSheet(language.ToLumina()); + public SubrowExcelSheet GetSubrowExcelSheet(ClientLanguage? language = null, string? name = null) where T : struct, IExcelSubrow + => this.Excel.GetSubrowSheet(language?.ToLumina(), name); /// public FileResource? GetFile(string path) @@ -170,6 +171,7 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager { this.luminaCancellationTokenSource.Cancel(); this.GameData.Dispose(); + this.rsvResolver.Dispose(); } private class LauncherTroubleshootingInfo diff --git a/Dalamud/Data/LuminaUtils.cs b/Dalamud/Data/LuminaUtils.cs new file mode 100644 index 000000000..6da67f426 --- /dev/null +++ b/Dalamud/Data/LuminaUtils.cs @@ -0,0 +1,22 @@ +using Lumina.Excel; + +namespace Dalamud.Data; + +/// +/// A helper class to easily resolve Lumina data within Dalamud. +/// +internal static class LuminaUtils +{ + private static ExcelModule Module => Service.Get().Excel; + + /// + /// Initializes a new instance of the class using the default . + /// + /// The type of Lumina sheet to resolve. + /// The id of the row to resolve. + /// A new object. + public static RowRef CreateRef(uint rowId) where T : struct, IExcelRow + { + return new(Module, rowId); + } +} diff --git a/Dalamud/Data/RsvResolver.cs b/Dalamud/Data/RsvResolver.cs new file mode 100644 index 000000000..3f507ff1d --- /dev/null +++ b/Dalamud/Data/RsvResolver.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +using Dalamud.Hooking; +using Dalamud.Logging.Internal; +using Dalamud.Memory; +using FFXIVClientStructs.FFXIV.Client.LayoutEngine; +using Lumina.Text.ReadOnly; + +namespace Dalamud.Data; + +/// +/// Provides functionality for resolving RSV strings. +/// +internal sealed unsafe class RsvResolver : IDisposable +{ + private static readonly ModuleLog Log = new("RsvProvider"); + + private readonly Hook addRsvStringHook; + + /// + /// Initializes a new instance of the class. + /// + public RsvResolver() + { + this.addRsvStringHook = Hook.FromAddress((nint)LayoutWorld.MemberFunctionPointers.AddRsvString, this.AddRsvStringDetour); + + this.addRsvStringHook.Enable(); + } + + private Dictionary Lookup { get; } = []; + + /// Attemps to resolve an RSV string. + /// + public bool TryResolve(ReadOnlySeString rsvString, out ReadOnlySeString resolvedString) => + this.Lookup.TryGetValue(rsvString, out resolvedString); + + /// + public void Dispose() + { + this.addRsvStringHook.Dispose(); + } + + private bool AddRsvStringDetour(LayoutWorld* @this, byte* rsvString, byte* resolvedString, nuint resolvedStringSize) + { + var rsv = new ReadOnlySeString(MemoryHelper.ReadRawNullTerminated((nint)rsvString)); + var resolved = new ReadOnlySeString(new ReadOnlySpan(resolvedString, (int)resolvedStringSize).ToArray()); + Log.Debug($"Resolving RSV \"{rsv}\" to \"{resolved}\"."); + this.Lookup[rsv] = resolved; + return this.addRsvStringHook.Original(@this, rsvString, resolvedString, resolvedStringSize); + } +} diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs index 058d6c0c2..244989476 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -1,6 +1,9 @@ -using Dalamud.Game.ClientState.Resolvers; +using Dalamud.Data; + using FFXIVClientStructs.FFXIV.Client.Game.UI; +using Lumina.Excel; + namespace Dalamud.Game.ClientState.Aetherytes; /// @@ -56,7 +59,7 @@ public interface IAetheryteEntry /// /// Gets the Aetheryte data related to this aetheryte. /// - ExcelResolver AetheryteData { get; } + RowRef AetheryteData { get; } } /// @@ -103,5 +106,5 @@ internal sealed class AetheryteEntry : IAetheryteEntry public bool IsApartment => this.data.IsApartment; /// - public ExcelResolver AetheryteData => new(this.AetheryteId); + public RowRef AetheryteData => LuminaUtils.CreateRef(this.AetheryteId); } diff --git a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs index a650a7b44..025de611d 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs @@ -1,6 +1,8 @@ +using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.ClientState.Resolvers; + +using Lumina.Excel; namespace Dalamud.Game.ClientState.Buddy; @@ -45,17 +47,17 @@ public interface IBuddyMember /// /// Gets the Mount data related to this buddy. It should only be used with companion buddies. /// - ExcelResolver MountData { get; } + RowRef MountData { get; } /// /// Gets the Pet data related to this buddy. It should only be used with pet buddies. /// - ExcelResolver PetData { get; } + RowRef PetData { get; } /// /// Gets the Trust data related to this buddy. It should only be used with battle buddies. /// - ExcelResolver TrustData { get; } + RowRef TrustData { get; } } /// @@ -94,13 +96,13 @@ internal unsafe class BuddyMember : IBuddyMember public uint DataID => this.Struct->DataId; /// - public ExcelResolver MountData => new(this.DataID); + public RowRef MountData => LuminaUtils.CreateRef(this.DataID); /// - public ExcelResolver PetData => new(this.DataID); + public RowRef PetData => LuminaUtils.CreateRef(this.DataID); /// - public ExcelResolver TrustData => new(this.DataID); + public RowRef TrustData => LuminaUtils.CreateRef(this.DataID); private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address; } diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 9dd999860..0fad33e15 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -19,7 +19,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Event; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Action = System.Action; diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 2d32cc04c..9b41ac758 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -1,10 +1,11 @@ using System.Numerics; using Dalamud.Data; -using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory; +using Lumina.Excel; + namespace Dalamud.Game.ClientState.Fates; /// @@ -20,7 +21,7 @@ public interface IFate : IEquatable /// /// Gets game data linked to this Fate. /// - Lumina.Excel.GeneratedSheets.Fate GameData { get; } + RowRef GameData { get; } /// /// Gets the time this started. @@ -105,7 +106,7 @@ public interface IFate : IEquatable /// /// Gets the territory this is located in. /// - ExcelResolver TerritoryType { get; } + RowRef TerritoryType { get; } /// /// Gets the address of this Fate in memory. @@ -185,7 +186,7 @@ internal unsafe partial class Fate : IFate public ushort FateId => this.Struct->FateId; /// - public Lumina.Excel.GeneratedSheets.Fate GameData => Service.Get().GetExcelSheet().GetRow(this.FateId); + public RowRef GameData => LuminaUtils.CreateRef(this.FateId); /// public int StartTimeEpoch => this.Struct->StartTimeEpoch; @@ -238,5 +239,5 @@ internal unsafe partial class Fate : IFate /// /// Gets the territory this is located in. /// - public ExcelResolver TerritoryType => new(this.Struct->TerritoryId); + public RowRef TerritoryType => LuminaUtils.CreateRef(this.Struct->TerritoryId); } diff --git a/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs b/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs index aa7e27fd2..81be0e762 100644 --- a/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs +++ b/Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs @@ -29,13 +29,13 @@ public unsafe class SMNGauge : JobGaugeBase /// /// Gets the summon that will return after the current summon expires. - /// This maps to the sheet. + /// This maps to the sheet. /// public SummonPet ReturnSummon => (SummonPet)this.Struct->ReturnSummon; /// /// Gets the summon glam for the . - /// This maps to the sheet. + /// This maps to the sheet. /// public PetGlam ReturnSummonGlam => (PetGlam)this.Struct->ReturnSummonGlam; diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs index 87fbf39c3..2fd723071 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs @@ -1,12 +1,8 @@ -using System.Numerics; - -using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Data; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.ClientState.Resolvers; -using Dalamud.Game.ClientState.Statuses; -using Dalamud.Game.Text.SeStringHandling; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel; +using Lumina.Excel.Sheets; namespace Dalamud.Game.ClientState.Objects.SubKinds; @@ -16,14 +12,14 @@ namespace Dalamud.Game.ClientState.Objects.SubKinds; public interface IPlayerCharacter : IBattleChara { /// - /// Gets the current world of the character. + /// Gets the current world of the character. /// - ExcelResolver CurrentWorld { get; } + RowRef CurrentWorld { get; } /// - /// Gets the home world of the character. + /// Gets the home world of the character. /// - ExcelResolver HomeWorld { get; } + RowRef HomeWorld { get; } } /// @@ -42,10 +38,10 @@ internal unsafe class PlayerCharacter : BattleChara, IPlayerCharacter } /// - public ExcelResolver CurrentWorld => new(this.Struct->CurrentWorld); + public RowRef CurrentWorld => LuminaUtils.CreateRef(this.Struct->CurrentWorld); /// - public ExcelResolver HomeWorld => new(this.Struct->HomeWorld); + public RowRef HomeWorld => LuminaUtils.CreateRef(this.Struct->HomeWorld); /// /// Gets the target actor ID of the PlayerCharacter. diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs index 72f6a9950..b04b54301 100644 --- a/Dalamud/Game/ClientState/Objects/Types/Character.cs +++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs @@ -1,10 +1,12 @@ using System.Runtime.CompilerServices; +using Dalamud.Data; using Dalamud.Game.ClientState.Objects.Enums; -using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory; -using Lumina.Excel.GeneratedSheets; + +using Lumina.Excel; +using Lumina.Excel.Sheets; namespace Dalamud.Game.ClientState.Objects.Types; @@ -61,7 +63,7 @@ public interface ICharacter : IGameObject /// /// Gets the ClassJob of this Chara. /// - public ExcelResolver ClassJob { get; } + public RowRef ClassJob { get; } /// /// Gets the level of this Chara. @@ -87,7 +89,7 @@ public interface ICharacter : IGameObject /// /// Gets the current online status of the character. /// - public ExcelResolver OnlineStatus { get; } + public RowRef OnlineStatus { get; } /// /// Gets the status flags. @@ -97,14 +99,14 @@ public interface ICharacter : IGameObject /// /// Gets the current mount for this character. Will be null if the character doesn't have a mount. /// - public ExcelResolver? CurrentMount { get; } + public RowRef? CurrentMount { get; } /// /// Gets the current minion summoned for this character. Will be null if the character doesn't have a minion. /// This method *will* return information about a spawned (but invisible) minion, e.g. if the character is riding a /// mount. /// - public ExcelResolver? CurrentMinion { get; } + public RowRef? CurrentMinion { get; } } /// @@ -150,7 +152,7 @@ internal unsafe class Character : GameObject, ICharacter public byte ShieldPercentage => this.Struct->CharacterData.ShieldValue; /// - public ExcelResolver ClassJob => new(this.Struct->CharacterData.ClassJob); + public RowRef ClassJob => LuminaUtils.CreateRef(this.Struct->CharacterData.ClassJob); /// public byte Level => this.Struct->CharacterData.Level; @@ -170,7 +172,7 @@ internal unsafe class Character : GameObject, ICharacter public uint NameId => this.Struct->NameId; /// - public ExcelResolver OnlineStatus => new(this.Struct->CharacterData.OnlineStatus); + public RowRef OnlineStatus => LuminaUtils.CreateRef(this.Struct->CharacterData.OnlineStatus); /// /// Gets the status flags. @@ -186,28 +188,28 @@ internal unsafe class Character : GameObject, ICharacter (this.Struct->IsCasting ? StatusFlags.IsCasting : StatusFlags.None); /// - public ExcelResolver? CurrentMount + public RowRef? CurrentMount { get { if (this.Struct->IsNotMounted()) return null; // just for safety. var mountId = this.Struct->Mount.MountId; - return mountId == 0 ? null : new ExcelResolver(mountId); + return mountId == 0 ? null : LuminaUtils.CreateRef(mountId); } } /// - public ExcelResolver? CurrentMinion + public RowRef? CurrentMinion { get { if (this.Struct->CompanionObject != null) - return new ExcelResolver(this.Struct->CompanionObject->BaseId); + return LuminaUtils.CreateRef(this.Struct->CompanionObject->BaseId); // this is only present if a minion is summoned but hidden (e.g. the player's on a mount). var hiddenCompanionId = this.Struct->CompanionData.CompanionId; - return hiddenCompanionId == 0 ? null : new ExcelResolver(hiddenCompanionId); + return hiddenCompanionId == 0 ? null : LuminaUtils.CreateRef(hiddenCompanionId); } } diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs index 34cd31dec..b764431f5 100644 --- a/Dalamud/Game/ClientState/Party/PartyMember.cs +++ b/Dalamud/Game/ClientState/Party/PartyMember.cs @@ -1,13 +1,15 @@ using System.Numerics; using System.Runtime.CompilerServices; +using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.ClientState.Statuses; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Memory; +using Lumina.Excel; + namespace Dalamud.Game.ClientState.Party; /// @@ -71,12 +73,12 @@ public interface IPartyMember /// /// Gets the territory this party member is located in. /// - ExcelResolver Territory { get; } + RowRef Territory { get; } /// /// Gets the World this party member resides in. /// - ExcelResolver World { get; } + RowRef World { get; } /// /// Gets the displayname of this party member. @@ -91,7 +93,7 @@ public interface IPartyMember /// /// Gets the classjob of this party member. /// - ExcelResolver ClassJob { get; } + RowRef ClassJob { get; } /// /// Gets the level of this party member. @@ -169,12 +171,12 @@ internal unsafe class PartyMember : IPartyMember /// /// Gets the territory this party member is located in. /// - public ExcelResolver Territory => new(this.Struct->TerritoryType); + public RowRef Territory => LuminaUtils.CreateRef(this.Struct->TerritoryType); /// /// Gets the World this party member resides in. /// - public ExcelResolver World => new(this.Struct->HomeWorld); + public RowRef World => LuminaUtils.CreateRef(this.Struct->HomeWorld); /// /// Gets the displayname of this party member. @@ -189,7 +191,7 @@ internal unsafe class PartyMember : IPartyMember /// /// Gets the classjob of this party member. /// - public ExcelResolver ClassJob => new(this.Struct->ClassJob); + public RowRef ClassJob => LuminaUtils.CreateRef(this.Struct->ClassJob); /// /// Gets the level of this party member. diff --git a/Dalamud/Game/ClientState/Resolvers/ExcelResolver{T}.cs b/Dalamud/Game/ClientState/Resolvers/ExcelResolver{T}.cs deleted file mode 100644 index 04003d9aa..000000000 --- a/Dalamud/Game/ClientState/Resolvers/ExcelResolver{T}.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Dalamud.Data; - -using Lumina.Excel; - -namespace Dalamud.Game.ClientState.Resolvers; - -/// -/// This object resolves a rowID within an Excel sheet. -/// -/// The type of Lumina sheet to resolve. -public class ExcelResolver where T : ExcelRow -{ - /// - /// Initializes a new instance of the class. - /// - /// The ID of the classJob. - internal ExcelResolver(uint id) - { - this.Id = id; - } - - /// - /// Gets the ID to be resolved. - /// - public uint Id { get; } - - /// - /// Gets GameData linked to this excel row. - /// - public T? GameData => Service.Get().GetExcelSheet()?.GetRow(this.Id); - - /// - /// Gets GameData linked to this excel row with the specified language. - /// - /// The language. - /// The ExcelRow in the specified language. - public T? GetWithLanguage(ClientLanguage language) => Service.Get().GetExcelSheet(language)?.GetRow(this.Id); -} diff --git a/Dalamud/Game/ClientState/Statuses/Status.cs b/Dalamud/Game/ClientState/Statuses/Status.cs index ad1a24b6a..f09d13fb3 100644 --- a/Dalamud/Game/ClientState/Statuses/Status.cs +++ b/Dalamud/Game/ClientState/Statuses/Status.cs @@ -1,6 +1,8 @@ +using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.ClientState.Resolvers; + +using Lumina.Excel; namespace Dalamud.Game.ClientState.Statuses; @@ -31,7 +33,7 @@ public unsafe class Status /// /// Gets the GameData associated with this status. /// - public Lumina.Excel.GeneratedSheets.Status GameData => new ExcelResolver(this.Struct->StatusId).GameData; + public RowRef GameData => LuminaUtils.CreateRef(this.Struct->StatusId); /// /// Gets the parameter value of the status. diff --git a/Dalamud/Game/Gui/ContextMenu/MenuItem.cs b/Dalamud/Game/Gui/ContextMenu/MenuItem.cs index 9b7cc2bc1..df1cb54a7 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuItem.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuItem.cs @@ -1,7 +1,7 @@ using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; namespace Dalamud.Game.Gui.ContextMenu; diff --git a/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs b/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs index 26365ab14..f09b3e105 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs @@ -1,11 +1,12 @@ +using Dalamud.Data; using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Game.ClientState.Resolvers; using Dalamud.Game.Network.Structures.InfoProxy; using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel; +using Lumina.Excel.Sheets; namespace Dalamud.Game.Gui.ContextMenu; @@ -46,7 +47,7 @@ public sealed unsafe class MenuTargetDefault : MenuTarget /// /// Gets the home world id of the target. /// - public ExcelResolver TargetHomeWorld => new((uint)this.Context->TargetHomeWorldId); + public RowRef TargetHomeWorld => LuminaUtils.CreateRef((uint)this.Context->TargetHomeWorldId); /// /// Gets the currently targeted character. Only shows up for specific targets, like friends, party finder listings, or party members. diff --git a/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs b/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs index ba72021ba..1c78c871b 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/JobFlagsExtensions.cs @@ -1,5 +1,5 @@ using Dalamud.Plugin.Services; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; namespace Dalamud.Game.Gui.PartyFinder.Types; diff --git a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs index 3461841ab..09de07e0d 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs @@ -5,7 +5,8 @@ using Dalamud.Data; using Dalamud.Game.Gui.PartyFinder.Internal; using Dalamud.Game.Text.SeStringHandling; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel; +using Lumina.Excel.Sheets; namespace Dalamud.Game.Gui.PartyFinder.Types; @@ -48,7 +49,7 @@ public interface IPartyFinderListing /// /// Gets a list of the classes/jobs that are currently present in the party. /// - IReadOnlyCollection> JobsPresent { get; } + IReadOnlyCollection> JobsPresent { get; } /// /// Gets the ID assigned to this listing by the game's server. @@ -73,17 +74,17 @@ public interface IPartyFinderListing /// /// Gets the world that this listing was created on. /// - Lazy World { get; } + RowRef World { get; } /// /// Gets the home world of the listing's host. /// - Lazy HomeWorld { get; } + RowRef HomeWorld { get; } /// /// Gets the current world of the listing's host. /// - Lazy CurrentWorld { get; } + RowRef CurrentWorld { get; } /// /// Gets the Party Finder category this listing is listed under. @@ -98,7 +99,7 @@ public interface IPartyFinderListing /// /// Gets the duty this listing is for. May be null for non-duty listings. /// - Lazy Duty { get; } + RowRef Duty { get; } /// /// Gets the type of duty this listing is for. @@ -216,12 +217,12 @@ internal class PartyFinderListing : IPartyFinderListing this.ContentId = listing.ContentId; this.Name = SeString.Parse(listing.Name.TakeWhile(b => b != 0).ToArray()); this.Description = SeString.Parse(listing.Description.TakeWhile(b => b != 0).ToArray()); - this.World = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.World)); - this.HomeWorld = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.HomeWorld)); - this.CurrentWorld = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.CurrentWorld)); + this.World = LuminaUtils.CreateRef(listing.World); + this.HomeWorld = LuminaUtils.CreateRef(listing.HomeWorld); + this.CurrentWorld = LuminaUtils.CreateRef(listing.CurrentWorld); this.Category = (DutyCategory)listing.Category; this.RawDuty = listing.Duty; - this.Duty = new Lazy(() => dataManager.GetExcelSheet().GetRow(listing.Duty)); + this.Duty = LuminaUtils.CreateRef(listing.Duty); this.DutyType = (DutyType)listing.DutyType; this.BeginnersWelcome = listing.BeginnersWelcome == 1; this.SecondsRemaining = listing.SecondsRemaining; @@ -231,10 +232,7 @@ internal class PartyFinderListing : IPartyFinderListing this.SlotsFilled = listing.NumSlotsFilled; this.LastPatchHotfixTimestamp = listing.LastPatchHotfixTimestamp; this.JobsPresent = listing.JobsPresent - .Select(id => new Lazy( - () => id == 0 - ? null - : dataManager.GetExcelSheet().GetRow(id))) + .Select(id => LuminaUtils.CreateRef(id)) .ToArray(); } @@ -251,13 +249,13 @@ internal class PartyFinderListing : IPartyFinderListing public SeString Description { get; } /// - public Lazy World { get; } + public RowRef World { get; } /// - public Lazy HomeWorld { get; } + public RowRef HomeWorld { get; } /// - public Lazy CurrentWorld { get; } + public RowRef CurrentWorld { get; } /// public DutyCategory Category { get; } @@ -266,7 +264,7 @@ internal class PartyFinderListing : IPartyFinderListing public ushort RawDuty { get; } /// - public Lazy Duty { get; } + public RowRef Duty { get; } /// public DutyType DutyType { get; } @@ -314,7 +312,7 @@ internal class PartyFinderListing : IPartyFinderListing public IReadOnlyCollection RawJobsPresent => this.jobsPresent; /// - public IReadOnlyCollection> JobsPresent { get; } + public IReadOnlyCollection> JobsPresent { get; } #region Indexers diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs index 7892e8a04..a62de369b 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs @@ -46,7 +46,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader var uploadObject = new UniversalisItemUploadRequest { - WorldId = clientState.LocalPlayer?.CurrentWorld.Id ?? 0, + WorldId = clientState.LocalPlayer?.CurrentWorld.RowId ?? 0, UploaderId = uploader.ToString(), ItemId = request.Listings.FirstOrDefault()?.CatalogId ?? 0, Listings = [], @@ -120,7 +120,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader var taxUploadObject = new UniversalisTaxUploadRequest { - WorldId = clientState.LocalPlayer?.CurrentWorld.Id ?? 0, + WorldId = clientState.LocalPlayer?.CurrentWorld.RowId ?? 0, UploaderId = clientState.LocalContentId.ToString(), TaxData = new UniversalisTaxData { @@ -158,7 +158,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader return; var itemId = purchaseHandler.CatalogId; - var worldId = clientState.LocalPlayer?.CurrentWorld.Id ?? 0; + var worldId = clientState.LocalPlayer?.CurrentWorld.RowId ?? 0; // ==================================================================================== diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 0e87880ec..ad8dbb600 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -16,7 +16,7 @@ using Dalamud.Hooking; using Dalamud.Networking.Http; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI.Info; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Serilog; namespace Dalamud.Game.Network.Internal; @@ -282,21 +282,17 @@ internal unsafe class NetworkHandlers : IInternalDisposableService if (this.configuration.DutyFinderTaskbarFlash) Util.FlashWindow(); - var cfConditionSheet = Service.Get().GetExcelSheet()!; - var cfCondition = cfConditionSheet.GetRow(conditionId); + var cfCondition = LuminaUtils.CreateRef(conditionId); - if (cfCondition == null) + if (!cfCondition.IsValid) { Log.Error("CFC key {ConditionId} not in Lumina data", conditionId); return result; } - var cfcName = cfCondition.Name.ToDalamudString(); + var cfcName = cfCondition.Value.Name.ToDalamudString(); if (cfcName.Payloads.Count == 0) - { cfcName = "Duty Roulette"; - cfCondition.Image = 112324; - } Task.Run(() => { @@ -308,7 +304,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Service.GetNullable()?.Print(b.Build()); } - this.CfPop.InvokeSafely(cfCondition); + this.CfPop.InvokeSafely(cfCondition.Value); }).ContinueWith( task => Log.Error(task.Exception, "CfPop.Invoke failed"), TaskContinuationOptions.OnlyOnFaulted); diff --git a/Dalamud/Game/Network/Structures/InfoProxy/CharacterData.cs b/Dalamud/Game/Network/Structures/InfoProxy/CharacterData.cs index 96de1c3b2..fcb46b0c3 100644 --- a/Dalamud/Game/Network/Structures/InfoProxy/CharacterData.cs +++ b/Dalamud/Game/Network/Structures/InfoProxy/CharacterData.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; -using Dalamud.Game.ClientState.Resolvers; -using Dalamud.Memory; +using Dalamud.Data; using FFXIVClientStructs.FFXIV.Client.UI.Info; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel; +using Lumina.Excel.Sheets; namespace Dalamud.Game.Network.Structures.InfoProxy; @@ -92,15 +92,15 @@ public unsafe class CharacterData /// /// Gets the applicable statues of the character. /// - public IReadOnlyList> Statuses + public IReadOnlyList> Statuses { get { - var statuses = new List>(); + var statuses = new List>(); for (var i = 0; i < 64; i++) { if ((this.StatusMask & (1UL << i)) != 0) - statuses.Add(new((uint)i)); + statuses.Add(LuminaUtils.CreateRef((uint)i)); } return statuses; @@ -125,22 +125,22 @@ public unsafe class CharacterData /// /// Gets the current world of the character. /// - public ExcelResolver CurrentWorld => new(this.Struct->CurrentWorld); + public RowRef CurrentWorld => LuminaUtils.CreateRef(this.Struct->CurrentWorld); /// /// Gets the home world of the character. /// - public ExcelResolver HomeWorld => new(this.Struct->HomeWorld); + public RowRef HomeWorld => LuminaUtils.CreateRef(this.Struct->HomeWorld); /// /// Gets the location of the character. /// - public ExcelResolver Location => new(this.Struct->Location); + public RowRef Location => LuminaUtils.CreateRef(this.Struct->Location); /// /// Gets the grand company of the character. /// - public ExcelResolver GrandCompany => new((uint)this.Struct->GrandCompany); + public RowRef GrandCompany => LuminaUtils.CreateRef((uint)this.Struct->GrandCompany); /// /// Gets the primary client language of the character. @@ -178,7 +178,7 @@ public unsafe class CharacterData /// /// Gets the job of the character. /// - public ExcelResolver ClassJob => new(this.Struct->Job); + public RowRef ClassJob => LuminaUtils.CreateRef(this.Struct->Job); /// /// Gets the name of the character. diff --git a/Dalamud/Game/Text/SeStringHandling/Payload.cs b/Dalamud/Game/Text/SeStringHandling/Payload.cs index b876598de..a38a8271d 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payload.cs @@ -38,13 +38,6 @@ public abstract partial class Payload /// public bool Dirty { get; protected set; } = true; - /// - /// Gets the Lumina instance to use for any necessary data lookups. - /// - [JsonIgnore] - // TODO: We should refactor this. It should not be possible to get IDataManager through here. - protected IDataManager DataResolver => Service.Get(); - /// /// Decodes a binary representation of a payload into its corresponding nice object payload. /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs index afc1cdc01..b038deb6f 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/AutoTranslatePayload.cs @@ -2,7 +2,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel.Sheets; +using Lumina.Text.ReadOnly; + using Newtonsoft.Json; using Serilog; @@ -106,28 +110,28 @@ public class AutoTranslatePayload : Payload, ITextProvider this.Key = GetInteger(reader); } + private static ReadOnlySeString ResolveTextCommand(TextCommand command) + { + // TextCommands prioritize the `Alias` field, if it not empty + // Example for this is /rangerpose2l which becomes /blackrangerposeb in chat + return !command.Alias.IsEmpty ? command.Alias : command.Command; + } + private string Resolve() { string value = null; - var sheet = this.DataResolver.GetExcelSheet(); + var excelModule = Service.Get().Excel; + var completionSheet = excelModule.GetSheet(); - Completion row = null; - try - { - // try to get the row in the Completion table itself, because this is 'easiest' - // The row may not exist at all (if the Key is for another table), or it could be the wrong row - // (again, if it's meant for another table) - row = sheet.GetRow(this.Key); - } - catch - { - } // don't care, row will be null + // try to get the row in the Completion table itself, because this is 'easiest' + // The row may not exist at all (if the Key is for another table), or it could be the wrong row + // (again, if it's meant for another table) - if (row?.Group == this.Group) + if (completionSheet.GetRowOrDefault(this.Key) is { } completion && completion.Group == this.Group) { // if the row exists in this table and the group matches, this is actually the correct data - value = row.Text; + value = completion.Text.ExtractText(); } else { @@ -135,34 +139,34 @@ public class AutoTranslatePayload : Payload, ITextProvider { // we need to get the linked table and do the lookup there instead // in this case, there will only be one entry for this group id - row = sheet.First(r => r.Group == this.Group); + var row = completionSheet.First(r => r.Group == this.Group); // many of the names contain valid id ranges after the table name, but we don't need those - var actualTableName = row.LookupTable.RawString.Split('[')[0]; + var actualTableName = row.LookupTable.ExtractText().Split('[')[0]; var name = actualTableName switch { - "Action" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "ActionComboRoute" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "BuddyAction" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "ClassJob" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "Companion" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Singular, - "CraftAction" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "GeneralAction" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "GuardianDeity" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "MainCommand" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "Mount" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Singular, - "Pet" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "PetAction" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "PetMirage" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "PlaceName" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, - "Race" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Masculine, - "TextCommand" => this.ResolveTextCommand(), - "Tribe" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Masculine, - "Weather" => this.DataResolver.GetExcelSheet().GetRow(this.Key).Name, + "Action" => excelModule.GetSheet().GetRow(this.Key).Name, + "ActionComboRoute" => excelModule.GetSheet().GetRow(this.Key).Name, + "BuddyAction" => excelModule.GetSheet().GetRow(this.Key).Name, + "ClassJob" => excelModule.GetSheet().GetRow(this.Key).Name, + "Companion" => excelModule.GetSheet().GetRow(this.Key).Singular, + "CraftAction" => excelModule.GetSheet().GetRow(this.Key).Name, + "GeneralAction" => excelModule.GetSheet().GetRow(this.Key).Name, + "GuardianDeity" => excelModule.GetSheet().GetRow(this.Key).Name, + "MainCommand" => excelModule.GetSheet().GetRow(this.Key).Name, + "Mount" => excelModule.GetSheet().GetRow(this.Key).Singular, + "Pet" => excelModule.GetSheet().GetRow(this.Key).Name, + "PetAction" => excelModule.GetSheet().GetRow(this.Key).Name, + "PetMirage" => excelModule.GetSheet().GetRow(this.Key).Name, + "PlaceName" => excelModule.GetSheet().GetRow(this.Key).Name, + "Race" => excelModule.GetSheet().GetRow(this.Key).Masculine, + "TextCommand" => AutoTranslatePayload.ResolveTextCommand(excelModule.GetSheet().GetRow(this.Key)), + "Tribe" => excelModule.GetSheet().GetRow(this.Key).Masculine, + "Weather" => excelModule.GetSheet().GetRow(this.Key).Name, _ => throw new Exception(actualTableName), }; - value = name; + value = name.ExtractText(); } catch (Exception e) { @@ -172,12 +176,4 @@ public class AutoTranslatePayload : Payload, ITextProvider return value; } - - private Lumina.Text.SeString ResolveTextCommand() - { - // TextCommands prioritize the `Alias` field, if it not empty - // Example for this is /rangerpose2l which becomes /blackrangerposeb in chat - var result = this.DataResolver.GetExcelSheet().GetRow(this.Key); - return result.Alias.Payloads.Count > 0 ? result.Alias : result.Command; - } } diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs index e3415b2dc..c31707ff2 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/ItemPayload.cs @@ -3,9 +3,10 @@ using System.IO; using System.Linq; using System.Text; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; -using Serilog; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -14,8 +15,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class ItemPayload : Payload { - private Item? item; - // mainly to allow overriding the name (for things like owo) // TODO: even though this is present in some item links, it may not really have a use at all // For things like owo, changing the text payload is probably correct, whereas changing the @@ -131,27 +130,13 @@ public class ItemPayload : Payload public uint RawItemId => this.rawItemId; /// - /// Gets the underlying Lumina Item represented by this payload. + /// Gets the underlying Lumina data represented by this payload. This is either a Item or EventItem . /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public Item? Item - { - get - { - // TODO(goat): This should be revamped/removed on an API level change. - if (this.Kind == ItemKind.EventItem) - { - Log.Warning("Event items cannot be fetched from the ItemPayload"); - return null; - } - - this.item ??= this.DataResolver.GetExcelSheet()!.GetRow(this.ItemId); - return this.item; - } - } + public RowRef Item => + this.Kind == ItemKind.EventItem + ? (RowRef)LuminaUtils.CreateRef(this.ItemId) + : (RowRef)LuminaUtils.CreateRef(this.ItemId); /// /// Gets a value indicating whether or not this item link is for a high-quality version of the item. @@ -183,7 +168,8 @@ public class ItemPayload : Payload /// public override string ToString() { - return $"{this.Type} - ItemId: {this.ItemId}, Kind: {this.Kind}, Name: {this.displayName ?? this.Item?.Name}"; + var name = this.displayName ?? (this.Item.GetValueOrDefault()?.Name ?? this.Item.GetValueOrDefault()?.Name)?.ExtractText(); + return $"{this.Type} - ItemId: {this.ItemId}, Kind: {this.Kind}, Name: {name}"; } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs index 7d975b347..7b672d07a 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/MapLinkPayload.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; using System.IO; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -11,11 +14,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class MapLinkPayload : Payload { - private Map map; - private TerritoryType territoryType; - private string placeNameRegion; - private string placeName; - [JsonProperty] private uint territoryTypeId; @@ -38,8 +36,8 @@ public class MapLinkPayload : Payload // this fudge is necessary basically to ensure we don't shift down a full tenth // because essentially values are truncated instead of rounded, so 3.09999f will become // 3.0f and not 3.1f - this.RawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, this.Map.SizeFactor, this.Map.OffsetX); - this.RawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, this.Map.SizeFactor, this.Map.OffsetY); + this.RawX = this.ConvertMapCoordinateToRawPosition(niceXCoord + fudgeFactor, this.Map.Value.SizeFactor, this.Map.Value.OffsetX); + this.RawY = this.ConvertMapCoordinateToRawPosition(niceYCoord + fudgeFactor, this.Map.Value.SizeFactor, this.Map.Value.OffsetY); } /// @@ -72,20 +70,14 @@ public class MapLinkPayload : Payload /// /// Gets the Map specified for this map link. /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public Map Map => this.map ??= this.DataResolver.GetExcelSheet().GetRow(this.mapId); + public RowRef Map => LuminaUtils.CreateRef(this.mapId); /// /// Gets the TerritoryType specified for this map link. /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public TerritoryType TerritoryType => this.territoryType ??= this.DataResolver.GetExcelSheet().GetRow(this.territoryTypeId); + public RowRef TerritoryType => LuminaUtils.CreateRef(this.territoryTypeId); /// /// Gets the internal x-coordinate for this map position. @@ -102,13 +94,13 @@ public class MapLinkPayload : Payload /// /// Gets the readable x-coordinate position for this map link. This value is approximate and unrounded. /// - public float XCoord => this.ConvertRawPositionToMapCoordinate(this.RawX, this.Map.SizeFactor, this.Map.OffsetX); + public float XCoord => this.ConvertRawPositionToMapCoordinate(this.RawX, this.Map.Value.SizeFactor, this.Map.Value.OffsetX); /// /// Gets the readable y-coordinate position for this map link. This value is approximate and unrounded. /// [JsonIgnore] - public float YCoord => this.ConvertRawPositionToMapCoordinate(this.RawY, this.Map.SizeFactor, this.Map.OffsetY); + public float YCoord => this.ConvertRawPositionToMapCoordinate(this.RawY, this.Map.Value.SizeFactor, this.Map.Value.OffsetY); // there is no Z; it's purely in the text payload where applicable @@ -143,18 +135,18 @@ public class MapLinkPayload : Payload /// Gets the region name for this map link. This corresponds to the upper zone name found in the actual in-game map UI. eg, "La Noscea". /// [JsonIgnore] - public string PlaceNameRegion => this.placeNameRegion ??= this.TerritoryType.PlaceNameRegion.Value?.Name; + public string PlaceNameRegion => this.TerritoryType.Value.PlaceNameRegion.Value.Name.ExtractText(); /// /// Gets the place name for this map link. This corresponds to the lower zone name found in the actual in-game map UI. eg, "Limsa Lominsa Upper Decks". /// [JsonIgnore] - public string PlaceName => this.placeName ??= this.TerritoryType.PlaceName.Value?.Name; + public string PlaceName => this.TerritoryType.Value.PlaceName.Value.Name.ExtractText(); /// /// Gets the data string for this map link, for use by internal game functions that take a string variant and not a binary payload. /// - public string DataString => $"m:{this.TerritoryType.RowId},{this.Map.RowId},{this.RawX},{this.RawY}"; + public string DataString => $"m:{this.territoryTypeId},{this.mapId},{this.RawX},{this.RawY}"; /// public override string ToString() diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs index b9d6bc896..07a13e5a3 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/PlayerPayload.cs @@ -2,7 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Text; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -12,8 +15,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class PlayerPayload : Payload { - private World world; - [JsonProperty] private uint serverId; @@ -43,11 +44,8 @@ public class PlayerPayload : Payload /// /// Gets the Lumina object representing the player's home server. /// - /// - /// Value is evaluated lazily and cached. - /// [JsonIgnore] - public World World => this.world ??= this.DataResolver.GetExcelSheet().GetRow(this.serverId); + public RowRef World => LuminaUtils.CreateRef(this.serverId); /// /// Gets or sets the player's displayed name. This does not contain the server name. @@ -72,7 +70,7 @@ public class PlayerPayload : Payload /// The world name will always be present. /// [JsonIgnore] - public string DisplayedName => $"{this.PlayerName}{(char)SeIconChar.CrossWorld}{this.World.Name}"; + public string DisplayedName => $"{this.PlayerName}{(char)SeIconChar.CrossWorld}{this.World.ValueNullable?.Name}"; /// public override PayloadType Type => PayloadType.Player; @@ -80,7 +78,7 @@ public class PlayerPayload : Payload /// public override string ToString() { - return $"{this.Type} - PlayerName: {this.PlayerName}, ServerId: {this.serverId}, ServerName: {this.World.Name}"; + return $"{this.Type} - PlayerName: {this.PlayerName}, ServerId: {this.serverId}, ServerName: {this.World.ValueNullable?.Name}"; } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs index e5b9e635e..19d494d8a 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/QuestPayload.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; using System.IO; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -11,8 +14,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class QuestPayload : Payload { - private Quest quest; - [JsonProperty] private uint questId; @@ -40,16 +41,13 @@ public class QuestPayload : Payload /// /// Gets the underlying Lumina Quest represented by this payload. /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public Quest Quest => this.quest ??= this.DataResolver.GetExcelSheet().GetRow(this.questId); + public RowRef Quest => LuminaUtils.CreateRef(this.questId); /// public override string ToString() { - return $"{this.Type} - QuestId: {this.questId}, Name: {this.Quest?.Name ?? "QUEST NOT FOUND"}"; + return $"{this.Type} - QuestId: {this.questId}, Name: {this.Quest.ValueNullable?.Name.ExtractText() ?? "QUEST NOT FOUND"}"; } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs index 3e10f7659..d102dfab6 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/StatusPayload.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; using System.IO; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -11,8 +14,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class StatusPayload : Payload { - private Status status; - [JsonProperty] private uint statusId; @@ -40,16 +41,13 @@ public class StatusPayload : Payload /// /// Gets the Lumina Status object represented by this payload. /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public Status Status => this.status ??= this.DataResolver.GetExcelSheet().GetRow(this.statusId); + public RowRef Status => LuminaUtils.CreateRef(this.statusId); /// public override string ToString() { - return $"{this.Type} - StatusId: {this.statusId}, Name: {this.Status.Name}"; + return $"{this.Type} - StatusId: {this.statusId}, Name: {this.Status.ValueNullable?.Name}"; } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs index 1cd96a81a..995c20211 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/UIForegroundPayload.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; using System.IO; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -11,8 +14,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class UIForegroundPayload : Payload { - private UIColor color; - [JsonProperty] private ushort colorKey; @@ -51,11 +52,8 @@ public class UIForegroundPayload : Payload /// /// Gets a Lumina UIColor object representing this payload. The actual color data is at UIColor.UIForeground. /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public UIColor UIColor => this.color ??= this.DataResolver.GetExcelSheet().GetRow(this.colorKey); + public RowRef UIColor => LuminaUtils.CreateRef(this.colorKey); /// /// Gets or sets the color key used as a lookup in the UIColor table for this foreground color. @@ -63,15 +61,11 @@ public class UIForegroundPayload : Payload [JsonIgnore] public ushort ColorKey { - get - { - return this.colorKey; - } + get => this.colorKey; set { this.colorKey = value; - this.color = null; this.Dirty = true; } } @@ -80,13 +74,13 @@ public class UIForegroundPayload : Payload /// Gets the Red/Green/Blue/Alpha values for this foreground color, encoded as a typical hex color. /// [JsonIgnore] - public uint RGBA => this.UIColor.UIForeground; + public uint RGBA => this.UIColor.Value.UIForeground; /// /// Gets the ABGR value for this foreground color, as ImGui requires it in PushColor. /// [JsonIgnore] - public uint ABGR => Interface.ColorHelpers.SwapEndianness(this.UIColor.UIForeground); + public uint ABGR => Interface.ColorHelpers.SwapEndianness(this.UIColor.Value.UIForeground); /// public override string ToString() diff --git a/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs b/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs index 68ed983ff..3049ccac3 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payloads/UIGlowPayload.cs @@ -1,7 +1,10 @@ using System.Collections.Generic; using System.IO; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Data; + +using Lumina.Excel; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling.Payloads; @@ -11,8 +14,6 @@ namespace Dalamud.Game.Text.SeStringHandling.Payloads; /// public class UIGlowPayload : Payload { - private UIColor color; - [JsonProperty] private ushort colorKey; @@ -57,7 +58,6 @@ public class UIGlowPayload : Payload set { this.colorKey = value; - this.color = null; this.Dirty = true; } } @@ -71,22 +71,19 @@ public class UIGlowPayload : Payload /// Gets the Red/Green/Blue/Alpha values for this glow color, encoded as a typical hex color. /// [JsonIgnore] - public uint RGBA => this.UIColor.UIGlow; + public uint RGBA => this.UIColor.Value.UIGlow; /// /// Gets the ABGR value for this glow color, as ImGui requires it in PushColor. /// [JsonIgnore] - public uint ABGR => Interface.ColorHelpers.SwapEndianness(this.UIColor.UIGlow); + public uint ABGR => Interface.ColorHelpers.SwapEndianness(this.UIColor.Value.UIGlow); /// /// Gets a Lumina UIColor object representing this payload. The actual color data is at UIColor.UIGlow. /// - /// - /// The value is evaluated lazily and cached. - /// [JsonIgnore] - public UIColor UIColor => this.color ??= this.DataResolver.GetExcelSheet().GetRow(this.colorKey); + public RowRef UIColor => LuminaUtils.CreateRef(this.colorKey); /// public override string ToString() diff --git a/Dalamud/Game/Text/SeStringHandling/SeString.cs b/Dalamud/Game/Text/SeStringHandling/SeString.cs index 3b83aed0c..baae181e3 100644 --- a/Dalamud/Game/Text/SeStringHandling/SeString.cs +++ b/Dalamud/Game/Text/SeStringHandling/SeString.cs @@ -7,7 +7,7 @@ using System.Text; using Dalamud.Data; using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Utility; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Newtonsoft.Json; namespace Dalamud.Game.Text.SeStringHandling; @@ -200,12 +200,12 @@ public class SeString case ItemPayload.ItemKind.Normal: case ItemPayload.ItemKind.Collectible: case ItemPayload.ItemKind.Hq: - var item = data.GetExcelSheet()?.GetRow(itemId); - displayName = item?.Name; + var item = data.GetExcelSheet()?.GetRowOrDefault(itemId); + displayName = item?.Name.ExtractText(); rarity = item?.Rarity ?? 1; break; case ItemPayload.ItemKind.EventItem: - displayName = data.GetExcelSheet()?.GetRow(itemId)?.Name; + displayName = data.GetExcelSheet()?.GetRowOrDefault(itemId)?.Name.ExtractText(); break; default: throw new ArgumentOutOfRangeException(nameof(kind), kind, null); @@ -251,7 +251,7 @@ public class SeString /// An SeString containing all the payloads necessary to display an item link in the chat log. public static SeString CreateItemLink(Item item, bool isHq, string? displayNameOverride = null) { - return CreateItemLink(item.RowId, isHq, displayNameOverride ?? item.Name); + return CreateItemLink(item.RowId, isHq, displayNameOverride ?? item.Name.ExtractText()); } /// @@ -360,15 +360,14 @@ public class SeString var mapSheet = data.GetExcelSheet(); var matches = data.GetExcelSheet() - .Where(row => row.Name.ToString().ToLowerInvariant() == placeName.ToLowerInvariant()) - .ToArray(); + .Where(row => row.Name.ExtractText().Equals(placeName, StringComparison.InvariantCultureIgnoreCase)); foreach (var place in matches) { - var map = mapSheet.FirstOrDefault(row => row.PlaceName.Row == place.RowId); - if (map != null && map.TerritoryType.Row != 0) + var map = mapSheet.Cast().FirstOrDefault(row => row!.Value.PlaceName.RowId == place.RowId); + if (map.HasValue && map.Value.TerritoryType.RowId != 0) { - return CreateMapLinkWithInstance(map.TerritoryType.Row, map.RowId, instance, xCoord, yCoord, fudgeFactor); + return CreateMapLinkWithInstance(map.Value.TerritoryType.RowId, map.Value.RowId, instance, xCoord, yCoord, fudgeFactor); } } diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs index 5a7e87e1b..ddff55923 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringColorStackSet.cs @@ -5,7 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Component.Text; using Lumina.Excel; -using Lumina.Excel.GeneratedSheets2; +using Lumina.Excel.Sheets; using Lumina.Text.Expressions; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs index 8afc1b473..9d62de193 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs @@ -18,7 +18,7 @@ using FFXIVClientStructs.FFXIV.Client.UI; using ImGuiNET; -using Lumina.Excel.GeneratedSheets2; +using Lumina.Excel.Sheets; using Lumina.Text.Parse; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs index 606c49c49..9a3b231e5 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.ClientState.Aetherytes; +using Dalamud.Game.ClientState.Aetherytes; using ImGuiNET; @@ -56,7 +56,7 @@ internal class AetherytesWidget : IDataWindowWidget ImGui.TextUnformatted($"{i}"); ImGui.TableNextColumn(); // Name - ImGui.TextUnformatted($"{info.AetheryteData.GameData?.PlaceName.Value?.Name}"); + ImGui.TextUnformatted($"{info.AetheryteData.ValueNullable?.PlaceName.ValueNullable?.Name}"); ImGui.TableNextColumn(); // ID ImGui.TextUnformatted($"{info.AetheryteId}"); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs index c16cc34e1..f30585406 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.ClientState; +using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.JobGauge; using Dalamud.Game.ClientState.JobGauge.Types; using Dalamud.Utility; @@ -39,7 +39,7 @@ internal class GaugeWidget : IDataWindowWidget return; } - var jobID = player.ClassJob.Id; + var jobID = player.ClassJob.RowId; JobGaugeBase? gauge = jobID switch { 19 => jobGauges.Get(), diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs index 963138bec..761dc49a8 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs @@ -1,4 +1,4 @@ -using System.Numerics; +using System.Numerics; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; @@ -56,8 +56,8 @@ internal class ObjectTableWidget : IDataWindowWidget { stateString += $"ObjectTableLen: {objectTable.Length}\n"; stateString += $"LocalPlayerName: {clientState.LocalPlayer.Name}\n"; - stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.GameData?.Name : clientState.LocalPlayer.CurrentWorld.Id.ToString())}\n"; - stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.GameData?.Name : clientState.LocalPlayer.HomeWorld.Id.ToString())}\n"; + stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.ValueNullable?.Name : clientState.LocalPlayer.CurrentWorld.RowId.ToString())}\n"; + stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.ValueNullable?.Name : clientState.LocalPlayer.HomeWorld.RowId.ToString())}\n"; stateString += $"LocalCID: {clientState.LocalContentId:X}\n"; stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n"; stateString += $"TerritoryType: {clientState.TerritoryType}\n\n"; diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs index 17aba0c71..f4b76942f 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringRendererTestWidget.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Numerics; using System.Text; @@ -16,7 +16,8 @@ using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; -using Lumina.Excel.GeneratedSheets2; +using Lumina.Excel; +using Lumina.Excel.Sheets; using Lumina.Text; using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; @@ -33,7 +34,7 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget private static readonly string[] ThemeNames = ["Dark", "Light", "Classic FF", "Clear Blue"]; private ImVectorWrapper testStringBuffer; private string testString = string.Empty; - private Addon[]? addons; + private ExcelSheet addons; private ReadOnlySeString? logkind; private SeStringDrawParams style; private bool interactable; @@ -53,7 +54,7 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget public void Load() { this.style = new() { GetEntity = this.GetEntity }; - this.addons = null; + this.addons = Service.Get().GetExcelSheet(); this.logkind = null; this.testString = string.Empty; this.interactable = this.useEntity = true; @@ -155,9 +156,9 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget if (this.logkind is null) { var tt = new SeStringBuilder(); - foreach (var uc in Service.Get().GetExcelSheet()!) + foreach (var uc in Service.Get().GetExcelSheet()) { - var ucsp = uc.Format.AsReadOnly().AsSpan(); + var ucsp = uc.Format.AsSpan(); if (ucsp.IsEmpty) continue; @@ -184,7 +185,6 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget if (ImGui.CollapsingHeader("Addon Table")) { - this.addons ??= Service.Get().GetExcelSheet()!.ToArray(); if (ImGui.BeginTable("Addon Sheet", 3)) { ImGui.TableSetupScrollFreeze(0, 1); @@ -197,25 +197,27 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget ImGui.TableHeadersRow(); var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper()); - clipper.Begin(this.addons.Length); + clipper.Begin(this.addons.Count); while (clipper.Step()) { for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + var row = this.addons.GetRowAt(i); + ImGui.TableNextRow(); ImGui.PushID(i); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted($"{this.addons[i].RowId}"); + ImGui.TextUnformatted($"{row.RowId}"); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); - ImGuiHelpers.SeStringWrapped(this.addons[i].Text.AsReadOnly(), this.style); + ImGuiHelpers.SeStringWrapped(row.Text, this.style); ImGui.TableNextColumn(); if (ImGui.Button("Print to Chat")) - Service.Get().Print(this.addons[i].Text.ToDalamudString()); + Service.Get().Print(row.Text.ToDalamudString()); ImGui.PopID(); } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs index 575bce23c..6b57f7a6a 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs @@ -1,4 +1,4 @@ -using System.Buffers.Binary; +using System.Buffers.Binary; using System.Linq; using System.Numerics; using System.Text; @@ -12,7 +12,8 @@ using Dalamud.Storage.Assets; using ImGuiNET; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel; +using Lumina.Excel.Sheets; namespace Dalamud.Interface.Internal.Windows.Data.Widgets; @@ -21,7 +22,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// internal class UiColorWidget : IDataWindowWidget { - private UIColor[]? colors; + private ExcelSheet colors; /// public string[]? CommandShortcuts { get; init; } = ["uicolor"]; @@ -36,15 +37,12 @@ internal class UiColorWidget : IDataWindowWidget public void Load() { this.Ready = true; - this.colors = null; + this.colors = Service.Get().GetExcelSheet(); } /// public unsafe void Draw() { - this.colors ??= Service.Get().GetExcelSheet()?.ToArray(); - if (this.colors is null) return; - Service.Get().CompileAndDrawWrapped( "· Color notation is #" + "RR" + @@ -73,12 +71,24 @@ internal class UiColorWidget : IDataWindowWidget ImGui.TableHeadersRow(); var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper()); - clipper.Begin(this.colors.Length, ImGui.GetFrameHeightWithSpacing()); + clipper.Begin(this.colors.Count, ImGui.GetFrameHeightWithSpacing()); while (clipper.Step()) { for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - var id = this.colors[i].RowId; + var row = this.colors.GetRowAt(i); + UIColor? adjacentRow = null; + if (i + 1 < this.colors.Count) + { + var adjRow = this.colors.GetRowAt(i + 1); + if (adjRow.RowId == row.RowId + 1) + { + adjacentRow = adjRow; + } + } + + var id = row.RowId; + ImGui.TableNextRow(); ImGui.TableNextColumn(); @@ -88,33 +98,33 @@ internal class UiColorWidget : IDataWindowWidget ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.PushID($"row{id}_col1"); - if (this.DrawColorColumn(this.colors[i].UIForeground) && - i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1) - DrawEdgePreview(id, this.colors[i].UIForeground, this.colors[i + 1].UIForeground); + if (this.DrawColorColumn(row.UIForeground) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.UIForeground, adjacentRow.Value.UIForeground); ImGui.PopID(); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.PushID($"row{id}_col2"); - if (this.DrawColorColumn(this.colors[i].UIGlow) && - i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1) - DrawEdgePreview(id, this.colors[i].UIGlow, this.colors[i + 1].UIGlow); + if (this.DrawColorColumn(row.UIGlow) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.UIGlow, adjacentRow.Value.UIGlow); ImGui.PopID(); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.PushID($"row{id}_col3"); - if (this.DrawColorColumn(this.colors[i].Unknown2) && - i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1) - DrawEdgePreview(id, this.colors[i].Unknown2, this.colors[i + 1].Unknown2); + if (this.DrawColorColumn(row.Unknown0) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.Unknown0, adjacentRow.Value.Unknown0); ImGui.PopID(); ImGui.TableNextColumn(); ImGui.AlignTextToFramePadding(); ImGui.PushID($"row{id}_col4"); - if (this.DrawColorColumn(this.colors[i].Unknown3) && - i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1) - DrawEdgePreview(id, this.colors[i].Unknown3, this.colors[i + 1].Unknown3); + if (this.DrawColorColumn(row.Unknown1) && + adjacentRow.HasValue) + DrawEdgePreview(id, row.Unknown1, adjacentRow.Value.Unknown1); ImGui.PopID(); } } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs index 3bc91088d..5bcd1845c 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs @@ -7,10 +7,9 @@ using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.Gui.ContextMenu; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Utility; using ImGuiNET; using Lumina.Excel; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Serilog; namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; @@ -45,9 +44,9 @@ internal class ContextMenuAgingStep : IAgingStep { var contextMenu = Service.Get(); var dataMgr = Service.Get(); - this.itemSheet = dataMgr.GetExcelSheet()!; - this.materiaSheet = dataMgr.GetExcelSheet()!; - this.stainSheet = dataMgr.GetExcelSheet()!; + this.itemSheet = dataMgr.GetExcelSheet(); + this.materiaSheet = dataMgr.GetExcelSheet(); + this.stainSheet = dataMgr.GetExcelSheet(); ImGui.Text(this.currentSubStep.ToString()); @@ -83,7 +82,7 @@ internal class ContextMenuAgingStep : IAgingStep case SubStep.TestDefault: if (this.targetCharacter is { } character) { - ImGui.Text($"Did you click \"{character.Name}\" ({character.ClassJob.GameData!.Abbreviation.ToDalamudString()})?"); + ImGui.Text($"Did you click \"{character.Name}\" ({character.ClassJob.Value.Abbreviation.ExtractText()})?"); if (ImGui.Button("Yes")) this.currentSubStep++; @@ -146,7 +145,7 @@ internal class ContextMenuAgingStep : IAgingStep var targetItem = (a.Target as MenuTargetInventory)!.TargetItem; if (targetItem is { } item) { - name = (this.itemSheet.GetRow(item.ItemId)?.Name.ToDalamudString() ?? $"Unknown ({item.ItemId})") + (item.IsHq ? $" {SeIconChar.HighQuality.ToIconString()}" : string.Empty); + name = (this.itemSheet.GetRowOrDefault(item.ItemId)?.Name.ExtractText() ?? $"Unknown ({item.ItemId})") + (item.IsHq ? $" {SeIconChar.HighQuality.ToIconString()}" : string.Empty); count = item.Quantity; } else @@ -194,7 +193,7 @@ internal class ContextMenuAgingStep : IAgingStep { var b = new StringBuilder(); b.AppendLine($"Target: {targetDefault.TargetName}"); - b.AppendLine($"Home World: {targetDefault.TargetHomeWorld.GameData?.Name.ToDalamudString() ?? "Unknown"} ({targetDefault.TargetHomeWorld.Id})"); + b.AppendLine($"Home World: {targetDefault.TargetHomeWorld.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({targetDefault.TargetHomeWorld.RowId})"); b.AppendLine($"Content Id: 0x{targetDefault.TargetContentId:X8}"); b.AppendLine($"Object Id: 0x{targetDefault.TargetObjectId:X8}"); Log.Verbose(b.ToString()); @@ -209,20 +208,20 @@ internal class ContextMenuAgingStep : IAgingStep b.AppendLine($"Content Id: 0x{character.ContentId:X8}"); b.AppendLine($"FC Tag: {character.FCTag}"); - b.AppendLine($"Job: {character.ClassJob.GameData?.Abbreviation.ToDalamudString() ?? "Unknown"} ({character.ClassJob.Id})"); - b.AppendLine($"Statuses: {string.Join(", ", character.Statuses.Select(s => s.GameData?.Name.ToDalamudString() ?? s.Id.ToString()))}"); - b.AppendLine($"Home World: {character.HomeWorld.GameData?.Name.ToDalamudString() ?? "Unknown"} ({character.HomeWorld.Id})"); - b.AppendLine($"Current World: {character.CurrentWorld.GameData?.Name.ToDalamudString() ?? "Unknown"} ({character.CurrentWorld.Id})"); + b.AppendLine($"Job: {character.ClassJob.ValueNullable?.Abbreviation.ExtractText() ?? "Unknown"} ({character.ClassJob.RowId})"); + b.AppendLine($"Statuses: {string.Join(", ", character.Statuses.Select(s => s.ValueNullable?.Name.ExtractText() ?? s.RowId.ToString()))}"); + b.AppendLine($"Home World: {character.HomeWorld.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({character.HomeWorld.RowId})"); + b.AppendLine($"Current World: {character.CurrentWorld.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({character.CurrentWorld.RowId})"); b.AppendLine($"Is From Other Server: {character.IsFromOtherServer}"); b.Append("Location: "); - if (character.Location.GameData is { } location) - b.Append($"{location.PlaceNameRegion.Value?.Name.ToDalamudString() ?? "Unknown"}/{location.PlaceNameZone.Value?.Name.ToDalamudString() ?? "Unknown"}/{location.PlaceName.Value?.Name.ToDalamudString() ?? "Unknown"}"); + if (character.Location.ValueNullable is { } location) + b.Append($"{location.PlaceNameRegion.ValueNullable?.Name.ExtractText() ?? "Unknown"}/{location.PlaceNameZone.ValueNullable?.Name.ExtractText() ?? "Unknown"}/{location.PlaceName.ValueNullable?.Name.ExtractText() ?? "Unknown"}"); else b.Append("Unknown"); - b.AppendLine($" ({character.Location.Id})"); + b.AppendLine($" ({character.Location.RowId})"); - b.AppendLine($"Grand Company: {character.GrandCompany.GameData?.Name.ToDalamudString() ?? "Unknown"} ({character.GrandCompany.Id})"); + b.AppendLine($"Grand Company: {character.GrandCompany.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({character.GrandCompany.RowId})"); b.AppendLine($"Client Language: {character.ClientLanguage}"); b.AppendLine($"Languages: {string.Join(", ", character.Languages)}"); b.AppendLine($"Gender: {character.Gender}"); @@ -241,7 +240,7 @@ internal class ContextMenuAgingStep : IAgingStep if (targetInventory.TargetItem is { } item) { var b = new StringBuilder(); - b.AppendLine($"Item: {(item.IsEmpty ? "None" : this.itemSheet.GetRow(item.ItemId)?.Name.ToDalamudString())} ({item.ItemId})"); + b.AppendLine($"Item: {(item.IsEmpty ? "None" : this.itemSheet.GetRowOrDefault(item.ItemId)?.Name.ExtractText())} ({item.ItemId})"); b.AppendLine($"Container: {item.ContainerType}"); b.AppendLine($"Slot: {item.InventorySlot}"); b.AppendLine($"Quantity: {item.Quantity}"); @@ -259,7 +258,7 @@ internal class ContextMenuAgingStep : IAgingStep Log.Verbose($"{materiaId} {materiaGrade}"); if (this.materiaSheet.GetRow(materiaId) is { } materia && materia.Item[materiaGrade].Value is { } materiaItem) - materias.Add($"{materiaItem.Name.ToDalamudString()}"); + materias.Add($"{materiaItem.Name.ExtractText()}"); else materias.Add($"Unknown (Id: {materiaId}, Grade: {materiaGrade})"); } @@ -275,7 +274,7 @@ internal class ContextMenuAgingStep : IAgingStep var stainId = item.Stains[i]; if (stainId != 0) { - var stainName = this.stainSheet.GetRow(stainId)?.Name.ToDalamudString().ToString() ?? "Unknown"; + var stainName = this.stainSheet.GetRowOrDefault(stainId)?.Name.ExtractText() ?? "Unknown"; b.AppendLine($" Stain {i + 1}: {stainName} ({stainId})"); } else @@ -285,13 +284,13 @@ internal class ContextMenuAgingStep : IAgingStep } if (item.Stains[0] != 0) - b.AppendLine($"{this.stainSheet.GetRow(item.Stains[0])?.Name.ToDalamudString() ?? "Unknown"} ({item.Stains[0]})"); + b.AppendLine($"{this.stainSheet.GetRowOrDefault(item.Stains[0])?.Name.ExtractText() ?? "Unknown"} ({item.Stains[0]})"); else b.AppendLine("None"); b.Append("Glamoured Item: "); if (item.GlamourId != 0) - b.AppendLine($"{this.itemSheet.GetRow(item.GlamourId)?.Name.ToDalamudString() ?? "Unknown"} ({item.GlamourId})"); + b.AppendLine($"{this.itemSheet.GetRowOrDefault(item.GlamourId)?.Name.ExtractText() ?? "Unknown"} ({item.GlamourId})"); else b.AppendLine("None"); diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LuminaAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LuminaAgingStep.cs index a07b21e54..0f411b8f1 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LuminaAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LuminaAgingStep.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Linq; - using Dalamud.Data; using Dalamud.Utility; using Lumina.Excel; @@ -11,31 +8,46 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; /// Test setup for Lumina. /// /// ExcelRow to run test on. -internal class LuminaAgingStep : IAgingStep - where T : ExcelRow +/// Whether or not the sheet is large. If it is large, the self test will iterate through the full sheet in one frame and benchmark the time taken. +internal class LuminaAgingStep(bool isLargeSheet) : IAgingStep + where T : struct, IExcelRow { private int step = 0; - private List rows; + private ExcelSheet rows; /// - public string Name => "Test Lumina"; + public string Name => $"Test Lumina ({typeof(T).Name})"; /// public SelfTestStepResult RunStep() { - var dataManager = Service.Get(); + this.rows ??= Service.Get().GetExcelSheet(); - this.rows ??= dataManager.GetExcelSheet().ToList(); + if (isLargeSheet) + { + var i = 0; + T currentRow = default; + foreach (var row in this.rows) + { + i++; + currentRow = row; + } - Util.ShowObject(this.rows[this.step]); + Util.ShowObject(currentRow); + return SelfTestStepResult.Pass; + } + else + { + Util.ShowObject(this.rows.GetRowAt(this.step)); - this.step++; - return this.step >= this.rows.Count ? SelfTestStepResult.Pass : SelfTestStepResult.Waiting; + this.step++; + return this.step >= this.rows.Count ? SelfTestStepResult.Pass : SelfTestStepResult.Waiting; + } } /// public void CleanUp() { - // ignored + this.step = 0; } } diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs index 51c9b35f6..3b3670228 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/SelfTestWindow.cs @@ -9,7 +9,7 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Logging.Internal; using ImGuiNET; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; namespace Dalamud.Interface.Internal.Windows.SelfTest; @@ -39,7 +39,11 @@ internal class SelfTestWindow : Window new GamepadStateAgingStep(), new ChatAgingStep(), new HoverAgingStep(), - new LuminaAgingStep(), + new LuminaAgingStep(true), + new LuminaAgingStep(true), + new LuminaAgingStep(true), + new LuminaAgingStep(true), + new LuminaAgingStep(false), new AddonLifecycleAgingStep(), new PartyFinderAgingStep(), new HandledExceptionAgingStep(), diff --git a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs index 34641fe4b..59f6b23c1 100644 --- a/Dalamud/Plugin/Internal/Types/LocalPlugin.cs +++ b/Dalamud/Plugin/Internal/Types/LocalPlugin.cs @@ -599,7 +599,7 @@ internal class LocalPlugin : IAsyncDisposable // Changes to Lumina should be upstreamed if feasible, and if there is a desire to re-add unpinned Lumina we // will need to put this behind some kind of feature flag somewhere. config.SharedAssemblies.Add((typeof(Lumina.GameData).Assembly.GetName(), true)); - config.SharedAssemblies.Add((typeof(Lumina.Excel.ExcelSheetImpl).Assembly.GetName(), true)); + config.SharedAssemblies.Add((typeof(Lumina.Excel.Sheets.Addon).Assembly.GetName(), true)); } private void EnsureLoader() diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 6b0555f23..f7c462d09 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -61,7 +61,7 @@ public interface IClientState /// /// Event that gets fired when a duty is ready. /// - public event Action CfPop; + public event Action CfPop; /// /// Gets the language of the client. diff --git a/Dalamud/Plugin/Services/IDataManager.cs b/Dalamud/Plugin/Services/IDataManager.cs index cead130aa..65c51a9fb 100644 --- a/Dalamud/Plugin/Services/IDataManager.cs +++ b/Dalamud/Plugin/Services/IDataManager.cs @@ -5,7 +5,9 @@ using Dalamud.Game; using Lumina; using Lumina.Data; +using Lumina.Data.Structs.Excel; using Lumina.Excel; +using Lumina.Excel.Exceptions; namespace Dalamud.Plugin.Services; @@ -37,17 +39,38 @@ public interface IDataManager /// /// Get an with the given Excel sheet row type. /// + /// Language of the sheet to get. Leave or empty to use the default language. + /// Explicitly provide the name of the sheet to get. Leave to use 's sheet name. Explicit names are necessary for quest/dungeon/cutscene sheets. /// The excel sheet type to get. /// The , giving access to game rows. - public ExcelSheet? GetExcelSheet() where T : ExcelRow; + /// + /// If the sheet type you want has subrows, use instead. + /// + /// Sheet name was not specified neither via 's nor . + /// does not have a valid . + /// Sheet does not exist. + /// Sheet had a mismatched column hash. + /// Sheet does not support nor . + /// Sheet was not a . + public ExcelSheet GetExcelSheet(ClientLanguage? language = null, string? name = null) where T : struct, IExcelRow; /// - /// Get an with the given Excel sheet row type with a specified language. + /// Get a with the given Excel sheet row type. /// - /// Language of the sheet to get. + /// Language of the sheet to get. Leave or empty to use the default language. + /// Explicitly provide the name of the sheet to get. Leave to use 's sheet name. Explicit names are necessary for quest/dungeon/cutscene sheets. /// The excel sheet type to get. - /// The , giving access to game rows. - public ExcelSheet? GetExcelSheet(ClientLanguage language) where T : ExcelRow; + /// The , giving access to game rows. + /// + /// If the sheet type you want has only rows, use instead. + /// + /// Sheet name was not specified neither via 's nor . + /// does not have a valid . + /// Sheet does not exist. + /// Sheet had a mismatched column hash. + /// Sheet does not support nor . + /// Sheet was not a . + public SubrowExcelSheet GetSubrowExcelSheet(ClientLanguage? language = null, string? name = null) where T : struct, IExcelSubrow; /// /// Get a with the given path. diff --git a/Dalamud/Utility/MapUtil.cs b/Dalamud/Utility/MapUtil.cs index 221f9cb55..3e72add9c 100644 --- a/Dalamud/Utility/MapUtil.cs +++ b/Dalamud/Utility/MapUtil.cs @@ -1,11 +1,11 @@ -using System.Numerics; +using System.Numerics; using Dalamud.Data; using Dalamud.Game.ClientState.Objects.Types; using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; namespace Dalamud.Utility; @@ -149,9 +149,7 @@ public static class MapUtil if (agentMap == null || agentMap->CurrentMapId == 0) throw new InvalidOperationException("Could not determine active map - data may not be loaded yet?"); - var territoryTransient = Service.Get() - .GetExcelSheet()! - .GetRow(agentMap->CurrentTerritoryId); + var territoryTransient = LuminaUtils.CreateRef(agentMap->CurrentTerritoryId); return WorldToMap( go.Position, @@ -161,7 +159,7 @@ public static class MapUtil */ -agentMap->CurrentOffsetX, -agentMap->CurrentOffsetY, - territoryTransient?.OffsetZ ?? 0, + territoryTransient.ValueNullable?.OffsetZ ?? 0, (uint)agentMap->CurrentMapSizeFactor, correctZOffset); } diff --git a/Dalamud/Utility/SeStringExtensions.cs b/Dalamud/Utility/SeStringExtensions.cs index b89f815bc..34ebb1450 100644 --- a/Dalamud/Utility/SeStringExtensions.cs +++ b/Dalamud/Utility/SeStringExtensions.cs @@ -1,4 +1,6 @@ -using Lumina.Text.Parse; +using Lumina.Text.Parse; + +using Lumina.Text.ReadOnly; using DSeString = Dalamud.Game.Text.SeStringHandling.SeString; using DSeStringBuilder = Dalamud.Game.Text.SeStringHandling.SeStringBuilder; @@ -20,6 +22,22 @@ public static class SeStringExtensions /// The re-parsed Dalamud SeString. public static DSeString ToDalamudString(this LSeString originalString) => DSeString.Parse(originalString.RawData); + /// + /// Convert a Lumina ReadOnlySeString into a Dalamud SeString. + /// This conversion re-parses the string. + /// + /// The original Lumina ReadOnlySeString. + /// The re-parsed Dalamud SeString. + public static DSeString ToDalamudString(this ReadOnlySeString originalString) => DSeString.Parse(originalString.Data.Span); + + /// + /// Convert a Lumina ReadOnlySeStringSpan into a Dalamud SeString. + /// This conversion re-parses the string. + /// + /// The original Lumina ReadOnlySeStringSpan. + /// The re-parsed Dalamud SeString. + public static DSeString ToDalamudString(this ReadOnlySeStringSpan originalString) => DSeString.Parse(originalString.Data); + /// Compiles and appends a macro string. /// Target SeString builder. /// Macro string in UTF-8 to compile and append to . diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index d8e05716e..e37afb765 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -21,7 +21,7 @@ using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Support; using ImGuiNET; -using Lumina.Excel.GeneratedSheets; +using Lumina.Excel.Sheets; using Serilog; using TerraFX.Interop.Windows; using Windows.Win32.Storage.FileSystem; @@ -813,7 +813,7 @@ public static class Util var names = data.GetExcelSheet(ClientLanguage.English)!; var rng = new Random(); - return names.ElementAt(rng.Next(0, names.Count() - 1)).Singular.RawString; + return names.GetRowAt(rng.Next(0, names.Count - 1)).Singular.ExtractText(); } /// @@ -891,13 +891,13 @@ public static class Util if (actor is ICharacter chara) { actorString += - $" Level: {chara.Level} ClassJob: {(resolveGameData ? chara.ClassJob.GameData?.Name : chara.ClassJob.Id.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n"; + $" Level: {chara.Level} ClassJob: {(resolveGameData ? chara.ClassJob.ValueNullable?.Name : chara.ClassJob.RowId.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n"; } if (actor is IPlayerCharacter pc) { actorString += - $" HomeWorld: {(resolveGameData ? pc.HomeWorld.GameData?.Name : pc.HomeWorld.Id.ToString())} CurrentWorld: {(resolveGameData ? pc.CurrentWorld.GameData?.Name : pc.CurrentWorld.Id.ToString())} FC: {pc.CompanyTag}\n"; + $" HomeWorld: {(resolveGameData ? pc.HomeWorld.ValueNullable?.Name : pc.HomeWorld.RowId.ToString())} CurrentWorld: {(resolveGameData ? pc.CurrentWorld.ValueNullable?.Name : pc.CurrentWorld.RowId.ToString())} FC: {pc.CompanyTag}\n"; } ImGui.TextUnformatted(actorString); @@ -925,13 +925,13 @@ public static class Util if (actor is Character chara) { actorString += - $" Level: {chara.Level} ClassJob: {(resolveGameData ? chara.ClassJob.GameData?.Name : chara.ClassJob.Id.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n"; + $" Level: {chara.Level} ClassJob: {(resolveGameData ? chara.ClassJob.ValueNullable?.Name : chara.ClassJob.RowId.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n"; } if (actor is PlayerCharacter pc) { actorString += - $" HomeWorld: {(resolveGameData ? pc.HomeWorld.GameData?.Name : pc.HomeWorld.Id.ToString())} CurrentWorld: {(resolveGameData ? pc.CurrentWorld.GameData?.Name : pc.CurrentWorld.Id.ToString())} FC: {pc.CompanyTag}\n"; + $" HomeWorld: {(resolveGameData ? pc.HomeWorld.ValueNullable?.Name : pc.HomeWorld.RowId.ToString())} CurrentWorld: {(resolveGameData ? pc.CurrentWorld.ValueNullable?.Name : pc.CurrentWorld.RowId.ToString())} FC: {pc.CompanyTag}\n"; } ImGui.TextUnformatted(actorString); diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 81fb80190..7c5f04e34 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 81fb801905b7fe8e02aa40dcf9942e0f707dcc21 +Subproject commit 7c5f04e346067f7a316ad9072fb8260122ba80f0 From 82fe5a387be1023e46dde4a503be4e2acd0fa279 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 1 Nov 2024 08:54:42 -0700 Subject: [PATCH 06/50] fix: Register DataManager as a service --- Dalamud/Interface/Internal/DalamudInterface.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index 793dfddd9..cdebdfb2e 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Console; +using Dalamud.Data; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Keys; @@ -56,6 +57,7 @@ internal class DalamudInterface : IInternalDisposableService private readonly Dalamud dalamud; private readonly DalamudConfiguration configuration; private readonly InterfaceManager interfaceManager; + private readonly DataManager dataManager; private readonly ChangelogWindow changelogWindow; private readonly ColorDemoWindow colorDemoWindow; @@ -97,6 +99,7 @@ internal class DalamudInterface : IInternalDisposableService DalamudConfiguration configuration, FontAtlasFactory fontAtlasFactory, InterfaceManager interfaceManager, + DataManager dataManager, PluginImageCache pluginImageCache, DalamudAssetManager dalamudAssetManager, Game.Framework framework, @@ -108,6 +111,7 @@ internal class DalamudInterface : IInternalDisposableService this.dalamud = dalamud; this.configuration = configuration; this.interfaceManager = interfaceManager; + this.dataManager = dataManager; this.WindowSystem = new WindowSystem("DalamudCore"); From 5fd43bf1027f3381177c68c825c8e94bbf34fba8 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 1 Nov 2024 08:55:53 -0700 Subject: [PATCH 07/50] deps: Bump Lumina to 5.2.0 --- Dalamud/Dalamud.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index a577f130b..f93a81b27 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -71,8 +71,8 @@ - - + + all @@ -136,16 +136,16 @@ - + - + - + - + From cffc123abd67f1610f629764d94a6ffe19a49516 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 1 Nov 2024 09:22:24 -0700 Subject: [PATCH 08/50] deps: bump coreplugin lumina --- Dalamud.CorePlugin/Dalamud.CorePlugin.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index 6cc92cbb5..6edea55bf 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -27,8 +27,8 @@ - - + + all From b570564258fdba95d3712b4163b67eda743d66a1 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 1 Nov 2024 09:54:55 -0700 Subject: [PATCH 09/50] fix: re-add invalid attributes per msbuild???? --- Dalamud/Dalamud.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index f93a81b27..79d7a64f8 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -136,16 +136,16 @@ - + - + - + - + From 30d56e4d112131efecc169e30448d0c5463119dc Mon Sep 17 00:00:00 2001 From: wolfcomp <4028289+wolfcomp@users.noreply.github.com> Date: Tue, 5 Nov 2024 06:14:51 +0100 Subject: [PATCH 10/50] fix all warnings and breaking changes for CS (#2061) --- Dalamud/Game/ClientState/Fates/Fate.cs | 10 ++++++++++ Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs | 4 ++-- Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs | 4 ++-- Dalamud/Game/Inventory/GameInventoryItem.cs | 2 +- Dalamud/Interface/Internal/UiDebug.cs | 6 +++--- .../Internal/Windows/Data/Widgets/FateTableWidget.cs | 2 +- .../SelfTest/AgingSteps/ContextMenuAgingStep.cs | 2 +- Dalamud/Interface/Windowing/Window.cs | 4 ++-- Dalamud/Utility/StringExtensions.cs | 2 +- lib/FFXIVClientStructs | 2 +- 10 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 9b41ac758..7b6f110a3 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -71,8 +71,14 @@ public interface IFate : IEquatable /// /// Gets a value indicating whether or not this has a EXP bonus. /// + [Obsolete("Use HasBonus instead")] bool HasExpBonus { get; } + /// + /// Gets a value indicating whether or not this has a bonus. + /// + bool HasBonus { get; } + /// /// Gets the icon id of this . /// @@ -216,8 +222,12 @@ internal unsafe partial class Fate : IFate public byte Progress => this.Struct->Progress; /// + [Obsolete("Use HasBonus instead")] public bool HasExpBonus => this.Struct->IsExpBonus; + /// + public bool HasBonus => this.Struct->IsBonus; + /// public uint IconId => this.Struct->IconId; diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs index b8a4a9bd8..2d5633bcb 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs @@ -127,7 +127,7 @@ internal unsafe class NamePlateUpdateContext : INamePlateUpdateContext /// /// Gets a pointer to the NamePlate addon's number array entries as a struct. /// - internal AddonNamePlate.NamePlateIntArrayData* NumberStruct { get; private set; } + internal AddonNamePlate.AddonNamePlateNumberArray* NumberStruct { get; private set; } /// /// Gets or sets a value indicating whether any handler in the current context has instantiated a part builder. @@ -142,7 +142,7 @@ internal unsafe class NamePlateUpdateContext : INamePlateUpdateContext { this.Addon = (AddonNamePlate*)args.Addon; this.NumberData = ((NumberArrayData**)args.NumberArrayData)![NamePlateGui.NumberArrayIndex]; - this.NumberStruct = (AddonNamePlate.NamePlateIntArrayData*)this.NumberData->IntArray; + this.NumberStruct = (AddonNamePlate.AddonNamePlateNumberArray*)this.NumberData->IntArray; this.StringData = ((StringArrayData**)args.StringArrayData)![NamePlateGui.StringArrayIndex]; this.HasParts = false; diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs index 99429d932..170fea687 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateHandler.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -490,7 +490,7 @@ internal unsafe class NamePlateUpdateHandler : INamePlateUpdateHandler private AddonNamePlate.NamePlateObject* NamePlateObject => &this.context.Addon->NamePlateObjectArray[this.NamePlateIndex]; - private AddonNamePlate.NamePlateIntArrayData.NamePlateObjectIntArrayData* ObjectData => + private AddonNamePlate.AddonNamePlateNumberArray.NamePlateObjectIntArrayData* ObjectData => this.context.NumberStruct->ObjectData.GetPointer(this.ArrayIndex); /// diff --git a/Dalamud/Game/Inventory/GameInventoryItem.cs b/Dalamud/Game/Inventory/GameInventoryItem.cs index 3844da15d..53aa9a9d9 100644 --- a/Dalamud/Game/Inventory/GameInventoryItem.cs +++ b/Dalamud/Game/Inventory/GameInventoryItem.cs @@ -63,7 +63,7 @@ public unsafe struct GameInventoryItem : IEquatable /// /// Gets the quantity of items in this item stack. /// - public uint Quantity => this.InternalItem.Quantity; + public int Quantity => this.InternalItem.Quantity; /// /// Gets the spiritbond of this item. diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs index f1a025d93..88294fdee 100644 --- a/Dalamud/Interface/Internal/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -300,7 +300,7 @@ internal unsafe class UiDebug { ImGui.Image( new IntPtr(kernelTexture->D3D11ShaderResourceView), - new Vector2(kernelTexture->Width, kernelTexture->Height)); + new Vector2(kernelTexture->ActualWidth, kernelTexture->ActualHeight)); ImGui.TreePop(); } } @@ -312,8 +312,8 @@ internal unsafe class UiDebug ImGui.Image( new IntPtr(textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView), new Vector2( - textureInfo->AtkTexture.KernelTexture->Width, - textureInfo->AtkTexture.KernelTexture->Height)); + textureInfo->AtkTexture.KernelTexture->ActualWidth, + textureInfo->AtkTexture.KernelTexture->ActualHeight)); ImGui.TreePop(); } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs index 77c05cb46..1a43f2b2d 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs @@ -153,7 +153,7 @@ internal class FateTableWidget : IDataWindowWidget } ImGui.TableNextColumn(); // HasExpBonus - ImGui.TextUnformatted(fate.HasExpBonus.ToString()); + ImGui.TextUnformatted(fate.HasBonus.ToString()); ImGui.TableNextColumn(); // Position DrawCopyableText(fate.Position.ToString(), "Click to copy Position"); diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs index 5bcd1845c..f08eccd96 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs @@ -141,7 +141,7 @@ internal class ContextMenuAgingStep : IAgingStep OnClicked = (IMenuItemClickedArgs a) => { SeString name; - uint count; + int count; var targetItem = (a.Target as MenuTargetInventory)!.TargetItem; if (targetItem is { } item) { diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs index d2a51235d..e507750dc 100644 --- a/Dalamud/Interface/Windowing/Window.cs +++ b/Dalamud/Interface/Windowing/Window.cs @@ -287,7 +287,7 @@ public abstract class Window this.IsFocused = false; - if (doSoundEffects && !this.DisableWindowSounds) UIModule.PlaySound(this.OnCloseSfxId, 0, 0, 0); + if (doSoundEffects && !this.DisableWindowSounds) UIGlobals.PlaySoundEffect(this.OnCloseSfxId); } return; @@ -307,7 +307,7 @@ public abstract class Window this.internalLastIsOpen = this.internalIsOpen; this.OnOpen(); - if (doSoundEffects && !this.DisableWindowSounds) UIModule.PlaySound(this.OnOpenSfxId, 0, 0, 0); + if (doSoundEffects && !this.DisableWindowSounds) UIGlobals.PlaySoundEffect(this.OnOpenSfxId); } this.PreDraw(); diff --git a/Dalamud/Utility/StringExtensions.cs b/Dalamud/Utility/StringExtensions.cs index 02dfdafbf..a63109167 100644 --- a/Dalamud/Utility/StringExtensions.cs +++ b/Dalamud/Utility/StringExtensions.cs @@ -40,7 +40,7 @@ public static class StringExtensions public static bool IsValidCharacterName(this string value, bool includeLegacy = true) { if (string.IsNullOrEmpty(value)) return false; - if (!FFXIVClientStructs.FFXIV.Client.UI.UIModule.IsPlayerCharacterName(value)) return false; + if (!FFXIVClientStructs.FFXIV.Client.UI.UIGlobals.IsValidPlayerCharacterName(value)) return false; return includeLegacy || value.Length <= 21; } } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 7c5f04e34..e59323cf3 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 7c5f04e346067f7a316ad9072fb8260122ba80f0 +Subproject commit e59323cf39e6acffd385006c156fa0105c255b54 From d4735980c8998dea7c01729ce356c0fa68d16b33 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Tue, 5 Nov 2024 22:37:45 +0100 Subject: [PATCH 11/50] [api11] Use Conditions address from CS (#2064) --- Dalamud/Game/ClientState/ClientStateAddressResolver.cs | 7 ------- Dalamud/Game/ClientState/Conditions/Condition.cs | 5 ++--- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 5a5351a38..625271d2a 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -50,11 +50,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// public IntPtr KeyboardStateIndexArray { get; private set; } - /// - /// Gets the address of the condition flag array. - /// - public IntPtr ConditionFlags { get; private set; } - // Functions /// @@ -93,8 +88,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; this.KeyboardStateIndexArray = sig.ScanText("0F B6 94 33 ?? ?? ?? ?? 84 D2") + 0x4; - this.ConditionFlags = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 8B D3 E8 ?? ?? ?? ?? 32 C0 48 83 C4 20"); - this.GamepadPoll = sig.ScanText("40 55 53 57 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 B4 24"); } } diff --git a/Dalamud/Game/ClientState/Conditions/Condition.cs b/Dalamud/Game/ClientState/Conditions/Condition.cs index 6fee02fd6..8f39df46f 100644 --- a/Dalamud/Game/ClientState/Conditions/Condition.cs +++ b/Dalamud/Game/ClientState/Conditions/Condition.cs @@ -28,10 +28,9 @@ internal sealed class Condition : IInternalDisposableService, ICondition private bool isDisposed; [ServiceManager.ServiceConstructor] - private Condition(ClientState clientState) + private unsafe Condition() { - var resolver = clientState.AddressResolver; - this.Address = resolver.ConditionFlags; + this.Address = (nint)FFXIVClientStructs.FFXIV.Client.Game.Conditions.Instance(); // Initialization for (var i = 0; i < MaxConditionEntries; i++) From c88e0086723a72564c07dfb7446bc6e55f9d179b Mon Sep 17 00:00:00 2001 From: KazWolfe Date: Mon, 11 Nov 2024 08:23:54 -0800 Subject: [PATCH 12/50] chore: Bump to API version 11 proper --- Dalamud/Dalamud.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 79d7a64f8..b3f1cf30a 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -9,7 +9,7 @@ - 10.0.0.15 + 11.0.0.0 XIV Launcher addon framework $(DalamudVersion) $(DalamudVersion) From c7facaf072099a98708d56256a7163cf3bbdf215 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Mon, 11 Nov 2024 17:28:40 +0100 Subject: [PATCH 13/50] Rewrite parts of ClientState (#2065) - Completely relies on hooks now, instead of the Framework.Update loop - Support for Logout codes --- Dalamud/Game/ClientState/ClientState.cs | 165 +++++++++++------- .../ClientState/ClientStateAddressResolver.cs | 7 + .../AgingSteps/LogoutEventAgingStep.cs | 2 +- .../Internal/AutoUpdate/AutoUpdateManager.cs | 4 +- Dalamud/Plugin/Services/IClientState.cs | 9 +- 5 files changed, 120 insertions(+), 67 deletions(-) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 0fad33e15..51f25fb5c 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -14,6 +14,7 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; +using FFXIVClientStructs.FFXIV.Application.Network; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Event; using FFXIVClientStructs.FFXIV.Client.UI; @@ -37,16 +38,12 @@ internal sealed class ClientState : IInternalDisposableService, IClientState private readonly ClientStateAddressResolver address; private readonly Hook setupTerritoryTypeHook; private readonly Hook uiModuleHandlePacketHook; - - [ServiceManager.ServiceDependency] - private readonly Framework framework = Service.Get(); + private readonly Hook processPacketPlayerSetupHook; + private readonly Hook onLogoutHook; [ServiceManager.ServiceDependency] private readonly NetworkHandlers networkHandlers = Service.Get(); - private bool lastConditionNone = true; - private bool lastFramePvP; - [ServiceManager.ServiceConstructor] private unsafe ClientState(TargetSigScanner sigScanner, Dalamud dalamud, GameLifecycle lifecycle) { @@ -62,18 +59,22 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.setupTerritoryTypeHook = Hook.FromAddress(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour); this.uiModuleHandlePacketHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour); - - this.framework.Update += this.FrameworkOnOnUpdateEvent; + this.processPacketPlayerSetupHook = Hook.FromAddress(this.address.ProcessPacketPlayerSetup, this.ProcessPacketPlayerSetupDetour); + this.onLogoutHook = Hook.FromAddress((nint)LogoutCallbackInterface.StaticVirtualTablePointer->OnLogout, this.OnLogoutDetour); this.networkHandlers.CfPop += this.NetworkHandlersOnCfPop; this.setupTerritoryTypeHook.Enable(); this.uiModuleHandlePacketHook.Enable(); + this.processPacketPlayerSetupHook.Enable(); + this.onLogoutHook.Enable(); } [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private unsafe delegate void SetupTerritoryTypeDelegate(EventFramework* eventFramework, ushort terriType); + private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet); + /// public event Action? TerritoryChanged; @@ -87,7 +88,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public event Action? Login; /// - public event Action? Logout; + public event IClientState.LogoutDelegate? Logout; /// public event Action? EnterPvP; @@ -165,23 +166,46 @@ internal sealed class ClientState : IInternalDisposableService, IClientState { this.setupTerritoryTypeHook.Dispose(); this.uiModuleHandlePacketHook.Dispose(); - this.framework.Update -= this.FrameworkOnOnUpdateEvent; + this.processPacketPlayerSetupHook.Dispose(); + this.onLogoutHook.Dispose(); this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop; } private unsafe void SetupTerritoryTypeDetour(EventFramework* eventFramework, ushort territoryType) { + Log.Debug("TerritoryType changed: {0}", territoryType); + this.TerritoryType = territoryType; this.TerritoryChanged?.InvokeSafely(territoryType); - Log.Debug("TerritoryType changed: {0}", territoryType); + var rowRef = LuminaUtils.CreateRef(territoryType); + if (rowRef.IsValid) + { + var isPvP = rowRef.Value.IsPvpZone; + if (isPvP != this.IsPvP) + { + this.IsPvP = isPvP; + this.IsPvPExcludingDen = this.IsPvP && this.TerritoryType != 250; + + if (this.IsPvP) + { + Log.Debug("EnterPvP"); + this.EnterPvP?.InvokeSafely(); + } + else + { + Log.Debug("LeavePvP"); + this.LeavePvP?.InvokeSafely(); + } + } + } this.setupTerritoryTypeHook.Original(eventFramework, territoryType); } private unsafe void UIModuleHandlePacketDetour(UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet) { - this.uiModuleHandlePacketHook!.Original(thisPtr, type, uintParam, packet); + this.uiModuleHandlePacketHook.Original(thisPtr, type, uintParam, packet); switch (type) { @@ -226,59 +250,74 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } } + private unsafe void ProcessPacketPlayerSetupDetour(nint a1, nint packet) + { + // Call original first, so everything is set up. + this.processPacketPlayerSetupHook.Original(a1, packet); + + var gameGui = Service.GetNullable(); + + try + { + Log.Debug("Login"); + this.IsLoggedIn = true; + this.Login?.InvokeSafely(); + gameGui?.ResetUiHideState(); + this.lifecycle.ResetLogout(); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during ProcessPacketPlayerSetupDetour"); + } + } + + private unsafe void OnLogoutDetour(LogoutCallbackInterface* thisPtr, LogoutCallbackInterface.LogoutParams* logoutParams) + { + var gameGui = Service.GetNullable(); + + if (logoutParams != null) + { + try + { + var type = logoutParams->Type; + var code = logoutParams->Code; + + Log.Debug("Logout: Type {type}, Code {code}", type, code); + + this.IsLoggedIn = false; + + if (this.Logout is { } callback) + { + foreach (var action in callback.GetInvocationList().Cast()) + { + try + { + action(type, code); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } + } + } + + gameGui?.ResetUiHideState(); + + this.lifecycle.SetLogout(); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during OnLogoutDetour"); + } + } + + this.onLogoutHook.Original(thisPtr, logoutParams); + } + private void NetworkHandlersOnCfPop(ContentFinderCondition e) { this.CfPop?.InvokeSafely(e); } - - private void FrameworkOnOnUpdateEvent(IFramework framework1) - { - var condition = Service.GetNullable(); - var gameGui = Service.GetNullable(); - var data = Service.GetNullable(); - - if (condition == null || gameGui == null || data == null) - return; - - if (condition.Any() && this.lastConditionNone && this.LocalPlayer != null) - { - Log.Debug("Is login"); - this.lastConditionNone = false; - this.IsLoggedIn = true; - this.Login?.InvokeSafely(); - gameGui.ResetUiHideState(); - - this.lifecycle.ResetLogout(); - } - - if (!condition.Any() && this.lastConditionNone == false) - { - Log.Debug("Is logout"); - this.lastConditionNone = true; - this.IsLoggedIn = false; - this.Logout?.InvokeSafely(); - gameGui.ResetUiHideState(); - - this.lifecycle.SetLogout(); - } - - this.IsPvP = GameMain.IsInPvPArea(); - this.IsPvPExcludingDen = this.IsPvP && this.TerritoryType != 250; - - if (this.IsPvP != this.lastFramePvP) - { - this.lastFramePvP = this.IsPvP; - - if (this.IsPvP) - { - this.EnterPvP?.InvokeSafely(); - } - else - { - this.LeavePvP?.InvokeSafely(); - } - } - } } /// @@ -322,7 +361,7 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat public event Action? Login; /// - public event Action? Logout; + public event IClientState.LogoutDelegate? Logout; /// public event Action? EnterPvP; @@ -394,7 +433,7 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat private void LoginForward() => this.Login?.Invoke(); - private void LogoutForward() => this.Logout?.Invoke(); + private void LogoutForward(int type, int code) => this.Logout?.Invoke(type, code); private void EnterPvPForward() => this.EnterPvP?.Invoke(); diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 625271d2a..6b46ffc0d 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -57,6 +57,11 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// public IntPtr SetupTerritoryType { get; private set; } + /// + /// Gets the address of the method which sets up the player. + /// + public IntPtr ProcessPacketPlayerSetup { get; private set; } + /// /// Gets the address of the method which polls the gamepads for data. /// Called every frame, even when `Enable Gamepad` is off in the settings. @@ -82,6 +87,8 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 83 EC 20 0F B7 DA"); + this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); + // These resolve to fixed offsets only, without the base address added in, so GetStaticAddressFromSig() can't be used. // lea rcx, ds:1DB9F74h[rax*4] KeyboardState // movzx edx, byte ptr [rbx+rsi+1D5E0E0h] KeyboardStateIndexArray diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs index 1869dd108..723182111 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/LogoutEventAgingStep.cs @@ -50,7 +50,7 @@ internal class LogoutEventAgingStep : IAgingStep } } - private void ClientStateOnOnLogout() + private void ClientStateOnOnLogout(int type, int code) { this.hasPassed = true; } diff --git a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs index 6404846f7..c25ec4ee4 100644 --- a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs +++ b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -89,7 +89,7 @@ internal class AutoUpdateManager : IServiceType t => { t.Result.Login += this.OnLogin; - t.Result.Logout += this.OnLogout; + t.Result.Logout += (int type, int code) => this.OnLogout(); }); Service.GetAsync().ContinueWith(t => { t.Result.Update += this.OnUpdate; }); diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index f7c462d09..bac2b3e3f 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -22,6 +22,13 @@ public interface IClientState /// The level of the corresponding ClassJob. public delegate void LevelChangeDelegate(uint classJobId, uint level); + /// + /// A delegate type used for the event. + /// + /// The type of logout. + /// The success/failure code. + public delegate void LogoutDelegate(int type, int code); + /// /// Event that gets fired when the current Territory changes. /// @@ -46,7 +53,7 @@ public interface IClientState /// /// Event that fires when a character is logging out. /// - public event Action Logout; + public event LogoutDelegate Logout; /// /// Event that fires when a character is entering PvP. From 167e76911edabb318b3781ab5788ec9ae50001ca Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Mon, 11 Nov 2024 08:39:22 -0800 Subject: [PATCH 14/50] bump clientstructs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index e59323cf3..751645811 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit e59323cf39e6acffd385006c156fa0105c255b54 +Subproject commit 7516458116708bdcf8f72848c7692f312be3b40c From 084f8b55e77c50dd2d01710783676a48ca66500a Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Mon, 11 Nov 2024 08:54:57 -0800 Subject: [PATCH 15/50] fix cs breaks --- Dalamud/Game/Addon/Events/PluginEventController.cs | 2 +- .../Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs | 4 ++-- Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dalamud/Game/Addon/Events/PluginEventController.cs b/Dalamud/Game/Addon/Events/PluginEventController.cs index d33e2e253..403a812db 100644 --- a/Dalamud/Game/Addon/Events/PluginEventController.cs +++ b/Dalamud/Game/Addon/Events/PluginEventController.cs @@ -165,7 +165,7 @@ internal unsafe class PluginEventController : IDisposable { var paramKeyMatches = currentEvent->Param == eventEntry.ParamKey; var eventListenerAddressMatches = (nint)currentEvent->Listener == this.EventListener.Address; - var eventTypeMatches = currentEvent->Type == eventType; + var eventTypeMatches = currentEvent->State.EventType == eventType; if (paramKeyMatches && eventListenerAddressMatches && eventTypeMatches) { diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs index 51b572985..8affa1eac 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs @@ -16,7 +16,7 @@ public unsafe partial class AddonTree { private static readonly Dictionary AddonTypeDict = []; - private static readonly Assembly? ClientStructsAssembly = typeof(Addon).Assembly; + private static readonly Assembly? ClientStructsAssembly = typeof(AddonAttribute).Assembly; /// /// Gets or sets a collection of names for field offsets that have been documented in FFXIVClientStructs. @@ -36,7 +36,7 @@ public unsafe partial class AddonTree { foreach (var t in from t in ClientStructsAssembly.GetTypes() where t.IsPublic - let xivAddonAttr = (Addon?)t.GetCustomAttribute(typeof(Addon), false) + let xivAddonAttr = (AddonAttribute?)t.GetCustomAttribute(typeof(AddonAttribute), false) where xivAddonAttr != null where xivAddonAttr.AddonIdentifiers.Contains(this.AddonName) select t) diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs index 6869763c8..0c3a947dd 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs @@ -36,7 +36,7 @@ public static class Events ImGui.TableSetupColumn("Type", WidthFixed); ImGui.TableSetupColumn("Param", WidthFixed); ImGui.TableSetupColumn("Flags", WidthFixed); - ImGui.TableSetupColumn("Unk29", WidthFixed); + ImGui.TableSetupColumn("StateFlags1", WidthFixed); ImGui.TableSetupColumn("Target", WidthFixed); ImGui.TableSetupColumn("Listener", WidthFixed); @@ -48,13 +48,13 @@ public static class Events ImGui.TableNextColumn(); ImGui.TextUnformatted($"{i++}"); ImGui.TableNextColumn(); - ImGui.TextUnformatted($"{evt->Type}"); + ImGui.TextUnformatted($"{evt->State.EventType}"); ImGui.TableNextColumn(); ImGui.TextUnformatted($"{evt->Param}"); ImGui.TableNextColumn(); - ImGui.TextUnformatted($"{evt->Flags}"); + ImGui.TextUnformatted($"{evt->State.StateFlags}"); ImGui.TableNextColumn(); - ImGui.TextUnformatted($"{evt->Unk29}"); + ImGui.TextUnformatted($"{evt->State.UnkFlags1}"); ImGui.TableNextColumn(); Gui.ClickToCopyText($"{(nint)evt->Target:X}"); ImGui.TableNextColumn(); From c0f05614c67d7755a751d6ec082be9614bc589e0 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Tue, 12 Nov 2024 17:20:29 +0100 Subject: [PATCH 16/50] [api11] Some code cleanup and signature replacements (#2066) * Remove unused code from ChatHandlers * Replace sigs in DalamudAtkTweaks * Resolve LocalContentId by using PlayerState.ContentId * Resolve BuddyList address via UIState.Buddy * Resolve ObjectTable address via GameObjectManager * Resolve FateTable address via FateManager * Resolve GroupManager address via GroupManager * Resolve JobGauges address via JobGaugeManager.CurrentGauge * Simplify ItemHover/Out event * Resolve ToggleUiHide address via RaptureAtkModule.SetUiVisibility * Resolve PopulateItemLinkObject via InventoryItem.Copy * Add byte[].AsPointer extension * Resolve addresses used by ToastGui via UIModule functions * Use Length from Span as ObjectTableLength * Replace OpenMapWithMapLink with CS call * Resolve FrameworkAddressResolver with CS vtable * Drop unnecessary ToArray in HandlePrintMessage * Clean up event calls in HandlePrintMessageDetour * Simplify LocalContentId further This pointer can't be null, because it's part of the .data section. * Compare SeStrings in FlyTextGui with SequenceEqual * Use CS types in FlyTextGuis internal code * Simplify reading SeStrings internally * Remove AsPointer again * Resolve Number/StringArray by type in NamePlateGui * Fix crashes in HandlePrintMessageDetour * Resolve InteractableLinkClicked with LogViewer.HandleLinkClick --- Dalamud/Game/ChatHandlers.cs | 91 +------- Dalamud/Game/ClientState/Buddy/BuddyList.cs | 18 +- Dalamud/Game/ClientState/ClientState.cs | 5 +- .../ClientState/ClientStateAddressResolver.cs | 44 ---- Dalamud/Game/ClientState/Fates/FateTable.cs | 44 +--- .../Game/ClientState/JobGauge/JobGauges.cs | 12 +- .../Game/ClientState/Objects/ObjectTable.cs | 65 +++--- .../ClientState/Objects/Types/Character.cs | 2 +- .../ClientState/Objects/Types/GameObject.cs | 2 +- Dalamud/Game/ClientState/Party/PartyList.cs | 10 +- Dalamud/Game/ClientState/Party/PartyMember.cs | 2 +- Dalamud/Game/Framework.cs | 36 +-- Dalamud/Game/FrameworkAddressResolver.cs | 40 ---- Dalamud/Game/Gui/ChatGui.cs | 182 +++++++-------- Dalamud/Game/Gui/ChatGuiAddressResolver.cs | 28 --- Dalamud/Game/Gui/FlyText/FlyTextGui.cs | 104 ++++----- Dalamud/Game/Gui/GameGui.cs | 147 +++---------- Dalamud/Game/Gui/GameGuiAddressResolver.cs | 18 -- Dalamud/Game/Gui/NamePlate/NamePlateGui.cs | 27 +-- .../Gui/NamePlate/NamePlateUpdateContext.cs | 4 +- Dalamud/Game/Gui/Toast/ToastGui.cs | 208 ++++++------------ .../Game/Gui/Toast/ToastGuiAddressResolver.cs | 30 --- Dalamud/Game/Internal/DalamudAtkTweaks.cs | 45 ++-- .../Windows/Data/Widgets/BuddyListWidget.cs | 4 +- Dalamud/Plugin/Services/IChatGui.cs | 2 +- 25 files changed, 343 insertions(+), 827 deletions(-) delete mode 100644 Dalamud/Game/FrameworkAddressResolver.cs delete mode 100644 Dalamud/Game/Gui/ChatGuiAddressResolver.cs delete mode 100644 Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index a41c6ff7a..c40744ca4 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -1,22 +1,14 @@ -using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; using CheapLoc; + using Dalamud.Configuration.Internal; -using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.Gui; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; -using Dalamud.Interface; -using Dalamud.Interface.ImGuiNotification; -using Dalamud.Interface.ImGuiNotification.Internal; using Dalamud.Interface.Internal; -using Dalamud.Interface.Internal.Windows; using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal; using Dalamud.Utility; @@ -27,49 +19,10 @@ namespace Dalamud.Game; /// Chat events and public helper functions. /// [ServiceManager.EarlyLoadedService] -internal class ChatHandlers : IServiceType +internal partial class ChatHandlers : IServiceType { - private static readonly ModuleLog Log = new("CHATHANDLER"); + private static readonly ModuleLog Log = new("ChatHandlers"); - private readonly Dictionary retainerSaleRegexes = new() - { - { - ClientLanguage.Japanese, - new Regex[] - { - new Regex(@"^(?:.+)マーケットに(?[\d,.]+)ギルで出品した(?.*)×(?[\d,.]+)が売れ、(?[\d,.]+)ギルを入手しました。$", RegexOptions.Compiled), - new Regex(@"^(?:.+)マーケットに(?[\d,.]+)ギルで出品した(?.*)が売れ、(?[\d,.]+)ギルを入手しました。$", RegexOptions.Compiled), - } - }, - { - ClientLanguage.English, - new Regex[] - { - new Regex(@"^(?.+) you put up for sale in the (?:.+) markets (?:have|has) sold for (?[\d,.]+) gil \(after fees\)\.$", RegexOptions.Compiled), - } - }, - { - ClientLanguage.German, - new Regex[] - { - new Regex(@"^Dein Gehilfe hat (?.+) auf dem Markt von (?:.+) für (?[\d,.]+) Gil verkauft\.$", RegexOptions.Compiled), - new Regex(@"^Dein Gehilfe hat (?.+) auf dem Markt von (?:.+) verkauft und (?[\d,.]+) Gil erhalten\.$", RegexOptions.Compiled), - } - }, - { - ClientLanguage.French, - new Regex[] - { - new Regex(@"^Un servant a vendu (?.+) pour (?[\d,.]+) gil à (?:.+)\.$", RegexOptions.Compiled), - } - }, - }; - - private readonly Regex urlRegex = new(@"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?", RegexOptions.Compiled); - - [ServiceManager.ServiceDependency] - private readonly Dalamud dalamud = Service.Get(); - [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -92,6 +45,9 @@ internal class ChatHandlers : IServiceType /// public bool IsAutoUpdateComplete { get; private set; } + [GeneratedRegex(@"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?", RegexOptions.Compiled)] + private static partial Regex CompiledUrlRegex(); + private void OnCheckMessageHandled(XivChatType type, int timestamp, ref SeString sender, ref SeString message, ref bool isHandled) { var textVal = message.TextValue; @@ -100,7 +56,7 @@ internal class ChatHandlers : IServiceType this.configuration.BadWords.Any(x => !string.IsNullOrEmpty(x) && textVal.Contains(x))) { // This seems to be in the user block list - let's not show it - Log.Debug("Blocklist triggered"); + Log.Debug("Filtered a message that contained a muted word"); isHandled = true; return; } @@ -127,41 +83,10 @@ internal class ChatHandlers : IServiceType return; #endif - if (type == XivChatType.RetainerSale) - { - foreach (var regex in this.retainerSaleRegexes[(ClientLanguage)this.dalamud.StartInfo.Language]) - { - var matchInfo = regex.Match(message.TextValue); - - // we no longer really need to do/validate the item matching since we read the id from the byte array - // but we'd be checking the main match anyway - var itemInfo = matchInfo.Groups["item"]; - if (!itemInfo.Success) - continue; - - var itemLink = message.Payloads.FirstOrDefault(x => x.Type == PayloadType.Item) as ItemPayload; - if (itemLink == default) - { - Log.Error("itemLink was null. Msg: {0}", BitConverter.ToString(message.Encode())); - break; - } - - Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.Item.RowId}, HQ {itemLink.IsHQ}"); - - var valueInfo = matchInfo.Groups["value"]; - // not sure if using a culture here would work correctly, so just strip symbols instead - if (!valueInfo.Success || !int.TryParse(valueInfo.Value.Replace(",", string.Empty).Replace(".", string.Empty), out var itemValue)) - continue; - - // Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale(itemLink.Item.RowId, itemValue, itemLink.IsHQ)); - break; - } - } - var messageCopy = message; var senderCopy = sender; - var linkMatch = this.urlRegex.Match(message.TextValue); + var linkMatch = CompiledUrlRegex().Match(message.TextValue); if (linkMatch.Value.Length > 0) this.LastLink = linkMatch.Value; } diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index db9b8e562..84cfd24a3 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -1,14 +1,12 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using Dalamud.Utility; -using Serilog; +using FFXIVClientStructs.FFXIV.Client.Game.UI; namespace Dalamud.Game.ClientState.Buddy; @@ -28,14 +26,9 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList [ServiceManager.ServiceDependency] private readonly ClientState clientState = Service.Get(); - private readonly ClientStateAddressResolver address; - [ServiceManager.ServiceConstructor] private BuddyList() { - this.address = this.clientState.AddressResolver; - - Log.Verbose($"Buddy list address {Util.DescribeAddress(this.address.BuddyList)}"); } /// @@ -76,14 +69,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } } - /// - /// Gets the address of the buddy list. - /// - internal IntPtr BuddyListAddress => this.address.BuddyList; - - private static int BuddyMemberSize { get; } = Marshal.SizeOf(); - - private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy*)this.BuddyListAddress; + private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => &UIState.Instance()->Buddy; /// public IBuddyMember? this[int index] diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 51f25fb5c..750ba34c5 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -17,6 +17,7 @@ using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Application.Network; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Event; +using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -111,7 +112,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState get { var agentMap = AgentMap.Instance(); - return agentMap != null ? AgentMap.Instance()->CurrentMapId : 0; + return agentMap != null ? agentMap->CurrentMapId : 0; } } @@ -119,7 +120,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public IPlayerCharacter? LocalPlayer => Service.GetNullable()?[0] as IPlayerCharacter; /// - public ulong LocalContentId => (ulong)Marshal.ReadInt64(this.address.LocalContentId); + public unsafe ulong LocalContentId => PlayerState.Instance()->ContentId; /// public bool IsLoggedIn { get; private set; } diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 6b46ffc0d..cc635f784 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -7,39 +7,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver { // Static offsets - /// - /// Gets the address of the actor table. - /// - public IntPtr ObjectTable { get; private set; } - - /// - /// Gets the address of the buddy list. - /// - public IntPtr BuddyList { get; private set; } - - /// - /// Gets the address of a pointer to the fate table. - /// - /// - /// This is a static address to a pointer, not the address of the table itself. - /// - public IntPtr FateTablePtr { get; private set; } - - /// - /// Gets the address of the Group Manager. - /// - public IntPtr GroupManager { get; private set; } - - /// - /// Gets the address of the local content id. - /// - public IntPtr LocalContentId { get; private set; } - - /// - /// Gets the address of job gauge data. - /// - public IntPtr JobGaugeData { get; private set; } - /// /// Gets the address of the keyboard state. /// @@ -74,17 +41,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.ObjectTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83 ?? ?? ?? ?? C6 83 ?? ?? ?? ?? ??"); - - this.BuddyList = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 45 84 E4 75 1A F6 45 12 04"); - - this.FateTablePtr = sig.GetStaticAddressFromSig("48 8B 15 ?? ?? ?? ?? 48 8B F1 44 0F B7 41"); - - this.GroupManager = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 B8 ?? ?? ?? ?? ?? 77 71"); - - this.LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 0D ?? ?? ?? ?? 48 8D 57 08"); - this.JobGaugeData = sig.GetStaticAddressFromSig("48 8B 3D ?? ?? ?? ?? 33 ED") + 0x8; - this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 83 EC 20 0F B7 DA"); this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index abd5bac41..1bf557ad5 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -4,9 +4,8 @@ using System.Collections.Generic; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using Dalamud.Utility; -using Serilog; +using CSFateManager = FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager; namespace Dalamud.Game.ClientState.Fates; @@ -20,55 +19,34 @@ namespace Dalamud.Game.ClientState.Fates; #pragma warning restore SA1015 internal sealed partial class FateTable : IServiceType, IFateTable { - private readonly ClientStateAddressResolver address; - [ServiceManager.ServiceConstructor] - private FateTable(ClientState clientState) + private FateTable() { - this.address = clientState.AddressResolver; - - Log.Verbose($"Fate table address {Util.DescribeAddress(this.address.FateTablePtr)}"); } /// - public IntPtr Address => this.address.FateTablePtr; + public unsafe IntPtr Address => (nint)CSFateManager.Instance(); /// public unsafe int Length { get { - var fateTable = this.FateTableAddress; - if (fateTable == IntPtr.Zero) + var fateManager = CSFateManager.Instance(); + if (fateManager == null) return 0; // Sonar used this to check if the table was safe to read - if (Struct->FateDirector == null) + if (fateManager->FateDirector == null) return 0; - if (Struct->Fates.First == null || Struct->Fates.Last == null) + if (fateManager->Fates.First == null || fateManager->Fates.Last == null) return 0; - return Struct->Fates.Count; + return fateManager->Fates.Count; } } - /// - /// Gets the address of the Fate table. - /// - internal unsafe IntPtr FateTableAddress - { - get - { - if (this.address.FateTablePtr == IntPtr.Zero) - return IntPtr.Zero; - - return *(IntPtr*)this.address.FateTablePtr; - } - } - - private unsafe FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager*)this.FateTableAddress; - /// public IFate? this[int index] { @@ -99,11 +77,11 @@ internal sealed partial class FateTable : IServiceType, IFateTable if (index >= this.Length) return IntPtr.Zero; - var fateTable = this.FateTableAddress; - if (fateTable == IntPtr.Zero) + var fateManager = CSFateManager.Instance(); + if (fateManager == null) return IntPtr.Zero; - return (IntPtr)this.Struct->Fates[index].Value; + return (IntPtr)fateManager->Fates[index].Value; } /// diff --git a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs index e0ae986c5..5a734ed87 100644 --- a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs +++ b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs @@ -5,9 +5,8 @@ using Dalamud.Game.ClientState.JobGauge.Types; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using Dalamud.Utility; -using Serilog; +using CSJobGaugeManager = FFXIVClientStructs.FFXIV.Client.Game.JobGaugeManager; namespace Dalamud.Game.ClientState.JobGauge; @@ -21,18 +20,15 @@ namespace Dalamud.Game.ClientState.JobGauge; #pragma warning restore SA1015 internal class JobGauges : IServiceType, IJobGauges { - private Dictionary cache = new(); + private Dictionary cache = []; [ServiceManager.ServiceConstructor] - private JobGauges(ClientState clientState) + private JobGauges() { - this.Address = clientState.AddressResolver.JobGaugeData; - - Log.Verbose($"JobGaugeData address {Util.DescribeAddress(this.Address)}"); } /// - public IntPtr Address { get; } + public unsafe IntPtr Address => (nint)(&CSJobGaugeManager.Instance()->CurrentGauge); /// public T Get() where T : JobGaugeBase diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index c0b9c6e12..8ea1b582f 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -7,15 +7,17 @@ using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Logging.Internal; using Dalamud.Plugin.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; +using FFXIVClientStructs.Interop; + using Microsoft.Extensions.ObjectPool; -using Serilog; - using CSGameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; +using CSGameObjectManager = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObjectManager; namespace Dalamud.Game.ClientState.Objects; @@ -29,10 +31,12 @@ namespace Dalamud.Game.ClientState.Objects; #pragma warning restore SA1015 internal sealed partial class ObjectTable : IServiceType, IObjectTable { - private const int ObjectTableLength = 599; + private static readonly ModuleLog Log = new("ObjectTable"); + + private static int objectTableLength; private readonly ClientState clientState; - private readonly CachedEntry[] cachedObjectTable = new CachedEntry[ObjectTableLength]; + private readonly CachedEntry[] cachedObjectTable; private readonly ObjectPool multiThreadedEnumerators = new DefaultObjectPoolProvider().Create(); @@ -46,29 +50,30 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { this.clientState = clientState; - var nativeObjectTableAddress = (CSGameObject**)this.clientState.AddressResolver.ObjectTable; + var nativeObjectTable = CSGameObjectManager.Instance()->Objects.IndexSorted; + objectTableLength = nativeObjectTable.Length; + + this.cachedObjectTable = new CachedEntry[objectTableLength]; for (var i = 0; i < this.cachedObjectTable.Length; i++) - this.cachedObjectTable[i] = new(nativeObjectTableAddress, i); + this.cachedObjectTable[i] = new(nativeObjectTable.GetPointer(i)); for (var i = 0; i < this.frameworkThreadEnumerators.Length; i++) this.frameworkThreadEnumerators[i] = new(this, i); - - Log.Verbose($"Object table address {Util.DescribeAddress(this.clientState.AddressResolver.ObjectTable)}"); } /// - public nint Address + public unsafe nint Address { get { _ = this.WarnMultithreadedUsage(); - return this.clientState.AddressResolver.ObjectTable; + return (nint)(&CSGameObjectManager.Instance()->Objects); } } /// - public int Length => ObjectTableLength; + public int Length => objectTableLength; /// public IGameObject? this[int index] @@ -77,7 +82,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { _ = this.WarnMultithreadedUsage(); - return index is >= ObjectTableLength or < 0 ? null : this.cachedObjectTable[index].Update(); + return (index >= objectTableLength || index < 0) ? null : this.cachedObjectTable[index].Update(); } } @@ -120,7 +125,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable { _ = this.WarnMultithreadedUsage(); - return index is < 0 or >= ObjectTableLength ? nint.Zero : (nint)this.cachedObjectTable[index].Address; + return (index >= objectTableLength || index < 0) ? nint.Zero : (nint)this.cachedObjectTable[index].Address; } /// @@ -172,33 +177,21 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable } /// Stores an object table entry, with preallocated concrete types. - internal readonly unsafe struct CachedEntry + /// Initializes a new instance of the struct. + /// A pointer to the object table entry this entry should be pointing to. + internal readonly unsafe struct CachedEntry(Pointer* gameObjectPtr) { - private readonly CSGameObject** gameObjectPtrPtr; - private readonly PlayerCharacter playerCharacter; - private readonly BattleNpc battleNpc; - private readonly Npc npc; - private readonly EventObj eventObj; - private readonly GameObject gameObject; - - /// Initializes a new instance of the struct. - /// The object table that this entry should be pointing to. - /// The slot index inside the table. - public CachedEntry(CSGameObject** ownerTable, int slot) - { - this.gameObjectPtrPtr = ownerTable + slot; - this.playerCharacter = new(nint.Zero); - this.battleNpc = new(nint.Zero); - this.npc = new(nint.Zero); - this.eventObj = new(nint.Zero); - this.gameObject = new(nint.Zero); - } + private readonly PlayerCharacter playerCharacter = new(nint.Zero); + private readonly BattleNpc battleNpc = new(nint.Zero); + private readonly Npc npc = new(nint.Zero); + private readonly EventObj eventObj = new(nint.Zero); + private readonly GameObject gameObject = new(nint.Zero); /// Gets the address of the underlying native object. May be null. public CSGameObject* Address { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => *this.gameObjectPtrPtr; + get => gameObjectPtr->Value; } /// Updates and gets the wrapped game object pointed by this struct. @@ -284,11 +277,11 @@ internal sealed partial class ObjectTable public bool MoveNext() { - if (this.index == ObjectTableLength) + if (this.index == objectTableLength) return false; var cache = this.owner!.cachedObjectTable.AsSpan(); - for (this.index++; this.index < ObjectTableLength; this.index++) + for (this.index++; this.index < objectTableLength; this.index++) { if (cache[this.index].Update() is { } ao) { diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs index b04b54301..a91ecc230 100644 --- a/Dalamud/Game/ClientState/Objects/Types/Character.cs +++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs @@ -161,7 +161,7 @@ internal unsafe class Character : GameObject, ICharacter public byte[] Customize => this.Struct->DrawData.CustomizeData.Data.ToArray(); /// - public SeString CompanyTag => MemoryHelper.ReadSeString((nint)Unsafe.AsPointer(ref this.Struct->FreeCompanyTag[0]), 6); + public SeString CompanyTag => SeString.Parse(this.Struct->FreeCompanyTag); /// /// Gets the target object ID of the character. diff --git a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs index f9fd87bf4..4209100f0 100644 --- a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs +++ b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs @@ -197,7 +197,7 @@ internal partial class GameObject internal unsafe partial class GameObject : IGameObject { /// - public SeString Name => MemoryHelper.ReadSeString((nint)Unsafe.AsPointer(ref this.Struct->Name[0]), 64); + public SeString Name => SeString.Parse(this.Struct->Name); /// public ulong GameObjectId => this.Struct->GetGameObjectId(); diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index 4fbd8fdfb..a016a8211 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -6,9 +6,8 @@ using System.Runtime.InteropServices; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; -using Dalamud.Utility; -using Serilog; +using CSGroupManager = FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager; namespace Dalamud.Game.ClientState.Party; @@ -28,14 +27,9 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList [ServiceManager.ServiceDependency] private readonly ClientState clientState = Service.Get(); - private readonly ClientStateAddressResolver address; - [ServiceManager.ServiceConstructor] private PartyList() { - this.address = this.clientState.AddressResolver; - - Log.Verbose($"Group manager address {Util.DescribeAddress(this.address.GroupManager)}"); } /// @@ -48,7 +42,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList public bool IsAlliance => this.GroupManagerStruct->MainGroup.AllianceFlags > 0; /// - public IntPtr GroupManagerAddress => this.address.GroupManager; + public unsafe IntPtr GroupManagerAddress => (nint)CSGroupManager.Instance(); /// public IntPtr GroupListAddress => (IntPtr)Unsafe.AsPointer(ref GroupManagerStruct->MainGroup.PartyMembers[0]); diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs index b764431f5..cf620a7ef 100644 --- a/Dalamud/Game/ClientState/Party/PartyMember.cs +++ b/Dalamud/Game/ClientState/Party/PartyMember.cs @@ -181,7 +181,7 @@ internal unsafe class PartyMember : IPartyMember /// /// Gets the displayname of this party member. /// - public SeString Name => MemoryHelper.ReadSeString((nint)Unsafe.AsPointer(ref Struct->Name[0]), 0x40); + public SeString Name => SeString.Parse(this.Struct->Name); /// /// Gets the sex of this party member. diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs index 07942f780..82c7f5f6c 100644 --- a/Dalamud/Game/Framework.cs +++ b/Dalamud/Game/Framework.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -16,6 +15,8 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; +using CSFramework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework; + namespace Dalamud.Game; /// @@ -31,11 +32,9 @@ internal sealed class Framework : IInternalDisposableService, IFramework private readonly Stopwatch updateStopwatch = new(); private readonly HitchDetector hitchDetector; - private readonly Hook updateHook; - private readonly Hook destroyHook; + private readonly Hook updateHook; + private readonly Hook destroyHook; - private readonly FrameworkAddressResolver addressResolver; - [ServiceManager.ServiceDependency] private readonly GameLifecycle lifecycle = Service.Get(); @@ -51,13 +50,10 @@ internal sealed class Framework : IInternalDisposableService, IFramework private ulong tickCounter; [ServiceManager.ServiceConstructor] - private Framework(TargetSigScanner sigScanner) + private unsafe Framework() { this.hitchDetector = new HitchDetector("FrameworkUpdate", this.configuration.FrameworkUpdateHitch); - this.addressResolver = new FrameworkAddressResolver(); - this.addressResolver.Setup(sigScanner); - this.frameworkDestroy = new(); this.frameworkThreadTaskScheduler = new(); this.FrameworkThreadTaskFactory = new( @@ -66,23 +62,13 @@ internal sealed class Framework : IInternalDisposableService, IFramework TaskContinuationOptions.None, this.frameworkThreadTaskScheduler); - this.updateHook = Hook.FromAddress(this.addressResolver.TickAddress, this.HandleFrameworkUpdate); - this.destroyHook = Hook.FromAddress(this.addressResolver.DestroyAddress, this.HandleFrameworkDestroy); + this.updateHook = Hook.FromAddress((nint)CSFramework.StaticVirtualTablePointer->Tick, this.HandleFrameworkUpdate); + this.destroyHook = Hook.FromAddress((nint)CSFramework.StaticVirtualTablePointer->Destroy, this.HandleFrameworkDestroy); this.updateHook.Enable(); this.destroyHook.Enable(); } - /// - /// A delegate type used during the native Framework::destroy. - /// - /// The native Framework address. - /// A value indicating if the call was successful. - public delegate bool OnRealDestroyDelegate(IntPtr framework); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate bool OnUpdateDetour(IntPtr framework); - /// public event IFramework.OnUpdateDelegate? Update; @@ -390,7 +376,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework } } - private bool HandleFrameworkUpdate(IntPtr framework) + private unsafe bool HandleFrameworkUpdate(CSFramework* thisPtr) { this.frameworkThreadTaskScheduler.BoundThread ??= Thread.CurrentThread; @@ -483,10 +469,10 @@ internal sealed class Framework : IInternalDisposableService, IFramework this.hitchDetector.Stop(); original: - return this.updateHook.OriginalDisposeSafe(framework); + return this.updateHook.OriginalDisposeSafe(thisPtr); } - private bool HandleFrameworkDestroy(IntPtr framework) + private unsafe bool HandleFrameworkDestroy(CSFramework* thisPtr) { this.frameworkDestroy.Cancel(); this.DispatchUpdateEvents = false; @@ -504,7 +490,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework ServiceManager.WaitForServiceUnload(); Log.Information("Framework::Destroy OK!"); - return this.destroyHook.OriginalDisposeSafe(framework); + return this.destroyHook.OriginalDisposeSafe(thisPtr); } } diff --git a/Dalamud/Game/FrameworkAddressResolver.cs b/Dalamud/Game/FrameworkAddressResolver.cs deleted file mode 100644 index 8ea371f2c..000000000 --- a/Dalamud/Game/FrameworkAddressResolver.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Dalamud.Game; - -/// -/// The address resolver for the class. -/// -internal sealed class FrameworkAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address for the function that is called once the Framework is destroyed. - /// - public IntPtr DestroyAddress { get; private set; } - - /// - /// Gets the address for the function that is called once the Framework is free'd. - /// - public IntPtr FreeAddress { get; private set; } - - /// - /// Gets the function that is called every tick. - /// - public IntPtr TickAddress { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - this.SetupFramework(sig); - } - - private void SetupFramework(ISigScanner scanner) - { - this.DestroyAddress = - scanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 3D ?? ?? ?? ?? 48 8B D9 48 85 FF"); - - this.FreeAddress = - scanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B D9 48 8B 0D ?? ?? ?? ?? 48 85 C9"); - - this.TickAddress = - scanner.ScanText("40 53 48 83 EC 20 FF 81 ?? ?? ?? ?? 48 8B D9 48 8D 4C 24 ??"); - } -} diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 6779f32a8..2482c36b4 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -11,11 +11,19 @@ using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; -using Dalamud.Memory; using Dalamud.Plugin.Services; using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.System.String; +using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Misc; +using FFXIVClientStructs.FFXIV.Component.GUI; + +using LinkMacroPayloadType = Lumina.Text.Payloads.LinkMacroPayloadType; +using LuminaSeStringBuilder = Lumina.Text.SeStringBuilder; +using ReadOnlySePayloadType = Lumina.Text.ReadOnly.ReadOnlySePayloadType; +using ReadOnlySeStringSpan = Lumina.Text.ReadOnly.ReadOnlySeStringSpan; namespace Dalamud.Game.Gui; @@ -27,14 +35,12 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui { private static readonly ModuleLog Log = new("ChatGui"); - private readonly ChatGuiAddressResolver address; - private readonly Queue chatQueue = new(); private readonly Dictionary<(string PluginName, uint CommandId), Action> dalamudLinkHandlers = new(); private readonly Hook printMessageHook; - private readonly Hook populateItemLinkHook; - private readonly Hook interactableLinkClickedHook; + private readonly Hook inventoryItemCopyHook; + private readonly Hook handleLinkClickHook; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -42,29 +48,20 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui private ImmutableDictionary<(string PluginName, uint CommandId), Action>? dalamudLinkHandlersCopy; [ServiceManager.ServiceConstructor] - private ChatGui(TargetSigScanner sigScanner) + private ChatGui() { - this.address = new ChatGuiAddressResolver(); - this.address.Setup(sigScanner); - - this.printMessageHook = Hook.FromAddress((nint)RaptureLogModule.Addresses.PrintMessage.Value, this.HandlePrintMessageDetour); - this.populateItemLinkHook = Hook.FromAddress(this.address.PopulateItemLinkObject, this.HandlePopulateItemLinkDetour); - this.interactableLinkClickedHook = Hook.FromAddress(this.address.InteractableLinkClicked, this.InteractableLinkClickedDetour); + this.printMessageHook = Hook.FromAddress(RaptureLogModule.Addresses.PrintMessage.Value, this.HandlePrintMessageDetour); + this.inventoryItemCopyHook = Hook.FromAddress(InventoryItem.Addresses.Copy.Value, this.InventoryItemCopyDetour); + this.handleLinkClickHook = Hook.FromAddress(LogViewer.Addresses.HandleLinkClick.Value, this.HandleLinkClickDetour); this.printMessageHook.Enable(); - this.populateItemLinkHook.Enable(); - this.interactableLinkClickedHook.Enable(); + this.inventoryItemCopyHook.Enable(); + this.handleLinkClickHook.Enable(); } [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate uint PrintMessageDelegate(RaptureLogModule* manager, XivChatType chatType, Utf8String* sender, Utf8String* message, int timestamp, byte silent); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void PopulateItemLinkDelegate(IntPtr linkObjectPtr, IntPtr itemInfoPtr); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void InteractableLinkClickedDelegate(IntPtr managerPtr, IntPtr messagePtr); - /// public event IChatGui.OnMessageDelegate? ChatMessage; @@ -78,7 +75,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; /// - public int LastLinkedItemId { get; private set; } + public uint LastLinkedItemId { get; private set; } /// public byte LastLinkedItemFlags { get; private set; } @@ -106,8 +103,8 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui void IInternalDisposableService.DisposeService() { this.printMessageHook.Dispose(); - this.populateItemLinkHook.Dispose(); - this.interactableLinkClickedHook.Dispose(); + this.inventoryItemCopyHook.Dispose(); + this.handleLinkClickHook.Dispose(); } /// @@ -275,21 +272,20 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui }); } - private void HandlePopulateItemLinkDetour(IntPtr linkObjectPtr, IntPtr itemInfoPtr) + private void InventoryItemCopyDetour(InventoryItem* thisPtr, InventoryItem* otherPtr) { + this.inventoryItemCopyHook.Original(thisPtr, otherPtr); + try { - this.populateItemLinkHook.Original(linkObjectPtr, itemInfoPtr); + this.LastLinkedItemId = otherPtr->ItemId; + this.LastLinkedItemFlags = (byte)otherPtr->Flags; - this.LastLinkedItemId = Marshal.ReadInt32(itemInfoPtr, 8); - this.LastLinkedItemFlags = Marshal.ReadByte(itemInfoPtr, 0x14); - - // Log.Verbose($"HandlePopulateItemLinkDetour {linkObjectPtr} {itemInfoPtr} - linked:{this.LastLinkedItemId}"); + // Log.Verbose($"InventoryItemCopyDetour {thisPtr} {otherPtr} - linked:{this.LastLinkedItemId}"); } catch (Exception ex) { - Log.Error(ex, "Exception onPopulateItemLink hook."); - this.populateItemLinkHook.Original(linkObjectPtr, itemInfoPtr); + Log.Error(ex, "Exception in InventoryItemCopyHook"); } } @@ -299,58 +295,57 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui try { - var originalSenderData = sender->AsSpan().ToArray(); - var originalMessageData = message->AsSpan().ToArray(); + var parsedSender = SeString.Parse(sender->AsSpan()); + var parsedMessage = SeString.Parse(message->AsSpan()); - var parsedSender = SeString.Parse(originalSenderData); - var parsedMessage = SeString.Parse(originalMessageData); + var terminatedSender = parsedSender.EncodeWithNullTerminator(); + var terminatedMessage = parsedMessage.EncodeWithNullTerminator(); // Call events var isHandled = false; - var invocationList = this.CheckMessageHandled!.GetInvocationList(); - foreach (var @delegate in invocationList) + if (this.CheckMessageHandled is { } handledCallback) { - try - { - var messageHandledDelegate = @delegate as IChatGui.OnCheckMessageHandledDelegate; - messageHandledDelegate!.Invoke(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); - } - catch (Exception e) - { - Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", @delegate.Method.Name); - } - } - - if (!isHandled) - { - invocationList = this.ChatMessage!.GetInvocationList(); - foreach (var @delegate in invocationList) + foreach (var action in handledCallback.GetInvocationList().Cast()) { try { - var messageHandledDelegate = @delegate as IChatGui.OnMessageDelegate; - messageHandledDelegate!.Invoke(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); + action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); } catch (Exception e) { - Log.Error(e, "Could not invoke registered OnMessageDelegate for {Name}", @delegate.Method.Name); + Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method); } } } - var possiblyModifiedSenderData = parsedSender.Encode(); - var possiblyModifiedMessageData = parsedMessage.Encode(); - - if (!Util.FastByteArrayCompare(originalSenderData, possiblyModifiedSenderData)) + if (!isHandled && this.ChatMessage is { } callback) { - Log.Verbose($"HandlePrintMessageDetour Sender modified: {SeString.Parse(originalSenderData)} -> {parsedSender}"); + foreach (var action in callback.GetInvocationList().Cast()) + { + try + { + action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); + } + catch (Exception e) + { + Log.Error(e, "Could not invoke registered OnMessageDelegate for {Name}", action.Method); + } + } + } + + var possiblyModifiedSenderData = parsedSender.EncodeWithNullTerminator(); + var possiblyModifiedMessageData = parsedMessage.EncodeWithNullTerminator(); + + if (!terminatedSender.SequenceEqual(possiblyModifiedSenderData)) + { + Log.Verbose($"HandlePrintMessageDetour Sender modified: {SeString.Parse(terminatedSender)} -> {parsedSender}"); sender->SetString(possiblyModifiedSenderData); } - if (!Util.FastByteArrayCompare(originalMessageData, possiblyModifiedMessageData)) + if (!terminatedMessage.SequenceEqual(possiblyModifiedMessageData)) { - Log.Verbose($"HandlePrintMessageDetour Message modified: {SeString.Parse(originalMessageData)} -> {parsedMessage}"); + Log.Verbose($"HandlePrintMessageDetour Message modified: {SeString.Parse(terminatedMessage)} -> {parsedMessage}"); message->SetString(possiblyModifiedMessageData); } @@ -374,42 +369,57 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui return messageId; } - private void InteractableLinkClickedDetour(IntPtr managerPtr, IntPtr messagePtr) + private void HandleLinkClickDetour(LogViewer* thisPtr, LinkData* linkData) { + if ((Payload.EmbeddedInfoType)(linkData->LinkType + 1) != Payload.EmbeddedInfoType.DalamudLink) + { + this.handleLinkClickHook.Original(thisPtr, linkData); + return; + } + + Log.Verbose($"InteractableLinkClicked: {Payload.EmbeddedInfoType.DalamudLink}"); + + var sb = LuminaSeStringBuilder.SharedPool.Get(); try { - var interactableType = (Payload.EmbeddedInfoType)(Marshal.ReadByte(messagePtr, 0x1B) + 1); + var seStringSpan = new ReadOnlySeStringSpan(linkData->Payload); - if (interactableType != Payload.EmbeddedInfoType.DalamudLink) + // read until link terminator + foreach (var payload in seStringSpan) { - this.interactableLinkClickedHook.Original(managerPtr, messagePtr); - return; + sb.Append(payload); + + if (payload.Type == ReadOnlySePayloadType.Macro && + payload.MacroCode == Lumina.Text.Payloads.MacroCode.Link && + payload.TryGetExpression(out var expr1) && + expr1.TryGetInt(out var expr1Val) && + expr1Val == (int)LinkMacroPayloadType.Terminator) + { + break; + } } - Log.Verbose($"InteractableLinkClicked: {Payload.EmbeddedInfoType.DalamudLink}"); + var seStr = SeString.Parse(sb.ToArray()); + if (seStr.Payloads.Count == 0 || seStr.Payloads[0] is not DalamudLinkPayload link) + return; - var payloadPtr = Marshal.ReadIntPtr(messagePtr, 0x10); - var seStr = MemoryHelper.ReadSeStringNullTerminated(payloadPtr); - var terminatorIndex = seStr.Payloads.IndexOf(RawPayload.LinkTerminator); - var payloads = terminatorIndex >= 0 ? seStr.Payloads.Take(terminatorIndex + 1).ToList() : seStr.Payloads; - if (payloads.Count == 0) return; - var linkPayload = payloads[0]; - if (linkPayload is DalamudLinkPayload link) + if (this.RegisteredLinkHandlers.TryGetValue((link.Plugin, link.CommandId), out var value)) { - if (this.RegisteredLinkHandlers.TryGetValue((link.Plugin, link.CommandId), out var value)) - { - Log.Verbose($"Sending DalamudLink to {link.Plugin}: {link.CommandId}"); - value.Invoke(link.CommandId, new SeString(payloads)); - } - else - { - Log.Debug($"No DalamudLink registered for {link.Plugin} with ID of {link.CommandId}"); - } + Log.Verbose($"Sending DalamudLink to {link.Plugin}: {link.CommandId}"); + value.Invoke(link.CommandId, seStr); + } + else + { + Log.Debug($"No DalamudLink registered for {link.Plugin} with ID of {link.CommandId}"); } } catch (Exception ex) { - Log.Error(ex, "Exception on InteractableLinkClicked hook"); + Log.Error(ex, "Exception in HandleLinkClickDetour"); + } + finally + { + LuminaSeStringBuilder.SharedPool.Return(sb); } } } @@ -451,7 +461,7 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; /// - public int LastLinkedItemId => this.chatGuiService.LastLinkedItemId; + public uint LastLinkedItemId => this.chatGuiService.LastLinkedItemId; /// public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags; diff --git a/Dalamud/Game/Gui/ChatGuiAddressResolver.cs b/Dalamud/Game/Gui/ChatGuiAddressResolver.cs deleted file mode 100644 index 366e79fc6..000000000 --- a/Dalamud/Game/Gui/ChatGuiAddressResolver.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Dalamud.Game.Gui; - -/// -/// The address resolver for the class. -/// -internal sealed class ChatGuiAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the native PopulateItemLinkObject method. - /// - public IntPtr PopulateItemLinkObject { get; private set; } - - /// - /// Gets the address of the native InteractableLinkClicked method. - /// - public IntPtr InteractableLinkClicked { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - // PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 FA F2 B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A"); - - // PopulateItemLinkObject = sig.ScanText( "48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A"); 5.0 - this.PopulateItemLinkObject = sig.ScanText("E8 ?? ?? ?? ?? 8B 4E FC"); - - this.InteractableLinkClicked = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 4B ?? E8 ?? ?? ?? ?? 33 D2"); - } -} diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs index 623bc51b3..b01f8c244 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -8,6 +9,9 @@ using Dalamud.IoC.Internal; using Dalamud.Memory; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Component.GUI; + using Serilog; namespace Dalamud.Game.Gui.FlyText; @@ -29,7 +33,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui private readonly Hook createFlyTextHook; [ServiceManager.ServiceConstructor] - private FlyTextGui(TargetSigScanner sigScanner) + private unsafe FlyTextGui(TargetSigScanner sigScanner) { this.Address = new FlyTextGuiAddressResolver(); this.Address.Setup(sigScanner); @@ -43,29 +47,29 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui /// /// Private delegate for the native CreateFlyText function's hook. /// - private delegate IntPtr CreateFlyTextDelegate( - IntPtr addonFlyText, + private unsafe delegate nint CreateFlyTextDelegate( + AtkUnitBase* thisPtr, FlyTextKind kind, int val1, int val2, - IntPtr text2, + byte* text2, uint color, uint icon, uint damageTypeIcon, - IntPtr text1, + byte* text1, float yOffset); /// /// Private delegate for the native AddFlyText function pointer. /// - private delegate void AddFlyTextDelegate( - IntPtr addonFlyText, + private unsafe delegate void AddFlyTextDelegate( + AtkUnitBase* thisPtr, uint actorIndex, uint messageMax, - IntPtr numbers, + NumberArrayData* numberArrayData, uint offsetNum, uint offsetNumMax, - IntPtr strings, + StringArrayData* stringArrayData, uint offsetStr, uint offsetStrMax, int unknown); @@ -87,26 +91,16 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui public unsafe void AddFlyText(FlyTextKind kind, uint actorIndex, uint val1, uint val2, SeString text1, SeString text2, uint color, uint icon, uint damageTypeIcon) { // Known valid flytext region within the atk arrays - var numIndex = 30; - var strIndex = 27; var numOffset = 161u; var strOffset = 28u; - // Get the UI module and flytext addon pointers - var gameGui = Service.GetNullable(); - if (gameGui == null) - return; - - var ui = (FFXIVClientStructs.FFXIV.Client.UI.UIModule*)gameGui.GetUIModule(); - var flytext = gameGui.GetAddonByName("_FlyText"); - - if (ui == null || flytext == IntPtr.Zero) + var flytext = RaptureAtkUnitManager.Instance()->GetAddonByName("_FlyText"); + if (flytext == null) return; // Get the number and string arrays we need - var atkArrayDataHolder = ui->GetRaptureAtkModule()->AtkModule.AtkArrayDataHolder; - var numArray = atkArrayDataHolder._NumberArrays[numIndex]; - var strArray = atkArrayDataHolder._StringArrays[strIndex]; + var numArray = AtkStage.Instance()->GetNumberArrayData(NumberArrayType.FlyText); + var strArray = AtkStage.Instance()->GetStringArrayData(StringArrayType.FlyText); // Write the values to the arrays using a known valid flytext region numArray->IntArray[numOffset + 0] = 1; // Some kind of "Enabled" flag for this section @@ -120,44 +114,35 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui numArray->IntArray[numOffset + 8] = 0; // Unknown numArray->IntArray[numOffset + 9] = 0; // Unknown, has something to do with yOffset - strArray->SetValue((int)strOffset + 0, text1.Encode(), false, true, false); - strArray->SetValue((int)strOffset + 1, text2.Encode(), false, true, false); + strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false); + strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false); this.addFlyTextNative( flytext, actorIndex, 1, - (IntPtr)numArray, + numArray, numOffset, 10, - (IntPtr)strArray, + strArray, strOffset, 2, 0); } - private static byte[] Terminate(byte[] source) - { - var terminated = new byte[source.Length + 1]; - Array.Copy(source, 0, terminated, 0, source.Length); - terminated[^1] = 0; - - return terminated; - } - - private IntPtr CreateFlyTextDetour( - IntPtr addonFlyText, + private unsafe nint CreateFlyTextDetour( + AtkUnitBase* thisPtr, FlyTextKind kind, int val1, int val2, - IntPtr text2, + byte* text2, uint color, uint icon, uint damageTypeIcon, - IntPtr text1, + byte* text1, float yOffset) { - var retVal = IntPtr.Zero; + var retVal = nint.Zero; try { Log.Verbose("[FlyText] Enter CreateFlyText detour!"); @@ -167,19 +152,19 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var tmpKind = kind; var tmpVal1 = val1; var tmpVal2 = val2; - var tmpText1 = text1 == IntPtr.Zero ? string.Empty : MemoryHelper.ReadSeStringNullTerminated(text1); - var tmpText2 = text2 == IntPtr.Zero ? string.Empty : MemoryHelper.ReadSeStringNullTerminated(text2); + var tmpText1 = text1 == null ? string.Empty : MemoryHelper.ReadSeStringNullTerminated((nint)text1); + var tmpText2 = text2 == null ? string.Empty : MemoryHelper.ReadSeStringNullTerminated((nint)text2); var tmpColor = color; var tmpIcon = icon; var tmpDamageTypeIcon = damageTypeIcon; var tmpYOffset = yOffset; - var cmpText1 = tmpText1.ToString(); - var cmpText2 = tmpText2.ToString(); + var originalText1 = tmpText1.EncodeWithNullTerminator(); + var originalText2 = tmpText2.EncodeWithNullTerminator(); - Log.Verbose($"[FlyText] Called with addonFlyText({addonFlyText.ToInt64():X}) " + + Log.Verbose($"[FlyText] Called with addonFlyText({(nint)thisPtr:X}) " + $"kind({kind}) val1({val1}) val2({val2}) damageTypeIcon({damageTypeIcon}) " + - $"text1({text1.ToInt64():X}, \"{tmpText1}\") text2({text2.ToInt64():X}, \"{tmpText2}\") " + + $"text1({(nint)text1:X}, \"{tmpText1}\") text2({(nint)text2:X}, \"{tmpText2}\") " + $"color({color:X}) icon({icon}) yOffset({yOffset})"); Log.Verbose("[FlyText] Calling flytext events!"); this.FlyTextCreated?.Invoke( @@ -204,12 +189,15 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui return IntPtr.Zero; } + var maybeModifiedText1 = tmpText1.EncodeWithNullTerminator(); + var maybeModifiedText2 = tmpText2.EncodeWithNullTerminator(); + // Check if any values have changed var dirty = tmpKind != kind || tmpVal1 != val1 || tmpVal2 != val2 || - tmpText1.ToString() != cmpText1 || - tmpText2.ToString() != cmpText2 || + !maybeModifiedText1.SequenceEqual(originalText1) || + !maybeModifiedText2.SequenceEqual(originalText2) || tmpDamageTypeIcon != damageTypeIcon || tmpColor != color || tmpIcon != icon || @@ -219,28 +207,26 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui if (!dirty) { Log.Verbose("[FlyText] Calling flytext with original args."); - return this.createFlyTextHook.Original(addonFlyText, kind, val1, val2, text2, color, icon, + return this.createFlyTextHook.Original(thisPtr, kind, val1, val2, text2, color, icon, damageTypeIcon, text1, yOffset); } - var terminated1 = Terminate(tmpText1.Encode()); - var terminated2 = Terminate(tmpText2.Encode()); - var pText1 = Marshal.AllocHGlobal(terminated1.Length); - var pText2 = Marshal.AllocHGlobal(terminated2.Length); - Marshal.Copy(terminated1, 0, pText1, terminated1.Length); - Marshal.Copy(terminated2, 0, pText2, terminated2.Length); + var pText1 = Marshal.AllocHGlobal(maybeModifiedText1.Length); + var pText2 = Marshal.AllocHGlobal(maybeModifiedText2.Length); + Marshal.Copy(maybeModifiedText1, 0, pText1, maybeModifiedText1.Length); + Marshal.Copy(maybeModifiedText2, 0, pText2, maybeModifiedText2.Length); Log.Verbose("[FlyText] Allocated and set strings."); retVal = this.createFlyTextHook.Original( - addonFlyText, + thisPtr, tmpKind, tmpVal1, tmpVal2, - pText2, + (byte*)pText2, tmpColor, tmpIcon, tmpDamageTypeIcon, - pText1, + (byte*)pText1, tmpYOffset); Log.Verbose("[FlyText] Returned from original. Delaying free task."); diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 80463a119..74e0a8df3 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -1,4 +1,3 @@ -using System.Numerics; using System.Runtime.InteropServices; using Dalamud.Game.Text.SeStringHandling.Payloads; @@ -18,7 +17,6 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; -using SharpDX; using Vector2 = System.Numerics.Vector2; using Vector3 = System.Numerics.Vector3; @@ -33,21 +31,19 @@ namespace Dalamud.Game.Gui; internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui { private static readonly ModuleLog Log = new("GameGui"); - + + [ServiceManager.ServiceDependency] + private readonly Framework framework = Service.Get(); + private readonly GameGuiAddressResolver address; private readonly Hook setGlobalBgmHook; - private readonly Hook handleItemHoverHook; - private readonly Hook handleItemOutHook; private readonly Hook handleActionHoverHook; private readonly Hook handleActionOutHook; private readonly Hook handleImmHook; - private readonly Hook toggleUiHideHook; + private readonly Hook setUiVisibilityHook; private readonly Hook utf8StringFromSequenceHook; - private GetUIMapObjectDelegate? getUIMapObject; - private OpenMapWithFlagDelegate? openMapWithFlag; - [ServiceManager.ServiceConstructor] private GameGui(TargetSigScanner sigScanner) { @@ -57,32 +53,27 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui Log.Verbose("===== G A M E G U I ====="); Log.Verbose($"GameGuiManager address {Util.DescribeAddress(this.address.BaseAddress)}"); Log.Verbose($"SetGlobalBgm address {Util.DescribeAddress(this.address.SetGlobalBgm)}"); - Log.Verbose($"HandleItemHover address {Util.DescribeAddress(this.address.HandleItemHover)}"); - Log.Verbose($"HandleItemOut address {Util.DescribeAddress(this.address.HandleItemOut)}"); Log.Verbose($"HandleImm address {Util.DescribeAddress(this.address.HandleImm)}"); this.setGlobalBgmHook = Hook.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); - this.handleItemHoverHook = Hook.FromAddress(this.address.HandleItemHover, this.HandleItemHoverDetour); - this.handleItemOutHook = Hook.FromAddress(this.address.HandleItemOut, this.HandleItemOutDetour); - this.handleActionHoverHook = Hook.FromAddress(this.address.HandleActionHover, this.HandleActionHoverDetour); this.handleActionOutHook = Hook.FromAddress(this.address.HandleActionOut, this.HandleActionOutDetour); this.handleImmHook = Hook.FromAddress(this.address.HandleImm, this.HandleImmDetour); - this.toggleUiHideHook = Hook.FromAddress(this.address.ToggleUiHide, this.ToggleUiHideDetour); + this.setUiVisibilityHook = Hook.FromAddress((nint)RaptureAtkModule.StaticVirtualTablePointer->SetUiVisibility, this.SetUiVisibilityDetour); this.utf8StringFromSequenceHook = Hook.FromAddress(this.address.Utf8StringFromSequence, this.Utf8StringFromSequenceDetour); this.setGlobalBgmHook.Enable(); - this.handleItemHoverHook.Enable(); - this.handleItemOutHook.Enable(); this.handleImmHook.Enable(); - this.toggleUiHideHook.Enable(); + this.setUiVisibilityHook.Enable(); this.handleActionHoverHook.Enable(); this.handleActionOutHook.Enable(); this.utf8StringFromSequenceHook.Enable(); + + this.framework.Update += this.FrameworkUpdate; } // Hooked delegates @@ -90,21 +81,9 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate Utf8String* Utf8StringFromSequenceDelegate(Utf8String* thisPtr, byte* sourcePtr, nuint sourceLen); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr GetUIMapObjectDelegate(IntPtr uiObject); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Ansi)] - private delegate bool OpenMapWithFlagDelegate(IntPtr uiMapObject, string flag); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr SetGlobalBgmDelegate(ushort bgmKey, byte a2, uint a3, uint a4, uint a5, byte a6); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr HandleItemHoverDelegate(IntPtr hoverState, IntPtr a2, IntPtr a3, ulong a4); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr HandleItemOutDelegate(IntPtr hoverState, IntPtr a2, IntPtr a3, ulong a4); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate void HandleActionHoverDelegate(IntPtr hoverState, HoverActionKind a2, uint a3, int a4, byte a5); @@ -113,9 +92,6 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr ToggleUiHideDelegate(IntPtr thisPtr, bool uiVisible); /// public event EventHandler? UiHideToggled; @@ -137,33 +113,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui /// public bool OpenMapWithMapLink(MapLinkPayload mapLink) - { - var uiModule = this.GetUIModule(); - - if (uiModule == IntPtr.Zero) - { - Log.Error("OpenMapWithMapLink: Null pointer returned from getUIObject()"); - return false; - } - - this.getUIMapObject ??= this.address.GetVirtualFunction(uiModule, 0, 8); - - var uiMapObjectPtr = this.getUIMapObject(uiModule); - - if (uiMapObjectPtr == IntPtr.Zero) - { - Log.Error("OpenMapWithMapLink: Null pointer returned from GetUIMapObject()"); - return false; - } - - this.openMapWithFlag ??= this.address.GetVirtualFunction(uiMapObjectPtr, 0, 63); - - var mapLinkString = mapLink.DataString; - - Log.Debug($"OpenMapWithMapLink: Opening Map Link: {mapLinkString}"); - - return this.openMapWithFlag(uiMapObjectPtr, mapLinkString); - } + => RaptureAtkModule.Instance()->OpenMapWithMapLink(mapLink.DataString); /// public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos) @@ -311,11 +261,11 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui /// void IInternalDisposableService.DisposeService() { + this.framework.Update -= this.FrameworkUpdate; + this.setGlobalBgmHook.Dispose(); - this.handleItemHoverHook.Dispose(); - this.handleItemOutHook.Dispose(); this.handleImmHook.Dispose(); - this.toggleUiHideHook.Dispose(); + this.setUiVisibilityHook.Dispose(); this.handleActionHoverHook.Dispose(); this.handleActionOutHook.Dispose(); this.utf8StringFromSequenceHook.Dispose(); @@ -359,51 +309,6 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return retVal; } - private IntPtr HandleItemHoverDetour(IntPtr hoverState, IntPtr a2, IntPtr a3, ulong a4) - { - var retVal = this.handleItemHoverHook.Original(hoverState, a2, a3, a4); - - if (retVal.ToInt64() == 22) - { - var itemId = (ulong)Marshal.ReadInt32(hoverState, 0x138); - this.HoveredItem = itemId; - - this.HoveredItemChanged?.InvokeSafely(this, itemId); - - Log.Verbose($"HoverItemId:{itemId} this:{hoverState.ToInt64()}"); - } - - return retVal; - } - - private IntPtr HandleItemOutDetour(IntPtr hoverState, IntPtr a2, IntPtr a3, ulong a4) - { - var retVal = this.handleItemOutHook.Original(hoverState, a2, a3, a4); - - if (a3 != IntPtr.Zero && a4 == 1) - { - var a3Val = Marshal.ReadByte(a3, 0x8); - - if (a3Val == 255) - { - this.HoveredItem = 0ul; - - try - { - this.HoveredItemChanged?.Invoke(this, 0ul); - } - catch (Exception e) - { - Log.Error(e, "Could not dispatch HoveredItemChanged event."); - } - - Log.Verbose("HoverItemId: 0"); - } - } - - return retVal; - } - private void HandleActionHoverDetour(IntPtr hoverState, HoverActionKind actionKind, uint actionId, int a4, byte a5) { this.handleActionHoverHook.Original(hoverState, actionKind, actionId, a4, a5); @@ -445,16 +350,14 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return retVal; } - private IntPtr ToggleUiHideDetour(IntPtr thisPtr, bool unknownByte) + private unsafe void SetUiVisibilityDetour(RaptureAtkModule* thisPtr, bool uiVisible) { - var result = this.toggleUiHideHook.Original(thisPtr, unknownByte); + this.setUiVisibilityHook.Original(thisPtr, uiVisible); this.GameUiHidden = !RaptureAtkModule.Instance()->IsUiVisible; this.UiHideToggled?.InvokeSafely(this, this.GameUiHidden); - Log.Debug("UiHide toggled: {0}", this.GameUiHidden); - - return result; + Log.Debug("GameUiHidden: {0}", this.GameUiHidden); } private char HandleImmDetour(IntPtr framework, char a2, byte a3) @@ -477,6 +380,24 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return thisPtr; // this function shouldn't need to return but the original asm moves this into rax before returning so be safe? } + + private unsafe void FrameworkUpdate(IFramework framework) + { + var agentItemDetail = AgentItemDetail.Instance(); + if (agentItemDetail != null) + { + var itemId = agentItemDetail->ItemId; + + if (this.HoveredItem != itemId) + { + Log.Verbose($"HoveredItem changed: {itemId}"); + + this.HoveredItem = itemId; + + this.HoveredItemChanged?.InvokeSafely(this, itemId); + } + } + } } /// diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index 5b02a2d09..a742541ea 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -15,16 +15,6 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver /// public IntPtr SetGlobalBgm { get; private set; } - /// - /// Gets the address of the native HandleItemHover method. - /// - public IntPtr HandleItemHover { get; private set; } - - /// - /// Gets the address of the native HandleItemOut method. - /// - public IntPtr HandleItemOut { get; private set; } - /// /// Gets the address of the native HandleActionHover method. /// @@ -40,11 +30,6 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver /// public IntPtr HandleImm { get; private set; } - /// - /// Gets the address of the native ToggleUiHide method. - /// - public IntPtr ToggleUiHide { get; private set; } - /// /// Gets the address of the native Utf8StringFromSequence method. /// @@ -54,13 +39,10 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver protected override void Setup64Bit(ISigScanner sig) { this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); - this.HandleItemHover = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 6C 24 48 48 8B 74 24 50 4C 89 B7 08 01 00 00"); - this.HandleItemOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 4D"); this.HandleActionHover = sig.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 83 F8 0F"); this.HandleActionOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 4D 85 C0 74 1F"); this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); - this.ToggleUiHide = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 44 0F B6 81"); this.Utf8StringFromSequence = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 41 22 66 C7 41 ?? ?? ?? 48 89 01 49 8B D8"); } } diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs b/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs index 2def0ea00..32192ad21 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Objects; @@ -20,16 +20,6 @@ namespace Dalamud.Game.Gui.NamePlate; [ServiceManager.EarlyLoadedService] internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui { - /// - /// The index for the number array used by the NamePlate addon. - /// - public const int NumberArrayIndex = 5; - - /// - /// The index for the string array used by the NamePlate addon. - /// - public const int StringArrayIndex = 4; - /// /// The index for of the FullUpdate entry in the NamePlate number array. /// @@ -81,18 +71,11 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui /// public unsafe void RequestRedraw() { - var addon = this.gameGui.GetAddonByName("NamePlate"); - if (addon != 0) + var addon = (AddonNamePlate*)this.gameGui.GetAddonByName("NamePlate"); + if (addon != null) { - var raptureAtkModule = RaptureAtkModule.Instance(); - if (raptureAtkModule == null) - { - return; - } - - ((AddonNamePlate*)addon)->DoFullUpdate = 1; - var namePlateNumberArrayData = raptureAtkModule->AtkArrayDataHolder.NumberArrays[NumberArrayIndex]; - namePlateNumberArrayData->SetValue(NumberArrayFullUpdateIndex, 1); + addon->DoFullUpdate = 1; + AtkStage.Instance()->GetNumberArrayData(NumberArrayType.NamePlate)->SetValue(NumberArrayFullUpdateIndex, 1); } } diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs index 876c4c2e0..fef3f9a86 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateUpdateContext.cs @@ -140,9 +140,9 @@ internal unsafe class NamePlateUpdateContext : INamePlateUpdateContext public void ResetState(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) { this.Addon = (AddonNamePlate*)addon; - this.NumberData = numberArrayData[NamePlateGui.NumberArrayIndex]; + this.NumberData = AtkStage.Instance()->GetNumberArrayData(NumberArrayType.NamePlate); this.NumberStruct = (AddonNamePlate.AddonNamePlateNumberArray*)this.NumberData->IntArray; - this.StringData = stringArrayData[NamePlateGui.StringArrayIndex]; + this.StringData = AtkStage.Instance()->GetStringArrayData(StringArrayType.NamePlate); this.HasParts = false; this.ActiveNamePlateCount = this.NumberStruct->ActiveNamePlateCount; diff --git a/Dalamud/Game/Gui/Toast/ToastGui.cs b/Dalamud/Game/Gui/Toast/ToastGui.cs index 0895d1b6b..6b55b3408 100644 --- a/Dalamud/Game/Gui/Toast/ToastGui.cs +++ b/Dalamud/Game/Gui/Toast/ToastGui.cs @@ -5,8 +5,11 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; +using Dalamud.Memory; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI; + namespace Dalamud.Game.Gui.Toast; /// @@ -17,8 +20,6 @@ internal sealed partial class ToastGui : IInternalDisposableService, IToastGui { private const uint QuestToastCheckmarkMagic = 60081; - private readonly ToastGuiAddressResolver address; - private readonly Queue<(byte[] Message, ToastOptions Options)> normalQueue = new(); private readonly Queue<(byte[] Message, QuestToastOptions Options)> questQueue = new(); private readonly Queue errorQueue = new(); @@ -30,16 +31,12 @@ internal sealed partial class ToastGui : IInternalDisposableService, IToastGui /// /// Initializes a new instance of the class. /// - /// Sig scanner to use. [ServiceManager.ServiceConstructor] - private ToastGui(TargetSigScanner sigScanner) + private unsafe ToastGui() { - this.address = new ToastGuiAddressResolver(); - this.address.Setup(sigScanner); - - this.showNormalToastHook = Hook.FromAddress(this.address.ShowNormalToast, this.HandleNormalToastDetour); - this.showQuestToastHook = Hook.FromAddress(this.address.ShowQuestToast, this.HandleQuestToastDetour); - this.showErrorToastHook = Hook.FromAddress(this.address.ShowErrorToast, this.HandleErrorToastDetour); + this.showNormalToastHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->ShowWideText, this.HandleNormalToastDetour); + this.showQuestToastHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->ShowText, this.HandleQuestToastDetour); + this.showErrorToastHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->ShowErrorText, this.HandleErrorToastDetour); this.showNormalToastHook.Enable(); this.showQuestToastHook.Enable(); @@ -48,16 +45,16 @@ internal sealed partial class ToastGui : IInternalDisposableService, IToastGui #region Marshal delegates - private delegate IntPtr ShowNormalToastDelegate(IntPtr manager, IntPtr text, int layer, byte isTop, byte isFast, int logMessageId); + private unsafe delegate void ShowNormalToastDelegate(UIModule* thisPtr, byte* text, int layer, byte isTop, byte isFast, uint logMessageId); - private delegate byte ShowQuestToastDelegate(IntPtr manager, int position, IntPtr text, uint iconOrCheck1, byte playSound, uint iconOrCheck2, byte alsoPlaySound); + private unsafe delegate void ShowQuestToastDelegate(UIModule* thisPtr, int position, byte* text, uint iconOrCheck1, byte playSound, uint iconOrCheck2, byte alsoPlaySound); - private delegate byte ShowErrorToastDelegate(IntPtr manager, IntPtr text, byte respectsHidingMaybe); + private unsafe delegate void ShowErrorToastDelegate(UIModule* thisPtr, byte* text, byte respectsHidingMaybe); #endregion #region Events - + /// public event IToastGui.OnNormalToastDelegate? Toast; @@ -102,32 +99,6 @@ internal sealed partial class ToastGui : IInternalDisposableService, IToastGui this.ShowError(message); } } - - private static byte[] Terminate(byte[] source) - { - var terminated = new byte[source.Length + 1]; - Array.Copy(source, 0, terminated, 0, source.Length); - terminated[^1] = 0; - - return terminated; - } - - private SeString ParseString(IntPtr text) - { - var bytes = new List(); - unsafe - { - var ptr = (byte*)text; - while (*ptr != 0) - { - bytes.Add(*ptr); - ptr += 1; - } - } - - // call events - return SeString.Parse(bytes.ToArray()); - } } /// @@ -149,36 +120,30 @@ internal sealed partial class ToastGui this.normalQueue.Enqueue((message.Encode(), options)); } - private void ShowNormal(byte[] bytes, ToastOptions? options = null) + private unsafe void ShowNormal(byte[] bytes, ToastOptions? options = null) { options ??= new ToastOptions(); - var manager = Service.GetNullable()?.GetUIModule(); - if (manager == null) - return; - - // terminate the string - var terminated = Terminate(bytes); - - unsafe + fixed (byte* ptr = bytes.NullTerminate()) { - fixed (byte* ptr = terminated) - { - this.HandleNormalToastDetour(manager!.Value, (IntPtr)ptr, 5, (byte)options.Position, (byte)options.Speed, 0); - } + this.HandleNormalToastDetour( + UIModule.Instance(), + ptr, + 5, + (byte)options.Position, + (byte)options.Speed, + 0); } } - private IntPtr HandleNormalToastDetour(IntPtr manager, IntPtr text, int layer, byte isTop, byte isFast, int logMessageId) + private unsafe void HandleNormalToastDetour(UIModule* thisPtr, byte* text, int layer, byte isTop, byte isFast, uint logMessageId) { - if (text == IntPtr.Zero) - { - return IntPtr.Zero; - } + if (text == null) + return; // call events var isHandled = false; - var str = this.ParseString(text); + var str = MemoryHelper.ReadSeStringNullTerminated((nint)text); var options = new ToastOptions { Position = (ToastPosition)isTop, @@ -189,18 +154,17 @@ internal sealed partial class ToastGui // do nothing if handled if (isHandled) - { - return IntPtr.Zero; - } + return; - var terminated = Terminate(str.Encode()); - - unsafe + fixed (byte* ptr = str.EncodeWithNullTerminator()) { - fixed (byte* message = terminated) - { - return this.showNormalToastHook.Original(manager, (IntPtr)message, layer, (byte)options.Position, (byte)options.Speed, logMessageId); - } + this.showNormalToastHook.Original( + thisPtr, + ptr, + layer, + (byte)(options.Position == ToastPosition.Top ? 1 : 0), + (byte)(options.Speed == ToastSpeed.Fast ? 1 : 0), + logMessageId); } } } @@ -224,45 +188,33 @@ internal sealed partial class ToastGui this.questQueue.Enqueue((message.Encode(), options)); } - private void ShowQuest(byte[] bytes, QuestToastOptions? options = null) + private unsafe void ShowQuest(byte[] bytes, QuestToastOptions? options = null) { options ??= new QuestToastOptions(); - var manager = Service.GetNullable()?.GetUIModule(); - if (manager == null) - return; - - // terminate the string - var terminated = Terminate(bytes); - var (ioc1, ioc2) = this.DetermineParameterOrder(options); - unsafe + fixed (byte* ptr = bytes.NullTerminate()) { - fixed (byte* ptr = terminated) - { - this.HandleQuestToastDetour( - manager!.Value, - (int)options.Position, - (IntPtr)ptr, - ioc1, - options.PlaySound ? (byte)1 : (byte)0, - ioc2, - 0); - } + this.HandleQuestToastDetour( + UIModule.Instance(), + (int)options.Position, + ptr, + ioc1, + (byte)(options.PlaySound ? 1 : 0), + ioc2, + 0); } } - private byte HandleQuestToastDetour(IntPtr manager, int position, IntPtr text, uint iconOrCheck1, byte playSound, uint iconOrCheck2, byte alsoPlaySound) + private unsafe void HandleQuestToastDetour(UIModule* thisPtr, int position, byte* text, uint iconOrCheck1, byte playSound, uint iconOrCheck2, byte alsoPlaySound) { - if (text == IntPtr.Zero) - { - return 0; - } + if (text == null) + return; // call events var isHandled = false; - var str = this.ParseString(text); + var str = SeString.Parse(text); var options = new QuestToastOptions { Position = (QuestToastPosition)position, @@ -275,27 +227,20 @@ internal sealed partial class ToastGui // do nothing if handled if (isHandled) - { - return 0; - } - - var terminated = Terminate(str.Encode()); + return; var (ioc1, ioc2) = this.DetermineParameterOrder(options); - unsafe + fixed (byte* ptr = str.EncodeWithNullTerminator()) { - fixed (byte* message = terminated) - { - return this.showQuestToastHook.Original( - manager, - (int)options.Position, - (IntPtr)message, - ioc1, - options.PlaySound ? (byte)1 : (byte)0, - ioc2, - 0); - } + this.showQuestToastHook.Original( + UIModule.Instance(), + (int)options.Position, + ptr, + ioc1, + (byte)(options.PlaySound ? 1 : 0), + ioc2, + 0); } } @@ -324,51 +269,32 @@ internal sealed partial class ToastGui this.errorQueue.Enqueue(message.Encode()); } - private void ShowError(byte[] bytes) + private unsafe void ShowError(byte[] bytes) { - var manager = Service.GetNullable()?.GetUIModule(); - if (manager == null) - return; - - // terminate the string - var terminated = Terminate(bytes); - - unsafe + fixed (byte* ptr = bytes.NullTerminate()) { - fixed (byte* ptr = terminated) - { - this.HandleErrorToastDetour(manager!.Value, (IntPtr)ptr, 0); - } + this.HandleErrorToastDetour(UIModule.Instance(), ptr, 0); } } - private byte HandleErrorToastDetour(IntPtr manager, IntPtr text, byte respectsHidingMaybe) + private unsafe void HandleErrorToastDetour(UIModule* thisPtr, byte* text, byte respectsHidingMaybe) { - if (text == IntPtr.Zero) - { - return 0; - } + if (text == null) + return; // call events var isHandled = false; - var str = this.ParseString(text); + var str = SeString.Parse(text); this.ErrorToast?.Invoke(ref str, ref isHandled); // do nothing if handled if (isHandled) - { - return 0; - } + return; - var terminated = Terminate(str.Encode()); - - unsafe + fixed (byte* ptr = str.EncodeWithNullTerminator()) { - fixed (byte* message = terminated) - { - return this.showErrorToastHook.Original(manager, (IntPtr)message, respectsHidingMaybe); - } + this.showErrorToastHook.Original(thisPtr, ptr, respectsHidingMaybe); } } } diff --git a/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs b/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs deleted file mode 100644 index 0a8775540..000000000 --- a/Dalamud/Game/Gui/Toast/ToastGuiAddressResolver.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Dalamud.Game.Gui.Toast; - -/// -/// An address resolver for the class. -/// -internal class ToastGuiAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the native ShowNormalToast method. - /// - public IntPtr ShowNormalToast { get; private set; } - - /// - /// Gets the address of the native ShowQuestToast method. - /// - public IntPtr ShowQuestToast { get; private set; } - - /// - /// Gets the address of the ShowErrorToast method. - /// - public IntPtr ShowErrorToast { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - this.ShowNormalToast = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 83 3D ?? ?? ?? ?? ??"); - this.ShowQuestToast = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 40 83 3D ?? ?? ?? ?? ??"); - this.ShowErrorToast = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 83 3D ?? ?? ?? ?? ?? 41 0F B6 F0"); - } -} diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index 39ff7c241..c147f76e6 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -6,9 +6,11 @@ using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Hooking; using Dalamud.Interface.Internal; using Dalamud.Interface.Windowing; +using Dalamud.Logging.Internal; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; -using Serilog; using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; @@ -20,12 +22,14 @@ namespace Dalamud.Game.Internal; [ServiceManager.EarlyLoadedService] internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService { + private static readonly ModuleLog Log = new("DalamudAtkTweaks"); + private readonly Hook hookAgentHudOpenSystemMenu; // TODO: Make this into events in Framework.Gui - private readonly Hook hookUiModuleRequestMainCommand; + private readonly Hook hookUiModuleExecuteMainCommand; - private readonly Hook hookAtkUnitBaseReceiveGlobalEvent; + private readonly Hook hookAtkUnitBaseReceiveGlobalEvent; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -44,12 +48,8 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CF 4C 89 B4 24 B8 08 00 00"); this.hookAgentHudOpenSystemMenu = Hook.FromAddress(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour); - - var uiModuleRequestMainCommandAddress = sigScanner.ScanText("40 53 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 01 8B DA 48 8B F1 FF 90 ?? ?? ?? ??"); - this.hookUiModuleRequestMainCommand = Hook.FromAddress(uiModuleRequestMainCommandAddress, this.UiModuleRequestMainCommandDetour); - - var atkUnitBaseReceiveGlobalEventAddress = sigScanner.ScanText("48 89 5C 24 ?? 48 89 7C 24 ?? 55 41 54 41 57"); - this.hookAtkUnitBaseReceiveGlobalEvent = Hook.FromAddress(atkUnitBaseReceiveGlobalEventAddress, this.AtkUnitBaseReceiveGlobalEventDetour); + this.hookUiModuleExecuteMainCommand = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->ExecuteMainCommand, this.UiModuleExecuteMainCommandDetour); + this.hookAtkUnitBaseReceiveGlobalEvent = Hook.FromAddress((nint)AtkUnitBase.StaticVirtualTablePointer->ReceiveGlobalEvent, this.AtkUnitBaseReceiveGlobalEventDetour); this.locDalamudPlugins = Loc.Localize("SystemMenuPlugins", "Dalamud Plugins"); this.locDalamudSettings = Loc.Localize("SystemMenuSettings", "Dalamud Settings"); @@ -57,18 +57,14 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService // this.contextMenu.ContextMenuOpened += this.ContextMenuOnContextMenuOpened; this.hookAgentHudOpenSystemMenu.Enable(); - this.hookUiModuleRequestMainCommand.Enable(); + this.hookUiModuleExecuteMainCommand.Enable(); this.hookAtkUnitBaseReceiveGlobalEvent.Enable(); } /// Finalizes an instance of the class. ~DalamudAtkTweaks() => this.Dispose(false); - private delegate void AgentHudOpenSystemMenuPrototype(void* thisPtr, AtkValue* atkValueArgs, uint menuSize); - - private delegate void UiModuleRequestMainCommand(void* thisPtr, int commandId); - - private delegate IntPtr AtkUnitBaseReceiveGlobalEvent(AtkUnitBase* thisPtr, ushort cmd, uint a3, IntPtr a4, uint* a5); + private delegate void AgentHudOpenSystemMenuPrototype(AgentHUD* thisPtr, AtkValue* atkValueArgs, uint menuSize); /// void IInternalDisposableService.DisposeService() => this.Dispose(true); @@ -81,7 +77,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService if (disposing) { this.hookAgentHudOpenSystemMenu.Dispose(); - this.hookUiModuleRequestMainCommand.Dispose(); + this.hookUiModuleExecuteMainCommand.Dispose(); this.hookAtkUnitBaseReceiveGlobalEvent.Dispose(); // this.contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened; @@ -116,22 +112,19 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService } */ - private IntPtr AtkUnitBaseReceiveGlobalEventDetour(AtkUnitBase* thisPtr, ushort cmd, uint a3, IntPtr a4, uint* arg) + private void AtkUnitBaseReceiveGlobalEventDetour(AtkUnitBase* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - // Log.Information("{0}: cmd#{1} a3#{2} - HasAnyFocus:{3}", MemoryHelper.ReadSeStringAsString(out _, new IntPtr(thisPtr->Name)), cmd, a3, WindowSystem.HasAnyWindowSystemFocus); - - // "SendHotkey" // 3 == Close - if (cmd == 12 && WindowSystem.HasAnyWindowSystemFocus && *arg == 3 && this.configuration.IsFocusManagementEnabled) + if (eventType == AtkEventType.InputReceived && WindowSystem.HasAnyWindowSystemFocus && atkEventData != null && *(int*)atkEventData == 3 && this.configuration.IsFocusManagementEnabled) { Log.Verbose($"Cancelling global event SendHotkey command due to WindowSystem {WindowSystem.FocusedWindowSystemNamespace}"); - return IntPtr.Zero; + return; } - return this.hookAtkUnitBaseReceiveGlobalEvent.Original(thisPtr, cmd, a3, a4, arg); + this.hookAtkUnitBaseReceiveGlobalEvent.Original(thisPtr, eventType, eventParam, atkEvent, atkEventData); } - private void AgentHudOpenSystemMenuDetour(void* thisPtr, AtkValue* atkValueArgs, uint menuSize) + private void AgentHudOpenSystemMenuDetour(AgentHUD* thisPtr, AtkValue* atkValueArgs, uint menuSize) { if (WindowSystem.HasAnyWindowSystemFocus && this.configuration.IsFocusManagementEnabled) { @@ -213,7 +206,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService this.hookAgentHudOpenSystemMenu.Original(thisPtr, atkValueArgs, menuSize + 2); } - private void UiModuleRequestMainCommandDetour(void* thisPtr, int commandId) + private unsafe void UiModuleExecuteMainCommandDetour(UIModule* thisPtr, uint commandId) { var dalamudInterface = Service.GetNullable(); @@ -226,7 +219,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService dalamudInterface?.OpenSettings(); break; default: - this.hookUiModuleRequestMainCommand.Original(thisPtr, commandId); + this.hookUiModuleExecuteMainCommand.Original(thisPtr, commandId); break; } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs index c35280f92..961d3c3c0 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.ClientState.Buddy; +using Dalamud.Game.ClientState.Buddy; using Dalamud.Utility; using ImGuiNET; @@ -32,8 +32,6 @@ internal class BuddyListWidget : IDataWindowWidget var buddyList = Service.Get(); ImGui.Checkbox("Resolve GameData", ref this.resolveGameData); - - ImGui.Text($"BuddyList: {buddyList.BuddyListAddress.ToInt64():X}"); { var member = buddyList.CompanionBuddy; if (member == null) diff --git a/Dalamud/Plugin/Services/IChatGui.cs b/Dalamud/Plugin/Services/IChatGui.cs index 09f485ac2..42bbd6b06 100644 --- a/Dalamud/Plugin/Services/IChatGui.cs +++ b/Dalamud/Plugin/Services/IChatGui.cs @@ -72,7 +72,7 @@ public interface IChatGui /// /// Gets the ID of the last linked item. /// - public int LastLinkedItemId { get; } + public uint LastLinkedItemId { get; } /// /// Gets the flags of the last linked item. From 67e6bcac61844e7e9a897f1b4f5cadf347b8d47a Mon Sep 17 00:00:00 2001 From: KazWolfe Date: Tue, 12 Nov 2024 08:23:22 -0800 Subject: [PATCH 17/50] fix: XivChatType respects the configured chat channel if not set (#2001) --- Dalamud/Game/Gui/ChatGui.cs | 4 +++- Dalamud/Game/Text/XivChatEntry.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 2482c36b4..36e27995d 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -168,8 +168,10 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui var sender = Utf8String.FromSequence(chat.Name.Encode()); var message = Utf8String.FromSequence(replacedMessage.BuiltString.Encode()); + + var targetChannel = chat.Type ?? this.configuration.GeneralChatType; - this.HandlePrintMessageDetour(RaptureLogModule.Instance(), chat.Type, sender, message, chat.Timestamp, (byte)(chat.Silent ? 1 : 0)); + this.HandlePrintMessageDetour(RaptureLogModule.Instance(), targetChannel, sender, message, chat.Timestamp, (byte)(chat.Silent ? 1 : 0)); sender->Dtor(true); message->Dtor(true); diff --git a/Dalamud/Game/Text/XivChatEntry.cs b/Dalamud/Game/Text/XivChatEntry.cs index 25f752054..eb40d6636 100644 --- a/Dalamud/Game/Text/XivChatEntry.cs +++ b/Dalamud/Game/Text/XivChatEntry.cs @@ -10,7 +10,7 @@ public sealed class XivChatEntry /// /// Gets or sets the type of entry. /// - public XivChatType Type { get; set; } = XivChatType.Debug; + public XivChatType? Type { get; set; } /// /// Gets or sets the message timestamp. From 57cc2f36f9aeb084774089bfe20467f1f242289d Mon Sep 17 00:00:00 2001 From: Julian Date: Tue, 12 Nov 2024 11:27:23 -0500 Subject: [PATCH 18/50] feat: Encourage developers to use DLLs for devPlugins (#1958) * Add file dialog to add a dev plugin * Require dev plugins to be paths to the DLL * Only allow .dlls in the dev plugin setting entry * Update dev plugin location hint * update wording --------- Co-authored-by: KazWolfe --- .../Windows/Settings/SettingsEntry.cs | 7 ++ .../Internal/Windows/Settings/SettingsTab.cs | 8 ++ .../Windows/Settings/SettingsWindow.cs | 26 ++++--- .../Widgets/DevPluginsSettingsEntry.cs | 76 +++++++++++++------ Dalamud/Plugin/Internal/PluginManager.cs | 6 +- 5 files changed, 87 insertions(+), 36 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs index a72cae024..46013b72c 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsEntry.cs @@ -40,6 +40,13 @@ public abstract class SettingsEntry /// public abstract void Draw(); + /// + /// Called after the draw function and when the style overrides are removed. + /// + public virtual void PostDraw() + { + } + /// /// Function to be called when the tab is opened. /// diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs index d06fe0fb6..bd4a702f5 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsTab.cs @@ -44,6 +44,14 @@ public abstract class SettingsTab : IDisposable ImGuiHelpers.ScaledDummy(15); } + public virtual void PostDraw() + { + foreach (var settingsEntry in this.Entries) + { + settingsEntry.PostDraw(); + } + } + public virtual void Load() { foreach (var settingsEntry in this.Entries) diff --git a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs index 5e3857f42..c678dff10 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/SettingsWindow.cs @@ -154,15 +154,23 @@ internal class SettingsWindow : Window } // Don't add padding for the about tab(credits) - using var padding = ImRaii.PushStyle(ImGuiStyleVar.WindowPadding, new Vector2(2, 2), - settingsTab is not SettingsTabAbout); - using var borderColor = ImRaii.PushColor(ImGuiCol.Border, ImGui.GetColorU32(ImGuiCol.ChildBg)); - using var tabChild = ImRaii.Child( - $"###settings_scrolling_{settingsTab.Title}", - new Vector2(-1, -1), - true); - if (tabChild) - settingsTab.Draw(); + { + using var padding = ImRaii.PushStyle( + ImGuiStyleVar.WindowPadding, + new Vector2(2, 2), + settingsTab is not SettingsTabAbout); + using var borderColor = ImRaii.PushColor( + ImGuiCol.Border, + ImGui.GetColorU32(ImGuiCol.ChildBg)); + using var tabChild = ImRaii.Child( + $"###settings_scrolling_{settingsTab.Title}", + new Vector2(-1, -1), + true); + if (tabChild) + settingsTab.Draw(); + } + + settingsTab.PostDraw(); } else if (settingsTab.IsOpen) { diff --git a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs index cfb1ff39f..fe4462ce2 100644 --- a/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs +++ b/Dalamud/Interface/Internal/Windows/Settings/Widgets/DevPluginsSettingsEntry.cs @@ -10,6 +10,7 @@ using Dalamud.Configuration; using Dalamud.Configuration.Internal; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; +using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Internal; @@ -25,6 +26,7 @@ public class DevPluginsSettingsEntry : SettingsEntry private bool devPluginLocationsChanged; private string devPluginTempLocation = string.Empty; private string devPluginLocationAddError = string.Empty; + private FileDialogManager fileDialogManager = new(); public DevPluginsSettingsEntry() { @@ -68,7 +70,23 @@ public class DevPluginsSettingsEntry : SettingsEntry } } - ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add dev plugin load locations.\nThese can be either the directory or DLL path.")); + ImGuiHelpers.SafeTextColoredWrapped(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsDevPluginLocationsHint", "Add dev plugin load locations.\nThis must be a path to the plugin DLL.")); + + var locationSelect = Loc.Localize("DalamudDevPluginLocationSelect", "Select Dev Plugin DLL"); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Folder, locationSelect)) + { + this.fileDialogManager.OpenFileDialog( + locationSelect, + ".dll", + (result, path) => + { + if (result) + { + this.devPluginTempLocation = path; + this.AddDevPlugin(); + } + }); + } ImGuiHelpers.ScaledDummy(5); @@ -167,26 +185,7 @@ public class DevPluginsSettingsEntry : SettingsEntry ImGui.NextColumn(); if (!string.IsNullOrEmpty(this.devPluginTempLocation) && ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) { - if (this.devPluginLocations.Any(r => string.Equals(r.Path, this.devPluginTempLocation, StringComparison.InvariantCultureIgnoreCase))) - { - this.devPluginLocationAddError = Loc.Localize("DalamudDevPluginLocationExists", "Location already exists."); - Task.Delay(5000).ContinueWith(t => this.devPluginLocationAddError = string.Empty); - } - else if (!ValidDevPluginPath(this.devPluginTempLocation)) - { - this.devPluginLocationAddError = Loc.Localize("DalamudDevPluginInvalid", "The entered value is not a valid path to a potential Dev Plugin.\nDid you mean to enter it as a custom plugin repository in the fields below instead?"); - Task.Delay(5000).ContinueWith(t => this.devPluginLocationAddError = string.Empty); - } - else - { - this.devPluginLocations.Add(new DevPluginLocationSettings - { - Path = this.devPluginTempLocation.Replace("\"", string.Empty), - IsEnabled = true, - }); - this.devPluginLocationsChanged = true; - this.devPluginTempLocation = string.Empty; - } + this.AddDevPlugin(); } ImGui.Columns(1); @@ -197,6 +196,39 @@ public class DevPluginsSettingsEntry : SettingsEntry } } + private void AddDevPlugin() + { + if (this.devPluginLocations.Any( + r => string.Equals(r.Path, this.devPluginTempLocation, StringComparison.InvariantCultureIgnoreCase))) + { + this.devPluginLocationAddError = Loc.Localize("DalamudDevPluginLocationExists", "Location already exists."); + Task.Delay(5000).ContinueWith(t => this.devPluginLocationAddError = string.Empty); + } + else if (!ValidDevPluginPath(this.devPluginTempLocation)) + { + this.devPluginLocationAddError = Loc.Localize( + "DalamudDevPluginInvalid", + "The entered value is not a valid path to a potential Dev Plugin.\nDid you mean to enter it as a custom plugin repository in the fields below instead?"); + Task.Delay(5000).ContinueWith(t => this.devPluginLocationAddError = string.Empty); + } + else + { + this.devPluginLocations.Add( + new DevPluginLocationSettings + { + Path = this.devPluginTempLocation.Replace("\"", string.Empty), + IsEnabled = true, + }); + this.devPluginLocationsChanged = true; + this.devPluginTempLocation = string.Empty; + } + } + + public override void PostDraw() + { + this.fileDialogManager.Draw(); + } + private static bool ValidDevPluginPath(string path) - => Path.IsPathRooted(path) && (Path.GetExtension(path) == ".dll" || !Path.Exists(path) || Directory.Exists(path)); + => Path.IsPathRooted(path) && Path.GetExtension(path) == ".dll"; } diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 6ad81a806..ddb59c027 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -772,11 +772,7 @@ internal class PluginManager : IInternalDisposableService Log.Verbose("Scanning dev plugins at {Path}", setting.Path); - if (Directory.Exists(setting.Path)) - { - devDllFiles.AddRange(new DirectoryInfo(setting.Path).GetFiles("*.dll", SearchOption.AllDirectories)); - } - else if (File.Exists(setting.Path)) + if (File.Exists(setting.Path)) { devDllFiles.Add(new FileInfo(setting.Path)); } From 401af52c40c60eae040eca5ad380d27f6e987a3a Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 08:31:41 -0800 Subject: [PATCH 19/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 751645811..9fe41b4d7 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 7516458116708bdcf8f72848c7692f312be3b40c +Subproject commit 9fe41b4d7e4255652e31cebaf00261ade5bbd618 From 486192662ddb453ffcfe8822ea6c34ddd2ccf0d7 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 08:40:10 -0800 Subject: [PATCH 20/50] New SetupTerritoryType sig Co-authored-by: Ottermandias <70807659+ottermandias@users.noreply.github.com> --- Dalamud/Game/ClientState/ClientStateAddressResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index cc635f784..c7d7837a4 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -41,7 +41,7 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 83 EC 20 0F B7 DA"); + this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC ?? 48 8B D9 48 89 6C 24"); this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); From 10fc7656d3da9cfea28bbecce360c37196569bc2 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 10:26:21 -0800 Subject: [PATCH 21/50] bump cs, ts name fix --- Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs | 4 ++-- lib/FFXIVClientStructs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index e568ff972..faf99aa2a 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -432,9 +432,9 @@ internal class TitleScreenMenuWindow : Window, IDisposable private unsafe void OnVersionStringDraw(AddonEvent ev, AddonArgs args) { - if (args is not AddonDrawArgs setupArgs) return; + if (args is not AddonDrawArgs drawArgs) return; - var addon = (AtkUnitBase*)setupArgs.Addon; + var addon = (AtkUnitBase*)drawArgs.Addon; var textNode = addon->GetTextNodeById(3); // look and feel init. should be harmless to set. diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 9fe41b4d7..f069f71f5 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 9fe41b4d7e4255652e31cebaf00261ade5bbd618 +Subproject commit f069f71f558664f6f8113f996a4828616deba6e0 From dc3818f42b4267ba29d72e31853d75cdb706585f Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 14:47:16 -0800 Subject: [PATCH 22/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index f069f71f5..3028a2a2a 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit f069f71f558664f6f8113f996a4828616deba6e0 +Subproject commit 3028a2a2ad45043374956d5560992974e312cb69 From c6b0db069e561520b0ab1c5561c95a1cffd99698 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 16:07:28 -0800 Subject: [PATCH 23/50] fix: InventoryItem#Copy is a vfunc now --- Dalamud/Game/Gui/ChatGui.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 36e27995d..fefc82790 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -51,7 +51,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui private ChatGui() { this.printMessageHook = Hook.FromAddress(RaptureLogModule.Addresses.PrintMessage.Value, this.HandlePrintMessageDetour); - this.inventoryItemCopyHook = Hook.FromAddress(InventoryItem.Addresses.Copy.Value, this.InventoryItemCopyDetour); + this.inventoryItemCopyHook = Hook.FromAddress((nint)InventoryItem.StaticVirtualTablePointer->Copy, this.InventoryItemCopyDetour); this.handleLinkClickHook = Hook.FromAddress(LogViewer.Addresses.HandleLinkClick.Value, this.HandleLinkClickDetour); this.printMessageHook.Enable(); From e2e11d5f05d499ce92ac7bb4dc0e8d8482dc94e7 Mon Sep 17 00:00:00 2001 From: goat Date: Wed, 13 Nov 2024 01:18:37 +0100 Subject: [PATCH 24/50] fix nonstandard sizeof() usage --- Dalamud.Boot/utils.cpp | 2 +- Dalamud.Boot/utils.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp index 65018add4..bbe47db82 100644 --- a/Dalamud.Boot/utils.cpp +++ b/Dalamud.Boot/utils.cpp @@ -103,7 +103,7 @@ bool utils::loaded_module::find_imported_function_pointer(const char* pcszDllNam ppFunctionAddress = nullptr; // This span might be too long in terms of meaningful data; it only serves to prevent accessing memory outsides boundaries. - for (const auto& importDescriptor : span_as(directory.VirtualAddress, directory.Size / sizeof IMAGE_IMPORT_DESCRIPTOR)) { + for (const auto& importDescriptor : span_as(directory.VirtualAddress, directory.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR))) { // Having all zero values signals the end of the table. We didn't find anything. if (!importDescriptor.OriginalFirstThunk && !importDescriptor.TimeDateStamp && !importDescriptor.ForwarderChain && !importDescriptor.FirstThunk) diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h index f10e277c0..eef405b26 100644 --- a/Dalamud.Boot/utils.h +++ b/Dalamud.Boot/utils.h @@ -121,7 +121,7 @@ namespace utils { memory_tenderizer(const void* pAddress, size_t length, DWORD dwNewProtect); template&& std::is_standard_layout_v>> - memory_tenderizer(const T& object, DWORD dwNewProtect) : memory_tenderizer(&object, sizeof T, dwNewProtect) {} + memory_tenderizer(const T& object, DWORD dwNewProtect) : memory_tenderizer(&object, sizeof(T), dwNewProtect) {} template memory_tenderizer(std::span s, DWORD dwNewProtect) : memory_tenderizer(&s[0], s.size(), dwNewProtect) {} From fd6d52d33b94b769bf61c506b436c5e5c0e5e159 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 20:23:53 -0800 Subject: [PATCH 25/50] Extend IconBrowserWidget range Resolves #2074. Co-authored-by: ItsBexy <103910869+itsbexy@users.noreply.github.com> --- .../Windows/Data/Widgets/IconBrowserWidget.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs index 886f5cb19..ff34574b5 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/IconBrowserWidget.cs @@ -17,6 +17,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// public class IconBrowserWidget : IDataWindowWidget { + private const int MaxIconId = 250_000; + private Vector2 iconSize = new(64.0f, 64.0f); private Vector2 editIconSize = new(64.0f, 64.0f); @@ -24,7 +26,7 @@ public class IconBrowserWidget : IDataWindowWidget private Task>? iconIdsTask; private int startRange; - private int stopRange = 200000; + private int stopRange = MaxIconId; private bool showTooltipImage; private Vector2 mouseDragStart; @@ -53,8 +55,8 @@ public class IconBrowserWidget : IDataWindowWidget { var texm = Service.Get(); - var result = new List<(int ItemId, string Path)>(200000); - for (var iconId = 0; iconId < 200000; iconId++) + var result = new List<(int ItemId, string Path)>(MaxIconId); + for (var iconId = 0; iconId < MaxIconId; iconId++) { // // Remove range 170,000 -> 180,000 by default, this specific range causes exceptions. // if (iconId is >= 170000 and < 180000) @@ -119,12 +121,19 @@ public class IconBrowserWidget : IDataWindowWidget ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X); if (ImGui.InputInt("##StartRange", ref this.startRange, 0, 0)) + { + this.startRange = Math.Clamp(this.startRange, 0, MaxIconId); this.valueRange = null; + } + ImGui.NextColumn(); ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X); if (ImGui.InputInt("##StopRange", ref this.stopRange, 0, 0)) + { + this.stopRange = Math.Clamp(this.stopRange, 0, MaxIconId); this.valueRange = null; + } ImGui.NextColumn(); ImGui.Checkbox("Show Image in Tooltip", ref this.showTooltipImage); From 18363882a7d1f350578364341298fc57cfecccb7 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Nov 2024 21:00:32 -0800 Subject: [PATCH 26/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 3028a2a2a..039aae133 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 3028a2a2ad45043374956d5560992974e312cb69 +Subproject commit 039aae133cc8bed940e85d7bb02e40d3ad6b3435 From d6ac69ff6bc7b03e324c38bed963dd25cac068c8 Mon Sep 17 00:00:00 2001 From: Blair Date: Thu, 14 Nov 2024 02:15:24 +1000 Subject: [PATCH 27/50] Update Lumina to 5.2.1 and Lumina.Excel to 7.1.0 (#2075) --- Dalamud.CorePlugin/Dalamud.CorePlugin.csproj | 4 ++-- Dalamud/Dalamud.csproj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index 6edea55bf..449d3db68 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -27,8 +27,8 @@ - - + + all diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index b3f1cf30a..8e912fe8f 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -71,8 +71,8 @@ - - + + all @@ -160,7 +160,7 @@ - + @@ -168,7 +168,7 @@ ??? - + From f1570cc16899982a691990dba0084c1cea8eac08 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Wed, 13 Nov 2024 17:15:57 +0100 Subject: [PATCH 28/50] Update DalamudPackager in .targets (#2076) Goat updated DalamudPackager and released it as Version 11.0.0: https://github.com/goatcorp/DalamudPackager/commit/c4348c30e74ba92225289bb21b7414993614befc On Nuget: https://www.nuget.org/packages/DalamudPackager --- targets/Dalamud.Plugin.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/Dalamud.Plugin.targets b/targets/Dalamud.Plugin.targets index 10b63c437..da897c252 100644 --- a/targets/Dalamud.Plugin.targets +++ b/targets/Dalamud.Plugin.targets @@ -14,7 +14,7 @@ - + From 605246997654a679fd12afdb18fe9936395d7486 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 13 Nov 2024 08:23:17 -0800 Subject: [PATCH 29/50] bumpy da cs --- Dalamud/Utility/Util.cs | 24 +----------------------- lib/FFXIVClientStructs | 2 +- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index e37afb765..15327a66c 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -162,29 +162,7 @@ public static class Util } /// - /// Gets the amount of commits in the current branch, or null if undetermined. - /// - /// The amount of commits in the current branch. - [Obsolete($"Planned for removal in API 11. Use {nameof(GetScmVersion)} for version tracking.")] - public static int? GetGitCommitCount() - { - if (gitCommitCountInternal != null) - return gitCommitCountInternal.Value; - - var asm = typeof(Util).Assembly; - var attrs = asm.GetCustomAttributes(); - - var value = attrs.First(a => a.Key == "GitCommitCount").Value; - if (value == null) - return null; - - gitCommitCountInternal = int.Parse(value); - return gitCommitCountInternal.Value; - } - - /// - /// Gets the git hash value from the assembly - /// or null if it cannot be found. + /// Gets the git hash value from the assembly or null if it cannot be found. /// /// The git hash of the assembly. public static string? GetGitHashClientStructs() diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 039aae133..78c39ef13 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 039aae133cc8bed940e85d7bb02e40d3ad6b3435 +Subproject commit 78c39ef1318525d766cdf31fd9a32e6f1c7cb453 From 8668ea0d595bcd5bb23455874d4445e3e391b774 Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 13 Nov 2024 22:43:42 +0100 Subject: [PATCH 30/50] Fix universalis upload with itemId 0 (#2077) --- .../MarketBoardItemRequest.cs | 5 +++++ .../UniversalisMarketBoardUploader.cs | 4 ++-- .../Game/Network/Internal/NetworkHandlers.cs | 20 ++++++++++++------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/MarketBoardItemRequest.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/MarketBoardItemRequest.cs index 7af647867..a32a92b13 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/MarketBoardItemRequest.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/MarketBoardItemRequest.cs @@ -30,6 +30,11 @@ internal class MarketBoardItemRequest /// public uint AmountToArrive { get; private set; } + /// + /// Gets or sets the offered catalog id used in this listing. + /// + public uint CatalogId { get; internal set; } + /// /// Gets the offered item listings. /// diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs index a62de369b..5215cf030 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs @@ -48,7 +48,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader { WorldId = clientState.LocalPlayer?.CurrentWorld.RowId ?? 0, UploaderId = uploader.ToString(), - ItemId = request.Listings.FirstOrDefault()?.CatalogId ?? 0, + ItemId = request.CatalogId, Listings = [], Sales = [], }; @@ -106,7 +106,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader // ==================================================================================== - Log.Verbose("Universalis data upload for item#{CatalogId} completed", request.Listings.FirstOrDefault()?.CatalogId ?? 0); + Log.Verbose("Universalis data upload for item#{CatalogId} completed", request.CatalogId); } /// diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index ad8dbb600..2017be4bf 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -367,7 +367,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService })); } - private IObservable> OnMarketBoardSalesBatch( + private IObservable<(uint CatalogId, List Sales)> OnMarketBoardSalesBatch( IObservable start) { var historyObservable = this.MbHistoryObservable.Publish().RefCount(); @@ -390,6 +390,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService // When a start packet is observed, begin observing a window of history packets. // We should only get one packet, which the window closing function ensures. // This packet is flattened to its sale entries and emitted. + uint catalogId = 0; return historyObservable .Do(LogHistoryObserved) .Window(start, UntilBatchEnd) @@ -398,9 +399,12 @@ internal unsafe class NetworkHandlers : IInternalDisposableService new List(), (agg, next) => { + catalogId = next.CatalogId; + agg.AddRange(next.InternalHistoryListings); return agg; - })); + })) + .Select(o => (catalogId, o)); } private IDisposable HandleMarketBoardItemRequest() @@ -411,7 +415,8 @@ internal unsafe class NetworkHandlers : IInternalDisposableService } var startObservable = this.MbItemRequestObservable - .Where(request => request.Ok).Do(LogStartObserved) + .Where(request => request.Ok) + .Do(LogStartObserved) .Publish() .RefCount(); return Observable.When( @@ -432,10 +437,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private void UploadMarketBoardData( MarketBoardItemRequest request, - ICollection sales, + (uint CatalogId, ICollection Sales) sales, ICollection listings) { - var catalogId = listings.FirstOrDefault()?.CatalogId ?? 0; + var catalogId = sales.CatalogId; if (listings.Count != request.AmountToArrive) { Log.Error( @@ -448,10 +453,11 @@ internal unsafe class NetworkHandlers : IInternalDisposableService "Market Board request resolved, starting upload: item#{CatalogId} listings#{ListingsObserved} sales#{SalesObserved}", catalogId, listings.Count, - sales.Count); + sales.Sales.Count); + request.CatalogId = catalogId; request.Listings.AddRange(listings); - request.History.AddRange(sales); + request.History.AddRange(sales.Sales); Task.Run(() => this.uploader.Upload(request)) .ContinueWith( From 3a6312422ce2fcc46a9c185d48bb4897e090ced8 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 13 Nov 2024 19:44:35 -0800 Subject: [PATCH 31/50] Move HandleActionHover to CS Co-authored-by: Infi --- Dalamud/Game/Gui/GameGui.cs | 2 +- Dalamud/Game/Gui/GameGuiAddressResolver.cs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 74e0a8df3..b21613a06 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -57,7 +57,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.setGlobalBgmHook = Hook.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); - this.handleActionHoverHook = Hook.FromAddress(this.address.HandleActionHover, this.HandleActionHoverDetour); + this.handleActionHoverHook = Hook.FromAddress(AgentActionDetail.Addresses.HandleActionHover.Value, this.HandleActionHoverDetour); this.handleActionOutHook = Hook.FromAddress(this.address.HandleActionOut, this.HandleActionOutDetour); this.handleImmHook = Hook.FromAddress(this.address.HandleImm, this.HandleImmDetour); diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index a742541ea..5a404fb2a 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -15,11 +15,6 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver /// public IntPtr SetGlobalBgm { get; private set; } - /// - /// Gets the address of the native HandleActionHover method. - /// - public IntPtr HandleActionHover { get; private set; } - /// /// Gets the address of the native HandleActionOut method. /// @@ -39,7 +34,6 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver protected override void Setup64Bit(ISigScanner sig) { this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); - this.HandleActionHover = sig.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 83 F8 0F"); this.HandleActionOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 4D 85 C0 74 1F"); this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); From 4e76669779de5d95ed93f9443e2c5bd43c478787 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 14 Nov 2024 06:56:00 +0100 Subject: [PATCH 32/50] Add new city to the upload (#2078) --- .../Universalis/Types/UniversalisTaxData.cs | 6 ++++++ .../Universalis/UniversalisMarketBoardUploader.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs index 72f54773d..2277920a7 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs @@ -48,4 +48,10 @@ internal class UniversalisTaxData /// [JsonProperty("sharlayan")] public uint Sharlayan { get; set; } + + /// + /// Gets or sets Tuliyollal's current tax rate. + /// + [JsonProperty("tuliyollal")] + public uint Tuliyollal { get; set; } } diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs index 5215cf030..9528a9b50 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs @@ -131,6 +131,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader Kugane = taxRates.KuganeTax, Crystarium = taxRates.CrystariumTax, Sharlayan = taxRates.SharlayanTax, + Tuliyollal = taxRates.TuliyollalTax, }, }; From 7c6ed6de76646f8ef0cf47ebdb5abb5828b40d41 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 14 Nov 2024 06:56:30 +0100 Subject: [PATCH 33/50] Use more of the CS version for ActionDetailHover (#2079) * - Use CS for delegate - Sync HoverActionKind with CS version * Undo changes in favor of #2080 --- Dalamud/Game/Gui/GameGui.cs | 34 ++++----- Dalamud/Game/Gui/HoverActionKind.cs | 107 +++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 18 deletions(-) diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index b21613a06..85ac36a66 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -92,7 +92,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3); - + /// public event EventHandler? UiHideToggled; @@ -138,7 +138,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui inView = false; return false; } - + pCoords *= MathF.Abs(1.0f / pCoords.W); screenPos = new Vector2(pCoords.X, pCoords.Y); @@ -166,7 +166,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui worldPos = default; return false; } - + var camera = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CameraManager.Instance()->CurrentCamera; if (camera == null) { @@ -221,7 +221,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui /// public IntPtr FindAgentInterface(void* addon) => this.FindAgentInterface((IntPtr)addon); - + /// public IntPtr FindAgentInterface(IntPtr addonPtr) { @@ -422,13 +422,13 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui this.gameGuiService.HoveredItemChanged += this.HoveredItemForward; this.gameGuiService.HoveredActionChanged += this.HoveredActionForward; } - + /// public event EventHandler? UiHideToggled; - + /// public event EventHandler? HoveredItemChanged; - + /// public event EventHandler? HoveredActionChanged; @@ -444,7 +444,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public HoveredAction HoveredAction => this.gameGuiService.HoveredAction; - + /// void IInternalDisposableService.DisposeService() { @@ -456,7 +456,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui this.HoveredItemChanged = null; this.HoveredActionChanged = null; } - + /// public bool OpenMapWithMapLink(MapLinkPayload mapLink) => this.gameGuiService.OpenMapWithMapLink(mapLink); @@ -464,7 +464,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos) => this.gameGuiService.WorldToScreen(worldPos, out screenPos); - + /// public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos, out bool inView) => this.gameGuiService.WorldToScreen(worldPos, out screenPos, out inView); @@ -476,26 +476,26 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public IntPtr GetUIModule() => this.gameGuiService.GetUIModule(); - + /// public IntPtr GetAddonByName(string name, int index = 1) => this.gameGuiService.GetAddonByName(name, index); - + /// public IntPtr FindAgentInterface(string addonName) => this.gameGuiService.FindAgentInterface(addonName); - + /// - public unsafe IntPtr FindAgentInterface(void* addon) + public unsafe IntPtr FindAgentInterface(void* addon) => this.gameGuiService.FindAgentInterface(addon); /// - public IntPtr FindAgentInterface(IntPtr addonPtr) + public IntPtr FindAgentInterface(IntPtr addonPtr) => this.gameGuiService.FindAgentInterface(addonPtr); private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled); - + private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId); - + private void HoveredActionForward(object sender, HoveredAction hoverAction) => this.HoveredActionChanged?.Invoke(sender, hoverAction); } diff --git a/Dalamud/Game/Gui/HoverActionKind.cs b/Dalamud/Game/Gui/HoverActionKind.cs index 4c7ae9166..ef8fe6400 100644 --- a/Dalamud/Game/Gui/HoverActionKind.cs +++ b/Dalamud/Game/Gui/HoverActionKind.cs @@ -16,6 +16,11 @@ public enum HoverActionKind /// Action = 28, + /// + /// A crafting action is hovered. + /// + CraftingAction = 29, + /// /// A general action is hovered. /// @@ -24,7 +29,7 @@ public enum HoverActionKind /// /// A companion order type of action is hovered. /// - CompanionOrder = 31, + CompanionOrder = 31, // Game Term: BuddyOrder /// /// A main command type of action is hovered. @@ -36,6 +41,11 @@ public enum HoverActionKind /// ExtraCommand = 33, + /// + /// A companion action is hovered. + /// + Companion = 34, + /// /// A pet order type of action is hovered. /// @@ -45,4 +55,99 @@ public enum HoverActionKind /// A trait is hovered. /// Trait = 36, + + /// + /// A buddy action is hovered. + /// + BuddyAction = 37, + + /// + /// A company action is hovered. + /// + CompanyAction = 38, + + /// + /// A mount is hovered. + /// + Mount = 39, + + /// + /// A chocobo race action is hovered. + /// + ChocoboRaceAction = 40, + + /// + /// A chocobo race item is hovered. + /// + ChocoboRaceItem = 41, + + /// + /// A deep dungeon equipment is hovered. + /// + DeepDungeonEquipment = 42, + + /// + /// A deep dungeon equipment 2 is hovered. + /// + DeepDungeonEquipment2 = 43, + + /// + /// A deep dungeon item is hovered. + /// + DeepDungeonItem = 44, + + /// + /// A quick chat is hovered. + /// + QuickChat = 45, + + /// + /// An action combo route is hovered. + /// + ActionComboRoute = 46, + + /// + /// A pvp trait is hovered. + /// + PvPSelectTrait = 47, + + /// + /// A squadron action is hovered. + /// + BgcArmyAction = 48, + + /// + /// A perform action is hovered. + /// + Perform = 49, + + /// + /// A deep dungeon magic stone is hovered. + /// + DeepDungeonMagicStone = 50, + + /// + /// A deep dungeon demiclone is hovered. + /// + DeepDungeonDemiclone = 51, + + /// + /// An eureka magia action is hovered. + /// + EurekaMagiaAction = 52, + + /// + /// An island sanctuary temporary item is hovered. + /// + MYCTemporaryItem = 53, + + /// + /// An ornament is hovered. + /// + Ornament = 54, + + /// + /// Glasses are hovered. + /// + Glasses = 55, } From 097f85eff6386557d1e6a29de3cec9a3d1b1d6e1 Mon Sep 17 00:00:00 2001 From: KazWolfe Date: Thu, 14 Nov 2024 08:29:28 -0800 Subject: [PATCH 34/50] Move more things to ClientStructs (#2080) * GameGui uses CS methods now Co-authored-by: Infi * Shove even more things over to CS * Clean up NetworkHandlers too * bump cs so things build at least --------- Co-authored-by: Infi --- .../AddonEventManagerAddressResolver.cs | 2 +- Dalamud/Game/ClientState/ClientState.cs | 12 ++- .../ClientState/ClientStateAddressResolver.cs | 11 +-- .../Game/Config/GameConfigAddressResolver.cs | 2 +- .../DutyState/DutyStateAddressResolver.cs | 2 +- Dalamud/Game/Gui/FlyText/FlyTextGui.cs | 73 +++---------------- .../Gui/FlyText/FlyTextGuiAddressResolver.cs | 29 -------- Dalamud/Game/Gui/GameGui.cs | 39 ++++------ Dalamud/Game/Gui/GameGuiAddressResolver.cs | 17 +---- .../PartyFinder/PartyFinderAddressResolver.cs | 18 ----- .../Game/Gui/PartyFinder/PartyFinderGui.cs | 21 ++---- Dalamud/Game/Internal/DalamudAtkTweaks.cs | 6 +- Dalamud/Game/Network/GameNetwork.cs | 30 ++++---- .../Network/GameNetworkAddressResolver.cs | 10 +-- .../Game/Network/Internal/NetworkHandlers.cs | 59 +++++++-------- .../NetworkHandlersAddressResolver.cs | 49 +------------ lib/FFXIVClientStructs | 2 +- 17 files changed, 96 insertions(+), 286 deletions(-) delete mode 100644 Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs delete mode 100644 Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs diff --git a/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs index 927ed87ab..415e1b169 100644 --- a/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs +++ b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs @@ -16,6 +16,6 @@ internal class AddonEventManagerAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner scanner) { - this.UpdateCursor = scanner.ScanText("48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 4C 8B F1 E8 ?? ?? ?? ?? 49 8B CE"); + this.UpdateCursor = scanner.ScanText("48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 4C 8B F1 E8 ?? ?? ?? ?? 49 8B CE"); // unnamed in CS } } diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 750ba34c5..6ceea4c6b 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -37,7 +37,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState private readonly GameLifecycle lifecycle; private readonly ClientStateAddressResolver address; - private readonly Hook setupTerritoryTypeHook; + private readonly Hook setupTerritoryTypeHook; private readonly Hook uiModuleHandlePacketHook; private readonly Hook processPacketPlayerSetupHook; private readonly Hook onLogoutHook; @@ -56,9 +56,10 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language; - Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(this.address.SetupTerritoryType)}"); + var setTerritoryTypeAddr = EventFramework.Addresses.SetTerritoryTypeId.Value; + Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(setTerritoryTypeAddr)}"); - this.setupTerritoryTypeHook = Hook.FromAddress(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour); + this.setupTerritoryTypeHook = Hook.FromAddress(setTerritoryTypeAddr, this.SetupTerritoryTypeDetour); this.uiModuleHandlePacketHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour); this.processPacketPlayerSetupHook = Hook.FromAddress(this.address.ProcessPacketPlayerSetup, this.ProcessPacketPlayerSetupDetour); this.onLogoutHook = Hook.FromAddress((nint)LogoutCallbackInterface.StaticVirtualTablePointer->OnLogout, this.OnLogoutDetour); @@ -70,10 +71,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.processPacketPlayerSetupHook.Enable(); this.onLogoutHook.Enable(); } - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private unsafe delegate void SetupTerritoryTypeDelegate(EventFramework* eventFramework, ushort terriType); - + private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet); /// diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index c7d7837a4..d44275ef8 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -19,11 +19,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver // Functions - /// - /// Gets the address of the method which sets the territory type. - /// - public IntPtr SetupTerritoryType { get; private set; } - /// /// Gets the address of the method which sets up the player. /// @@ -41,9 +36,7 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC ?? 48 8B D9 48 89 6C 24"); - - this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); + this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); // not in cs struct // These resolve to fixed offsets only, without the base address added in, so GetStaticAddressFromSig() can't be used. // lea rcx, ds:1DB9F74h[rax*4] KeyboardState @@ -51,6 +44,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; this.KeyboardStateIndexArray = sig.ScanText("0F B6 94 33 ?? ?? ?? ?? 84 D2") + 0x4; - this.GamepadPoll = sig.ScanText("40 55 53 57 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 B4 24"); + this.GamepadPoll = sig.ScanText("40 55 53 57 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 B4 24"); // unnamed in cs } } diff --git a/Dalamud/Game/Config/GameConfigAddressResolver.cs b/Dalamud/Game/Config/GameConfigAddressResolver.cs index c171932a9..2491c4033 100644 --- a/Dalamud/Game/Config/GameConfigAddressResolver.cs +++ b/Dalamud/Game/Config/GameConfigAddressResolver.cs @@ -13,6 +13,6 @@ internal sealed class GameConfigAddressResolver : BaseAddressResolver /// protected override void Setup64Bit(ISigScanner scanner) { - this.ConfigChangeAddress = scanner.ScanText("E8 ?? ?? ?? ?? 48 8B 3F 49 3B 3E"); + this.ConfigChangeAddress = scanner.ScanText("E8 ?? ?? ?? ?? 48 8B 3F 49 3B 3E"); // unnamed in CS } } diff --git a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs index 01a0d39b7..1bca93efb 100644 --- a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs +++ b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs @@ -16,6 +16,6 @@ internal class DutyStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.ContentDirectorNetworkMessage = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC ?? 33 FF 48 8B D9 41 0F B7 08"); + this.ContentDirectorNetworkMessage = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC ?? 33 FF 48 8B D9 41 0F B7 08"); // unnamed in cs } } diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs index b01f8c244..cbf166cfc 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs @@ -23,62 +23,21 @@ namespace Dalamud.Game.Gui.FlyText; internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui { /// - /// The native function responsible for adding fly text to the UI. See . + /// The hook that fires when the game creates a fly text element. /// - private readonly AddFlyTextDelegate addFlyTextNative; - - /// - /// The hook that fires when the game creates a fly text element. See . - /// - private readonly Hook createFlyTextHook; + private readonly Hook createFlyTextHook; [ServiceManager.ServiceConstructor] private unsafe FlyTextGui(TargetSigScanner sigScanner) { - this.Address = new FlyTextGuiAddressResolver(); - this.Address.Setup(sigScanner); - - this.addFlyTextNative = Marshal.GetDelegateForFunctionPointer(this.Address.AddFlyText); - this.createFlyTextHook = Hook.FromAddress(this.Address.CreateFlyText, this.CreateFlyTextDetour); + this.createFlyTextHook = Hook.FromAddress(AddonFlyText.Addresses.CreateFlyText.Value, this.CreateFlyTextDetour); this.createFlyTextHook.Enable(); } - /// - /// Private delegate for the native CreateFlyText function's hook. - /// - private unsafe delegate nint CreateFlyTextDelegate( - AtkUnitBase* thisPtr, - FlyTextKind kind, - int val1, - int val2, - byte* text2, - uint color, - uint icon, - uint damageTypeIcon, - byte* text1, - float yOffset); - - /// - /// Private delegate for the native AddFlyText function pointer. - /// - private unsafe delegate void AddFlyTextDelegate( - AtkUnitBase* thisPtr, - uint actorIndex, - uint messageMax, - NumberArrayData* numberArrayData, - uint offsetNum, - uint offsetNumMax, - StringArrayData* stringArrayData, - uint offsetStr, - uint offsetStrMax, - int unknown); - /// public event IFlyTextGui.OnFlyTextCreatedDelegate? FlyTextCreated; - private FlyTextGuiAddressResolver Address { get; } - /// /// Disposes of managed and unmanaged resources. /// @@ -94,7 +53,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var numOffset = 161u; var strOffset = 28u; - var flytext = RaptureAtkUnitManager.Instance()->GetAddonByName("_FlyText"); + var flytext = (AddonFlyText*)RaptureAtkUnitManager.Instance()->GetAddonByName("_FlyText"); if (flytext == null) return; @@ -116,23 +75,13 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false); strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false); - - this.addFlyTextNative( - flytext, - actorIndex, - 1, - numArray, - numOffset, - 10, - strArray, - strOffset, - 2, - 0); + + flytext->AddFlyText(actorIndex, 1, numArray, numOffset, 10, strArray, strOffset, 2, 0); } private unsafe nint CreateFlyTextDetour( - AtkUnitBase* thisPtr, - FlyTextKind kind, + AddonFlyText* thisPtr, + int kind, int val1, int val2, byte* text2, @@ -149,7 +98,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var handled = false; - var tmpKind = kind; + var tmpKind = (FlyTextKind)kind; var tmpVal1 = val1; var tmpVal2 = val2; var tmpText1 = text1 == null ? string.Empty : MemoryHelper.ReadSeStringNullTerminated((nint)text1); @@ -193,7 +142,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var maybeModifiedText2 = tmpText2.EncodeWithNullTerminator(); // Check if any values have changed - var dirty = tmpKind != kind || + var dirty = (int)tmpKind != kind || tmpVal1 != val1 || tmpVal2 != val2 || !maybeModifiedText1.SequenceEqual(originalText1) || @@ -219,7 +168,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui retVal = this.createFlyTextHook.Original( thisPtr, - tmpKind, + (int)tmpKind, tmpVal1, tmpVal2, (byte*)pText2, diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs b/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs deleted file mode 100644 index aa7c8026f..000000000 --- a/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Dalamud.Game.Gui.FlyText; - -/// -/// An address resolver for the class. -/// -internal class FlyTextGuiAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the native AddFlyText method, which occurs - /// when the game adds fly text elements to the UI. Multiple fly text - /// elements can be added in a single AddFlyText call. - /// - public IntPtr AddFlyText { get; private set; } - - /// - /// Gets the address of the native CreateFlyText method, which occurs - /// when the game creates a new fly text element. This method is called - /// once per fly text element, and can be called multiple times per - /// AddFlyText call. - /// - public IntPtr CreateFlyText { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - this.AddFlyText = sig.ScanText("E8 ?? ?? ?? ?? FF C7 41 D1 C7"); - this.CreateFlyText = sig.ScanText("E8 ?? ?? ?? ?? 48 8B F8 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 18"); - } -} diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 85ac36a66..54f4253cd 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -38,11 +38,11 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui private readonly GameGuiAddressResolver address; private readonly Hook setGlobalBgmHook; - private readonly Hook handleActionHoverHook; - private readonly Hook handleActionOutHook; + private readonly Hook handleActionHoverHook; + private readonly Hook handleActionOutHook; private readonly Hook handleImmHook; private readonly Hook setUiVisibilityHook; - private readonly Hook utf8StringFromSequenceHook; + private readonly Hook utf8StringFromSequenceHook; [ServiceManager.ServiceConstructor] private GameGui(TargetSigScanner sigScanner) @@ -57,14 +57,14 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.setGlobalBgmHook = Hook.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); - this.handleActionHoverHook = Hook.FromAddress(AgentActionDetail.Addresses.HandleActionHover.Value, this.HandleActionHoverDetour); - this.handleActionOutHook = Hook.FromAddress(this.address.HandleActionOut, this.HandleActionOutDetour); + this.handleActionHoverHook = Hook.FromAddress(AgentActionDetail.Addresses.HandleActionHover.Value, this.HandleActionHoverDetour); + this.handleActionOutHook = Hook.FromAddress((nint)AgentActionDetail.StaticVirtualTablePointer->ReceiveEvent, this.HandleActionOutDetour); this.handleImmHook = Hook.FromAddress(this.address.HandleImm, this.HandleImmDetour); this.setUiVisibilityHook = Hook.FromAddress((nint)RaptureAtkModule.StaticVirtualTablePointer->SetUiVisibility, this.SetUiVisibilityDetour); - this.utf8StringFromSequenceHook = Hook.FromAddress(this.address.Utf8StringFromSequence, this.Utf8StringFromSequenceDetour); + this.utf8StringFromSequenceHook = Hook.FromAddress(Utf8String.Addresses.Ctor_FromSequence.Value, this.Utf8StringFromSequenceDetour); this.setGlobalBgmHook.Enable(); this.handleImmHook.Enable(); @@ -77,19 +77,10 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui } // Hooked delegates - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate Utf8String* Utf8StringFromSequenceDelegate(Utf8String* thisPtr, byte* sourcePtr, nuint sourceLen); - + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr SetGlobalBgmDelegate(ushort bgmKey, byte a2, uint a3, uint a4, uint a5, byte a6); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void HandleActionHoverDelegate(IntPtr hoverState, HoverActionKind a2, uint a3, int a4, byte a5); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr HandleActionOutDelegate(IntPtr agentActionDetail, IntPtr a2, IntPtr a3, int a4); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3); @@ -309,24 +300,24 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return retVal; } - private void HandleActionHoverDetour(IntPtr hoverState, HoverActionKind actionKind, uint actionId, int a4, byte a5) + private void HandleActionHoverDetour(AgentActionDetail* hoverState, ActionKind actionKind, uint actionId, int a4, byte a5) { this.handleActionHoverHook.Original(hoverState, actionKind, actionId, a4, a5); - this.HoveredAction.ActionKind = actionKind; + this.HoveredAction.ActionKind = (HoverActionKind)actionKind; this.HoveredAction.BaseActionID = actionId; - this.HoveredAction.ActionID = (uint)Marshal.ReadInt32(hoverState, 0x3C); + this.HoveredAction.ActionID = hoverState->ActionId; this.HoveredActionChanged?.InvokeSafely(this, this.HoveredAction); - Log.Verbose($"HoverActionId: {actionKind}/{actionId} this:{hoverState.ToInt64():X}"); + Log.Verbose($"HoverActionId: {actionKind}/{actionId} this:{(nint)hoverState:X}"); } - private IntPtr HandleActionOutDetour(IntPtr agentActionDetail, IntPtr a2, IntPtr a3, int a4) + private AtkValue* HandleActionOutDetour(AgentActionDetail* agentActionDetail, AtkValue* a2, AtkValue* a3, uint a4, ulong a5) { - var retVal = this.handleActionOutHook.Original(agentActionDetail, a2, a3, a4); + var retVal = this.handleActionOutHook.Original(agentActionDetail, a2, a3, a4, a5); - if (a3 != IntPtr.Zero && a4 == 1) + if (a3 != null && a4 == 1) { - var a3Val = Marshal.ReadByte(a3, 0x8); + var a3Val = a3->Int; if (a3Val == 255) { diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index 5a404fb2a..bdd579c7e 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -15,28 +15,15 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver /// public IntPtr SetGlobalBgm { get; private set; } - /// - /// Gets the address of the native HandleActionOut method. - /// - public IntPtr HandleActionOut { get; private set; } - /// /// Gets the address of the native HandleImm method. /// public IntPtr HandleImm { get; private set; } - /// - /// Gets the address of the native Utf8StringFromSequence method. - /// - public IntPtr Utf8StringFromSequence { get; private set; } - /// protected override void Setup64Bit(ISigScanner sig) { - this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); - this.HandleActionOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 4D 85 C0 74 1F"); - this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); - - this.Utf8StringFromSequence = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 41 22 66 C7 41 ?? ?? ?? 48 89 01 49 8B D8"); + this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); // unnamed in CS + this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); // unnamed in CS } } diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs deleted file mode 100644 index 0b267694c..000000000 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Dalamud.Game.Gui.PartyFinder; - -/// -/// The address resolver for the class. -/// -internal class PartyFinderAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the native ReceiveListing method. - /// - public IntPtr ReceiveListing { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - this.ReceiveListing = sig.ScanText("40 53 41 57 48 83 EC ?? 48 8B D9 4C 8B FA"); - } -} diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs index ef4055b29..0b25a87be 100644 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs +++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs @@ -7,6 +7,8 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI.Info; + using Serilog; namespace Dalamud.Game.Gui.PartyFinder; @@ -15,12 +17,11 @@ namespace Dalamud.Game.Gui.PartyFinder; /// This class handles interacting with the native PartyFinder window. /// [ServiceManager.EarlyLoadedService] -internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderGui +internal sealed unsafe class PartyFinderGui : IInternalDisposableService, IPartyFinderGui { - private readonly PartyFinderAddressResolver address; private readonly nint memory; - private readonly Hook receiveListingHook; + private readonly Hook receiveListingHook; /// /// Initializes a new instance of the class. @@ -29,18 +30,12 @@ internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderG [ServiceManager.ServiceConstructor] private PartyFinderGui(TargetSigScanner sigScanner) { - this.address = new PartyFinderAddressResolver(); - this.address.Setup(sigScanner); - this.memory = Marshal.AllocHGlobal(PartyFinderPacket.PacketSize); - this.receiveListingHook = Hook.FromAddress(this.address.ReceiveListing, this.HandleReceiveListingDetour); + this.receiveListingHook = Hook.FromAddress(InfoProxyCrossRealm.Addresses.ReceiveListing.Value, this.HandleReceiveListingDetour); this.receiveListingHook.Enable(); } - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ReceiveListingDelegate(nint managerPtr, nint data); - /// public event IPartyFinderGui.PartyFinderListingEventDelegate? ReceiveListing; @@ -61,18 +56,18 @@ internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderG } } - private void HandleReceiveListingDetour(nint managerPtr, nint data) + private void HandleReceiveListingDetour(InfoProxyCrossRealm* infoProxy, nint packet) { try { - this.HandleListingEvents(data); + this.HandleListingEvents(packet); } catch (Exception ex) { Log.Error(ex, "Exception on ReceiveListing hook."); } - this.receiveListingHook.Original(managerPtr, data); + this.receiveListingHook.Original(infoProxy, packet); } private void HandleListingEvents(nint data) diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index c147f76e6..7834ab58f 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -24,7 +24,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService { private static readonly ModuleLog Log = new("DalamudAtkTweaks"); - private readonly Hook hookAgentHudOpenSystemMenu; + private readonly Hook hookAgentHudOpenSystemMenu; // TODO: Make this into events in Framework.Gui private readonly Hook hookUiModuleExecuteMainCommand; @@ -45,9 +45,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService [ServiceManager.ServiceConstructor] private DalamudAtkTweaks(TargetSigScanner sigScanner) { - var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CF 4C 89 B4 24 B8 08 00 00"); - - this.hookAgentHudOpenSystemMenu = Hook.FromAddress(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour); + this.hookAgentHudOpenSystemMenu = Hook.FromAddress(AgentHUD.Addresses.OpenSystemMenu.Value, this.AgentHudOpenSystemMenuDetour); this.hookUiModuleExecuteMainCommand = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->ExecuteMainCommand, this.UiModuleExecuteMainCommandDetour); this.hookAtkUnitBaseReceiveGlobalEvent = Hook.FromAddress((nint)AtkUnitBase.StaticVirtualTablePointer->ReceiveGlobalEvent, this.AtkUnitBaseReceiveGlobalEventDetour); diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs index b51232ece..5022eb93d 100644 --- a/Dalamud/Game/Network/GameNetwork.cs +++ b/Dalamud/Game/Network/GameNetwork.cs @@ -6,6 +6,9 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.Network; + using Serilog; namespace Dalamud.Game.Network; @@ -14,10 +17,10 @@ namespace Dalamud.Game.Network; /// This class handles interacting with game network events. /// [ServiceManager.EarlyLoadedService] -internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork +internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetwork { private readonly GameNetworkAddressResolver address; - private readonly Hook processZonePacketDownHook; + private readonly Hook processZonePacketDownHook; private readonly Hook processZonePacketUpHook; private readonly HitchDetector hitchDetectorUp; @@ -25,11 +28,9 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); - - private IntPtr baseAddress; - + [ServiceManager.ServiceConstructor] - private GameNetwork(TargetSigScanner sigScanner) + private unsafe GameNetwork(TargetSigScanner sigScanner) { this.hitchDetectorUp = new HitchDetector("GameNetworkUp", this.configuration.GameNetworkUpHitch); this.hitchDetectorDown = new HitchDetector("GameNetworkDown", this.configuration.GameNetworkDownHitch); @@ -37,20 +38,19 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork this.address = new GameNetworkAddressResolver(); this.address.Setup(sigScanner); + var onReceivePacketAddress = (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket; + Log.Verbose("===== G A M E N E T W O R K ====="); - Log.Verbose($"ProcessZonePacketDown address {Util.DescribeAddress(this.address.ProcessZonePacketDown)}"); + Log.Verbose($"OnReceivePacket address {Util.DescribeAddress(onReceivePacketAddress)}"); Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}"); - this.processZonePacketDownHook = Hook.FromAddress(this.address.ProcessZonePacketDown, this.ProcessZonePacketDownDetour); + this.processZonePacketDownHook = Hook.FromAddress(onReceivePacketAddress, this.ProcessZonePacketDownDetour); this.processZonePacketUpHook = Hook.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour); this.processZonePacketDownHook.Enable(); this.processZonePacketUpHook.Enable(); } - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ProcessZonePacketDownDelegate(IntPtr a, uint targetId, IntPtr dataPtr); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4); @@ -64,10 +64,8 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork this.processZonePacketUpHook.Dispose(); } - private void ProcessZonePacketDownDetour(IntPtr a, uint targetId, IntPtr dataPtr) + private void ProcessZonePacketDownDetour(PacketDispatcher* dispatcher, uint targetId, IntPtr dataPtr) { - this.baseAddress = a; - this.hitchDetectorDown.Start(); // Go back 0x10 to get back to the start of the packet header @@ -78,7 +76,7 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork // Call events this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr, 0x12), 0, targetId, NetworkMessageDirection.ZoneDown); - this.processZonePacketDownHook.Original(a, targetId, dataPtr + 0x10); + this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); } catch (Exception ex) { @@ -96,7 +94,7 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); - this.processZonePacketDownHook.Original(a, targetId, dataPtr + 0x10); + this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); } this.hitchDetectorDown.Stop(); diff --git a/Dalamud/Game/Network/GameNetworkAddressResolver.cs b/Dalamud/Game/Network/GameNetworkAddressResolver.cs index 69b97c59d..de92f7c10 100644 --- a/Dalamud/Game/Network/GameNetworkAddressResolver.cs +++ b/Dalamud/Game/Network/GameNetworkAddressResolver.cs @@ -5,11 +5,6 @@ namespace Dalamud.Game.Network; /// internal sealed class GameNetworkAddressResolver : BaseAddressResolver { - /// - /// Gets the address of the ProcessZonePacketDown method. - /// - public IntPtr ProcessZonePacketDown { get; private set; } - /// /// Gets the address of the ProcessZonePacketUp method. /// @@ -18,9 +13,6 @@ internal sealed class GameNetworkAddressResolver : BaseAddressResolver /// protected override void Setup64Bit(ISigScanner sig) { - // ProcessZonePacket = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 7A FF 0F B7 57 02 8D 42 89 3D 5F 02 00 00 0F 87 60 01 00 00 4C 8D 05"); - // ProcessZonePacket = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 73 FF 0F B7 57 02 8D 42 ?? 3D ?? ?? 00 00 0F 87 60 01 00 00 4C 8D 05"); - this.ProcessZonePacketDown = sig.ScanText("40 53 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 44 24 ?? 8B F2"); - this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); + this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); // unnamed in cs } } diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 2017be4bf..a25444f10 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -15,6 +15,9 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Networking.Http; using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent; +using FFXIVClientStructs.FFXIV.Client.Network; using FFXIVClientStructs.FFXIV.Client.UI.Info; using Lumina.Excel.Sheets; using Serilog; @@ -35,13 +38,13 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private readonly NetworkHandlersAddressResolver addressResolver; - private readonly Hook cfPopHook; - private readonly Hook mbPurchaseHook; - private readonly Hook mbHistoryHook; + private readonly Hook cfPopHook; + private readonly Hook mbPurchaseHook; + private readonly Hook mbHistoryHook; private readonly Hook customTalkHook; // used for marketboard taxes - private readonly Hook mbItemRequestStartHook; - private readonly Hook mbOfferingsHook; - private readonly Hook mbSendPurchaseRequestHook; + private readonly Hook mbItemRequestStartHook; + private readonly Hook mbOfferingsHook; + private readonly Hook mbSendPurchaseRequestHook; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -134,14 +137,14 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.handleMarketBoardPurchaseHandler = this.HandleMarketBoardPurchaseHandler(); this.mbPurchaseHook = - Hook.FromAddress( - this.addressResolver.MarketBoardPurchasePacketHandler, + Hook.FromAddress( + PacketDispatcher.Addresses.HandleMarketBoardPurchasePacket.Value, this.MarketPurchasePacketDetour); this.mbPurchaseHook.Enable(); this.mbHistoryHook = - Hook.FromAddress( - this.addressResolver.MarketBoardHistoryPacketHandler, + Hook.FromAddress( + InfoProxyItemSearch.Addresses.ProcessItemHistory.Value, this.MarketHistoryPacketDetour); this.mbHistoryHook.Enable(); @@ -151,22 +154,22 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.CustomTalkReceiveResponseDetour); this.customTalkHook.Enable(); - this.mbItemRequestStartHook = Hook.FromAddress( - this.addressResolver.MarketBoardItemRequestStartPacketHandler, + this.mbItemRequestStartHook = Hook.FromAddress( + PacketDispatcher.Addresses.HandleMarketBoardItemRequestStartPacket.Value, this.MarketItemRequestStartDetour); this.mbItemRequestStartHook.Enable(); - this.mbOfferingsHook = Hook.FromAddress( - this.addressResolver.InfoProxyItemSearchAddPage, + this.mbOfferingsHook = Hook.FromAddress( + (nint)InfoProxyItemSearch.StaticVirtualTablePointer->AddPage, this.MarketBoardOfferingsDetour); this.mbOfferingsHook.Enable(); - this.mbSendPurchaseRequestHook = Hook.FromAddress( - this.addressResolver.BuildMarketBoardPurchaseHandlerPacket, + this.mbSendPurchaseRequestHook = Hook.FromAddress( + InfoProxyItemSearch.Addresses.SendPurchaseRequestPacket.Value, this.MarketBoardSendPurchaseRequestDetour); this.mbSendPurchaseRequestHook.Enable(); - this.cfPopHook = Hook.FromAddress(this.addressResolver.CfPopPacketHandler, this.CfPopDetour); + this.cfPopHook = Hook.FromAddress(PublicContentDirector.Addresses.HandleEnterContentInfoPacket.Value, this.CfPopDetour); this.cfPopHook.Enable(); } @@ -183,8 +186,6 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private delegate byte MarketBoardSendPurchaseRequestPacket(InfoProxyItemSearch* infoProxy); - private delegate nint CfPopDelegate(nint packetData); - /// /// Event which gets fired when a duty is ready. /// @@ -263,7 +264,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.cfPopHook.Dispose(); } - private unsafe nint CfPopDetour(nint packetData) + private unsafe nint CfPopDetour(PublicContentDirector.EnterContentInfoPacket* packetData) { var result = this.cfPopHook.OriginalDisposeSafe(packetData); @@ -529,7 +530,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService return this.configuration.IsMbCollect; } - private nint MarketPurchasePacketDetour(nint a1, nint packetData) + private void MarketPurchasePacketDetour(PacketDispatcher* a1, nint packetData) { try { @@ -540,10 +541,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketPurchasePacketHandler threw an exception"); } - return this.mbPurchaseHook.OriginalDisposeSafe(a1, packetData); + this.mbPurchaseHook.OriginalDisposeSafe(a1, packetData); } - private nint MarketHistoryPacketDetour(nint a1, nint packetData, uint a3, char a4) + private void MarketHistoryPacketDetour(InfoProxyItemSearch* a1, nint packetData) { try { @@ -554,7 +555,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketHistoryPacketDetour threw an exception"); } - return this.mbHistoryHook.OriginalDisposeSafe(a1, packetData, a3, a4); + this.mbHistoryHook.OriginalDisposeSafe(a1, packetData); } private void CustomTalkReceiveResponseDetour(nuint a1, ushort eventId, byte responseId, uint* args, byte argCount) @@ -573,7 +574,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.customTalkHook.OriginalDisposeSafe(a1, eventId, responseId, args, argCount); } - private nint MarketItemRequestStartDetour(nint a1, nint packetRef) + private void MarketItemRequestStartDetour(PacketDispatcher* a1, nint packetRef) { try { @@ -584,10 +585,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketItemRequestStartDetour threw an exception"); } - return this.mbItemRequestStartHook.OriginalDisposeSafe(a1, packetRef); + this.mbItemRequestStartHook.OriginalDisposeSafe(a1, packetRef); } - private byte MarketBoardOfferingsDetour(nint a1, nint packetRef) + private void MarketBoardOfferingsDetour(InfoProxyItemSearch* a1, nint packetRef) { try { @@ -598,10 +599,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketBoardOfferingsDetour threw an exception"); } - return this.mbOfferingsHook.OriginalDisposeSafe(a1, packetRef); + this.mbOfferingsHook.OriginalDisposeSafe(a1, packetRef); } - private byte MarketBoardSendPurchaseRequestDetour(InfoProxyItemSearch* infoProxyItemSearch) + private bool MarketBoardSendPurchaseRequestDetour(InfoProxyItemSearch* infoProxyItemSearch) { try { diff --git a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs index 166b4d67a..18c48b67d 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs @@ -5,62 +5,17 @@ /// internal class NetworkHandlersAddressResolver : BaseAddressResolver { - /// - /// Gets or sets the pointer to the method responsible for handling CfPop packets. - /// - public nint CfPopPacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the method responsible for handling market board history. In this case, we are - /// sigging the packet handler method directly. - /// - public nint MarketBoardHistoryPacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the method responsible for processing the market board purchase packet. In this - /// case, we are sigging the packet handler method directly. - /// - public nint MarketBoardPurchasePacketHandler { get; set; } - /// /// Gets or sets the pointer to the method responsible for custom talk events. Necessary for marketboard tax data, /// as this isn't really exposed anywhere else. /// public nint CustomTalkEventResponsePacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the method responsible for the marketboard ItemRequestStart packet. - /// - public nint MarketBoardItemRequestStartPacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the InfoProxyItemSearch.AddPage method, used to load market data. - /// - public nint InfoProxyItemSearchAddPage { get; set; } - - /// - /// Gets or sets the pointer to the method inside InfoProxyItemSearch that is responsible for building and sending - /// a purchase request packet. - /// - public nint BuildMarketBoardPurchaseHandlerPacket { get; set; } /// protected override void Setup64Bit(ISigScanner scanner) { - this.CfPopPacketHandler = scanner.ScanText("40 53 57 48 83 EC 78 48 8B D9 48 8D 0D"); - - // TODO: I know this is a CC. I want things working for now. (KW) - this.MarketBoardHistoryPacketHandler = scanner.ScanText( - "40 53 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 85 C0 74 2F 4C 8B 00 48 8B C8 41 FF 90 18 01 00 00 48 8B C8 BA 0B 00 00 00 E8 ?? ?? ?? ?? 48 85 C0 74 10 48 8B D3 48 8B C8 48 83 C4 20 5B E9 ?? ?? ?? ?? 48 83 C4 20 5B C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC 40 53"); - this.MarketBoardPurchasePacketHandler = - scanner.ScanText("40 55 56 41 56 48 8B EC 48 83 EC ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 45 ?? 48 8B 0D ?? ?? ?? ?? 4C 8B F2"); this.CustomTalkEventResponsePacketHandler = - scanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 48 8B C8 44 0F B6 CF 0F B6 44 24 ?? 44 0F B7 C6 88 44 24 ?? 8B D5 48 89 5C 24"); - this.MarketBoardItemRequestStartPacketHandler = - scanner.ScanText("48 89 5C 24 08 57 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B FA E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 74 4A"); - this.InfoProxyItemSearchAddPage = - scanner.ScanText("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 0F B6 82 ?? ?? ?? ?? 48 8B FA 48 8B D9 38 41 19 74 54"); - this.BuildMarketBoardPurchaseHandlerPacket = - scanner.ScanText("40 53 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 8B D0 48 85 C0 0F 84 ?? ?? ?? ?? 8B 8B"); + scanner.ScanText( + "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 48 8B C8 44 0F B6 CF 0F B6 44 24 ?? 44 0F B7 C6 88 44 24 ?? 8B D5 48 89 5C 24"); // unnamed in CS } } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 78c39ef13..121f2f8d8 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 78c39ef1318525d766cdf31fd9a32e6f1c7cb453 +Subproject commit 121f2f8d82bce8632e79790a002d5e1ac17a635c From 913fe25cc562119e5fe864b02efc7c2197c4f111 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 08:47:32 -0800 Subject: [PATCH 35/50] InventoryItem size bump --- Dalamud/Game/Inventory/GameInventoryItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/Inventory/GameInventoryItem.cs b/Dalamud/Game/Inventory/GameInventoryItem.cs index 53aa9a9d9..145ddf841 100644 --- a/Dalamud/Game/Inventory/GameInventoryItem.cs +++ b/Dalamud/Game/Inventory/GameInventoryItem.cs @@ -18,7 +18,7 @@ public unsafe struct GameInventoryItem : IEquatable [FieldOffset(0)] internal readonly InventoryItem InternalItem; - private const int StructSizeInBytes = 0x40; + private const int StructSizeInBytes = 0x48; /// /// The view of the backing data, in . From 76df39f7b3e105d9c479ee3d59453267903dfc63 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 10:36:32 -0800 Subject: [PATCH 36/50] bump cs again --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 121f2f8d8..8ed804220 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 121f2f8d82bce8632e79790a002d5e1ac17a635c +Subproject commit 8ed8042205701aa5a1eb32c0fe437e006202fb06 From 7feeae99101b5e729cfbc2d279c238fbc70c2e81 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 15:21:07 -0800 Subject: [PATCH 37/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 8ed804220..6cc4f1c12 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 8ed8042205701aa5a1eb32c0fe437e006202fb06 +Subproject commit 6cc4f1c12c37785101a262cceac249753db3b1f5 From b483b63bf2d82327580c6e5f0c784a7fae7db897 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 15:34:56 -0800 Subject: [PATCH 38/50] GameInventoryItem matches StructSize from CS --- Dalamud/Game/Inventory/GameInventoryItem.cs | 20 +++++--------------- lib/FFXIVClientStructs | 2 +- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Dalamud/Game/Inventory/GameInventoryItem.cs b/Dalamud/Game/Inventory/GameInventoryItem.cs index 145ddf841..fb27346e0 100644 --- a/Dalamud/Game/Inventory/GameInventoryItem.cs +++ b/Dalamud/Game/Inventory/GameInventoryItem.cs @@ -9,7 +9,7 @@ namespace Dalamud.Game.Inventory; /// /// Dalamud wrapper around a ClientStructs InventoryItem. /// -[StructLayout(LayoutKind.Explicit, Size = StructSizeInBytes)] +[StructLayout(LayoutKind.Explicit, Size = InventoryItem.StructSize)] public unsafe struct GameInventoryItem : IEquatable { /// @@ -17,22 +17,12 @@ public unsafe struct GameInventoryItem : IEquatable /// [FieldOffset(0)] internal readonly InventoryItem InternalItem; - - private const int StructSizeInBytes = 0x48; - + /// /// The view of the backing data, in . /// [FieldOffset(0)] - private fixed ulong dataUInt64[StructSizeInBytes / 0x8]; - - static GameInventoryItem() - { - Debug.Assert( - sizeof(InventoryItem) == StructSizeInBytes, - $"Definition of {nameof(InventoryItem)} has been changed. " + - $"Update {nameof(StructSizeInBytes)} to {sizeof(InventoryItem)} to accommodate for the size change."); - } + private fixed ulong dataUInt64[InventoryItem.StructSize / 0x8]; /// /// Initializes a new instance of the struct. @@ -157,7 +147,7 @@ public unsafe struct GameInventoryItem : IEquatable /// true if the current object is equal to the parameter; otherwise, false. public readonly bool Equals(in GameInventoryItem other) { - for (var i = 0; i < StructSizeInBytes / 8; i++) + for (var i = 0; i < InventoryItem.StructSize / 8; i++) { if (this.dataUInt64[i] != other.dataUInt64[i]) return false; @@ -173,7 +163,7 @@ public unsafe struct GameInventoryItem : IEquatable public override int GetHashCode() { var k = 0x5a8447b91aff51b4UL; - for (var i = 0; i < StructSizeInBytes / 8; i++) + for (var i = 0; i < InventoryItem.StructSize / 8; i++) k ^= this.dataUInt64[i]; return unchecked((int)(k ^ (k >> 32))); } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 6cc4f1c12..81ea903b7 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 6cc4f1c12c37785101a262cceac249753db3b1f5 +Subproject commit 81ea903b7dc90bd07a38703738fa7f84b1ed0775 From 94f16ac16e3e44c822665641983966239e96eff6 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Fri, 15 Nov 2024 00:36:10 +0100 Subject: [PATCH 39/50] Alias ClientState.IsLoggedIn to AgentLobby.IsLoggedIn (#2082) --- Dalamud/Game/ClientState/ClientState.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 6ceea4c6b..364466cce 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Runtime.InteropServices; using Dalamud.Data; using Dalamud.Game.ClientState.Conditions; @@ -71,7 +70,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.processPacketPlayerSetupHook.Enable(); this.onLogoutHook.Enable(); } - + private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet); /// @@ -121,7 +120,14 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public unsafe ulong LocalContentId => PlayerState.Instance()->ContentId; /// - public bool IsLoggedIn { get; private set; } + public unsafe bool IsLoggedIn + { + get + { + var agentLobby = AgentLobby.Instance(); + return agentLobby != null && agentLobby->IsLoggedIn; + } + } /// public bool IsPvP { get; private set; } @@ -259,7 +265,6 @@ internal sealed class ClientState : IInternalDisposableService, IClientState try { Log.Debug("Login"); - this.IsLoggedIn = true; this.Login?.InvokeSafely(); gameGui?.ResetUiHideState(); this.lifecycle.ResetLogout(); @@ -283,8 +288,6 @@ internal sealed class ClientState : IInternalDisposableService, IClientState Log.Debug("Logout: Type {type}, Code {code}", type, code); - this.IsLoggedIn = false; - if (this.Logout is { } callback) { foreach (var action in callback.GetInvocationList().Cast()) From ee63f6087796c69db922b849008169f561d10e20 Mon Sep 17 00:00:00 2001 From: ItsBexy <103910869+ItsBexy@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:36:27 -0700 Subject: [PATCH 40/50] Update UIDebug2, ImGuiComponents, ImGuiHelpers (#2081) * Update ImGuiComponents & ImGuiHelpers Took some helper functions created for `UiDebug2`, and incorporated them into `ImGuiComponents` / `ImGuiHelpers` instead - `IconButton()` (and its various overloads) now includes an optional size parameter - `IconButtonSelect()` has been added, allowing a row or grid of IconButtons to serve as a radio-like input - `HelpMarker()` now includes an optional color parameter - `ClickToCopyText()` now includes an optional color parameter, and includes the `FontAwesome.Copy` icon in the tooltip. - Implemented ImRaii in these files These changes are intended not to break any existing calls in plugins or within Dalamud itself. * Fix ambiguous overloads * UiDebug2 Updates - Fixed XY coordinate display - Added AtkValue table to AddonTree display - Restored old behaviour wherein the Addon display initially only shows the Root node and a collapsed node list - The above should also fix the Element Selector / Search behaviour, allowing it to scroll correctly to the searched node - Now displays field offsets for any node/component whose pointer exists in the addon struct - Tidied up node tree headers by removing memory addresses (they're still readable in the opened tree, of course) --- .../ImGuiComponents.ColorPickerWithPalette.cs | 8 +- .../ImGuiComponents.DisabledButton.cs | 54 ++-- .../Components/ImGuiComponents.HelpMarker.cs | 39 ++- .../Components/ImGuiComponents.IconButton.cs | 284 +++++++++++------- .../ImGuiComponents.IconButtonSelect.cs | 87 ++++++ .../ImGuiComponents.TextWithLabel.cs | 10 +- .../ImGuiComponents.ToggleSwitch.cs | 7 +- .../UiDebug2/Browsing/AddonTree.AtkValues.cs | 124 ++++++++ .../UiDebug2/Browsing/AddonTree.FieldNames.cs | 12 + .../Internal/UiDebug2/Browsing/AddonTree.cs | 53 ++-- .../Internal/UiDebug2/Browsing/Events.cs | 8 +- .../UiDebug2/Browsing/NodeTree.Component.cs | 38 ++- .../UiDebug2/Browsing/NodeTree.Editor.cs | 5 +- .../UiDebug2/Browsing/NodeTree.Res.cs | 55 +++- .../Internal/UiDebug2/ElementSelector.cs | 2 +- .../Internal/UiDebug2/Utility/Gui.cs | 99 ++---- .../Internal/UiDebug2/Utility/NodeBounds.cs | 10 +- Dalamud/Interface/Utility/ImGuiHelpers.cs | 32 +- 18 files changed, 649 insertions(+), 278 deletions(-) create mode 100644 Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs create mode 100644 Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs diff --git a/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs b/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs index aa707aecb..e2f68eab2 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs @@ -1,6 +1,8 @@ using System.Numerics; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -41,7 +43,9 @@ public static partial class ImGuiComponents ImGui.OpenPopup($"###ColorPickerPopup{id}"); } - if (ImGui.BeginPopup($"###ColorPickerPopup{id}")) + using var popup = ImRaii.Popup($"###ColorPickerPopup{id}"); + + if (popup) { if (ImGui.ColorPicker4($"###ColorPicker{id}", ref existingColor, flags)) { @@ -61,8 +65,6 @@ public static partial class ImGuiComponents ImGui.SameLine(); } } - - ImGui.EndPopup(); } return selectedColor; diff --git a/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs b/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs index 907ad0aeb..ab2ed4724 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs @@ -1,5 +1,7 @@ using System.Numerics; +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -21,17 +23,16 @@ public static partial class ImGuiComponents /// Indicator if button is clicked. public static bool DisabledButton(FontAwesomeIcon icon, int? id = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, float alphaMult = .5f) { - ImGui.PushFont(UiBuilder.IconFont); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var text = icon.ToIconString(); + if (id.HasValue) + { + text = $"{text}##{id}"; + } - var text = icon.ToIconString(); - if (id.HasValue) - text = $"{text}##{id}"; - - var button = DisabledButton(text, defaultColor, activeColor, hoveredColor, alphaMult); - - ImGui.PopFont(); - - return button; + return DisabledButton(text, defaultColor, activeColor, hoveredColor, alphaMult); + } } /// @@ -45,31 +46,28 @@ public static partial class ImGuiComponents /// Indicator if button is clicked. public static bool DisabledButton(string labelWithId, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, float alphaMult = .5f) { + using var col = new ImRaii.Color(); + if (defaultColor.HasValue) - ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value); + { + col.Push(ImGuiCol.Button, defaultColor.Value); + } if (activeColor.HasValue) - ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value); + { + col.Push(ImGuiCol.ButtonActive, activeColor.Value); + } if (hoveredColor.HasValue) - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value); + { + col.Push(ImGuiCol.ButtonHovered, hoveredColor.Value); + } var style = ImGui.GetStyle(); - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, style.Alpha * alphaMult); - var button = ImGui.Button(labelWithId); - - ImGui.PopStyleVar(); - - if (defaultColor.HasValue) - ImGui.PopStyleColor(); - - if (activeColor.HasValue) - ImGui.PopStyleColor(); - - if (hoveredColor.HasValue) - ImGui.PopStyleColor(); - - return button; + using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, style.Alpha * alphaMult)) + { + return ImGui.Button(labelWithId); + } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs b/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs index f0ecf2f2f..3392136d1 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs @@ -1,3 +1,7 @@ +using Dalamud.Interface.Utility.Raii; + +using FFXIVClientStructs.FFXIV.Common.Math; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -18,17 +22,32 @@ public static partial class ImGuiComponents /// /// The text to display on hover. /// The icon to use. - public static void HelpMarker(string helpText, FontAwesomeIcon icon) + /// The color of the icon. + public static void HelpMarker(string helpText, FontAwesomeIcon icon, Vector4? color = null) { + using var col = new ImRaii.Color(); + + if (color.HasValue) + { + col.Push(ImGuiCol.TextDisabled, color.Value); + } + ImGui.SameLine(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextDisabled(icon.ToIconString()); - ImGui.PopFont(); - if (!ImGui.IsItemHovered()) return; - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0f); - ImGui.TextUnformatted(helpText); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.TextDisabled(icon.ToIconString()); + } + + if (ImGui.IsItemHovered()) + { + using (ImRaii.Tooltip()) + { + using (ImRaii.TextWrapPos(ImGui.GetFontSize() * 35.0f)) + { + ImGui.TextUnformatted(helpText); + } + } + } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs index dc2c99608..5e64fe463 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs @@ -1,6 +1,8 @@ using System.Numerics; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -15,8 +17,26 @@ public static partial class ImGuiComponents /// /// The icon for the button. /// Indicator if button is clicked. - public static bool IconButton(FontAwesomeIcon icon) - => IconButton(icon, null, null, null); + public static bool IconButton(FontAwesomeIcon icon) => IconButton(icon, null); + + /// + /// IconButton component to use an icon as a button. + /// + /// The icon for the button. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(FontAwesomeIcon icon, Vector2 size) => IconButton(icon, null, null, null, size); + + /// + /// IconButton component to use an icon as a button. + /// + /// The icon for the button. + /// The default color of the button. + /// The color of the button when active. + /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(FontAwesomeIcon icon, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) => IconButton($"{icon.ToIconString()}", defaultColor, activeColor, hoveredColor, size); /// /// IconButton component to use an icon as a button. @@ -24,8 +44,28 @@ public static partial class ImGuiComponents /// The ID of the button. /// The icon for the button. /// Indicator if button is clicked. - public static bool IconButton(int id, FontAwesomeIcon icon) - => IconButton(id, icon, null, null, null); + public static bool IconButton(int id, FontAwesomeIcon icon) => IconButton(id, icon, null); + + /// + /// IconButton component to use an icon as a button. + /// + /// The ID of the button. + /// The icon for the button. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(int id, FontAwesomeIcon icon, Vector2 size) => IconButton(id, icon, null, null, null, size); + + /// + /// IconButton component to use an icon as a button with color options. + /// + /// The ID of the button. + /// The icon for the button. + /// The default color of the button. + /// The color of the button when active. + /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(int id, FontAwesomeIcon icon, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor, size); /// /// IconButton component to use an icon as a button. @@ -33,51 +73,45 @@ public static partial class ImGuiComponents /// The ID of the button. /// The icon for the button. /// Indicator if button is clicked. - public static bool IconButton(string id, FontAwesomeIcon icon) - => IconButton(id, icon, null, null, null); + public static bool IconButton(string id, FontAwesomeIcon icon) => IconButton(id, icon, null); + + /// + /// IconButton component to use an icon as a button. + /// + /// The ID of the button. + /// The icon for the button. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(string id, FontAwesomeIcon icon, Vector2 size) + => IconButton(id, icon, null, null, null, size); + + /// + /// IconButton component to use an icon as a button with color options. + /// + /// The ID of the button. + /// The icon for the button. + /// The default color of the button. + /// The color of the button when active. + /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(string id, FontAwesomeIcon icon, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) + => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor, size); /// /// IconButton component to use an icon as a button. /// /// Text already containing the icon string. /// Indicator if button is clicked. - public static bool IconButton(string iconText) - => IconButton(iconText, null, null, null); + public static bool IconButton(string iconText) => IconButton(iconText, null); /// /// IconButton component to use an icon as a button. /// - /// The icon for the button. - /// The default color of the button. - /// The color of the button when active. - /// The color of the button when hovered. + /// Text already containing the icon string. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. /// Indicator if button is clicked. - public static bool IconButton(FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) - => IconButton($"{icon.ToIconString()}", defaultColor, activeColor, hoveredColor); - - /// - /// IconButton component to use an icon as a button with color options. - /// - /// The ID of the button. - /// The icon for the button. - /// The default color of the button. - /// The color of the button when active. - /// The color of the button when hovered. - /// Indicator if button is clicked. - public static bool IconButton(int id, FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) - => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor); - - /// - /// IconButton component to use an icon as a button with color options. - /// - /// The ID of the button. - /// The icon for the button. - /// The default color of the button. - /// The color of the button when active. - /// The color of the button when hovered. - /// Indicator if button is clicked. - public static bool IconButton(string id, FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) - => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor); + public static bool IconButton(string iconText, Vector2 size) => IconButton(iconText, null, null, null, size); /// /// IconButton component to use an icon as a button with color options. @@ -86,62 +120,72 @@ public static partial class ImGuiComponents /// The default color of the button. /// The color of the button when active. /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. /// Indicator if button is clicked. - public static bool IconButton(string iconText, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + public static bool IconButton(string iconText, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) { - var numColors = 0; + using var col = new ImRaii.Color(); if (defaultColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value); - numColors++; + col.Push(ImGuiCol.Button, defaultColor.Value); } if (activeColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonActive, activeColor.Value); } if (hoveredColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonHovered, hoveredColor.Value); + } + + if (size.HasValue) + { + size *= ImGuiHelpers.GlobalScale; } var icon = iconText; - if (icon.Contains("#")) - icon = icon[..icon.IndexOf("#", StringComparison.Ordinal)]; + if (icon.Contains('#')) + { + icon = icon[..icon.IndexOf('#', StringComparison.Ordinal)]; + } - ImGui.PushID(iconText); + bool button; - ImGui.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon); - ImGui.PopFont(); - - var dl = ImGui.GetWindowDrawList(); - var cursor = ImGui.GetCursorScreenPos(); - - // Draw an ImGui button with the icon and text - var buttonWidth = iconSize.X + (ImGui.GetStyle().FramePadding.X * 2); - var buttonHeight = ImGui.GetFrameHeight(); - var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight)); - - // Draw the icon on the window drawlist - var iconPos = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X, cursor.Y + ImGui.GetStyle().FramePadding.Y); - - ImGui.PushFont(UiBuilder.IconFont); - dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon); - ImGui.PopFont(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var iconSize = ImGui.CalcTextSize(icon); + var cursor = ImGui.GetCursorScreenPos(); - ImGui.PopID(); + var width = size is { X: not 0 } ? size.Value.X : iconSize.X + (ImGui.GetStyle().FramePadding.X * 2); + var height = size is { Y: not 0 } ? size.Value.Y : ImGui.GetFrameHeight(); - if (numColors > 0) - ImGui.PopStyleColor(numColors); + var buttonSize = new Vector2(width, height); + + using (ImRaii.PushId(iconText)) + { + button = ImGui.Button(string.Empty, buttonSize); + } + + var iconPos = cursor + ((buttonSize - iconSize) / 2f); + + ImGui.GetWindowDrawList().AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon); + } return button; } + /// + /// IconButton component to use an icon as a button with color options. + /// + /// Icon to show. + /// Text to show. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon & text. + /// Indicator if button is clicked. + public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector2 size) => IconButtonWithText(icon, text, null, null, null, size); + /// /// IconButton component to use an icon as a button with color options. /// @@ -150,61 +194,72 @@ public static partial class ImGuiComponents /// The default color of the button. /// The color of the button when active. /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon & text. /// Indicator if button is clicked. - public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) { - var numColors = 0; + using var col = new ImRaii.Color(); if (defaultColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value); - numColors++; + col.Push(ImGuiCol.Button, defaultColor.Value); } if (activeColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonActive, activeColor.Value); } if (hoveredColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonHovered, hoveredColor.Value); } - ImGui.PushID(text); + if (size.HasValue) + { + size *= ImGuiHelpers.GlobalScale; + } + + bool button; + + Vector2 iconSize; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + iconSize = ImGui.CalcTextSize(icon.ToIconString()); + } + + var textStr = text; + if (textStr.Contains('#')) + { + textStr = textStr[..textStr.IndexOf('#', StringComparison.Ordinal)]; + } + + var framePadding = ImGui.GetStyle().FramePadding; + var iconPadding = 3 * ImGuiHelpers.GlobalScale; - ImGui.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - ImGui.PopFont(); - - var textSize = ImGui.CalcTextSize(text); - var dl = ImGui.GetWindowDrawList(); var cursor = ImGui.GetCursorScreenPos(); - var iconPadding = 3 * ImGuiHelpers.GlobalScale; - - // Draw an ImGui button with the icon and text - var buttonWidth = iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; - var buttonHeight = ImGui.GetFrameHeight(); - var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight)); - - // Draw the icon on the window drawlist - var iconPos = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X, cursor.Y + ImGui.GetStyle().FramePadding.Y); - - ImGui.PushFont(UiBuilder.IconFont); - dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); - ImGui.PopFont(); - - // Draw the text on the window drawlist - var textPos = new Vector2(iconPos.X + iconSize.X + iconPadding, cursor.Y + ImGui.GetStyle().FramePadding.Y); - dl.AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), text); + using (ImRaii.PushId(text)) + { + var textSize = ImGui.CalcTextSize(textStr); - ImGui.PopID(); + var width = size is { X: not 0 } ? size.Value.X : iconSize.X + textSize.X + (framePadding.X * 2) + iconPadding; + var height = size is { Y: not 0 } ? size.Value.Y : ImGui.GetFrameHeight(); - if (numColors > 0) - ImGui.PopStyleColor(numColors); + button = ImGui.Button(string.Empty, new Vector2(width, height)); + } + + var iconPos = cursor + framePadding; + var textPos = new Vector2(iconPos.X + iconSize.X + iconPadding, cursor.Y + framePadding.Y); + + var dl = ImGui.GetWindowDrawList(); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); + } + + dl.AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), textStr); return button; } @@ -217,16 +272,15 @@ public static partial class ImGuiComponents /// Width. internal static float GetIconButtonWithTextWidth(FontAwesomeIcon icon, string text) { - ImGui.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - ImGui.PopFont(); - - var textSize = ImGui.CalcTextSize(text); - var dl = ImGui.GetWindowDrawList(); - var cursor = ImGui.GetCursorScreenPos(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - var iconPadding = 3 * ImGuiHelpers.GlobalScale; - - return iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; + var textSize = ImGui.CalcTextSize(text); + + var iconPadding = 3 * ImGuiHelpers.GlobalScale; + + return iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; + } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs b/Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs new file mode 100644 index 000000000..3f9c469bb --- /dev/null +++ b/Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +using Dalamud.Interface.Utility; + +using ImGuiNET; + +namespace Dalamud.Interface.Components; + +public static partial class ImGuiComponents +{ + /// + /// A radio-like input that uses icon buttons. + /// + /// The type of the value being set. + /// Text that will be used to generate individual labels for the buttons. + /// The value to set. + /// The icons that will be displayed on each button. + /// The options that each button will apply. + /// Arranges the buttons in a grid with the given number of columns. 0 = ignored (all buttons drawn in one row). + /// Sets the size of all buttons. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// The default color of the button range. + /// The color of the actively-selected button. + /// The color of the buttons when hovered. + /// True if any button is clicked. + internal static bool IconButtonSelect(string label, ref T val, IEnumerable optionIcons, IEnumerable optionValues, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + { + var options = optionIcons.Zip(optionValues, static (icon, value) => new KeyValuePair(icon, value)); + return IconButtonSelect(label, ref val, options, columns, buttonSize, defaultColor, activeColor, hoveredColor); + } + + /// + /// A radio-like input that uses icon buttons. + /// + /// The type of the value being set. + /// Text that will be used to generate individual labels for the buttons. + /// The value to set. + /// A list of all icon/option pairs. + /// Arranges the buttons in a grid with the given number of columns. 0 = ignored (all buttons drawn in one row). + /// Sets the size of all buttons. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// The default color of the button range. + /// The color of the actively-selected button. + /// The color of the buttons when hovered. + /// True if any button is clicked. + internal static unsafe bool IconButtonSelect(string label, ref T val, IEnumerable> options, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + { + defaultColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.Button); + activeColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.ButtonActive); + hoveredColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.ButtonHovered); + + var result = false; + + var innerSpacing = ImGui.GetStyle().ItemInnerSpacing; + var y = ImGui.GetCursorPosY(); + + var optArr = options.ToArray(); + for (var i = 0; i < optArr.Length; i++) + { + if (i > 0) + { + if (columns == 0 || i % columns != 0) + { + ImGui.SameLine(0, innerSpacing.X); + } + else + { + y += (buttonSize is { Y: not 0 } ? buttonSize.Value.Y * ImGuiHelpers.GlobalScale : ImGui.GetFrameHeight()) + innerSpacing.Y; + + ImGui.SetCursorPosY(y); + } + } + + optArr[i].Deconstruct(out var icon, out var option); + + var selected = val is not null && val.Equals(option); + + if (IconButton($"{label}{option}{i}", icon, selected ? activeColor : defaultColor, activeColor, hoveredColor, buttonSize)) + { + val = option; + result = true; + } + } + + return result; + } +} diff --git a/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs b/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs index 597b472c6..43b54fc93 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs @@ -1,3 +1,5 @@ +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -24,7 +26,13 @@ public static partial class ImGuiComponents else { ImGui.Text(value + "*"); - if (ImGui.IsItemHovered()) ImGui.SetTooltip(hint); + if (ImGui.IsItemHovered()) + { + using (ImRaii.Tooltip()) + { + ImGui.TextUnformatted(hint); + } + } } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs b/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs index 64f3d01eb..6d6e0f6c3 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs @@ -36,9 +36,14 @@ public static partial class ImGuiComponents } if (ImGui.IsItemHovered()) + { drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(!v ? colors[(int)ImGuiCol.ButtonActive] : new Vector4(0.78f, 0.78f, 0.78f, 1.0f)), height * 0.5f); + } else + { drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(!v ? colors[(int)ImGuiCol.Button] * 0.6f : new Vector4(0.35f, 0.35f, 0.35f, 1.0f)), height * 0.50f); + } + drawList.AddCircleFilled(new Vector2(p.X + radius + ((v ? 1 : 0) * (width - (radius * 2.0f))), p.Y + radius), radius - 1.5f, ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1))); return changed; @@ -62,7 +67,7 @@ public static partial class ImGuiComponents // TODO: animate ImGui.InvisibleButton(id, new Vector2(width, height)); - var dimFactor = 0.5f; + const float dimFactor = 0.5f; drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(v ? colors[(int)ImGuiCol.Button] * dimFactor : new Vector4(0.55f, 0.55f, 0.55f, 1.0f) * dimFactor), height * 0.50f); drawList.AddCircleFilled(new Vector2(p.X + radius + ((v ? 1 : 0) * (width - (radius * 2.0f))), p.Y + radius), radius - 1.5f, ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1) * dimFactor)); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs new file mode 100644 index 000000000..4b7a531c0 --- /dev/null +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs @@ -0,0 +1,124 @@ +using Dalamud.Interface.Internal.UiDebug2.Utility; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Memory; +using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Component.GUI; + +using ImGuiNET; + +using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; + +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; + +public unsafe partial class AddonTree +{ + /// + /// Prints a table of AtkValues associated with a given addon. + /// + /// The addon to look up. + internal static void PrintAtkValues(AtkUnitBase* addon) + { + var atkValue = addon->AtkValues; + if (addon->AtkValuesCount > 0 && atkValue != null) + { + using var tree = ImRaii.TreeNode($"Atk Values [{addon->AtkValuesCount}]###atkValues_{addon->NameString}"); + if (tree) + { + using (ImRaii.Table( + "atkUnitBase_atkValueTable", + 3, + ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg)) + { + ImGui.TableSetupColumn("Index"); + ImGui.TableSetupColumn("Type"); + ImGui.TableSetupColumn("Value"); + ImGui.TableHeadersRow(); + + try + { + for (var i = 0; i < addon->AtkValuesCount; i++) + { + ImGui.TableNextColumn(); + if (atkValue->Type == 0) + { + ImGui.TextDisabled($"#{i}"); + } + else + { + ImGui.Text($"#{i}"); + } + + ImGui.TableNextColumn(); + if (atkValue->Type == 0) + { + ImGui.TextDisabled("Not Set"); + } + else + { + ImGui.Text($"{atkValue->Type}"); + } + + ImGui.TableNextColumn(); + + switch (atkValue->Type) + { + case 0: + break; + case ValueType.Int: + case ValueType.UInt: + { + ImGui.TextUnformatted($"{atkValue->Int}"); + break; + } + + case ValueType.ManagedString: + case ValueType.String8: + case ValueType.String: + { + if (atkValue->String == null) + { + ImGui.TextDisabled("null"); + } + else + { + var str = MemoryHelper.ReadSeStringNullTerminated(new nint(atkValue->String)); + Util.ShowStruct(str, (ulong)atkValue); + } + + break; + } + + case ValueType.Bool: + { + ImGui.TextUnformatted($"{atkValue->Byte != 0}"); + break; + } + + case ValueType.Pointer: + ImGui.TextUnformatted($"{(nint)atkValue->Pointer}"); + break; + + default: + { + ImGui.TextDisabled("Unhandled Type"); + ImGui.SameLine(); + Util.ShowStruct(atkValue); + break; + } + } + + atkValue++; + } + } + catch (Exception ex) + { + ImGui.TextColored(new(1, 0, 0, 1), $"{ex}"); + } + } + } + + Gui.PaddedSeparator(); + } + } +} diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs index 8affa1eac..0b1dcb66c 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs @@ -23,6 +23,11 @@ public unsafe partial class AddonTree /// internal Dictionary> FieldNames { get; set; } = []; + /// + /// Gets or sets the size of the addon according to its Attributes in FFXIVClientStructs. + /// + internal int AddonSize { get; set; } + private object? GetAddonObj(AtkUnitBase* addon) { if (addon == null) @@ -42,6 +47,13 @@ public unsafe partial class AddonTree select t) { AddonTypeDict[this.AddonName] = t; + + var size = t.StructLayoutAttribute?.Size; + if (size != null) + { + this.AddonSize = size.Value; + } + break; } } diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs index 2823a2058..9d6575a55 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Numerics; using Dalamud.Interface.Components; + using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; @@ -19,14 +20,12 @@ namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// public unsafe partial class AddonTree : IDisposable { - private readonly nint initialPtr; - private AddonPopoutWindow? window; private AddonTree(string name, nint ptr) { this.AddonName = name; - this.initialPtr = ptr; + this.InitialPtr = ptr; this.PopulateFieldNames(ptr); } @@ -35,6 +34,11 @@ public unsafe partial class AddonTree : IDisposable /// internal string AddonName { get; init; } + /// + /// Gets the addon's pointer at the time this was created. + /// + internal nint InitialPtr { get; init; } + /// /// Gets or sets a collection of trees representing nodes within this addon. /// @@ -81,7 +85,7 @@ public unsafe partial class AddonTree : IDisposable { if (AddonTrees.TryGetValue(name, out var tree)) { - if (tree.initialPtr == ptr) + if (tree.InitialPtr == ptr) { return tree; } @@ -143,34 +147,47 @@ public unsafe partial class AddonTree : IDisposable ImGui.SetTooltip("Toggle Popout Window"); } - ImGui.Separator(); - - PrintFieldValuePair("Address", $"{(nint)addon:X}"); + PaddedSeparator(1); var uldManager = addon->UldManager; + + PrintFieldValuePair("Address", $"{(nint)addon:X}"); + PrintFieldValuePair("Agent", $"{GameGui.FindAgentInterface(addon):X}"); + PrintFieldValuePairs( ("X", $"{addon->X}"), - ("Y", $"{addon->X}"), + ("Y", $"{addon->Y}"), ("Scale", $"{addon->Scale}"), ("Widget Count", $"{uldManager.ObjectCount}")); - ImGui.Separator(); - var addonObj = this.GetAddonObj(addon); if (addonObj != null) { + PaddedSeparator(); ShowStruct(addonObj, (ulong)addon); } - ImGui.Dummy(new(25 * ImGui.GetIO().FontGlobalScale)); - ImGui.Separator(); + PaddedSeparator(); - ResNodeTree.PrintNodeList(uldManager.NodeList, uldManager.NodeListCount, this); + PrintAtkValues(addon); - ImGui.Dummy(new(25 * ImGui.GetIO().FontGlobalScale)); - ImGui.Separator(); + if (addon->RootNode != null) + { + ResNodeTree.GetOrCreate(addon->RootNode, this).Print(0); + PaddedSeparator(); + } - ResNodeTree.PrintNodeListAsTree(addon->CollisionNodeList, (int)addon->CollisionNodeListCount, "Collision List", this, new(0.5F, 0.7F, 1F, 1F)); + if (uldManager.NodeList != null) + { + var count = uldManager.NodeListCount; + ResNodeTree.PrintNodeListAsTree(uldManager.NodeList, count, $"Node List [{count}]:", this, new(0, 0.85F, 1, 1)); + PaddedSeparator(); + } + + if (addon->CollisionNodeList != null) + { + ResNodeTree.PrintNodeListAsTree(addon->CollisionNodeList, (int)addon->CollisionNodeListCount, "Collision List", this, new(0.5F, 0.7F, 1F, 1F)); + } if (SearchResults.Length > 0 && Countdown <= 0) { @@ -218,7 +235,7 @@ public unsafe partial class AddonTree : IDisposable private bool ValidateAddon(out AtkUnitBase* addon) { addon = (AtkUnitBase*)GameGui.GetAddonByName(this.AddonName); - if (addon == null || (nint)addon != this.initialPtr) + if (addon == null || (nint)addon != this.InitialPtr) { this.Dispose(); return false; @@ -231,7 +248,7 @@ public unsafe partial class AddonTree : IDisposable { if (this.window == null) { - this.window = new AddonPopoutWindow(this, $"{this.AddonName}###addonPopout{this.initialPtr}"); + this.window = new AddonPopoutWindow(this, $"{this.AddonName}###addonPopout{this.InitialPtr}"); PopoutWindows.AddWindow(this.window); } else diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs index 0c3a947dd..45a2d90eb 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs @@ -1,4 +1,6 @@ -using Dalamud.Interface.Internal.UiDebug2.Utility; +using System.Numerics; + +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -56,9 +58,9 @@ public static class Events ImGui.TableNextColumn(); ImGui.TextUnformatted($"{evt->State.UnkFlags1}"); ImGui.TableNextColumn(); - Gui.ClickToCopyText($"{(nint)evt->Target:X}"); + ImGuiHelpers.ClickToCopyText($"{(nint)evt->Target:X}", null, new Vector4(0.6f, 0.6f, 0.6f, 1)); ImGui.TableNextColumn(); - Gui.ClickToCopyText($"{(nint)evt->Listener:X}"); + ImGuiHelpers.ClickToCopyText($"{(nint)evt->Listener:X}", null, new Vector4(0.6f, 0.6f, 0.6f, 1)); evt = evt->NextEvent; } } diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs index d9fcf52cc..4a1989441 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs @@ -34,11 +34,13 @@ internal unsafe class ComponentNodeTree : ResNodeTree private AtkUldManager* UldManager => &this.Component->UldManager; + private int? ComponentFieldOffset { get; set; } + /// private protected override string GetHeaderText() { var childCount = (int)this.UldManager->NodeListCount; - return $"{this.componentType} Component Node{(childCount > 0 ? $" [+{childCount}]" : string.Empty)} (Node: {(nint)this.Node:X} / Comp: {(nint)this.Component:X})"; + return $"{this.componentType} Component Node{(childCount > 0 ? $" [+{childCount}]" : string.Empty)}"; } /// @@ -62,10 +64,10 @@ internal unsafe class ComponentNodeTree : ResNodeTree } /// - private protected override void PrintFieldNames() + private protected override void PrintFieldLabels() { - this.PrintFieldName((nint)this.Node, new(0, 0.85F, 1, 1)); - this.PrintFieldName((nint)this.Component, new(0f, 0.5f, 0.8f, 1f)); + this.PrintFieldLabel((nint)this.Node, new(0, 0.85F, 1, 1), this.NodeFieldOffset); + this.PrintFieldLabel((nint)this.Component, new(0f, 0.5f, 0.8f, 1f), this.ComponentFieldOffset); } /// @@ -108,6 +110,34 @@ internal unsafe class ComponentNodeTree : ResNodeTree } } + /// + private protected override void GetFieldOffset() + { + var nodeFound = false; + var componentFound = false; + for (var i = 0; i < this.AddonTree.AddonSize; i += 0x8) + { + var readPtr = Marshal.ReadIntPtr(this.AddonTree.InitialPtr + i); + + if (readPtr == (nint)this.Node) + { + this.NodeFieldOffset = i; + nodeFound = true; + } + + if (readPtr == (nint)this.Component) + { + this.ComponentFieldOffset = i; + componentFound = true; + } + + if (nodeFound && componentFound) + { + break; + } + } + } + private void PrintComponentObject() { PrintFieldValuePair("Component", $"{(nint)this.Component:X}"); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs index 6cb178bd7..6b6522bb4 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Numerics; +using Dalamud.Interface.Components; using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Utility.Raii; @@ -370,8 +371,8 @@ internal unsafe partial class TextNodeTree var hAlign = (int)alignment % 3; var vAlign = ((int)alignment - hAlign) / 3; - var hAlignInput = IconButtonSelect($"{label}H", ref hAlign, [0, 1, 2], [AlignLeft, AlignCenter, AlignRight]); - var vAlignInput = IconButtonSelect($"{label}V", ref vAlign, [0, 1, 2], [ArrowsUpToLine, GripLines, ArrowsDownToLine]); + var hAlignInput = ImGuiComponents.IconButtonSelect($"{label}H", ref hAlign, [AlignLeft, AlignCenter, AlignRight], [0, 1, 2], 3u, new(25, 0)); + var vAlignInput = ImGuiComponents.IconButtonSelect($"{label}V", ref vAlign, [ArrowsUpToLine, GripLines, ArrowsDownToLine], [0, 1, 2], 3u, new(25, 0)); if (hAlignInput || vAlignInput) { diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs index a333940c1..2edf4e570 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using Dalamud.Interface.Components; using Dalamud.Interface.Internal.UiDebug2.Utility; @@ -60,6 +61,11 @@ internal unsafe partial class ResNodeTree : IDisposable /// private protected NodeType NodeType { get; init; } + /// + /// Gets or sets the offset of this node within its parent Addon. + /// + private protected int? NodeFieldOffset { get; set; } + /// /// Clears this NodeTree's popout window, if it has one. /// @@ -164,19 +170,26 @@ internal unsafe partial class ResNodeTree : IDisposable internal void WriteTreeHeading() { ImGui.TextUnformatted(this.GetHeaderText()); - this.PrintFieldNames(); + this.PrintFieldLabels(); } /// - /// If the given pointer has been identified as a field within the addon struct, this method prints that field's name. + /// If the given pointer is referenced with the addon struct, the offset within the addon will be printed. If the given pointer has been identified as a field within the addon struct, this method also prints that field's name. /// /// The pointer to check. /// The text color to use. - private protected void PrintFieldName(nint ptr, Vector4 color) + /// The field offset of the pointer, if it was found in the addon. + private protected void PrintFieldLabel(nint ptr, Vector4 color, int? fieldOffset) { + if (fieldOffset != null) + { + ImGui.SameLine(0, -1); + ImGui.TextColored(color * 0.85f, $"[0x{fieldOffset:X}]"); + } + if (this.AddonTree.FieldNames.TryGetValue(ptr, out var result)) { - ImGui.SameLine(); + ImGui.SameLine(0, -1); ImGui.TextColored(color, string.Join(".", result)); } } @@ -188,7 +201,15 @@ internal unsafe partial class ResNodeTree : IDisposable private protected virtual string GetHeaderText() { var count = this.GetDirectChildCount(); - return $"{this.NodeType} Node{(count > 0 ? $" [+{count}]" : string.Empty)} ({(nint)this.Node:X})"; + return $"{this.NodeType} Node{(count > 0 ? $" [+{count}]" : string.Empty)}"; + } + + /// + /// Prints any field names for the node. + /// + private protected virtual void PrintFieldLabels() + { + this.PrintFieldLabel((nint)this.Node, new(0, 0.85F, 1, 1), this.NodeFieldOffset); } /// @@ -201,11 +222,6 @@ internal unsafe partial class ResNodeTree : IDisposable ImGui.NewLine(); } - /// - /// Prints any field names for the node. - /// - private protected virtual void PrintFieldNames() => this.PrintFieldName((nint)this.Node, new(0, 0.85F, 1, 1)); - /// /// Prints all direct children of this node. /// @@ -227,6 +243,21 @@ internal unsafe partial class ResNodeTree : IDisposable { } + /// + /// Attempts to retrieve the field offset of the given pointer within the parent addon. + /// + private protected virtual void GetFieldOffset() + { + for (var i = 0; i < this.AddonTree.AddonSize; i += 0x8) + { + if (Marshal.ReadIntPtr(this.AddonTree.InitialPtr + i) == (nint)this.Node) + { + this.NodeFieldOffset = i; + break; + } + } + } + private int GetDirectChildCount() { var count = 0; @@ -273,6 +304,8 @@ internal unsafe partial class ResNodeTree : IDisposable ImGui.SetNextItemOpen(true, ImGuiCond.Always); } + this.GetFieldOffset(); + using var col = ImRaii.PushColor(Text, displayColor); using var tree = ImRaii.TreeNode(label, SpanFullWidth); @@ -281,7 +314,7 @@ internal unsafe partial class ResNodeTree : IDisposable new NodeBounds(this.Node).Draw(visible ? new(0.1f, 1f, 0.1f, 1f) : new(1f, 0f, 0.2f, 1f)); } - ImGui.SameLine(); + ImGui.SameLine(0, -1); this.WriteTreeHeading(); col.Pop(); diff --git a/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs b/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs index 7f603fdac..6693c3fb0 100644 --- a/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs +++ b/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs @@ -79,7 +79,7 @@ internal unsafe class ElementSelector : IDisposable /// internal void DrawInterface() { - using (ImRaii.Child("###sidebar_elementSelector", new(250, 0), true)) + using (ImRaii.Child("###sidebar_elementSelector", new(250, -1), true)) { using (ImRaii.PushFont(IconFont)) { diff --git a/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs b/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs index 954e5cb72..cc73b79c6 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; using System.Numerics; -using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using FFXIVClientStructs.FFXIV.Client.Graphics; @@ -17,40 +16,6 @@ namespace Dalamud.Interface.Internal.UiDebug2.Utility; /// internal static class Gui { - /// - /// A radio-button-esque input that uses Fontawesome icon buttons. - /// - /// The type of value being set. - /// The label for the inputs. - /// The value being set. - /// A list of all options. - /// A list of icons corresponding to the options. - /// true if a button is clicked. - internal static unsafe bool IconButtonSelect(string label, ref T val, List options, List icons) - { - var ret = false; - - for (var i = 0; i < options.Count; i++) - { - if (i > 0) - { - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - } - - var option = options[i]; - var icon = icons.Count > i ? icons[i] : FontAwesomeIcon.Question; - var color = *ImGui.GetStyleColorVec4(val is not null && val.Equals(option) ? ButtonActive : Button); - - if (ImGuiComponents.IconButton($"{label}{option}{i}", icon, color)) - { - val = option; - ret = true; - } - } - - return ret; - } - /// /// Prints field name and its value. /// @@ -61,13 +26,14 @@ internal static class Gui { ImGui.TextUnformatted($"{fieldName}:"); ImGui.SameLine(); + var grey60 = new Vector4(0.6f, 0.6f, 0.6f, 1); if (copy) { - ClickToCopyText(value); + ImGuiHelpers.ClickToCopyText(value, null, grey60); } else { - ImGui.TextColored(new(0.6f, 0.6f, 0.6f, 1), value); + ImGui.TextColored(grey60, value); } } @@ -102,7 +68,10 @@ internal static class Gui /// Colors the text itself either white or black, depending on the luminosity of the background color. internal static void PrintColor(Vector4 color, string fmt) { - using (new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)).Push(Button, color).Push(ButtonActive, color).Push(ButtonHovered, color)) + using (new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)) + .Push(Button, color) + .Push(ButtonActive, color) + .Push(ButtonHovered, color)) { ImGui.SmallButton(fmt); } @@ -117,39 +86,6 @@ internal static class Gui 0.5f) * vector4.W; } - /// - /// Print out text that can be copied when clicked. - /// - /// The text to show. - /// The text to copy when clicked. - internal static void ClickToCopyText(string text, string? textCopy = null) - { - using (ImRaii.PushColor(Text, new Vector4(0.6f, 0.6f, 0.6f, 1))) - { - textCopy ??= text; - ImGui.TextUnformatted($"{text}"); - } - - if (ImGui.IsItemHovered()) - { - using (ImRaii.Tooltip()) - { - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.TextUnformatted(FontAwesomeIcon.Copy.ToIconString()); - } - - ImGui.SameLine(); - ImGui.TextUnformatted($"{textCopy}"); - } - } - - if (ImGui.IsItemClicked()) - { - ImGui.SetClipboardText($"{textCopy}"); - } - } - /// /// Draws a tooltip that changes based on the cursor's x-position within the hovered item. /// @@ -176,4 +112,23 @@ internal static class Gui return true; } + + /// + /// Draws a separator with some padding above and below. + /// + /// Governs whether to pad above, below, or both. + /// The amount of padding. + internal static void PaddedSeparator(uint mask = 0b11, float padding = 5f) + { + if ((mask & 0b10) > 0) + { + ImGui.Dummy(new(padding * ImGui.GetIO().FontGlobalScale)); + } + + ImGui.Separator(); + if ((mask & 0b01) > 0) + { + ImGui.Dummy(new(padding * ImGui.GetIO().FontGlobalScale)); + } + } } diff --git a/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs b/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs index cffd676f7..3d28cb836 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs @@ -6,7 +6,7 @@ using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; -using static System.Math; +using static System.MathF; using static Dalamud.Interface.ColorHelpers; namespace Dalamud.Interface.Internal.UiDebug2.Utility; @@ -133,7 +133,7 @@ public unsafe struct NodeBounds if (p.Y > Min(p1.Y, p2.Y) && p.Y <= Max(p1.Y, p2.Y) && p.X <= Max(p1.X, p2.X) && - (p1.X.Equals(p2.X) || p.X <= ((p.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y)) + p1.X)) + (p1.X.Equals(p2.X) || p.X <= (((p.Y - p1.Y) * (p2.X - p1.X)) / (p2.Y - p1.Y)) + p1.X)) { inside = !inside; } @@ -144,12 +144,12 @@ public unsafe struct NodeBounds private static Vector2 TransformPoint(Vector2 p, Vector2 o, float r, Vector2 s) { - var cosR = (float)Cos(r); - var sinR = (float)Sin(r); + var cosR = Cos(r); + var sinR = Sin(r); var d = (p - o) * s; return new( - o.X + (d.X * cosR) - (d.Y * sinR), + (o.X + (d.X * cosR)) - (d.Y * sinR), o.Y + (d.X * sinR) + (d.Y * cosR)); } diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs index 8ce7a48d7..8dedae5cd 100644 --- a/Dalamud/Interface/Utility/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -167,17 +167,41 @@ public static class ImGuiHelpers /// /// The text to show. /// The text to copy when clicked. - public static void ClickToCopyText(string text, string? textCopy = null) + /// The color of the text. + public static void ClickToCopyText(string text, string? textCopy = null, Vector4? color = null) { textCopy ??= text; - ImGui.Text($"{text}"); + + using (var col = new ImRaii.Color()) + { + if (color.HasValue) + { + col.Push(ImGuiCol.Text, color.Value); + } + + ImGui.TextUnformatted($"{text}"); + } + if (ImGui.IsItemHovered()) { ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); - if (textCopy != text) ImGui.SetTooltip(textCopy); + + using (ImRaii.Tooltip()) + { + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.TextUnformatted(FontAwesomeIcon.Copy.ToIconString()); + } + + ImGui.SameLine(); + ImGui.TextUnformatted(textCopy); + } } - if (ImGui.IsItemClicked()) ImGui.SetClipboardText($"{textCopy}"); + if (ImGui.IsItemClicked()) + { + ImGui.SetClipboardText(textCopy); + } } /// Draws a SeString. From 3100f9134d0be73bd56d16acdb251f8c437a8e5c Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 16:50:08 -0800 Subject: [PATCH 41/50] cs bumppppp --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 81ea903b7..b5240ab1f 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 81ea903b7dc90bd07a38703738fa7f84b1ed0775 +Subproject commit b5240ab1f8b0170a4637f0a3f99baa9f6bd3966b From ec8389a34e49bf6a37d6372c9972d180628b8659 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 20:27:30 -0800 Subject: [PATCH 42/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index b5240ab1f..5f4e7bf27 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit b5240ab1f8b0170a4637f0a3f99baa9f6bd3966b +Subproject commit 5f4e7bf271f8144ad72f289890b29351afa248b2 From bdc24ad23edec9cb49288313d31f229d1b53b77d Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 23:10:59 -0800 Subject: [PATCH 43/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 5f4e7bf27..679b5353c 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 5f4e7bf271f8144ad72f289890b29351afa248b2 +Subproject commit 679b5353cd61baa03b26d95efcac059f8e29b141 From d7abf9fe0d530f591d31b2ba8617f42065733148 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 23:24:32 -0800 Subject: [PATCH 44/50] Fix Fate aging step; report Tuliyollal taxes - FATE table test needs a zone with FATEs in it. - Use a `byte` rather than an `int` so we don't overflow like crazy. --- .../Windows/SelfTest/AgingSteps/FateTableAgingStep.cs | 8 +++++++- .../Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs index a8fe60aa9..eef986302 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs @@ -9,7 +9,7 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; /// internal class FateTableAgingStep : IAgingStep { - private int index = 0; + private byte index = 0; /// public string Name => "Test FateTable"; @@ -21,6 +21,12 @@ internal class FateTableAgingStep : IAgingStep ImGui.Text("Checking fate table..."); + if (fateTable.Length == 0) + { + ImGui.Text("Go to a zone that has FATEs currently up."); + return SelfTestStepResult.Waiting; + } + if (this.index == fateTable.Length - 1) { return SelfTestStepResult.Pass; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs index 78d43662c..513141fa9 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs @@ -169,6 +169,7 @@ internal class MarketBoardAgingStep : IAgingStep ImGui.Text($"Kugane: {this.marketTaxRate.KuganeTax.ToString()}"); ImGui.Text($"Crystarium: {this.marketTaxRate.CrystariumTax.ToString()}"); ImGui.Text($"Sharlayan: {this.marketTaxRate.SharlayanTax.ToString()}"); + ImGui.Text($"Tuliyollal: {this.marketTaxRate.TuliyollalTax.ToString()}"); ImGui.Separator(); if (ImGui.Button("Looks Correct / Skip")) { From bd5f1a32773534c56cc8d036ee3acb6797e8708d Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 23:51:08 -0800 Subject: [PATCH 45/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 679b5353c..73ac85afd 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 679b5353cd61baa03b26d95efcac059f8e29b141 +Subproject commit 73ac85afd50968fb9c099507ce05da48bdf72e3c From d6dad8b44a01ffa7a24a50fd5deda29c03860c1e Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 15 Nov 2024 00:20:51 -0800 Subject: [PATCH 46/50] fix gamepad inputs for imgui --- Dalamud/Game/ClientState/GamePad/GamepadInput.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Dalamud/Game/ClientState/GamePad/GamepadInput.cs b/Dalamud/Game/ClientState/GamePad/GamepadInput.cs index 32439cd08..d9dcea60f 100644 --- a/Dalamud/Game/ClientState/GamePad/GamepadInput.cs +++ b/Dalamud/Game/ClientState/GamePad/GamepadInput.cs @@ -16,25 +16,25 @@ public struct GamepadInput /// /// Left analogue stick's horizontal value, -99 for left, 99 for right. /// - [FieldOffset(0x88)] + [FieldOffset(0x78)] public int LeftStickX; /// /// Left analogue stick's vertical value, -99 for down, 99 for up. /// - [FieldOffset(0x8C)] + [FieldOffset(0x7C)] public int LeftStickY; /// /// Right analogue stick's horizontal value, -99 for left, 99 for right. /// - [FieldOffset(0x90)] + [FieldOffset(0x80)] public int RightStickX; /// /// Right analogue stick's vertical value, -99 for down, 99 for up. /// - [FieldOffset(0x94)] + [FieldOffset(0x84)] public int RightStickY; /// @@ -43,7 +43,7 @@ public struct GamepadInput /// /// This is a bitfield. /// - [FieldOffset(0x98)] + [FieldOffset(0x88)] public ushort ButtonsRaw; /// @@ -52,7 +52,7 @@ public struct GamepadInput /// /// This is a bitfield. /// - [FieldOffset(0x9C)] + [FieldOffset(0x8C)] public ushort ButtonsPressed; /// @@ -61,7 +61,7 @@ public struct GamepadInput /// /// This is a bitfield. /// - [FieldOffset(0xA0)] + [FieldOffset(0x90)] public ushort ButtonsReleased; /// @@ -70,6 +70,6 @@ public struct GamepadInput /// /// This is a bitfield. /// - [FieldOffset(0xA4)] + [FieldOffset(0x94)] public ushort ButtonsRepeat; } From b4d9ebe1ef8928124ee278f8a11defc1ef8fe03d Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 15 Nov 2024 08:11:24 -0800 Subject: [PATCH 47/50] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 73ac85afd..fca10adca 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 73ac85afd50968fb9c099507ce05da48bdf72e3c +Subproject commit fca10adcab3911c1be79cb0753987a2a02b7e058 From 9ac1f5cdb4a7b2aa1a73cb1546e29281f61fdca6 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 15 Nov 2024 08:18:39 -0800 Subject: [PATCH 48/50] Revert OnLogin event to only fire when LocalPlayer is present --- Dalamud/Game/ClientState/ClientState.cs | 35 ++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 364466cce..5f22eb54b 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -38,11 +38,16 @@ internal sealed class ClientState : IInternalDisposableService, IClientState private readonly ClientStateAddressResolver address; private readonly Hook setupTerritoryTypeHook; private readonly Hook uiModuleHandlePacketHook; - private readonly Hook processPacketPlayerSetupHook; private readonly Hook onLogoutHook; + [ServiceManager.ServiceDependency] + private readonly Framework framework = Service.Get(); + [ServiceManager.ServiceDependency] private readonly NetworkHandlers networkHandlers = Service.Get(); + + private bool lastConditionNone = true; + [ServiceManager.ServiceConstructor] private unsafe ClientState(TargetSigScanner sigScanner, Dalamud dalamud, GameLifecycle lifecycle) @@ -60,14 +65,13 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.setupTerritoryTypeHook = Hook.FromAddress(setTerritoryTypeAddr, this.SetupTerritoryTypeDetour); this.uiModuleHandlePacketHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour); - this.processPacketPlayerSetupHook = Hook.FromAddress(this.address.ProcessPacketPlayerSetup, this.ProcessPacketPlayerSetupDetour); this.onLogoutHook = Hook.FromAddress((nint)LogoutCallbackInterface.StaticVirtualTablePointer->OnLogout, this.OnLogoutDetour); + this.framework.Update += this.FrameworkOnOnUpdateEvent; this.networkHandlers.CfPop += this.NetworkHandlersOnCfPop; this.setupTerritoryTypeHook.Enable(); this.uiModuleHandlePacketHook.Enable(); - this.processPacketPlayerSetupHook.Enable(); this.onLogoutHook.Enable(); } @@ -171,8 +175,9 @@ internal sealed class ClientState : IInternalDisposableService, IClientState { this.setupTerritoryTypeHook.Dispose(); this.uiModuleHandlePacketHook.Dispose(); - this.processPacketPlayerSetupHook.Dispose(); this.onLogoutHook.Dispose(); + + this.framework.Update -= this.FrameworkOnOnUpdateEvent; this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop; } @@ -255,24 +260,24 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } } - private unsafe void ProcessPacketPlayerSetupDetour(nint a1, nint packet) + private void FrameworkOnOnUpdateEvent(IFramework framework1) { - // Call original first, so everything is set up. - this.processPacketPlayerSetupHook.Original(a1, packet); - + var condition = Service.GetNullable(); var gameGui = Service.GetNullable(); + var data = Service.GetNullable(); - try + if (condition == null || gameGui == null || data == null) + return; + + if (condition.Any() && this.lastConditionNone && this.LocalPlayer != null) { - Log.Debug("Login"); + Log.Debug("Is login"); + this.lastConditionNone = false; this.Login?.InvokeSafely(); - gameGui?.ResetUiHideState(); + gameGui.ResetUiHideState(); + this.lifecycle.ResetLogout(); } - catch (Exception ex) - { - Log.Error(ex, "Exception during ProcessPacketPlayerSetupDetour"); - } } private unsafe void OnLogoutDetour(LogoutCallbackInterface* thisPtr, LogoutCallbackInterface.LogoutParams* logoutParams) From b43a6b6bce2e5b9b25c89fa5ba74a87241b9dd46 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 15 Nov 2024 08:24:04 -0800 Subject: [PATCH 49/50] fix: OnLogin needs to be callable again --- Dalamud/Game/ClientState/ClientState.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 5f22eb54b..1fade54b6 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -22,6 +22,8 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Lumina.Excel.Sheets; +using TerraFX.Interop.Windows; + using Action = System.Action; namespace Dalamud.Game.ClientState; @@ -309,6 +311,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } gameGui?.ResetUiHideState(); + this.lastConditionNone = true; // unblock login flag this.lifecycle.SetLogout(); } From 4436c58749b5d4e09c186c566254ae88a2423a04 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Fri, 15 Nov 2024 08:25:19 -0800 Subject: [PATCH 50/50] covfefe --- Dalamud/Game/ClientState/ClientState.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 1fade54b6..4ab69b391 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -22,8 +22,6 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Lumina.Excel.Sheets; -using TerraFX.Interop.Windows; - using Action = System.Action; namespace Dalamud.Game.ClientState;