package me.lethunderhawk.fluxapi.util.command; import io.papermc.paper.command.brigadier.BasicCommand; import io.papermc.paper.command.brigadier.CommandSourceStack; import me.lethunderhawk.fluxapi.util.interfaces.FluxAPIModule; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NonNull; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.function.BiConsumer; import java.util.stream.Collectors; public abstract class CustomCommand implements BasicCommand { protected CommandNode rootCommand; protected final FluxAPIModule module; public CustomCommand(FluxAPIModule module) { this.module = module; this.rootCommand = getRootCommand(); createHelpCommand(); createCommands(); } public void execute(CommandSourceStack commandSourceStack, String[] args) { CommandSender sender = commandSourceStack.getSender(); if (args.length == 0) { if(rootCommand.getExecutor() != null) { rootCommand.getExecutor().accept(sender, args); }else{ sendHelp(sender); } return; } List argList = new ArrayList<>(Arrays.asList(args)); CommandNode currentNode = rootCommand; CommandNode targetNode = null; // Traverse the command tree while (!argList.isEmpty()) { String nextArg = argList.get(0); CommandNode nextNode = currentNode.getSubCommand(nextArg); if (nextNode == null) { // No matching subcommand found, use current node if it has an executor targetNode = currentNode.getExecutor() != null ? currentNode : null; break; } currentNode = nextNode; argList.remove(0); // If this is the last argument or node has no further subcommands if (argList.isEmpty() || nextNode.getSubCommands().isEmpty()) { targetNode = nextNode; break; } } if (targetNode == null) { module.sendText(sender, Component.text("Unknown command. Use /" + rootCommand.getName() +" help for available commands.", NamedTextColor.RED)); return; } if (targetNode.getExecutor() == null) { module.sendText(sender,Component.text("This command requires additional arguments.", NamedTextColor.RED)); sendSubCommands(sender, targetNode); return; } // Execute the command with remaining arguments String[] remainingArgs = argList.toArray(new String[0]); targetNode.getExecutor().accept(sender, remainingArgs); } @ApiStatus.OverrideOnly public @NonNull Collection suggest(final CommandSourceStack commandSourceStack, final String[] args) { List suggestions = new ArrayList<>(); if (args.length == 0) { CommandNode currentNode = rootCommand; if (currentNode.getTabCompleter() != null) { suggestions.addAll(currentNode.getTabCompleter().apply(commandSourceStack.getSender(), args)); } else { // Suggest subcommands suggestions.addAll(currentNode.getSubCommandNames()); } return suggestions; } // Start at root and traverse CommandNode currentNode = rootCommand; List argList = new ArrayList<>(Arrays.asList(args)); String lastArg = args[args.length - 1].toLowerCase(); // Try to traverse as far as possible for (int i = 0; i < argList.size() - 1; i++) { String arg = argList.get(i); CommandNode nextNode = currentNode.getSubCommand(arg); if (nextNode == null) { break; } currentNode = nextNode; } // Get suggestions from current node if (currentNode.getTabCompleter() != null) { suggestions.addAll(currentNode.getTabCompleter().apply(commandSourceStack.getSender(), args)); } else { // Suggest subcommands suggestions.addAll(currentNode.getSubCommandNames().stream() .filter(name -> name.toLowerCase().startsWith(lastArg)) .collect(Collectors.toList())); } return suggestions; } @ApiStatus.OverrideOnly public abstract CommandNode getRootCommand(); private void createHelpCommand() { rootCommand.addSubCommand(new CommandNode("help", "Displays this help menu", this::sendHelp)); } @ApiStatus.OverrideOnly public abstract void createCommands(); protected void sendHelp(CommandSender sender, String[] strings) { sendHelp(sender); } protected void sendHelp(CommandSender sender) { sender.sendMessage("§6=== Available Commands ==="); for (CommandNode cmd : rootCommand.getSubCommands()) { sender.sendMessage("/" + rootCommand.getName() + " " + cmd.getName() + " §7- " + cmd.getDescription()); } } private void sendSubCommands(CommandSender sender, CommandNode node) { sender.sendMessage("§6Available subcommands:"); for (CommandNode subCmd : node.getSubCommands()) { sender.sendMessage("§e" + subCmd.getName() + " §7- " + subCmd.getDescription()); } } public CommandNode registerSubCommand(String name, String description, BiConsumer executor) { return rootCommand.registerSubCommand(name, description, executor); } public void reload(){ createCommands(); } }