Commando is a completely automatic annotation-based Kotlin command framework for Bukkit.
Commando is accessible via Jitpack. You can use it in your plugin by adding the Jitpack repository:
Maven
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
Gradle
repositories {
maven { url 'https://jitpack.io' }
}
After adding the repository, you can include Commando with the following:
Maven
<dependency>
<groupId>com.github.honkling.commando</groupId>
<artifactId>spigot</artifactId> # Replace with your platform
<version>COMMIT-SHA</version> # Replace with latest commit
</dependency>
Gradle
dependencies {
// Replace `spigot` with your platform and `COMMIT-SHA` with the latest commit
implementation 'com.github.honkling.commando:spigot:COMMIT-SHA'
}
When your plugin enables, you can register commands using the CommandManager.
Supply the package containing all your commands, and you're good to go.
Java
@Override
public void onEnable() {
// Replace with manager for your platform
SpigotCommandManager commandManager = new SpigotCommandManager(this);
commandManager.registerCommands("me.honkling.example.commands");
}
Kotlin
override fun onEnable() {
// Replace with manager for your platform
val commandManager = SpigotCommandManager(this)
commandManager.registerCommands("me.honkling.example.commands")
}
You can create a command just by defining the command name and a default method.
Java
package me.honkling.example.commands;
import me.honkling.commando.common.annotations.Command;
@Command("example")
public class Example {
public static void example(Player executor) {
executor.sendMessage("hello world!");
}
}
Kotlin
@file:Command("example")
package me.honkling.example.commands
import me.honkling.commando.common.annotations.Command
fun example(executor: Player) {
executor.sendMessage("hello world!")
}
Notice how you don't have to tell the command handler that the method is the default.
Commando registers all public methods of this file as a subcommand. The method named identically to the command will be chosen as the default.
Thusly, if you defined another method as so:
Java
public static void test(Player executor) {
executor.sendMessage("Test passing");
}
Kotlin
fun test(executor: Player) {
executor.sendMessage("Test passing")
}
This method will run when /example test
is executed.
Commando will derive the command arguments by the function parameters.
Java
// This command is defined by Commando as /example test (player)
public static void test(Player executor, Player target) {
target.sendMessage("Hello from ${executor.name}!");
executor.sendMessage("Said hello to ${target.name}!");
}
Kotlin
// This command is defined by Commando as /example test (player)
fun test(executor: Player, target: Player) {
target.sendMessage("Hello from ${executor.name}!")
executor.sendMessage("Said hello to ${target.name}!")
}
You can also make parameters optional by marking them as nullable.
Do note, if this does not appear to work in-game, Commando provides its own Optional annotation that will work fine.
Java
public static void test(Player executor, Player target, @Nullable int target) {
target.sendMessage("Hello from ${executor.name}! (x${amount ?: 1})")
executor.sendMessage("Said hello to ${target.name}!")
}
Kotlin
fun test(executor: Player, target: Player, amount: Int?) {
target.sendMessage("Hello from ${executor.name}! (x${amount ?: 1})")
executor.sendMessage("Said hello to ${target.name}!")
}
By default, Commando supports players, offline players, strings, integers, doubles and booleans.
However, you can register your own types with the CommandManager.
Let's say we have a data class, Example
, which accepts an integer and a string.
We can write a type parser for this, as so:
Java
package me.honkling.example.types;
import me.honkling.commando.common.generic.ICommandSender;
import me.honkling.commando.common.types.Type;
import me.honkling.example.lib.Example;
public class ExampleType implements Type<Example> {
// Takes an input string, parses it, and returns an Example
// NOTE: 'input' is the rest of the args concatenated together, in case you want to occupy multiple arguments.
@Override
public Example match(ICommandSender<?> sender, String input) {
String[] chunks = input.split(" ")[0].split(":");
int integer = Integer.parseInt(chunks[0]);
String str = chunks[1];
return Example(integer, str);
}
// Tests if an input could be parsed as an Example
// NOTE: 'input' is the rest of the args concatenated together, in case you want to occupy multiple arguments.
@Override
public boolean matches(ICommandSender<?> sender, String input) {
return input.matches("^(\\d+):\\S+");
}
// Returns a list of tab completions for Example
@Override
public List<String> complete(ICommandSender<?> sender, String input) {
List<String> suggestions = new ArrayList<>();
String first = input.split(" ")[0];
if (!first.contains(":")) {
suggestions.add(String.format("%s:", first));
return suggestions;
}
return suggestions;
}
}
Kotlin
package me.honkling.example.types
import me.honkling.commando.common.generic.ICommandSender
import me.honkling.commando.common.types.Type
import me.honkling.example.lib.Example
object ExampleType : Type<Example> {
// Takes an input string, parses it, and returns an Example
// NOTE: 'input' is the rest of the args concatenated together, in case you want to occupy multiple arguments.
override fun match(sender: ICommandSender<*>, input: String): Example {
val chunks = input.split(" ")[0].split(":")
val int = chunks[0].toInt()
val str = chunks[1]
return Example(int, str)
}
// Tests if an input could be parsed as an Example
// NOTE: 'input' is the rest of the args concatenated together, in case you want to occupy multiple arguments.
override fun matches(sender: ICommandSender<*>, input: String): Boolean {
return input.matches(Regex("^(\\d+):\\S+"))
}
// Returns a list of tab completions for Example
override fun complete(sender: ICommandSender<*>, input: String): List<String> {
val first = input.split(" ")[0]
if (!first.contains(":"))
return listOf("$first:")
return emptyList()
}
}
Then, we can register the type when our plugin enables.
Java
commandManager.getTypes().put(Example.class, new ExampleType());
Kotlin
commandManager.types[Example::class.java] = ExampleType
You can provide extra parameters to the Command annotation.
This allows you to set permissions, usage messages, etc.
Below shows all the parameters you can use.
@Target(AnnotationTarget.FILE)
annotation class Command(
val name: String,
vararg val aliases: String,
val description: String = "A Commando command.",
val usage: String = "Invalid usage. Please check /{0} help.", // {0} is substituted with the command name
val permission: String = "commando.{0}", // {0} is substituted with the command name
val permissionMessage: String = "You don't have permission to do that."
)
Here's an example command using these parameters.
@file:Command(
"cake", // name
"the-lie", "the-cake", // aliases
description = "Gives a cake.",
usage = "Invalid usage. /cake [player]",
permission = "cakecore.cake",
permissionMessage = "You need cakecore.cake to do that!"
)
package me.honkling.example.commands
import me.honkling.commando.common.annotations.Command
import org.bukkit.inventory.ItemStack
import org.bukkit.Material
fun cake(executor: Player, target: Player?) {
val player = target ?: executor
player.inventory.addItem(ItemStack(Material.CAKE))
player.sendMessage("Here is your cake!")
}
Commando uses the MIT license.
Check out the LICENSE file for further details.