PluginCommandManager.cs 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using Dalamud.Game.Command;
  6. using Dalamud.Plugin.Services;
  7. using HuntBuddy.Attributes;
  8. namespace HuntBuddy
  9. {
  10. public class PluginCommandManager<THost> : IDisposable
  11. {
  12. private readonly ICommandManager commandManager;
  13. private readonly (string, CommandInfo)[] pluginCommands;
  14. private readonly THost host;
  15. public PluginCommandManager(THost host, ICommandManager commandManager)
  16. {
  17. this.commandManager = commandManager;
  18. this.host = host;
  19. this.pluginCommands = host!.GetType().GetMethods(
  20. BindingFlags.NonPublic | BindingFlags.Public |
  21. BindingFlags.Static | BindingFlags.Instance)
  22. .Where(method => method.GetCustomAttribute<CommandAttribute>() != null)
  23. .SelectMany(this.GetCommandInfoTuple)
  24. .ToArray();
  25. this.AddCommandHandlers();
  26. }
  27. // http://codebetter.com/patricksmacchia/2008/11/19/an-easy-and-efficient-way-to-improve-net-code-performances/
  28. // Benchmarking this myself gave similar results, so I'm doing this to somewhat counteract using reflection to access command attributes.
  29. // I like the convenience of attributes, but in principle it's a bit slower to use them as opposed to just initializing CommandInfos directly.
  30. // It's usually sub-1 millisecond anyways, though. It probably doesn't matter at all.
  31. private void AddCommandHandlers()
  32. {
  33. foreach (var t in this.pluginCommands)
  34. {
  35. var (command, commandInfo) = t;
  36. this.commandManager.AddHandler(command, commandInfo);
  37. }
  38. }
  39. private void RemoveCommandHandlers()
  40. {
  41. foreach (var t in this.pluginCommands)
  42. {
  43. var (command, _) = t;
  44. this.commandManager.RemoveHandler(command);
  45. }
  46. }
  47. private IEnumerable<(string, CommandInfo)> GetCommandInfoTuple(MethodInfo method)
  48. {
  49. var handlerDelegate = (CommandInfo.HandlerDelegate)Delegate.CreateDelegate(
  50. typeof(CommandInfo.HandlerDelegate),
  51. this.host,
  52. method);
  53. var command = handlerDelegate.Method.GetCustomAttribute<CommandAttribute>();
  54. var aliases = handlerDelegate.Method.GetCustomAttribute<AliasesAttribute>();
  55. var helpMessage = handlerDelegate.Method.GetCustomAttribute<HelpMessageAttribute>();
  56. var doNotShowInHelp = handlerDelegate.Method.GetCustomAttribute<DoNotShowInHelpAttribute>();
  57. var commandInfo = new CommandInfo(handlerDelegate)
  58. {
  59. HelpMessage = helpMessage?.HelpMessage ?? string.Empty,
  60. ShowInHelp = doNotShowInHelp == null,
  61. };
  62. // Create list of tuples that will be filled with one tuple per alias, in addition to the base command tuple.
  63. var commandInfoTuples = new List<(string, CommandInfo)> { (command!.Command, commandInfo) };
  64. if (aliases == null)
  65. {
  66. return commandInfoTuples;
  67. }
  68. // ReSharper disable once LoopCanBeConvertedToQuery
  69. foreach (var t in aliases.Aliases)
  70. {
  71. commandInfoTuples.Add((t, commandInfo));
  72. }
  73. return commandInfoTuples;
  74. }
  75. public void Dispose()
  76. {
  77. this.RemoveCommandHandlers();
  78. GC.SuppressFinalize(this);
  79. }
  80. }
  81. }