Command Basics
Commands in Minestom are created by extending theCommand class and defining syntaxes with arguments.
Creating Your First Command
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.CommandSender;
import net.kyori.adventure.text.Component;
public class HelloCommand extends Command {
public HelloCommand() {
super("hello");
setDefaultExecutor(this::defaultExecutor);
}
private void defaultExecutor(CommandSender sender, CommandContext context) {
sender.sendMessage(Component.text("Hello, world!"));
}
}
Registering Commands
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager;
CommandManager commandManager = MinecraftServer.getCommandManager();
commandManager.register(new HelloCommand());
Command Arguments
Arguments define what inputs your command accepts.Basic Argument Types
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.arguments.Argument;
// String argument
Argument<String> name = ArgumentType.String("name");
// Integer argument
Argument<Integer> amount = ArgumentType.Integer("amount");
// Double argument
Argument<Double> multiplier = ArgumentType.Double("multiplier");
// Boolean argument
Argument<Boolean> enabled = ArgumentType.Boolean("enabled");
// Word (single word, no spaces)
Argument<String> mode = ArgumentType.Word("mode");
Minecraft-Specific Arguments
// Block state
Argument<Block> block = ArgumentType.BlockState("block");
// Entity type
ArgumentEntityType entityType = ArgumentType.EntityType("entity");
// Entity selector (@p, @a, etc.)
Argument<EntitySelector> selector = ArgumentType.Entity("target");
// Position (absolute or relative)
Argument<RelativeVec> position = ArgumentType.RelativeVec3("pos");
// Item stack
Argument<ItemStack> item = ArgumentType.ItemStack("item");
Command Syntaxes
Syntaxes define the structure of your command and what happens when executed.Adding Syntaxes
public class HealthCommand extends Command {
public HealthCommand() {
super("health");
setDefaultExecutor(this::defaultExecutor);
// Create arguments
var modeArg = ArgumentType.Word("mode").from("set", "add");
var valueArg = ArgumentType.Integer("value").between(0, 100);
// Define syntaxes
addSyntax(this::executeWithMode, modeArg);
addSyntax(this::executeComplete, modeArg, valueArg);
}
private void defaultExecutor(CommandSender sender, CommandContext context) {
sender.sendMessage(Component.text("Usage: /health <set|add> <value>"));
}
private void executeWithMode(CommandSender sender, CommandContext context) {
String mode = context.get("mode");
sender.sendMessage(Component.text("/health " + mode + " [value]"));
}
private void executeComplete(CommandSender sender, CommandContext context) {
Player player = (Player) sender;
String mode = context.get("mode");
int value = context.get("value");
switch (mode.toLowerCase()) {
case "set" -> player.setHealth(value);
case "add" -> player.setHealth(player.getHealth() + value);
}
player.sendMessage(Component.text("Health: " + player.getHealth()));
}
}
Argument Constraints
Restricting Values
// Integer between min and max
var value = ArgumentType.Integer("value").between(0, 100);
// Word from specific options
var mode = ArgumentType.Word("mode").from("set", "add", "remove");
// Enum argument
var gameMode = ArgumentType.Enum("mode", GameMode.class)
.setFormat(ArgumentEnum.Format.LOWER_CASED);
Default Values
// Set default value
var position = ArgumentType.RelativeVec3("pos")
.setDefaultValue(() -> new RelativeVec(
new Vec(0, 0, 0),
RelativeVec.CoordinateType.RELATIVE,
true, true, true
));
Argument Callbacks
Handle invalid arguments with custom error messages:import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.arguments.number.ArgumentNumber;
public class HealthCommand extends Command {
public HealthCommand() {
super("health");
var modeArg = ArgumentType.Word("mode").from("set", "add");
var valueArg = ArgumentType.Integer("value").between(0, 100);
// Set error callbacks
setArgumentCallback(this::onModeError, modeArg);
setArgumentCallback(this::onValueError, valueArg);
addSyntax(this::execute, modeArg, valueArg);
}
private void onModeError(CommandSender sender, ArgumentSyntaxException exception) {
sender.sendMessage(Component.text(
"Invalid mode: '" + exception.getInput() + "'. Use 'set' or 'add'"
));
}
private void onValueError(CommandSender sender, ArgumentSyntaxException exception) {
int error = exception.getErrorCode();
String input = exception.getInput();
switch (error) {
case ArgumentNumber.NOT_NUMBER_ERROR ->
sender.sendMessage(Component.text("'" + input + "' is not a number"));
case ArgumentNumber.TOO_LOW_ERROR ->
sender.sendMessage(Component.text(input + " is too low (min: 0)"));
case ArgumentNumber.TOO_HIGH_ERROR ->
sender.sendMessage(Component.text(input + " is too high (max: 100)"));
}
}
}
Command Conditions
Restrict who can execute commands:import net.minestom.server.command.builder.condition.Conditions;
public class AdminCommand extends Command {
public AdminCommand() {
super("admin");
// Only players can execute
setCondition(Conditions::playerOnly);
// Or custom condition
setCondition((sender, commandString) -> {
if (sender instanceof Player player) {
return player.hasPermission("admin.command");
}
return false;
});
}
}
Advanced Examples
Summon Command
From the Minestom demo - summons entities at specified positions:import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType;
import net.minestom.server.utils.location.RelativeVec;
public class SummonCommand extends Command {
public SummonCommand() {
super("summon");
setCondition(Conditions::playerOnly);
var entityType = ArgumentType.EntityType("entity");
var position = ArgumentType.RelativeVec3("pos")
.setDefaultValue(() -> new RelativeVec(
new Vec(0, 0, 0),
RelativeVec.CoordinateType.RELATIVE,
true, true, true
));
addSyntax(this::execute, entityType, position);
}
private void execute(CommandSender sender, CommandContext context) {
Player player = (Player) sender;
EntityType type = context.get("entity");
RelativeVec relPos = context.get("pos");
// Create entity
EntityCreature entity = new EntityCreature(type);
// Calculate absolute position
Pos spawnPos = relPos.fromSender(sender);
// Spawn in player's instance
entity.setInstance(player.getInstance(), spawnPos);
player.sendMessage(Component.text("Summoned " + type.name()));
}
}
Teleport Command
public class TeleportCommand extends Command {
public TeleportCommand() {
super("tp", "teleport");
setCondition(Conditions::playerOnly);
var position = ArgumentType.RelativeVec3("position");
var target = ArgumentType.Entity("target")
.onlyPlayers(true)
.singleEntity(true);
// /tp <position>
addSyntax(this::teleportToPos, position);
// /tp <player>
addSyntax(this::teleportToPlayer, target);
}
private void teleportToPos(CommandSender sender, CommandContext context) {
Player player = (Player) sender;
RelativeVec relPos = context.get("position");
Pos pos = relPos.fromSender(sender);
player.teleport(pos);
player.sendMessage(Component.text("Teleported to " + pos));
}
private void teleportToPlayer(CommandSender sender, CommandContext context) {
Player player = (Player) sender;
EntityFinder finder = context.get("target");
Player target = finder.findFirstPlayer(sender);
if (target != null) {
player.teleport(target.getPosition());
player.sendMessage(Component.text("Teleported to " + target.getUsername()));
}
}
}
Subcommands
Organize related commands:public class GameCommand extends Command {
public GameCommand() {
super("game");
// Add subcommands
addSubcommand(new StartCommand());
addSubcommand(new StopCommand());
addSubcommand(new StatusCommand());
}
private static class StartCommand extends Command {
public StartCommand() {
super("start");
setDefaultExecutor((sender, context) -> {
sender.sendMessage(Component.text("Game started!"));
});
}
}
private static class StopCommand extends Command {
public StopCommand() {
super("stop");
setDefaultExecutor((sender, context) -> {
sender.sendMessage(Component.text("Game stopped!"));
});
}
}
}
Unknown Command Handler
Handle unknown commands globally:CommandManager commandManager = MinecraftServer.getCommandManager();
commandManager.setUnknownCommandCallback((sender, command) -> {
sender.sendMessage(
Component.text("Unknown command: " + command, NamedTextColor.RED)
);
});
Console Commands
Commands work from both players and console:public class StopCommand extends Command {
public StopCommand() {
super("stop");
// No condition = both players and console can use
setDefaultExecutor((sender, context) -> {
if (sender instanceof Player player) {
player.kick(Component.text("Server stopping"));
}
MinecraftServer.stopCleanly();
});
}
}
Testing Commands
Test your commands with different argument types:public class TestCommand extends Command {
public TestCommand() {
super("test");
var blockArg = ArgumentType.BlockState("block");
blockArg.setCallback((sender, exception) -> {
exception.printStackTrace();
});
setDefaultExecutor((sender, context) -> {
sender.sendMessage(Component.text("Test command executed"));
});
addSyntax((sender, context) -> {
Block block = context.get("block");
sender.sendMessage(Component.text("Block: " + block.key()));
}, blockArg);
}
}
Next Steps
Entities
Create commands to spawn and manage entities
Blocks
Build commands to manipulate blocks
