CustomCommand.java 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package me.lethunderhawk.fluxapi.util.command;
  2. import io.papermc.paper.command.brigadier.BasicCommand;
  3. import io.papermc.paper.command.brigadier.CommandSourceStack;
  4. import me.lethunderhawk.fluxapi.util.interfaces.FluxAPIModule;
  5. import net.kyori.adventure.text.Component;
  6. import net.kyori.adventure.text.format.NamedTextColor;
  7. import org.bukkit.command.CommandSender;
  8. import org.jetbrains.annotations.ApiStatus;
  9. import org.jspecify.annotations.NonNull;
  10. import java.util.ArrayList;
  11. import java.util.Arrays;
  12. import java.util.Collection;
  13. import java.util.List;
  14. import java.util.function.BiConsumer;
  15. import java.util.stream.Collectors;
  16. public abstract class CustomCommand implements BasicCommand {
  17. protected CommandNode rootCommand;
  18. protected final FluxAPIModule module;
  19. public CustomCommand(FluxAPIModule module) {
  20. this.module = module;
  21. this.rootCommand = getRootCommand();
  22. createHelpCommand();
  23. createCommands();
  24. }
  25. public void execute(CommandSourceStack commandSourceStack, String[] args) {
  26. CommandSender sender = commandSourceStack.getSender();
  27. if (args.length == 0) {
  28. if(rootCommand.getExecutor() != null) {
  29. rootCommand.getExecutor().accept(sender, args);
  30. }else{
  31. sendHelp(sender);
  32. }
  33. return;
  34. }
  35. List<String> argList = new ArrayList<>(Arrays.asList(args));
  36. CommandNode currentNode = rootCommand;
  37. CommandNode targetNode = null;
  38. // Traverse the command tree
  39. while (!argList.isEmpty()) {
  40. String nextArg = argList.get(0);
  41. CommandNode nextNode = currentNode.getSubCommand(nextArg);
  42. if (nextNode == null) {
  43. // No matching subcommand found, use current node if it has an executor
  44. targetNode = currentNode.getExecutor() != null ? currentNode : null;
  45. break;
  46. }
  47. currentNode = nextNode;
  48. argList.remove(0);
  49. // If this is the last argument or node has no further subcommands
  50. if (argList.isEmpty() || nextNode.getSubCommands().isEmpty()) {
  51. targetNode = nextNode;
  52. break;
  53. }
  54. }
  55. if (targetNode == null) {
  56. module.sendText(sender, Component.text("Unknown command. Use /" + rootCommand.getName() +" help for available commands.", NamedTextColor.RED));
  57. return;
  58. }
  59. if (targetNode.getExecutor() == null) {
  60. module.sendText(sender,Component.text("This command requires additional arguments.", NamedTextColor.RED));
  61. sendSubCommands(sender, targetNode);
  62. return;
  63. }
  64. // Execute the command with remaining arguments
  65. String[] remainingArgs = argList.toArray(new String[0]);
  66. targetNode.getExecutor().accept(sender, remainingArgs);
  67. }
  68. @ApiStatus.OverrideOnly
  69. public @NonNull Collection<String> suggest(final CommandSourceStack commandSourceStack, final String[] args) {
  70. List<String> suggestions = new ArrayList<>();
  71. if (args.length == 0) {
  72. CommandNode currentNode = rootCommand;
  73. if (currentNode.getTabCompleter() != null) {
  74. suggestions.addAll(currentNode.getTabCompleter().apply(commandSourceStack.getSender(), args));
  75. } else {
  76. // Suggest subcommands
  77. suggestions.addAll(currentNode.getSubCommandNames());
  78. }
  79. return suggestions;
  80. }
  81. // Start at root and traverse
  82. CommandNode currentNode = rootCommand;
  83. List<String> argList = new ArrayList<>(Arrays.asList(args));
  84. String lastArg = args[args.length - 1].toLowerCase();
  85. // Try to traverse as far as possible
  86. for (int i = 0; i < argList.size() - 1; i++) {
  87. String arg = argList.get(i);
  88. CommandNode nextNode = currentNode.getSubCommand(arg);
  89. if (nextNode == null) {
  90. break;
  91. }
  92. currentNode = nextNode;
  93. }
  94. // Get suggestions from current node
  95. if (currentNode.getTabCompleter() != null) {
  96. suggestions.addAll(currentNode.getTabCompleter().apply(commandSourceStack.getSender(), args));
  97. } else {
  98. // Suggest subcommands
  99. suggestions.addAll(currentNode.getSubCommandNames().stream()
  100. .filter(name -> name.toLowerCase().startsWith(lastArg))
  101. .collect(Collectors.toList()));
  102. }
  103. return suggestions;
  104. }
  105. @ApiStatus.OverrideOnly
  106. public abstract CommandNode getRootCommand();
  107. private void createHelpCommand() {
  108. rootCommand.addSubCommand(new CommandNode("help", "Displays this help menu", this::sendHelp));
  109. }
  110. @ApiStatus.OverrideOnly
  111. public abstract void createCommands();
  112. protected void sendHelp(CommandSender sender, String[] strings) {
  113. sendHelp(sender);
  114. }
  115. protected void sendHelp(CommandSender sender) {
  116. sender.sendMessage("§6=== Available Commands ===");
  117. for (CommandNode cmd : rootCommand.getSubCommands()) {
  118. sender.sendMessage("/" + rootCommand.getName() + " " + cmd.getName() + " §7- " + cmd.getDescription());
  119. }
  120. }
  121. private void sendSubCommands(CommandSender sender, CommandNode node) {
  122. sender.sendMessage("§6Available subcommands:");
  123. for (CommandNode subCmd : node.getSubCommands()) {
  124. sender.sendMessage("§e" + subCmd.getName() + " §7- " + subCmd.getDescription());
  125. }
  126. }
  127. public CommandNode registerSubCommand(String name, String description, BiConsumer<CommandSender, String[]> executor) {
  128. return rootCommand.registerSubCommand(name, description, executor);
  129. }
  130. public void reload(){
  131. createCommands();
  132. }
  133. }