Intake is a Java library for parsing user commands.
Commands can be registered via two ways:
- Methods can be annotated with
@Command
. - A class can implement
CommandCallable
.
Several commands can be registered on an implementation of Dispatcher
, which
can parse input and execute the correct command.
Sub-commands are supported. This is because all Dispatcher
s are also
CommandCallable
s, so you can add a dispatcher to another dispatcher to another
dispatcher!
In addition, Intake supports completion of arguments through
CommandCallable
s and CommandCompleter
s
The API supports a rich amount of metadata about each command, allowing the inspection of registered commands, their parameters, their permissions, and their usage instructions.
This library is taken from WorldEdit 6.x. You may be familiar with the older command framework in WorldEdit — while this library is based off of that framework, it has been taken from a newer version of WorldEdit, which at the time of writing, has not been released.
Please be aware that the library is currently a work in progress. While it was
merged into WorldEdit's main
branch, it was done after more than a year
since it was originally written. There may be some small bugs here and
and there is currently a dire lack of unit tests.
There is currently some legacy code as well, which tends to be stable but very poorly documented.
It is strongly recommended that if you use this library in a plugin or mod
for Minecraft, the library should be shaded and the com.sk89q.intake
package
relocated to something internal to your project (i.e. myproject.internal.intake
).
Intake currently requires some version of Google Guava that is equal to or newer than 10.0.1. Guava is not bundled with the library.
Currently, Intake is available in sk89q's Maven repository:
<repositories>
<repository>
<id>maven.sk89q.com</id>
<url>http://maven.sk89q.com/repo/</url>
</repository>
</repositories>
As a dependency,
<dependencies>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>intake</artifactId>
<version>{version here}</version>
</dependency>
</dependencies>
No release of Intake has been made. Only snapshot builds are available.
Note: The API is subject to change in snapshot builds.
An effort was made to make transitioning from the older command framework easier with fairly minimal changes required to the code. If you are planning to convert from the old command framework to this version, be aware of these changes:
- The
CommandPermissions
annotation was renamed toRequire
. - The
WrappedCommandException
class was renamed toInvocationCommandException
. - A considerable amount of code was split up and decoupled. Now,
SimpleDispatcher
is the main class for registering commands and the "annotated commands" support is separate. - The old
CommandManager
has been replaced with a newerParametricBuilder
class that is more powerful, allowing for injection of arbitrary types (i.e.yourMethod(String name, int age)
instead of justyourMethod(Player player, CommandContext args)
). There is some backwards compatibility ifLegacyCommandsHelper
is registered on the instance ofParametricBuilder
. Injector
was removed. Pass instances toParametricBuilder
.
Define some commands:
public class MyCommands {
@Command(aliases = "age", desc = "Set age")
@Require("example.age")
public void setAge(User user, @Optional @Default("20") int age) {
user.setAge(age);
user.message("Your age was set to: " + age);
}
@Command(aliases = "love", desc = "Broadcast your love")
@Require("example.love")
public void broadcastLove(User user, String name) {
Platform.broadcast(user.getName() + " loves " + name + "!");
}
}
We may want to be able to check permisisons:
public class ActorAuthorizer implements Authorizer {
@Override
public boolean testPermission(CommandLocals locals, String permission) {
User sender = locals.get(User.class);
if (sender == null) {
throw new RuntimeException("Uh oh! A user didn't use this command.");
} else {
return sender.testPermission(permission);
}
}
}
Create a default argument completer for parameters:
public class MyCompleter implements CommandCompleter {
@Override
public List<String> getSuggestions(String arguments, CommandLocals locals) {
List<String> suggestions = new ArrayList<String>();
Collection<User> users = Platform.getConnectedUsers();
for (User user : users) {
if (user.getName().toLowerCase().startsWith(arguments.toLowerCase().trim())) {
suggestions.add(user.getName());
}
}
return suggestions;
}
}
Perhaps use some custom types of parameters:
public class MyBinding extends BindingHelper {
@BindingMatch(type = MyObject.class,
behavior = BindingBehavior.CONSUMES,
consumedCount = 1,
provideModifiers = true)
public MyObject getString(ArgumentStack context, Annotation[] modifiers) {
return Platform.parseAsMyObject(context.next());
}
}
Perhaps catch exceptions thrown by commands:
public class MyExceptionConverter extends ExceptionConverterHelper {
@ExceptionMatch
public void convert(NumberFormatException e) throws CommandException {
throw new CommandException("Number expected! Instead, I got something else.");
}
}
Then build the dispatcher:
ParametricBuilder builder = new ParametricBuilder();
builder.setAuthorizer(new MyAuthorizer());
builder.setDefaultCompleter(new MyCompleter());
builder.addBinding(new MyBinding());
builder.addExceptionConverter(new MyExceptionConverter());
// Add support for commands that were created for the older
// command framework in WorldEdit
builder.addInvokeListener(new LegacyCommandsHandler());
Dispatcher dispatcher = new CommandGraph()
.builder(builder)
.commands()
.registerMethods(new MyCommands())
.registerMethods(new MoreCommands())
.group("debug", "dbug") // Subcommands
.describeAs("Debugging commands")
.registerMethods(new DebuggingCommands())
.parent()
.graph()
.getDispatcher();
Execute a command:
CommandLocals locals = new CommandLocals();
locals.put(User.class, caller);
String[] parentCommands = new String[0]; // No parent commands
// Note: Prefix characters (/, ., etc.) must be removed
dispatcher.call("love bob", locals, String[] parentCommands);
Inspect a command:
CommandMapping mapping = dispatcher.get("love");
String desc = mapping.getDescription().getShortDescription();
Documentation is currently a work-in-progress.
Find it here: https://github.com/sk89q/Intake/wiki
Use Maven 3 to compile Intake.
mvn clean package
Intake is available under the GNU Lesser General Public License.
We happily accept contributions, especially through pull requests on GitHub.
- Visit our website
- IRC channel (#sk89q on irc.esper.net)