# Command System Bookshelf provides a command framework with namespace-aware registration, flag argument parsing, and vanilla command merging. It also includes a toggleable command system tied to configuration. --- ## BookshelfCommand The `BookshelfCommand` class provides Brigadier-level command node injection with cleaner namespace handling. ```java public class MyCommand extends BookshelfCommand { public MyCommand() { super("mycommand", "myplugin.command.mycommand"); withArguments(new StringArgument("name")); executesPlayer((player, args) -> { String name = (String) args.get("name"); player.sendMessage("Hello, " + name); }); } } // Register with a namespace new MyCommand().register(plugin); ``` ### Constructors | Constructor | Description | |---|---| | `BookshelfCommand(String name)` | Creates a command with no default permission. | | `BookshelfCommand(String name, String permission)` | Creates a command with a permission node. | | `BookshelfCommand(String name, String permission, String... aliases)` | Creates a command with permission and aliases. | --- ## ToggleableCommand Commands can extend `ToggleableCommand`, which integrates with the plugin's `config.yml` to allow server owners to enable or disable individual commands. ``` Registration Flow: │ ├── Check if command should unregister vanilla version │ └── Unregister vanilla version │ ├── Auto-create config entry if not present │ └── commands.<category>.<name>: true │ ├── Check config.yml │ ├── commands.disabled == true? ──▶ Skip registration │ └── commands.<category>.<name> == false? ──▶ Skip registration │ └── Register command ``` Commands specify a category during construction, which maps to the config path: ```java // This creates config entry: commands.utility.fly public class FlyCommand extends ToggleableCommand { public FlyCommand(BookshelfPlugin plugin) { super(plugin, "fly", "utility"); withPermission("lodestone.bookshelf.commands.utility.fly"); executesPlayer(this::executeCommand); } } ``` --- ## FlagArgument `FlagArgument` extends `GreedyStringArgument` to support Unix-style flag parsing within commands. Flags can be simple toggles or carry values. ```java // Define a command with flags withOptionalArguments( new FlagArgument("flags", Set.of('s', 'f'), Set.of('n')) ); // -s and -f are toggle flags // -n expects a value (e.g., -n 5) ``` ### Parsing Flags ```java executesPlayer((player, args) -> { String input = (String) args.getOptionalUnchecked("flags").orElse(""); // Check for simple flag presence boolean silent = FlagArgument.hasFlags(input, "s"); // Parse flags with values FlagArgument.ParsedFlags parsed = FlagArgument.parseFlags( input, Set.of('n') ); if (parsed.hasFlag('f')) { // Force flag is set } String count = parsed.getFlagValue('n'); // e.g., "5" // Get the input without any flags String cleanInput = FlagArgument.sanitizeInput(input, Set.of('n')); }); ``` ### FlagArgument Static Methods | Method | Description | |---|---| | `hasFlags(String input, String flagSet)` | Returns `true` if all specified flags are present in the input. | | `parseFlags(String input, Set<Character> valueFlags)` | Parses all flags and their values from the input string. Returns a `ParsedFlags` object. | | `sanitizeInput(String input, Set<Character> valueFlags)` | Removes all flags and their values from the input, returning only non-flag arguments. | --- ## CommandFlagArgument `CommandFlagArgument` extends `FlagArgument` and adds tab completion for commands. It suggests command names and their arguments alongside flag suggestions. Useful for commands like `/sudo` where the argument is itself a command. ```java withArguments( new CommandFlagArgument("command", Set.of('s'), Set.of()) ); // Suggests command names and arguments with flag support ``` --- ## Vanilla Command Merging Bookshelf provides two approaches for merging custom branches into existing vanilla commands without replacing them entirely. ### MergeableCommand Extend `MergeableCommand` to add branches to a vanilla command while preserving its original behavior. ```java public class EnhancedGive extends MergeableCommand { public EnhancedGive() { super("give"); withArguments(new LiteralArgument("custom")); executesPlayer((player, args) -> { player.sendMessage("Custom give branch!"); }); } } // Merge with vanilla /give instead of replacing it new EnhancedGive().merge(plugin); ``` ### CommandMerger For more control, use `CommandMerger` to compose multiple branches onto an existing command. ```java CommandMerger.merge("give") .addBranch( Commands.literal("custom") .executes(ctx -> { // Custom branch logic return 1; }) ) .addBranch( Commands.literal("special") .then(Commands.argument("item", StringArgumentType.word())) .executes(ctx -> { // Special branch logic return 1; }) ) .register("myplugin"); ``` | Method | Description | |---|---| | `CommandMerger.merge(String)` | Starts merging with the named command. | | `addBranch(ArgumentBuilder)` | Adds a custom branch to the merged tree. | | `addBranches(ArgumentBuilder...)` | Adds multiple branches. | | `register(String namespace)` | Registers the merged command. | | `hasVanillaCommand()` | Returns `true` if the vanilla command was found. | | `getVanillaNode()` | Returns the vanilla command's Brigadier node. | --- ## Permission Convention All Bookshelf commands follow this permission pattern: ``` lodestone.bookshelf.commands.<category>.<command> ``` Categories include: `moderation`, `utility`, `gamemode`, `attributes`, `vanilla`, `infractions`. Some commands have additional sub-permissions for specific features: ``` lodestone.bookshelf.commands.moderation.vanish.target # Vanish another player lodestone.bookshelf.commands.vanilla.enchant.bypass # Bypass enchant restrictions lodestone.bookshelf.commands.nightvision.other # Toggle NV for others ``` --- ## Creating a New Command Here is the standard pattern for adding a command to Bookshelf: ```java public class MyCommand extends ToggleableCommand { public MyCommand(BookshelfPlugin plugin) { super(plugin, "mycommand", "utility"); withPermission("lodestone.bookshelf.commands.utility.mycommand"); withAliases("mc", "mycmd"); withArguments(new EntitySelectorArgument.OnePlayer("target")); withOptionalArguments(new FlagArgument("flags", Set.of('s'))); executesPlayer(this::executeCommand); } private void executeCommand(Player player, CommandArguments args) { Player target = (Player) args.get("target"); String flags = (String) args.getOptionalUnchecked("flags").orElse(""); boolean silent = FlagArgument.hasFlags(flags, "s"); // Command logic here target.sendMessage("Hello from MyCommand!"); if (!silent) { player.sendMessage("Command executed on " + target.getName()); } } } ``` Register the command in the plugin's initialization: ```java new MyCommand(plugin).register(); ``` The command will auto-register in `config.yml` under `commands.utility.mycommand: true` and respect the toggleable system. --- ## Related Pages - [[Bookshelf/Developers/API Reference]] - Core API and utilities - [[Events]] - Custom event system - [[Bookshelf/Developers/Overview]] - Module architecture