Skip to main content
Parameter annotations control how command arguments are parsed, validated, and mapped to method parameters.

@Sender

Marks a parameter as the command sender.
@Command("heal")
public void healCommand(@Sender Player player) {
    player.setHealth(20.0);
}
Sender parameters don’t consume command arguments. They’re automatically populated with the command executor.

Custom Sender Types

You can register custom sender types using SenderProvider:
blade.binder().bindSender(CustomPlayer.class, (sender, context) -> {
    return customPlayerManager.wrap(sender);
});

@Command("custom")
public void customCommand(@Sender CustomPlayer player) {
    // Receives your custom type
}

@Name

Sets the display name for a parameter.
value
String
required
The parameter name shown in usage messages and errors.
@Command("teleport")
public void teleportCommand(
    @Sender Player player,
    @Name("target") Player target,
    @Name("destination") Location location
) {
    // Usage: /teleport <target> <destination>
}
Without @Name, Blade uses the compiled bytecode parameter name (like arg0, arg1) unless you compile with the -parameters flag.

@Opt

Marks a parameter as optional with configurable default values.
value
Type
default:"EMPTY_OR_CUSTOM"
The type of default value:
  • EMPTY_OR_CUSTOM - Use EMPTY or CUSTOM based on whether custom value is provided
  • EMPTY - Use null (or primitive default like 0, false)
  • SENDER - Use the command sender as default (for player types)
  • CUSTOM - Use the value from custom() field
custom
String
default:""
Custom default value when value = Type.CUSTOM. Parsed according to parameter type.
treatErrorAsEmpty
boolean
default:"false"
If true, parsing errors result in an empty value instead of failing command execution.
@Command("teleport")
public void teleportCommand(
    @Sender Player player,
    @Name("target") Player target,
    @Name("world") @Opt World world
) {
    if (world == null) {
        world = player.getWorld();
    }
    // Usage: /teleport <target> [world]
}

@Flag

Creates a command flag that can be specified with -f or --flagName.
value
char
required
Single character for short flag format (-f).
longName
String
default:""
Long flag name for --flagName format.
description
String
default:""
Description shown in help messages.
required
boolean
default:"false"
Whether the flag must be present.
@Command("teleport")
public void teleportCommand(
    @Sender Player player,
    @Name("location") Location location,
    @Flag(value = 'f', longName = "force", description = "Force teleport even if unsafe") boolean force
) {
    if (force || isSafe(location)) {
        player.teleport(location);
    }
}
// Usage: /teleport <location> [-f/--force]

@Greedy

Consumes all remaining arguments into a single string.
@Command("announce")
public void announceCommand(
    @Sender Player player,
    @Name("message") @Greedy String message
) {
    Bukkit.broadcastMessage(message);
}
// /announce Hello everyone! -> message = "Hello everyone!"
// No quotes needed
Greedy parameters must be the last parameter in the method (excluding flags). Only works with String parameters.

@Range

Validates that numeric parameters fall within a specified range.
min
double
default:"Double.NaN"
Minimum allowed value (inclusive). NaN means no minimum.
max
double
default:"Double.NaN"
Maximum allowed value (inclusive). NaN means no maximum.
@Command("setlevel")
public void setLevelCommand(
    @Sender Player player,
    @Name("level") @Range(min = 1, max = 100) int level
) {
    player.setLevel(level);
}
// Valid: /setlevel 50
// Invalid: /setlevel 0, /setlevel 101
Works with all numeric types: int, long, float, double, and their wrapper classes.

@Provider

Overrides the argument provider for a specific parameter.
value
Class<? extends ArgumentProvider<?>>
required
The custom argument provider class to use.
scope
Scope
default:"BOTH"
What to override:
  • PARSER - Override only argument parsing
  • SUGGESTIONS - Override only tab completion
  • BOTH - Override both parsing and suggestions
public class OnlinePlayerProvider implements ArgumentProvider<Player> {
    @Override
    public Player provide(Context context, Argument argument) throws BladeExitMessage {
        String name = argument.getString();
        Player player = Bukkit.getPlayerExact(name);
        if (player == null) {
            throw new BladeExitMessage("Player '" + name + "' is not online");
        }
        return player;
    }
    
    @Override
    public List<String> suggest(Context context, Argument argument) {
        return Bukkit.getOnlinePlayers().stream()
            .map(Player::getName)
            .collect(Collectors.toList());
    }
}

@Command("message")
public void messageCommand(
    @Sender Player player,
    @Name("target") @Provider(OnlinePlayerProvider.class) Player target,
    @Name("message") @Greedy String message
) {
    target.sendMessage(player.getName() + " -> " + message);
}
@Command("lookup")
public void lookupCommand(
    @Name("player") @Provider(value = OfflinePlayerProvider.class, scope = Scope.PARSER) Player player
) {
    // Custom parsing, default tab completion
}

@Data

Passes custom string data to an argument provider’s methods.
value
String[]
required
Array of strings passed to the argument provider.
public class ItemProvider implements ArgumentProvider<ItemStack> {
    @Override
    public ItemStack provide(Context context, Argument argument) throws BladeExitMessage {
        String[] data = argument.getParameterData();
        String category = data.length > 0 ? data[0] : "all";
        
        // Use category to filter items
        return parseItem(argument.getString(), category);
    }
}

@Command("give")
public void giveCommand(
    @Sender Player player,
    @Name("weapon") @Data("weapons") @Provider(ItemProvider.class) ItemStack item
) {
    player.getInventory().addItem(item);
}
For type-safe metadata, consider creating a custom annotation marked with @Forwarded instead of using string-based @Data.

Build docs developers (and LLMs) love