Command System
Amber provides utilities to simplify command creation and registration across all mod loaders. While the command system uses Minecraft's native Brigadier library, Amber offers helper methods to reduce boilerplate and ensure consistent behavior across platforms.
Overview
The command utilities include:
- SimpleCommands: Helper class for creating base commands
- Cross-platform registration: Works consistently on Fabric, Forge, and NeoForge
- Reduced boilerplate: Less code for common command patterns
- Platform abstraction: Handles platform differences automatically
Basic Usage
Creating a Base Command
Use SimpleCommands.createBaseCommand() to create a base command that shows mod information:
import com.iamkaf.amber.api.commands.v1.SimpleCommands;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
public class MyModCommands {
public static void registerCommands() {
// Create base command that shows mod info
LiteralArgumentBuilder<CommandSourceStack> baseCommand =
SimpleCommands.createBaseCommand("mymod");
// Add subcommands
baseCommand.then(createReloadCommand());
baseCommand.then(createConfigCommand());
baseCommand.then(createDebugCommand());
// Register command (platform-specific)
registerOnPlatform(baseCommand);
}
private static void registerOnPlatform(LiteralArgumentBuilder<CommandSourceStack> command) {
// This would be registered through your platform's command registration event
CommandRegistrationEvent.EVENT.register(
(dispatcher, registry, environment) -> dispatcher.register(command)
);
}
}Adding Subcommands
Add subcommands to extend functionality:
private static LiteralArgumentBuilder<CommandSourceStack> createReloadCommand() {
return Commands.literal("reload")
.requires(source -> source.hasPermission(2)) // Require OP level 2
.executes(context -> {
// Reload configuration
ConfigManager.reload();
context.getSource().sendSuccess(
() -> Component.literal("Configuration reloaded successfully!"),
true
);
return Command.SINGLE_SUCCESS;
});
}
private static LiteralArgumentBuilder<CommandSourceStack> createConfigCommand() {
return Commands.literal("config")
.then(Commands.literal("get")
.then(Commands.argument("key", StringArgumentType.string())
.executes(context -> {
String key = StringArgumentType.getString(context, "key");
Object value = ConfigManager.get().getConfigValue(key);
if (value != null) {
context.getSource().sendSuccess(
() -> Component.literal(key + " = " + value),
true
);
return Command.SINGLE_SUCCESS;
} else {
context.getSource().sendFailure(
Component.literal("Unknown config key: " + key)
);
return 0;
}
})))
.then(Commands.literal("set")
.requires(source -> source.hasPermission(2))
.then(Commands.argument("key", StringArgumentType.string())
.then(Commands.argument("value", StringArgumentType.greedyString())
.executes(context -> {
String key = StringArgumentType.getString(context, "key");
String value = StringArgumentType.getString(context, "value");
try {
ConfigManager.setConfigValue(key, value);
context.getSource().sendSuccess(
() -> Component.literal("Set " + key + " = " + value),
true
);
return Command.SINGLE_SUCCESS;
} catch (Exception e) {
context.getSource().sendFailure(
Component.literal("Error: " + e.getMessage())
);
return 0;
}
})))));
}
private static LiteralArgumentBuilder<CommandSourceStack> createDebugCommand() {
return Commands.literal("debug")
.requires(source -> source.hasPermission(4)) // Require OP level 4
.then(Commands.literal("info")
.executes(context -> {
CommandSourceStack source = context.getSource();
ServerPlayer player = source.getPlayer();
if (player != null) {
// Send debug information
player.sendSystemMessage(Component.literal("=== Debug Info ==="));
player.sendSystemMessage(Component.literal("Platform: " + Platform.getPlatformName()));
player.sendSystemMessage(Component.literal("Environment: " + Platform.getEnvironment()));
player.sendSystemMessage(Component.literal("Position: " + player.blockPosition()));
player.sendSystemMessage(Component.literal("Dimension: " + player.level().dimension().location()));
}
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("spawn")
.then(Commands.argument("entity", EntityArgumentType.entity())
.executes(context -> {
ServerPlayer player = context.getSource().getPlayer();
Entity entity = EntityArgumentType.getEntity(context, "entity");
if (player != null && entity != null) {
// Spawn entity at player position
Entity newEntity = entity.getType().create(player.level());
if (newEntity != null) {
newEntity.setPos(player.getX(), player.getY(), player.getZ());
player.level().addFreshEntity(newEntity);
context.getSource().sendSuccess(
() -> Component.literal("Spawned " + entity.getType().getDescription()),
true
);
return Command.SINGLE_SUCCESS;
}
}
return 0;
})));
}Advanced Command Examples
Item Management Commands
Create commands for managing items:
public class ItemCommands {
public static LiteralArgumentBuilder<CommandSourceStack> createItemCommand() {
return Commands.literal("item")
.then(createGiveCommand())
.then(createEnchantCommand())
.then(createRepairCommand());
}
private static LiteralArgumentBuilder<CommandSourceStack> createGiveCommand() {
return Commands.literal("give")
.requires(source -> source.hasPermission(2))
.then(Commands.argument("target", EntityArgumentType.player())
.then(Commands.argument("item", ItemStackArgumentType.itemStack())
.executes(context -> {
ServerPlayer target = EntityArgumentType.getPlayer(context, "target");
ItemStack stack = ItemStackArgumentType.getItemStack(context, "item").create(1, false);
target.getInventory().add(stack);
target.sendSystemMessage(
Component.literal("Given " + stack.getDisplayName().getString())
);
context.getSource().sendSuccess(
() -> Component.literal("Gave " + stack.getDisplayName().getString() +
" to " + target.getName().getString()),
true
);
return Command.SINGLE_SUCCESS;
})
.then(Commands.argument("count", IntegerArgumentType.integer(1, 64))
.executes(context -> {
ServerPlayer target = EntityArgumentType.getPlayer(context, "target");
int count = IntegerArgumentType.getInteger(context, "count");
ItemStack stack = ItemStackArgumentType.getItemStack(context, "item").create(count, false);
target.getInventory().add(stack);
context.getSource().sendSuccess(
() -> Component.literal("Gave " + count + "x " +
stack.getDisplayName().getString() + " to " +
target.getName().getString()),
true
);
return Command.SINGLE_SUCCESS;
})))));
}
private static LiteralArgumentBuilder<CommandSourceStack> createEnchantCommand() {
return Commands.literal("enchant")
.requires(source -> source.hasPermission(2))
.then(Commands.argument("target", EntityArgumentType.player())
.then(Commands.argument("enchantment", StringArgumentType.word())
.suggests((context, builder) -> {
// Suggest available enchantments
return SharedSuggestionProvider.suggest(
Stream.of(Enchantments.values())
.map(enchantment -> enchantment.getDescriptionId().replace("enchantment.minecraft.", "")),
builder
);
})
.then(Commands.argument("level", IntegerArgumentType.integer(1, 5))
.executes(context -> {
ServerPlayer target = EntityArgumentType.getPlayer(context, "target");
String enchantName = StringArgumentType.getString(context, "enchantment");
int level = IntegerArgumentType.getInteger(context, "level");
// Find enchantment by name
Enchantment enchantment = findEnchantment(enchantName);
if (enchantment == null) {
context.getSource().sendFailure(
Component.literal("Unknown enchantment: " + enchantName)
);
return 0;
}
// Enchant held item
ItemStack heldItem = target.getMainHandItem();
if (heldItem.isEmpty()) {
context.getSource().sendFailure(
Component.literal("Target must hold an item")
);
return 0;
}
heldItem.enchant(enchantment, level);
context.getSource().sendSuccess(
() -> Component.literal("Enchanted " + heldItem.getDisplayName().getString() +
" with " + enchantment.getFullname(level).getString()),
true
);
return Command.SINGLE_SUCCESS;
})))));
}
private static Enchantment findEnchantment(String name) {
for (Enchantment enchantment : Enchantments.values()) {
String enchantName = enchantment.getDescriptionId().replace("enchantment.minecraft.", "");
if (enchantName.equalsIgnoreCase(name)) {
return enchantment;
}
}
return null;
}
}World Management Commands
Create commands for world manipulation:
public class WorldCommands {
public static LiteralArgumentBuilder<CommandSourceStack> createWorldCommand() {
return Commands.literal("world")
.then(createTimeCommand())
.then(createWeatherCommand())
.then(createTeleportCommand());
}
private static LiteralArgumentBuilder<CommandSourceStack> createTimeCommand() {
return Commands.literal("time")
.requires(source -> source.hasPermission(2))
.then(Commands.literal("set")
.then(Commands.argument("time", IntegerArgumentType.integer(0, 24000))
.executes(context -> {
int time = IntegerArgumentType.getInteger(context, "time");
ServerLevel level = context.getSource().getLevel();
level.setDayTime(time);
context.getSource().sendSuccess(
() -> Component.literal("Set time to " + time),
true
);
return Command.SINGLE_SUCCESS;
})))
.then(Commands.literal("add")
.then(Commands.argument("amount", IntegerArgumentType.integer(-24000, 24000))
.executes(context -> {
int amount = IntegerArgumentType.getInteger(context, "amount");
ServerLevel level = context.getSource().getLevel();
level.setDayTime(level.getDayTime() + amount);
context.getSource().sendSuccess(
() -> Component.literal("Added " + amount + " to time"),
true
);
return Command.SINGLE_SUCCESS;
})))
.then(Commands.literal("query")
.executes(context -> {
ServerLevel level = context.getSource().getLevel();
long time = level.getDayTime();
long day = time / 24000;
long dayTime = time % 24000;
context.getSource().sendSuccess(
() -> Component.literal("Day " + day + ", Time: " + dayTime),
true
);
return Command.SINGLE_SUCCESS;
}));
}
private static LiteralArgumentBuilder<CommandSourceStack> createWeatherCommand() {
return Commands.literal("weather")
.requires(source -> source.hasPermission(2))
.then(Commands.literal("clear")
.executes(context -> {
ServerLevel level = context.getSource().getLevel();
level.setWeatherParameters(6000, 0, false, false);
context.getSource().sendSuccess(
() -> Component.literal("Set weather to clear"),
true
);
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("rain")
.executes(context -> {
ServerLevel level = context.getSource().getLevel();
level.setWeatherParameters(0, 6000, true, false);
context.getSource().sendSuccess(
() -> Component.literal("Set weather to rain"),
true
);
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("thunder")
.executes(context -> {
ServerLevel level = context.getSource().getLevel();
level.setWeatherParameters(0, 6000, true, true);
context.getSource().sendSuccess(
() -> Component.literal("Set weather to thunder"),
true
);
return Command.SINGLE_SUCCESS;
}));
}
private static LiteralArgumentBuilder<CommandSourceStack> createTeleportCommand() {
return Commands.literal("tp")
.requires(source -> source.hasPermission(2))
.then(Commands.argument("target", EntityArgumentType.player())
.then(Commands.argument("destination", EntityArgumentType.player())
.executes(context -> {
ServerPlayer target = EntityArgumentType.getPlayer(context, "target");
ServerPlayer destination = EntityArgumentType.getPlayer(context, "destination");
target.teleportTo(
destination.level(),
destination.getX(),
destination.getY(),
destination.getZ(),
destination.getYRot(),
destination.getXRot()
);
context.getSource().sendSuccess(
() -> Component.literal("Teleported " + target.getName().getString() +
" to " + destination.getName().getString()),
true
);
return Command.SINGLE_SUCCESS;
})))
.then(Commands.argument("target", EntityArgumentType.player())
.then(Commands.argument("position", Vec3ArgumentType.vec3())
.executes(context -> {
ServerPlayer target = EntityArgumentType.getPlayer(context, "target");
Vec3 pos = Vec3ArgumentType.getVec3(context, "position");
target.teleportTo(
target.level(),
pos.x, pos.y, pos.z,
target.getYRot(),
target.getXRot()
);
context.getSource().sendSuccess(
() -> Component.literal("Teleported " + target.getName().getString() +
" to " + String.format("%.2f, %.2f, %.2f", pos.x, pos.y, pos.z)),
true
);
return Command.SINGLE_SUCCESS;
})));
}
}Command Registration
Platform-Specific Registration
Register commands through your platform's command registration event:
public class CommandRegistrar {
public static void registerCommands() {
// Create command tree
LiteralArgumentBuilder<CommandSourceStack> root =
SimpleCommands.createBaseCommand("mymod");
// Add subcommands
root.then(ItemCommands.createItemCommand());
root.then(WorldCommands.createWorldCommand());
root.then(ConfigCommands.createConfigCommand());
// Register on platform
registerOnPlatform(root);
}
// Fabric registration
private static void registerOnPlatform(LiteralArgumentBuilder<CommandSourceStack> command) {
// This would be called from your mod initializer
// The actual registration depends on the platform
}
}Client-Side Commands
Create commands that only work on the client:
public class ClientCommands {
public static void registerClientCommands() {
if (!Platform.isClient()) return;
LiteralArgumentBuilder<CommandSourceStack> command =
Commands.literal("mymodclient")
.then(Commands.literal("hud")
.executes(context -> {
// Toggle HUD
HudManager.toggle();
Minecraft.getInstance().player.sendSystemMessage(
Component.literal("HUD " + (HudManager.isEnabled() ? "enabled" : "disabled"))
);
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("particles")
.then(Commands.argument("enabled", BoolArgumentType.bool())
.executes(context -> {
boolean enabled = BoolArgumentType.getBool(context, "enabled");
ParticleManager.setEnabled(enabled);
Minecraft.getInstance().player.sendSystemMessage(
Component.literal("Particles " + (enabled ? "enabled" : "disabled"))
);
return Command.SINGLE_SUCCESS;
})));
// Register client command (platform-specific)
registerClientCommand(command);
}
}Best Practices
1. Permission Levels
Always check permissions for administrative commands:
// Good - Check permissions
Commands.literal("admin")
.requires(source -> source.hasPermission(2)) // Require OP level 2
.executes(context -> {
// Admin command logic
});
// Bad - No permission check
Commands.literal("admin")
.executes(context -> {
// Anyone can run this!
});2. Error Handling
Provide clear error messages:
.executes(context -> {
try {
// Command logic
return Command.SINGLE_SUCCESS;
} catch (Exception e) {
context.getSource().sendFailure(
Component.literal("Error: " + e.getMessage())
);
return 0;
}
});3. Feedback
Always provide feedback to the user:
// Good - Provide feedback
context.getSource().sendSuccess(
() -> Component.literal("Command executed successfully!"),
true
);
// Also notify affected players
target.sendSystemMessage(
Component.literal("Something happened to you!")
);4. Argument Validation
Validate arguments before using them:
.then(Commands.argument("count", IntegerArgumentType.integer(1, 64))
.executes(context -> {
int count = IntegerArgumentType.getInteger(context, "count");
// Count is guaranteed to be between 1 and 64
}));5. Suggestions
Provide helpful suggestions for arguments:
.then(Commands.argument("enchantment", StringArgumentType.word())
.suggests((context, builder) -> {
return SharedSuggestionProvider.suggest(
Arrays.asList("sharpness", "protection", "fire_aspect"),
builder
);
}));The command utilities in Amber provide a foundation for creating cross-platform commands while leveraging Minecraft's powerful Brigadier system.