Skip to content

Quick Start

Tamás Ádám edited this page Nov 14, 2021 · 14 revisions

Creating A Dispatcher

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.

Basics

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?

Authorizer

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).

Async Executor

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();

Message Provider

Messages have their own wiki page, you can find it by clicking here.

Messenger

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

Registration Handler

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();

Defining Commands

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)

Route

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() {}

Permission

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.

Run Async

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");
}

Dispatching commands

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!");

Redirecting Commands

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 As Parameter

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.