-
-
Notifications
You must be signed in to change notification settings - Fork 7
Creating Commands Using Annotations
Building a command using Literal.Builder
and Argument.Builder
can often be difficult to follow when building a complex command. Since 4.1.0
, commands can alternatively be declared using annotations. Using the annotations improves both readability and maintainability. In addition, the project bundles several annotation processors to ensure correctness.
This page will create the tell command provided in the cookbook using annotations.
Since all annotations have the same lexical scope as a class, it is recommended that each command is declared in a separate class.
public class TellCommand {
}
A Literal
can be created from a @Literal
annotation on either TellCommand
or a method in TellCommand
. A @Literal
annotation on TellCommand
will yield a Literal
with no behaviour. Conversely, a @Literal
annotation on a method will yield a Literal
with the method as its behaviour.
An annotated method's must not be private or static. In addition, its signature must match either Command.run(CommandContext)
or Executable.execute(DefaultableContext)
.
A namespace
is the path to a command, namespace = {"foo", "bar"}
is equivalent to /foo bar
. In addition, a Literal
will be implicitly created for each name in a namespace
that has not been explicitly declared. A class may not contain empty or duplicate namespace
s.
import com.karuslabs.commons.command.DefaultableContext;
import com.karuslabs.commons.command.annotations.Literal;
public class TellCommand {
@Literal(namespace = {"tell"}, aliases = "t")
public void tell(DefaultableContext<CommandSender> context) {
context.getSource().sendMessage("Hello darkness my old friend");
}
}
An Argument
can be created from an @Argument
annotation. @Argument
annotations follow the same conventions as @Literal
annotations mentioned in the previous section.
In addition, an @Argument
annotation contains two optional type
and suggestions
fields. Each field must be bound to an ArgumentType
and SuggestionProvider
annotated with @Bind
respectively. The last name in the namespace
of an @Argument
is used if type
is empty. No SuggestionProvider
is bound if suggestions
is empty. More information on binding fields is provided in the next section.
import com.karuslabs.commons.command.DefaultableContext;
import com.karuslabs.commons.command.annotations.Literal;
@Argument(namespace = {"tell", "players"})
public class TellCommand {
@Argument(namespace = {"tell", "players", "message"}, type = "string", suggestions = "suggestions")
public void send(DefaultableContext<CommandSender> context) {
CommandSender sender = context.getSource();
List<Player> recipents = context.getArgument("players", List.class);
String entered = context.getArgument("message", String.class);
recipents.forEach(p -> p.sendMessage(sender + " says: " + entered));
}
}
Fields annotated with @Bind
can be bound to an Argument
. They must be either ArgumentType
s or SuggestionProvider
s. A @Bind
annotation contains an optional name
to which the annotated field is bound. If name
is empty, the annotated field's name is used instead.
import com.karuslabs.commons.command.DefaultableContext;
import com.karuslabs.commons.command.annotations.Argument;
import com.karuslabs.commons.command.annotations.Bind;
import com.karuslabs.commons.command.types.PlayersType;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
@Argument(namespace = {"tell", "players"})
public class TellCommand {
@Bind PlayersType players = new PlayersType();
@Bind ArgumentType<String> string = StringArgumentType.string();
@Bind("something") SuggestionProvider<CommandSender> suggestions = (context, builder) -> builder.suggest("\"Hello World!\"").buildFuture();
@Argument(namespace = {"tell", "players", "message"}, type = "string", suggestions = "something")
public void send(DefaultableContext<CommandSender> context) {
CommandSender sender = context.getSource();
List<Player> recipents = context.getArgument("players", List.class);
String entered = context.getArgument("message", String.class);
recipents.forEach(p -> p.sendMessage(sender + " says: " + entered));
}
}
A CommandNode
can be created from TellCommand
using Commands.from(Object, String)
. After which it can be registered to a Dispatcher
.
Alternatively, TellCommand
can be appended to a Literal
via Literal.Builder.then(Object, String)
.
import com.karuslabs.commons.command.Commands;
CommandNode<CommandSender> tell = Commands.from(new TellCommand(), "tell");
Dispatcher dispatcher = Dispatcher.of(yourPlugin);
dispatcher.getRoot().addChild(tell);
dispatcher.update(); // Only needed if we added "tell" after YourPlugin.onLoad() and YourPlugin.onEnable() has been called.
After finishing this guide, TellCommand
should look something like this.
import com.karuslabs.commons.command.Commands;
import com.karuslabs.commons.command.DefaultableContext;
import com.karuslabs.commons.command.Dispatcher;
import com.karuslabs.commons.command.annotations.Argument;
import com.karuslabs.commons.command.annotations.Bind;
import com.karuslabs.commons.command.annotations.Literal;
import com.karuslabs.commons.command.types.PlayersType;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
@Argument(namespace = {"tell", "players"})
public class TellCommand {
@Bind PlayersType players = new PlayersType();
@Bind ArgumentType<String> string = StringArgumentType.string();
@Bind("something") SuggestionProvider<CommandSender> suggestions = (context, builder) -> builder.suggest("\"Hello World!\"").buildFuture();
@Literal(namespace = {"tell"}, aliases = "t")
public void tell(DefaultableContext<CommandSender> context) {
context.getSource().sendMessage("Hello darkness my old friend");
}
@Argument(namespace = {"tell", "players", "message"}, type = "string", suggestions = "something")
public void send(DefaultableContext<CommandSender> context) {
CommandSender sender = context.getSource();
List<Player> recipents = context.getArgument("players", List.class);
String entered = context.getArgument("message", String.class);
recipents.forEach(p -> p.sendMessage(sender.getName() + " says: " + entered));
}
}
CommandNode<CommandSender> tell = Commands.from(new TellCommand(), "tell");
Dispatcher dispatcher = Dispatcher.of(yourPlugin);
dispatcher.getRoot().addChild(tell);
dispatcher.update(); // Only needed if we added "tell" after YourPlugin.onLoad() and YourPlugin.onEnable() has been called.
Testing the tell
command in-game will yield the following.