Simple command framework with yet powerful features and ability to adapt. Part of the okaeri-platform.
Commands commands = new OkaeriCommands();
commands.registerCommand(ExampleCommand.class); // built for DI (accepts empty constructor by default)
// commands.registerCommand(new ExampleCommand()); // pass own instance (e.g. with custom constructor parameters)
// call manually
commands.call("cmd hello");
// call and get result
Object result = commands.call("cmd woah");
// TODO
# single thread performance on ryzen 3600, about 1,000,000 invocations per second
Benchmark Mode Cnt Score Error Units
BenchmarkCommands.command_complex thrpt 5 1109842.766 ± 47168.401 ops/s
BenchmarkCommands.command_medium thrpt 5 1112048.204 ± 46700.652 ops/s
BenchmarkCommands.command_simple thrpt 5 1253307.288 ± 31533.820 ops/s
// platform specific (bukkit)
// makes all executors async,
// to include/exclude single use @Async/@Sync
@Async
// declare command service using annotation
// aliases can be also specified here
@Command(label = "cmd", description = "Example command service")
public class ExampleCommand implements CommandService {
// cmd
//
// empty pattern represents command without
// arguments (similar to @Default in other frameworks)
//
// no pattern + method name starting with underscore
// is the same as using @Executor(pattern = "")
//
@Executor
public String _def() {
return "called default";
}
// cmd woah
//
// simple commands with no additional effort
// using method name is same as pattern = "woah"
//
// it also works for simple arguments:
// @Executor
// String woah(@Arg String name) -> cmd woah <name>
//
@Executor(description = "Prints woah message")
public String woah() {
return "woah!";
}
// cmd hello
// cmd hello everyone
//
// two patterns are possible in single method, they should
// however match (e.g. have all the same non-static arguments)
//
@Executor(pattern = {"hello", "hello everyone"}, description = "Prints hello message")
public String hello_everyone() {
return "hello!";
}
// cmd hi
// cmd hii
//
// overwrite usage/description with Commands#resolveText()
// available out-of-the-box in OkaeriPoland/okaeri-platform i18n integration
//
@Executor(pattern = {"hi", "hii"}, description = "${command-cmd-hi-description}", usage = "${command-cmd-hi-usage}")
public String hi() {
return "hi!";
}
// cmd hello <name>
//
// required arguments can be specified using '<PARAMETER_NAME>'
// or '*' (recommended with -parameters compiler option, uses in-order param name)
//
@Completion(arg = "name", value = "@allplayers")
@Executor(pattern = "hello *", description = "Prints hello message with name")
public String hello_name(@Arg("name") String name) {
return "hello " + name;
}
// cmd hi [name]
//
// optional arguments can be specified using '[PARAMETER_NAME]'
// or '?' (recommended with -parameters compiler option, uses in-order param name)
//
// optional arguments require param to be an Optional or preferably
// in-house eu.okaeri.commands.service.Option with additional utilities
//
@Executor(pattern = "hi ?", description = "Prints hi message with optional name")
public String hi_name(@Arg("name") Option<String> name) {
return "hi, " + name.getOr("guest");
}
// cmd salute [nameAndSurname]
// cmd salute John Doe
//
// accepts specific number of arguments into one parameter
// example: *:2 same as <name:2>, ?:2 same as [name:2]
//
// optional arguments require param to be an Optional or preferably
// in-house eu.okaeri.commands.service.Option with additional utilities
//
@Executor(pattern = "salute ?:2" , description = "Prints salute message with optional name and surname")
public String salute_nameAndSurname(@Arg("nameAndSurname") Option<String> name) {
return "salute " + name.getOr("some guest") + "!";
}
// cmd print <message>
// cmd print Some message with unspecified length
//
// accepts all sequential arguments into one parameter
//
@Executor(pattern = "print *..." , description = "Prints system out message")
public void log_message(@Arg("message") String message) {
System.out.println(message);
}
// cmd player <player> set <perm> <value> [flag]
//
// mix and match pattern elements, return values to be processed by adapter
// see below for recommended and simplified pattern variant (for -parameters compiler flag)
//
@Executor(pattern = "player <player> set <perm> <value> [flag]", description = "Complex command test")
public String complex1(@Arg("player") String name, @Arg("perm") String perm, @Arg("value") String value, @Arg("flag") Option<String> flag) {
return name + " " + perm + " " + value + " " + flag;
}
// recommended usage
//
// mix param types and resolve unknown values by overriding Commands#resolveMissingArgument (e.g. DI)
// preserve param names using javac's -parameters or specify them manually @Arg("name")
//
// specify inline string completions or use previously registered named completion from Commands#registerCompletion
// you can also pass @CompletionData and read it in your custom completion implementation as you like
//
@Completion(arg = "name", value = "@allplayers", data = @CompletionData(name = "limit", value = "10"))
@Completion(arg = "perm", value = {"build", "break", "place"})
@Completion(arg = "value", value = {"allow", "deny"})
@Completion(arg = "flag", value = {"-silent"})
@Executor(pattern = "player * set2 * * ?", description = "Complex command test")
public String complex2(@Arg String name, int huh, @Arg String perm, @RawArgs String[] args, @Arg String value, String randomElement, @Arg Option<String> flag) {
return (">> " + name + " " + perm + " " + value + " " + flag + "\n" + Arrays.toString(args));
}
}
It is highly recommended to use -parameters
compiler flag for better overall feature support.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<!-- ... -->
<configuration>
<!-- ... -->
<args>
<arg>-java-parameters</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
compileJava {
options.compilerArgs << '-parameters'
}
compileKotlin {
kotlinOptions {
javaParameters = true
}
}