-
Notifications
You must be signed in to change notification settings - Fork 1
Quick Start
Now that you have Grapefruit installed, the fun part begins. First, you need to create a CommandDispatcher
. You can do so by using CommandDispatcherBuilder
which you can acquire by calling CommandDispatcher#builder
. This method will expect a TypeToken<YourCommandSourceType>
object.
public class CommandSource {
private final Set<String> permissions = new HashSet<>();
public void print(final String message) {
System.out.println(message);
}
public boolean isPermitted(final String permission) {
return this.permissions.contains(permission);
}
public boolean addPermission(final String permission) {
return this.permissions.add(permission);
}
}
In this example the above class will represent a command source. Now create a dispatcher.
final CommandDispatcher<CommandSource> dispatcher = CommandDispatcher.builder(TypeToken.of(CommandSource.class))
.withMessenger(CommandSource::print)
.withAuthorizer(CommandSource::isPermitted)
.build();
As you can see, CommandDispatcherBuilder
has a few functions: withAuthorizer
, withAsyncExecutor
, withMessageProvider
, withMessenger
and withRegistrationHandler
. So what are these?
Grapefruit allows you to specify permissions for each command to make them available only to a certain group of users. However, it doesn't know how to check whether a user is allowed to execute a certain command. That's where CommandAuthorizer
comes in. You can create a class and implement it, or you can use a lamdba expression as shown above.
Implementing CommandAuthorizer
looks something like this:
public class MyCommandAuthorizer implements CommandAuthorizer<CommandSource> {
@Override
public boolean isAuthorized(final @NotNull CommandSource source, final @NotNull String permission) {
return source.isPermitted(permission);
}
}
And the lambda expression form:
final CommandAuthorizer<CommandSource> authorizer = CommandSource::isPermitted
If you don't need permissions and permission checks, you safely skip this step, Grapefruit by default allows users to execute every command (if no permission and/or authorizer is present).
As you'll se later, Grapefruit let's you decide whether a command should be executed on a separate thread or not. By default ForkJoinPool#commonPool
is used, but you are free to use your own java.util.concurrent.Executor
if you prefer that.
final CommandDispatcher<CommandSource> dispatcher = CommandDispatcher.builder(TypeToken.of(CommandSource.class))
.withAsyncExecutor(myCustomExecutor)
.build();
Messages have their own wiki page, you can find it by clicking here.
Messenger is responsible for displaying messages to your CommandSource. Grapefruit by default uses a java.lang.System.Logger
instance to achieve this, so if you are fine with that, you can skip this section. However, in case you need a little bit more sophisticated approach, it's possible to create your own Messenger
. Just as CommandAuthorizer
, Messenger
is also a functional interface, so it's up to you whether you use lamda expressions or a custom class implementing it.
public class MyMessenger implements Messenger<CommandSource> {
@Override
public void sendMessage(final @NotNull CommandSource source, final @NotNull String message) {
source.print(message);
}
}
final Messenger messenger = CommandSource::print
On certain platforms you might need additional registration logic for each command (a great example could be Minecraft plugins, where you need to register all your commands so that the server can execute them). The easiest way is to use a custom registration handler, like so:
public class MyRegistrationHandler implements CommandRegistrationHandler<CommandSource> {
@Override
public void accept(final @NotNull CommandRegistrationContext<CommandSource> context) {
final String[] rootAliases = context.route().get(0).split(" ");
for (final String alias : rootAliases) {
// register command
}
}
}
And then pass it to the dispatcher builder:
final CommandDispatcher<CommandSource> dispatcher = CommandDispatcher.builder(TypeToken.of(CommandSource.class))
.withRegistrationHandler(new MyRegistrationHandler())
.build();
Now that you have your CommandDispatcher
built, you can finally start writing commands. Consider the following example:
public class MyCommands implements CommandContainer {
@CommandDefinition(route = "hello")
public void onHelloCommand(final @Source CommandSource source) {
source.print("Hello there!");
}
}
final CommandDispatcher<CommandSource> dispatcher = //...
dispatcher.registerCommands(new MyCommands());
Every class that contains commands need to implement CommandContainer
. You use methods to write your command logic and then mark these methods with @CommandDefinition
so that the dispatcher recognizes them upon registration. If you're wondering why CommandSource is annotated with @Source
, you can find out more about that here. In case you don't need the source object for your command logic, you can omit it completely:
@CommandDefinition(route = "login")
public void onLoginCommand(final String username, final String password) {
System.out.printf("Logging in %s", username);
login(username, password);
}
@CommandDefinition
has three properties:
- route (required)
- permission (optional, "" (empty string) by default)
- runAsync (optional, false by default)
Grapefruit organizes commands into nodes which end up forming a graph (or a tree, whichever you prefer). Route will be transformed into nodes and the command method will be registered on the last node. This makes it working with parent/child relations a dream.
@CommandDefinition(route = "root child0 child1")
public void onChildCommand(final @Source CommandSource source) {
source.print("Hey!");
}
Also, Grapefruit supports aliases, which you can specify by using pipe characters (|
)
@CommandDefinition(route = "pm|dm|message")
public void onPmCommand(final @Source CommandSource source,
final User user,
final @Greedy String message) {
user.print(message);
source.print("Your message has been sent to " + user.getName());
}
Don't mind the @Greedy
annotation for now, this page has all the details about it. Aliases work with child commands as well:
@CommandDefinition(route = "root|parent child|sub")
public void onSomeCommand() {}
Permissions are optional properties, if you omit them, everyone will get access to your commands.
@CommandDefinition(route = "test", permission = "some.permission")
public void onCommandWithPermission(final @Source CommandSource source) {
source.print("Congratulations, you have the required permission");
}
Before executing commands the dispatcher reaches out to it's authorizer to check if a user has the required permission, and proceeds to executing the command only, if the user does have it.
In case you need a command to run asynchronously you can set the runAsync
property to true to indicate this.
@CommandDefinition(route = "test", runAsync = true)
public void onAsyncCommand(final @Source CommandSource source) {
source.print("Yaay, this command is async");
}
Now you have everything to dispatch your commands.
public class MyCommands implements CommandContainer {
@CommandDefinition(route = "pm|dm|message", permission = "pm.permission")
public void onPmCommand(final @Source CommandSource source,
final User user,
final @Greedy String message) {
user.print(message);
source.print("Your message has been sent to " + user.getName());
}
}
final CommandDispatcher<CommandSource> dispatcher = //...
dispatcher.registerCommands(new MyCommands());
final CommandSource source = new CommandSource();
source.addPermission("pm.permission");
dispatcher.dispatchCommand(source, "pm john Hello there!");
Grapefruit makes it possible to create so called redirect command nodes that basically - you guessed it - redirect to another. This means, that it is possible to have command somecommand
be a shortcut for some long command route
. Let's see it in action:
@Redirect(from = "test")
@CommandDefinition(route = "parent child grandchild")
public void testCommand(final @Source CommandSource source) {
System.out.println("Hello there!");
}
@Redirect
is repeatable meaning that you can have as many nodes redirecting to a certain command, as you wish. Please note however, that a the route to redirect from cannot be the same as your commands route (in other words Redirect#from
and CommandDefinition#route
can't be the same). @Redirect
supports aliases just as @CommandDefinition
does.
If the command you want to redirect to has parameters, you need to specify a value for them using Redirect#arguments
.
@Redirect(from = "test", arguments = {"10", "Hello there!"})
@CommandDefinition(route = "parent child grandchild")
public void testCommand(final @Source CommandSource source, final int count, final @Greedy String message) {
for (int i = 0; i < count; i++) {
System.out.println(message);
}
}
In the above case dispatching parent child grandchild
will fail, since the command has two parameters, but test
won't since arguments provided by the user will be ignored, and the predefined values will be taken into account.
CommandContext
(just like the source of the command) is a "shadow" parameter, meaning that while being a parameter of a certain command method, no user specified input is required for it. However, similarly how the source has to be the first parameter of the method, CommandContext
must be the last one.
@CommandDefinition(route = "test")
public void commandWithContext(...someparams, final CommandContext context) {
context.put("hello", "there");
}
As you can see in the above example, there are ..someparams
which don't matter in this case, but the last parameter is the context itself, which makes it easy for pre/post processors and command methods to pass data to each other.