# 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