MacroMate, reworked macro copying, extra copying settings

This commit is contained in:
Asriel Camora
2024-07-27 01:32:47 -07:00
parent f2d03730ce
commit 95371739c4
7 changed files with 293 additions and 87 deletions
+7
View File
@@ -127,6 +127,7 @@ public class MacroCopyConfiguration
OpenWindow, // useful for big macros
CopyToMacro, // (add option for down or right) (max macro count; open copy-paste window if too much)
CopyToClipboard,
CopyToMacroMate
}
public CopyType Type { get; set; } = CopyType.OpenWindow;
@@ -137,6 +138,10 @@ public class MacroCopyConfiguration
public int StartMacroIdx { get; set; } = 1;
public int MaxMacroCount { get; set; } = 5;
// CopyToMacroMate
public string MacroMateName { get; set; } = "Craftimizer";
public string MacroMateParent { get; set; } = string.Empty;
// Add /nextmacro [down]
public bool UseNextMacro { get; set; }
@@ -156,6 +161,8 @@ public class MacroCopyConfiguration
// For SND; Cannot use CopyToMacro
public bool CombineMacro { get; set; }
public bool ShowCopiedMessage { get; set; } = true;
}
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
+2
View File
@@ -32,6 +32,7 @@ public sealed class Plugin : IDalamudPlugin
public Hooks Hooks { get; }
public Chat Chat { get; }
public CommunityMacros CommunityMacros { get; }
public Ipc Ipc { get; }
public AttributeCommandManager AttributeCommandManager { get; }
public Plugin(IDalamudPluginInterface pluginInterface)
@@ -44,6 +45,7 @@ public sealed class Plugin : IDalamudPlugin
Hooks = new();
Chat = new();
CommunityMacros = new();
Ipc = new();
AttributeCommandManager = new();
var assembly = Assembly.GetExecutingAssembly();
+1
View File
@@ -32,6 +32,7 @@ public sealed class Service
public static WindowSystem WindowSystem => Plugin.WindowSystem;
public static Chat Chat => Plugin.Chat;
public static CommunityMacros CommunityMacros => Plugin.CommunityMacros;
public static Ipc Ipc => Plugin.Ipc;
#pragma warning restore CS8618
internal static void Initialize(Plugin plugin, IDalamudPluginInterface iface)
+69
View File
@@ -0,0 +1,69 @@
using Craftimizer.Plugin;
using Dalamud.Plugin;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using DotNext.Reflection;
using DotNext.Collections.Generic;
namespace Craftimizer.Utils;
public sealed class Ipc
{
[AttributeUsage(AttributeTargets.Property)]
private sealed class IPCCallAttribute(string? name) : Attribute
{
public string? Name { get; } = name;
}
public Ipc()
{
foreach (var prop in typeof(Ipc).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (prop.GetCustomAttribute<IPCCallAttribute>() is not { } attr)
continue;
if (prop.GetMethod is not { } getMethod)
throw new InvalidOperationException("Property must have a getter");
if (getMethod.GetCustomAttribute<CompilerGeneratedAttribute>() is null)
throw new InvalidOperationException("Property must have an auto getter");
var type = prop.PropertyType;
if (!typeof(Delegate).IsAssignableFrom(type))
throw new InvalidOperationException("Property type must be a delegate");
if (type.GetMethod("Invoke") is not { } typeMethod)
throw new InvalidOperationException("Delegate type has no Invoke");
var returnsVoid = typeMethod.ReturnType == typeof(void);
var propSubscriber = typeof(IDalamudPluginInterface).GetMethod("GetIpcSubscriber", typeMethod.GetParameters().Length + 1, [typeof(string)]);
if (propSubscriber is null)
throw new InvalidOperationException("GetIpcSubscriber method not found");
var callGateSubscriber = propSubscriber.MakeGenericMethod([.. typeMethod.GetParameterTypes(), returnsVoid ? typeof(int) : typeMethod.ReturnType]).Invoke(Service.PluginInterface, [attr.Name ?? prop.Name]);
if (callGateSubscriber is null)
throw new InvalidOperationException("CallGateSubscriber is null");
var invokeFunc = callGateSubscriber.GetType().GetMethod(returnsVoid ? "InvokeAction" : "InvokeFunc");
if (invokeFunc is null)
throw new InvalidOperationException("Subscriber Invoke method not found");
prop.SetValue(this, Delegate.CreateDelegate(type, callGateSubscriber, invokeFunc));
Log.Debug($"Bound {prop.Name} IPC to {type}");
}
}
[IPCCall("MacroMate.IsAvailable")]
public Func<bool> MacroMateIsAvailable { get; private set; } = null!;
[IPCCall("MacroMate.CreateOrUpdateMacro")]
public Func<string, string, string?, uint?, bool> MacroMateCreateMacro { get; private set; } = null!;
[IPCCall("MacroMate.ValidateGroupPath")]
public Func<string, (bool, string?)> MacroMateValidateGroupPath { get; private set; } = null!;
}
+123 -46
View File
@@ -30,59 +30,74 @@ public static class MacroCopy
return;
}
var config = Service.Configuration.MacroCopy;
var macros = new List<string>();
var s = new List<string>();
for (var i = 0; i < actions.Count; ++i)
{
if (config.UseMacroLock && s.Count == 0)
{
s.Add("/mlock");
}
var macros = GetMacros(actions, Service.Configuration.MacroCopy);
s.Add(GetActionCommand(actions[i], config));
if (config.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !config.CombineMacro)
{
if (i != actions.Count - 1 && (i != actions.Count - 2 || config.ForceNotification))
{
if (s.Count == MacroSize - 1)
{
if (GetEndCommand(macros.Count, false, config) is { } endCommand)
s.Add(endCommand);
}
if (s.Count == MacroSize)
{
macros.Add(string.Join(Environment.NewLine, s));
s.Clear();
}
}
}
}
if (s.Count > 0)
{
if (s.Count < MacroSize || (config.Type != MacroCopyConfiguration.CopyType.CopyToMacro && config.CombineMacro))
{
if (GetEndCommand(macros.Count, true, config) is { } endCommand)
s.Add(endCommand);
}
macros.Add(string.Join(Environment.NewLine, s));
}
switch (config.Type)
switch (Service.Configuration.MacroCopy.Type)
{
case MacroCopyConfiguration.CopyType.OpenWindow:
Service.Plugin.OpenMacroClipboard(macros);
break;
case MacroCopyConfiguration.CopyType.CopyToMacro:
CopyToMacro(macros, config);
CopyToMacro(macros);
break;
case MacroCopyConfiguration.CopyType.CopyToClipboard:
CopyToClipboard(macros);
break;
case MacroCopyConfiguration.CopyType.CopyToMacroMate:
CopyToMacroMate(macros[0]);
break;
}
}
private static List<string> GetMacros(IReadOnlyList<ActionType> actions, MacroCopyConfiguration config)
{
var mustSplit = (config.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !config.CombineMacro) && config.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate;
var macros = new List<string>();
var m = new List<string>();
for (var i = 0; i < actions.Count; ++i)
{
var a = actions[i];
var isLast = i == actions.Count - 1;
var isSecondLast = i == actions.Count - 2;
if (config.UseMacroLock && m.Count == 0)
m.Add("/mlock");
m.Add(GetActionCommand(a, config));
if (mustSplit && !isLast)
{
var endLine = GetEndCommand(macros.Count, false, config);
if (endLine != null && m.Count == MacroSize - 1)
{
if (!isSecondLast || config.ForceNotification)
m.Add(endLine);
}
}
if (mustSplit && m.Count == MacroSize)
{
macros.Add(string.Join(Environment.NewLine, m));
m.Clear();
}
}
if (m.Count != MacroSize && m.Count != 0)
{
if (GetEndCommand(macros.Count, true, config) is { } endLine)
m.Add(endLine);
}
if (m.Count != 0)
macros.Add(string.Join(Environment.NewLine, m));
return macros;
}
private static string GetActionCommand(ActionType action, MacroCopyConfiguration config)
{
var actionBase = action.Base();
@@ -124,15 +139,19 @@ public static class MacroCopy
return null;
}
private static void CopyToMacro(List<string> macros, MacroCopyConfiguration config)
private static void CopyToMacro(List<string> macros)
{
var config = Service.Configuration.MacroCopy;
int i, macroIdx;
for (
i = 0, macroIdx = config.StartMacroIdx;
i < macros.Count && i < config.MaxMacroCount && macroIdx < 100;
i++, macroIdx += config.CopyDown ? 10 : 1)
SetMacro(macroIdx, config.SharedMacro, macros[i]);
SetMacro(macroIdx, config.SharedMacro, macros[i], i + 1);
if (config.ShowCopiedMessage)
{
Service.Plugin.DisplayNotification(new()
{
Content = i > 1 ? "Copied macro to User Macros." : $"Copied {i} macros to User Macros.",
@@ -140,6 +159,7 @@ public static class MacroCopy
Title = "Macro Copied",
Type = NotificationType.Success
});
}
if (i < macros.Count)
{
Service.Plugin.OpenMacroClipboard(macros);
@@ -154,28 +174,85 @@ public static class MacroCopy
}
}
private static unsafe void SetMacro(int idx, bool isShared, string macroText)
private static unsafe void SetMacro(int idx, bool isShared, string macroText, int macroIdx)
{
if (idx >= 100 || idx < 0)
throw new ArgumentOutOfRangeException(nameof(idx), "Macro index must be between 0 and 99");
var set = isShared ? 1u : 0u;
var module = RaptureMacroModule.Instance();
var macro = module->GetMacro(isShared ? 1u : 0u, (uint)idx);
var macro = module->GetMacro(set, (uint)idx);
if (macro->IsEmpty())
{
macro->Name.SetString($"Craftimizer Macro {macroIdx}");
macro->SetIcon((uint)(macroIdx > 10 ? 66161 : (66161 + macroIdx)));
}
var text = Utf8String.FromString(macroText.ReplaceLineEndings("\n"));
module->ReplaceMacroLines(macro, text);
text->Dtor();
IMemorySpace.Free(text);
RaptureHotbarModule.Instance()->ReloadMacroSlots((byte)set, (byte)idx);
}
private static void CopyToClipboard(List<string> macros)
{
ImGui.SetClipboardText(string.Join(Environment.NewLine + Environment.NewLine, macros));
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
{
Service.Plugin.DisplayNotification(new()
{
Content = macros.Count > 1 ? "Copied macro to clipboard." : $"Copied {macros.Count} macros to clipboard.",
MinimizedText = macros.Count > 1 ? "Copied macro" : $"Copied {macros.Count} macros",
Content = macros.Count == 1 ? "Copied macro to clipboard." : $"Copied {macros.Count} macros to clipboard.",
MinimizedText = macros.Count == 1 ? "Copied macro" : $"Copied {macros.Count} macros",
Title = "Macro Copied",
Type = NotificationType.Success
});
}
}
private static void CopyToMacroMate(string macro)
{
if (!Service.Ipc.MacroMateIsAvailable())
{
Service.Plugin.DisplayNotification(new()
{
Content = "Please check if it installed and enabled.",
MinimizedText = "Macro Mate is unavailable",
Title = "Macro Mate Unavailable",
Type = NotificationType.Error
});
return;
}
var parentPath = Service.Configuration.MacroCopy.MacroMateParent;
if (string.IsNullOrWhiteSpace(parentPath))
parentPath = "/";
var (isValidParent, parentError) = Service.Ipc.MacroMateValidateGroupPath(parentPath);
if (!isValidParent)
{
Service.Plugin.DisplayNotification(new()
{
Content = parentError!,
MinimizedText = parentError,
Title = "Macro Mate Invalid Parent",
Type = NotificationType.Error
});
return;
}
Service.Ipc.MacroMateCreateMacro(Service.Configuration.MacroCopy.MacroMateName, macro, parentPath, null);
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
{
Service.Plugin.DisplayNotification(new()
{
Content = "Copied macro to Macro Mate.",
MinimizedText = "Copied macro",
Title = "Macro Copied",
Type = NotificationType.Success
});
}
}
}
+6 -3
View File
@@ -39,7 +39,7 @@ public sealed class MacroClipboard : Window, IDisposable
private void DrawMacro(int idx, string macro)
{
using var id = ImRaii.PushId(idx);
using var panel = ImRaii2.GroupPanel($"Macro {idx + 1}", -1, out var availWidth);
using var panel = ImRaii2.GroupPanel(Macros.Count == 1 ? "Macro" : $"Macro {idx + 1}", -1, out var availWidth);
var cursor = ImGui.GetCursorPos();
@@ -56,15 +56,18 @@ public sealed class MacroClipboard : Window, IDisposable
if (buttonClicked)
{
ImGui.SetClipboardText(macro);
if (Service.Configuration.MacroCopy.ShowCopiedMessage)
{
Service.Plugin.DisplayNotification(new()
{
Content = $"Macro {idx + 1} copied to clipboard.",
MinimizedText = $"Copied macro {idx + 1}",
Content = Macros.Count == 1 ? "Copied macro to clipboard." : $"Copied macro {idx + 1} to clipboard.",
MinimizedText = Macros.Count == 1 ? "Copied macro" : $"Copied macro {idx + 1}",
Title = "Macro Copied",
Type = NotificationType.Success
});
}
}
}
if (buttonHovered)
ImGuiUtils.Tooltip("Copy to Clipboard");
+52 -5
View File
@@ -93,6 +93,22 @@ public sealed class Settings : Window, IDisposable
ImGuiUtils.TooltipWrapped(tooltip);
}
private static void DrawOption(string label, string tooltip, string value, Action<string> setter, ref bool isDirty)
{
ImGui.SetNextItemWidth(OptionWidth);
var text = value.ToString();
if (ImGui.InputText(label, ref text, 255, ImGuiInputTextFlags.AutoSelectAll))
{
if (value != text)
{
setter(text);
isDirty = true;
}
}
if (ImGui.IsItemHovered())
ImGuiUtils.TooltipWrapped(tooltip);
}
private static void DrawOption<T>(string label, string tooltip, Func<T, string> getName, Func<T, string> getTooltip, T value, Action<T> setter, ref bool isDirty) where T : struct, Enum
{
ImGui.SetNextItemWidth(OptionWidth);
@@ -147,6 +163,7 @@ public sealed class Settings : Window, IDisposable
MacroCopyConfiguration.CopyType.OpenWindow => "Open a Window",
MacroCopyConfiguration.CopyType.CopyToMacro => "Copy to Macros",
MacroCopyConfiguration.CopyType.CopyToClipboard => "Copy to Clipboard",
MacroCopyConfiguration.CopyType.CopyToMacroMate => "Copy to Macro Mate",
_ => "Unknown",
};
@@ -157,6 +174,7 @@ public sealed class Settings : Window, IDisposable
"Copy, view, and choose at your own leisure.",
MacroCopyConfiguration.CopyType.CopyToMacro => "Copy directly to the game's macro system.",
MacroCopyConfiguration.CopyType.CopyToClipboard => "Copy to your clipboard. Macros are separated by a blank line.",
MacroCopyConfiguration.CopyType.CopyToMacroMate => "Copy directly to a Macro Mate macro. Requires the Macro Mate plugin.",
_ => "Unknown"
};
@@ -270,7 +288,35 @@ public sealed class Settings : Window, IDisposable
ref isDirty
);
}
else if (Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacroMate)
{
DrawOption(
"Macro Name",
"The name of the macro to be created or updated in Macro Mate.",
Config.MacroCopy.MacroMateName,
v => Config.MacroCopy.MacroMateName = v,
ref isDirty
);
DrawOption(
"Macro Parent",
"The name of the parent group of the new macro. Leave blank or \"/\" if there is none.",
Config.MacroCopy.MacroMateParent,
v => Config.MacroCopy.MacroMateParent = v,
ref isDirty
);
}
DrawOption(
"Show Copied Message",
"Shows a notification in the bottom right when a macro is copied successfully.",
Config.MacroCopy.ShowCopiedMessage,
v => Config.MacroCopy.ShowCopiedMessage = v,
ref isDirty
);
if (Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate)
{
DrawOption(
"Use Macro Chain",
"Replaces the last step with /nextmacro to run the next macro " +
@@ -291,6 +337,7 @@ public sealed class Settings : Window, IDisposable
if (ImGui.IsItemHovered())
ImGuiUtils.Tooltip("Macro Chain is not installed");
}
}
DrawOption(
"Add Macro Lock",
@@ -311,8 +358,7 @@ public sealed class Settings : Window, IDisposable
if (Config.MacroCopy.AddNotification)
{
var isForceUseful = Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !Config.MacroCopy.CombineMacro;
using (var d = ImRaii.Disabled(!isForceUseful))
if ((Config.MacroCopy.Type == MacroCopyConfiguration.CopyType.CopyToMacro || !Config.MacroCopy.CombineMacro) && Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate)
{
DrawOption(
"Force Notification",
@@ -323,8 +369,6 @@ public sealed class Settings : Window, IDisposable
ref isDirty
);
}
if (!isForceUseful && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGuiUtils.Tooltip("Only useful when Combine Macro is off");
DrawOption(
"Add Notification Sound",
@@ -336,7 +380,7 @@ public sealed class Settings : Window, IDisposable
if (Config.MacroCopy.AddNotificationSound)
{
using (var d = ImRaii.Disabled(Config.MacroCopy.UseNextMacro))
if (!Config.MacroCopy.UseNextMacro && Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate)
{
DrawOption(
"Intermediate Notification Sound",
@@ -379,6 +423,8 @@ public sealed class Settings : Window, IDisposable
ref isDirty
);
if (Config.MacroCopy.Type != MacroCopyConfiguration.CopyType.CopyToMacroMate)
{
DrawOption(
"Combine Macro",
"Doesn't split the macro into smaller macros.",
@@ -388,6 +434,7 @@ public sealed class Settings : Window, IDisposable
);
}
}
}
if (isDirty)
Config.Save();