mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
feat: Use Debug Command Handler for Dalamud Commands (#2018)
* feat: new command handler that works off a hook * cr comment * Use ClientStructs for sig
This commit is contained in:
parent
9de58b0cb9
commit
0fb7585973
2 changed files with 30 additions and 57 deletions
|
|
@ -2,39 +2,33 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Dalamud.Console;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Internal.Types;
|
||||
using Dalamud.Plugin.Services;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.Shell;
|
||||
|
||||
namespace Dalamud.Game.Command;
|
||||
|
||||
/// <summary>
|
||||
/// This class manages registered in-game slash commands.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal sealed class CommandManager : IInternalDisposableService, ICommandManager
|
||||
internal sealed unsafe class CommandManager : IInternalDisposableService, ICommandManager
|
||||
{
|
||||
private static readonly ModuleLog Log = new("Command");
|
||||
|
||||
private readonly ConcurrentDictionary<string, IReadOnlyCommandInfo> commandMap = new();
|
||||
private readonly ConcurrentDictionary<(string, IReadOnlyCommandInfo), string> commandAssemblyNameMap = new();
|
||||
private readonly Regex commandRegexEn = new(@"^The command (?<command>.+) does not exist\.$", RegexOptions.Compiled);
|
||||
private readonly Regex commandRegexJp = new(@"^そのコマンドはありません。: (?<command>.+)$", RegexOptions.Compiled);
|
||||
private readonly Regex commandRegexDe = new(@"^„(?<command>.+)“ existiert nicht als Textkommando\.$", RegexOptions.Compiled);
|
||||
private readonly Regex commandRegexFr = new(@"^La commande texte “(?<command>.+)” n'existe pas\.$", RegexOptions.Compiled);
|
||||
private readonly Regex commandRegexCn = new(@"^^(“|「)(?<command>.+)(”|」)(出现问题:该命令不存在|出現問題:該命令不存在)。$", RegexOptions.Compiled);
|
||||
private readonly Regex currentLangCommandRegex;
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly ChatGui chatGui = Service<ChatGui>.Get();
|
||||
private readonly Hook<ShellCommands.Delegates.TryInvokeDebugCommand>? tryInvokeDebugCommandHook;
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly ConsoleManager console = Service<ConsoleManager>.Get();
|
||||
|
|
@ -42,16 +36,11 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag
|
|||
[ServiceManager.ServiceConstructor]
|
||||
private CommandManager(Dalamud dalamud)
|
||||
{
|
||||
this.currentLangCommandRegex = (ClientLanguage)dalamud.StartInfo.Language switch
|
||||
{
|
||||
ClientLanguage.Japanese => this.commandRegexJp,
|
||||
ClientLanguage.English => this.commandRegexEn,
|
||||
ClientLanguage.German => this.commandRegexDe,
|
||||
ClientLanguage.French => this.commandRegexFr,
|
||||
_ => this.commandRegexEn,
|
||||
};
|
||||
this.tryInvokeDebugCommandHook = Hook<ShellCommands.Delegates.TryInvokeDebugCommand>.FromAddress(
|
||||
(nint)ShellCommands.MemberFunctionPointers.TryInvokeDebugCommand,
|
||||
this.OnTryInvokeDebugCommand);
|
||||
this.tryInvokeDebugCommandHook.Enable();
|
||||
|
||||
this.chatGui.CheckMessageHandled += this.OnCheckMessageHandled;
|
||||
this.console.Invoke += this.ConsoleOnInvoke;
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +173,8 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag
|
|||
/// </summary>
|
||||
/// <param name="assemblyName">The name of the assembly.</param>
|
||||
/// <returns>A list of commands and their associated activation string.</returns>
|
||||
public List<KeyValuePair<(string Command, IReadOnlyCommandInfo CommandInfo), string>> GetHandlersByAssemblyName(string assemblyName)
|
||||
public List<KeyValuePair<(string Command, IReadOnlyCommandInfo CommandInfo), string>> GetHandlersByAssemblyName(
|
||||
string assemblyName)
|
||||
{
|
||||
return this.commandAssemblyNameMap.Where(c => c.Value == assemblyName).ToList();
|
||||
}
|
||||
|
|
@ -193,7 +183,7 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag
|
|||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.console.Invoke -= this.ConsoleOnInvoke;
|
||||
this.chatGui.CheckMessageHandled -= this.OnCheckMessageHandled;
|
||||
this.tryInvokeDebugCommandHook?.Dispose();
|
||||
}
|
||||
|
||||
private bool ConsoleOnInvoke(string arg)
|
||||
|
|
@ -201,29 +191,12 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag
|
|||
return arg.StartsWith('/') && this.ProcessCommand(arg);
|
||||
}
|
||||
|
||||
private void OnCheckMessageHandled(XivChatType type, int timestamp, ref SeString sender, ref SeString message, ref bool isHandled)
|
||||
private int OnTryInvokeDebugCommand(ShellCommands* self, Utf8String* command, UIModule* uiModule)
|
||||
{
|
||||
if (type == XivChatType.ErrorMessage && timestamp == 0)
|
||||
{
|
||||
var cmdMatch = this.currentLangCommandRegex.Match(message.TextValue).Groups["command"];
|
||||
if (cmdMatch.Success)
|
||||
{
|
||||
// Yes, it's a chat command.
|
||||
var command = cmdMatch.Value;
|
||||
if (this.ProcessCommand(command)) isHandled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always match for china, since they patch in language files without changing the ClientLanguage.
|
||||
cmdMatch = this.commandRegexCn.Match(message.TextValue).Groups["command"];
|
||||
if (cmdMatch.Success)
|
||||
{
|
||||
// Yes, it's a Chinese fallback chat command.
|
||||
var command = cmdMatch.Value;
|
||||
if (this.ProcessCommand(command)) isHandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
var result = this.tryInvokeDebugCommandHook!.OriginalDisposeSafe(self, command, uiModule);
|
||||
if (result != -1) return result;
|
||||
|
||||
return this.ProcessCommand(command->ToString()) ? 0 : result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ public static class Util
|
|||
var asm = typeof(Util).Assembly;
|
||||
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
|
||||
|
||||
return gitHashInternal = attrs.First(a => a.Key == "GitHash").Value;
|
||||
return gitHashInternal = attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value ?? "N/A";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue