Skip to content

Commit

Permalink
Add macro list functionality
Browse files Browse the repository at this point in the history
- Requested by StreamController plugin.
- Hack some things around to keep code "clean"
- This'll all be fixed in the refactor.
  • Loading branch information
KazWolfe committed Aug 6, 2024
1 parent 0561790 commit fccbc70
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 9 deletions.
67 changes: 62 additions & 5 deletions FFXIVPlugin/ActionExecutor/Strategies/MacroStrategy.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<RaptureMacroModule.Macro>(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<ExecutableAction>? GetAllowedItems() => null;
public unsafe List<ExecutableAction> GetAllowedItems() {
var items = new List<ExecutableAction>();

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) {
Expand Down Expand Up @@ -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.
Expand All @@ -72,4 +90,43 @@ private int GetAdjustedIconId(uint item) {
return (int)slot.IconId;
}).Result;
}

private List<ExecutableAction> GetValidMacrosFromCollection(Span<RaptureMacroModule.Macro> span, int pageId) {
var result = new List<ExecutableAction>();

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;
}

/// <summary>
/// Checks if the macro is named, and returns the name.
/// </summary>
/// <param name="macro">A pointer to the macro to check.</param>
/// <param name="id">The ID of the macro for name generation purposes.</param>
/// <param name="isShared">The share state of the macro for name generation purposes.</param>
/// <param name="name">An out var containing the determined name of the macro.</param>
/// <returns>Returns true if the macro was named, false if a generic name was used.</returns>
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;
}
}
3 changes: 3 additions & 0 deletions FFXIVPlugin/Server/Controllers/ActionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public Dictionary<string, List<ExecutableAction>> GetActions() {
Dictionary<string, List<ExecutableAction>> 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;

Expand Down
2 changes: 1 addition & 1 deletion FFXIVPlugin/XIVDeck.FFXIVPlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

<ItemGroup>
<!-- Image conversion to extract PNGs from Lumina -->
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />

<!-- Webserver and socketing -->
<PackageReference Include="EmbedIO" Version="3.5.2" />
Expand Down
6 changes: 3 additions & 3 deletions FFXIVPlugin/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit fccbc70

Please sign in to comment.