Skip to content

PaperMC Commands with Java and Brigadier

Reedwork lets you build PaperMC commands in Java using annotations instead of manually constructing Brigadier command nodes and registering every command component yourself.

Define your command structure with @Command and @SubCommand, implement execution with @CommandHandler, and let Reedwork map Java method parameters to the corresponding Paper command arguments.

The central idea is simple:

Your Java parameter types determine how command arguments are resolved.

This allows command handlers to work with typed Java values instead of raw command input.

The following example defines a root command with a default handler, a player argument, and aliases:

HelloReedworkCommand.java
package dev.reedworkmc.examples.command.commands;
import dev.reedworkmc.reedwork.annotation.Command;
import dev.reedworkmc.reedwork.annotation.CommandHandler;
import dev.reedworkmc.reedwork.annotation.SubCommand;
import dev.reedworkmc.reedwork.command.CommandContext;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.entity.Player;
@Command(
value = "helloreedwork",
description = "Send a welcome message to a player",
permission = "reedwork.example.hello",
cooldown = 10,
aliases = {"hellorw", "hrw"}
)
public final class HelloReedworkCommand {
private static final Component HELLO_MESSAGE = Component.text("Hello from Reedwork!", NamedTextColor.YELLOW);
@CommandHandler
public boolean greetSelf(CommandContext context) {
context.player().sendMessage(HELLO_MESSAGE);
return true;
}
@SubCommand("<target>")
public boolean greetTarget(CommandContext context, Player target) {
target.sendMessage(HELLO_MESSAGE);
context.player().sendMessage("Hello message sent to " + target.getName());
return true;
}
}

From this class, Reedwork creates command paths equivalent to:

/helloreedwork
/helloreedwork <target>
/hellorw
/hrw

The Player target parameter tells Reedwork that <target> should use a player argument.

You do not need to manually create the corresponding Brigadier argument node or convert the parsed value into a Player.

Reedwork handles that connection between the command declaration and the Java method.

A Reedwork command consists of several cooperating parts:

Part Purpose
@Command Declares the root command and its metadata.
@SubCommand Defines additional command paths and arguments.
@CommandHandler Defines the Java method executed for a command path.
Java parameter types Determine how command arguments are parsed and resolved.
@Suggest Adds custom command suggestions.

Together, these declarations describe the command that Reedwork registers with Paper.

This keeps the command definition close to the Java code that implements its behavior.

The @Command annotation defines the root command and its metadata.

Attribute Description
value The command name.
description Description associated with the command.
permission Permission required to access the command.
cooldown Command cooldown in seconds.
aliases Alternative names for the command.
usage Optional command usage string.

For example:

@Command(
value = "helloreedwork",
description = "Send a welcome message to a player",
permission = "reedwork.example.hello",
cooldown = 10,
aliases = {"hellorw", "hrw"},
usage = "/helloreedwork [target]"
)

The @Command annotation defines the root of the command tree.

Execution paths are added through command handlers and subcommands.

A method annotated with @CommandHandler defines an execution path.

@CommandHandler
public boolean greetSelf(CommandContext context) {
context.player().sendMessage(HELLO_MESSAGE);
return true;
}

For this handler, the corresponding command path is:

/helloreedwork

A handler can receive a CommandContext, which provides information about the current command execution.

Returning true reports successful command execution.

A command can have a default handler while also exposing additional paths through subcommands.

Additional command paths can be declared with @SubCommand.

@SubCommand("<target>")
public boolean greetTarget(CommandContext context, Player target) {
target.sendMessage(HELLO_MESSAGE);
context.player().sendMessage(
"Hello message sent to " + target.getName()
);
return true;
}

This creates the command path:

/helloreedwork <target>

The value of @SubCommand describes the command structure relative to the root command.

The text inside angle brackets defines the argument name shown by the command system:

<target>

The argument name does not determine the Java type.

The Java method parameter does:

@SubCommand("<target>")
public boolean greetTarget(
CommandContext context,
Player target
) {
// ...
}

Here, <target> is the command argument name and Player determines how Reedwork resolves its value.

This separation is important:

  • @SubCommand defines the command structure and argument names.
  • The Java parameter type determines the argument resolver.
  • Reedwork connects the command argument with the resolved Java value.

A subcommand can contain multiple arguments and literal command components.

@SubCommand("give <player> <item> with <name> <amount>")
public boolean give(
CommandContext context,
Player player,
ItemStack item,
String name,
Integer amount
) {
// ...
}

This describes:

/give <player> <item> with <name> <amount>

The literal with becomes part of the command path.

The Java parameters are mapped independently:

Java parameter Generated argument
Player player Player argument
ItemStack item Item Stack argument
String name String argument
Integer amount Integer argument

This lets you describe structured Paper commands directly through Java method signatures.

Automatic parameter mapping is one of Reedwork’s core features.

Java method parameters are mapped to the corresponding Paper command argument types.

For example:

@SubCommand("give <player> <item> with <name> <amount>")
public boolean give(
CommandContext context,
Player player,
ItemStack item,
String name,
Integer amount
) {
// ...
}

Reedwork uses the parameter types to determine how each argument should be parsed and resolved.

When the command executes, the handler receives the resulting Java values.

You do not need to manually:

  • construct Brigadier argument nodes
  • extract raw command arguments
  • convert parsed values
  • resolve supported player or entity arguments
  • perform basic argument type validation

Reedwork handles the infrastructure between the command input and your Java method.

For more information about Paper’s available command argument types, see the Paper command argument documentation.

Reedwork provides parameter resolvers for many of Paper’s Minecraft-specific Brigadier argument types.

The available resolvers currently include the following categories.

Java type Paper argument Description
Boolean Boolean Boolean values.
Double Double Decimal numbers.
Float Float Floating-point numbers.
Integer Integer Integer values.
Long Long Long integer values.
String String String command arguments.
UUID UUID UUID values.
Java type Paper argument Description
BlockState Block State Resolves a block state.
BlockPosition Block Position Resolves a block position.
Component Component Resolves Adventure components.
Criteria Objective Criteria Resolves scoreboard objective criteria.
DisplaySlot Scoreboard Display Slot Resolves a scoreboard display slot.
Entity Entity Resolves an entity.
FinePosition Fine Position Resolves a precise world position.
GameMode Game Mode Resolves a Minecraft game mode.
HexColor Hex Color Resolves hexadecimal colors.
HeightMap Height Map Resolves a height map.
ItemStack Item Stack Resolves an item stack argument.
ItemStackPredicate Item Predicate Resolves an item predicate.
Key Key Resolves Adventure keys.
LookAnchor Entity Anchor Resolves an entity look anchor.
NamedTextColor Named Color Resolves Adventure named colors.
NamespacedKey Namespaced Key Resolves Bukkit namespaced keys.
Player Player Resolves a player.
Style Style Resolves Adventure text styles.
World World Resolves a world.
Java type Paper argument Description
DoubleRange Double Range Resolves a double range.
IntegerRange Integer Range Resolves an integer range.
Java type Paper argument Description
List<Entity> Entities Resolves multiple entities.
List<Player> Players Resolves multiple players.
List<PlayerProfile> Player Profiles Resolves multiple player profiles.

Some Paper argument types do not currently have a corresponding Reedwork resolver.

These include:

  • resource(RegistryKey)
  • resourceKey(RegistryKey)
  • signedMessage()
  • time(int)
  • templateMirror()
  • templateRotation()

Reedwork’s resolver system is designed to map Paper command arguments to Java types where a corresponding representation is available.

Keeping unsupported types documented makes the resolver coverage explicit and helps developers determine whether a command can be represented directly through method parameters.

String parameters receive special treatment based on their position in the command.

A String parameter followed by another command argument consumes a single argument.

For example:

/foo <name> <amount>

with:

String name,
Integer amount

creates a normal String argument for name.

When a String parameter is the final argument of a command, Reedwork treats it as a greedy string.

For example:

/announce <message>

with:

String message

allows message to consume the remaining command input.

This supports commands such as:

/announce Hello everyone, welcome to the server!

without requiring you to manually select a greedy-string Brigadier argument.

This positional behavior allows structured command arguments and free-form text to be combined in the same command.

Argument parsing and command suggestions are separate concerns.

The Java parameter type determines how the argument is parsed and resolved.

The @Suggest annotation determines which values are suggested to the player.

This allows Reedwork to combine automatic argument mapping with application-specific completion logic.

For example, a suggestion provider can read available values from an injected application service:

@Transient
public final class PetSuggestionProvider
implements SuggestionProvider<CommandSourceStack> {
private final PetIndex petIndex;
public PetSuggestionProvider(PetIndex petIndex) {
this.petIndex = petIndex;
}
@Override
public CompletableFuture<Suggestions> getSuggestions(
CommandContext<CommandSourceStack> context,
SuggestionsBuilder builder
) {
petIndex.getPetTypes()
.forEach(builder::suggest);
return builder.buildFuture();
}
}

The provider can then be attached to a command parameter:

@SubCommand("<petType> <owner>")
public boolean onCommand(
CommandContext context,
@Suggest(PetSuggestionProvider.class) String petType,
Player owner
) {
handleCommand(context.sender(), petType, owner);
return true;
}

The resulting command can be used as:

/pet <petType> <owner>

petType remains a normal String argument while PetSuggestionProvider supplies custom suggestions.

The owner parameter is independently mapped to a Paper player argument because its Java type is Player.

This gives Reedwork three distinct layers:

Layer Responsibility
@SubCommand Defines the command structure.
Java parameter type Determines argument parsing and resolution.
@Suggest Provides command completion suggestions.

Suggestion providers can also use Reedwork’s dependency injection system, allowing application services to be injected without relying on global state.

Commands can define a Bukkit/Paper permission directly through @Command.

@Command(
value = "helloreedwork",
permission = "reedwork.example.hello"
)

The permission becomes part of the command’s access rules.

A command can define a cooldown directly in its declaration.

@Command(
value = "helloreedwork",
cooldown = 10
)

The cooldown value is specified in seconds.

Reedwork handles the cooldown rather than requiring each command implementation to maintain its own cooldown state.

Alternative command names can be declared with aliases.

@Command(
value = "helloreedwork",
aliases = {"hellorw", "hrw"}
)

The command can then be invoked using:

/helloreedwork
/hellorw
/hrw

Aliases keep alternative command names in the command declaration instead of requiring separate registration logic.

The usage attribute can be used to specify a custom usage string.

@Command(
value = "helloreedwork",
usage = "/helloreedwork [target]"
)

Specifying usage is optional.

If usage is omitted, Reedwork automatically generates usage information from the available command paths and their arguments.

This means you normally do not need to maintain a separate usage string whenever the command structure changes.

Reedwork removes the need to manually register each command with Bukkit or Paper.

The plugin initializes Reedwork and scans the package containing its components.

CommandExample.java
package dev.reedworkmc.examples.command;
import dev.reedworkmc.reedwork.Reedwork;
import org.bukkit.plugin.java.JavaPlugin;
public final class CommandExample extends JavaPlugin {
@Override
public void onEnable() {
// Plugin startup logic
Reedwork.create(this).scan("dev.reedworkmc.examples.command");
}
}

The scan call discovers the command and registers it automatically.

Adding another Reedwork command therefore does not require another explicit command registration step in the plugin’s main class.

Traditional command implementations can require you to work directly with command nodes, argument types, parsing, suggestions, permissions, and registration.

Reedwork moves much of that infrastructure into annotations and Java method signatures.

A command class can describe:

  • the command structure
  • command metadata
  • permissions
  • aliases
  • cooldowns
  • usage
  • argument types
  • argument resolution
  • custom suggestions
  • execution handlers

Reedwork then connects that declaration to Paper’s command infrastructure.

You write the command behavior your plugin needs.

Reedwork handles the repetitive command infrastructure around it.

Command handlers often depend on application services.

Instead of creating those services manually, Reedwork can resolve constructor dependencies automatically.

For example, a command can depend directly on a service:

public final class HelloCommand {
private final GreeterService greeterService;
public HelloCommand(GreeterService greeterService) {
this.greeterService = greeterService;
}
}

This keeps command dependencies explicit and makes larger Paper plugins easier to structure.

Learn more about dependency injection in Reedwork.

Commands are only one part of the Reedwork framework.

Continue with the related documentation:

  • Dependency injection — manage services and constructor dependencies.
  • Events — automatically discover and register Bukkit and Paper event listeners.
  • Enchantments — create and register custom Minecraft enchantments.
  • Utilities — use reusable utilities for Paper plugin development.

If you are new to Reedwork, start with the introduction and then follow the installation guide.

The complete runnable command example is available in the ReedworkExamples repository.