diff --git a/FFXIVPlugin/ActionExecutor/Strategies/MacroStrategy.cs b/FFXIVPlugin/ActionExecutor/Strategies/MacroStrategy.cs index 6832b83..c7b079d 100644 --- a/FFXIVPlugin/ActionExecutor/Strategies/MacroStrategy.cs +++ b/FFXIVPlugin/ActionExecutor/Strategies/MacroStrategy.cs @@ -1,5 +1,8 @@ -using static FFXIVClientStructs.FFXIV.Client.UI.Misc.RaptureHotbarModule; +using System; +using static FFXIVClientStructs.FFXIV.Client.UI.Misc.RaptureHotbarModule; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Client.UI.Shell; using XIVDeck.FFXIVPlugin.Base; @@ -17,18 +20,33 @@ public class MacroStrategy : IActionStrategy { public unsafe ExecutableAction GetExecutableActionById(uint actionId) { var macro = GetMacro((actionId / 100 > 0), (int)actionId % 100); + _ = TryGetMacroName( + ref Unsafe.AsRef(macro), + (int)actionId % 100, + (actionId / 100 > 0), + out var macroName + ); + // Macros are weird, inasmuch as they can't be null. Something will always exist, even if empty. return new ExecutableAction { ActionId = (int)actionId, - ActionName = macro->Name.ToString(), + ActionName = macroName, Category = null, HotbarSlotType = HotbarSlotType.Macro, IconId = this.GetIconId(actionId) }; } - // Macros will always return null, as they're selected on the client side and we don't get a say. - public List? GetAllowedItems() => null; + public unsafe List GetAllowedItems() { + var items = new List(); + + if (Injections.ClientState.LocalPlayer != null) + items.AddRange(this.GetValidMacrosFromCollection(RaptureMacroModule.Instance()->Individual, 0)); + + items.AddRange(this.GetValidMacrosFromCollection(RaptureMacroModule.Instance()->Shared, 1)); + + return items; + } public unsafe void Execute(uint actionId, ActionPayload? _) { if (actionId > 199) { @@ -61,7 +79,7 @@ private int GetAdjustedIconId(uint item) { var macroPage = item / 100; var macroId = item % 100; - return Injections.Framework.RunOnTick(() => { + return Injections.Framework.RunOnFrameworkThread(() => { // It's terrifying that creating a virtual hotbar slot is probably the easiest way to get a macro icon ID, // but here we are. @@ -72,4 +90,43 @@ private int GetAdjustedIconId(uint item) { return (int)slot.IconId; }).Result; } + + private List GetValidMacrosFromCollection(Span span, int pageId) { + var result = new List(); + + for (var i = 0; i < span.Length; i++) { + ref var macro = ref span[i]; + + var macroId = (100 * pageId) + i; + var macroIconId = (int)macro.IconId; + var wasMacroNamed = TryGetMacroName(ref macro, i, pageId == 1, out var macroName); + + if (!wasMacroNamed && macroIconId == 0) continue; + + result.Add(new ExecutableAction { + ActionId = macroId, + ActionName = macroName, + HotbarSlotType = HotbarSlotType.Macro, + IconId = macroIconId, // intentionally not resolving the proper icon id here. it's very slow. + }); + } + + return result; + } + + /// + /// Checks if the macro is named, and returns the name. + /// + /// A pointer to the macro to check. + /// The ID of the macro for name generation purposes. + /// The share state of the macro for name generation purposes. + /// An out var containing the determined name of the macro. + /// Returns true if the macro was named, false if a generic name was used. + private static unsafe bool TryGetMacroName(ref RaptureMacroModule.Macro macro, int id, bool isShared, out string name) { + name = macro.Name.ToString(); + if (!name.IsNullOrEmpty()) return true; + + name = isShared ? $"Shared Macro {id}" : $"Individual Macro {id}"; + return false; + } } diff --git a/FFXIVPlugin/Server/Controllers/ActionController.cs b/FFXIVPlugin/Server/Controllers/ActionController.cs index 9639074..8ea449c 100644 --- a/FFXIVPlugin/Server/Controllers/ActionController.cs +++ b/FFXIVPlugin/Server/Controllers/ActionController.cs @@ -35,6 +35,9 @@ public Dictionary> GetActions() { Dictionary> actions = new(); foreach (var (type, strategy) in XIVDeckPlugin.Instance.ActionDispatcher.GetStrategies()) { + // hack, we don't want to list macros here. they're slow and annoying. + if (type is HotbarSlotType.Macro) continue; + var allowedItems = strategy.GetAllowedItems(); if (allowedItems == null || allowedItems.Count == 0) continue; diff --git a/FFXIVPlugin/XIVDeck.FFXIVPlugin.csproj b/FFXIVPlugin/XIVDeck.FFXIVPlugin.csproj index a0269d5..6f0e002 100644 --- a/FFXIVPlugin/XIVDeck.FFXIVPlugin.csproj +++ b/FFXIVPlugin/XIVDeck.FFXIVPlugin.csproj @@ -47,7 +47,7 @@ - + diff --git a/FFXIVPlugin/packages.lock.json b/FFXIVPlugin/packages.lock.json index 6b3dcc3..48e1e1d 100644 --- a/FFXIVPlugin/packages.lock.json +++ b/FFXIVPlugin/packages.lock.json @@ -31,9 +31,9 @@ }, "SixLabors.ImageSharp": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "lFIdxgGDA5iYkUMRFOze7BGLcdpoLFbR+a20kc1W7NepvzU7ejtxtWOg9RvgG7kb9tBoJ3ONYOK6kLil/dgF1w==" + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "lNtlq7dSI/QEbYey+A0xn48z5w4XHSffF8222cC4F4YwTXfEImuiBavQcWjr49LThT/pRmtWJRcqA/PlL+eJ6g==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive",