- 1. Introduction to Cloud
- 2. Getting Started
- 3. Core
- 4. Annotations
- 4.1. Install
- 4.2. Annotation Parser
- 4.3. @CommandMethod
- 4.4. @Argument
- 4.5. @Flag
- 4.6. @CommandDescription
- 4.7. @CommandPermission
- 4.8. @ProxiedBy
- 4.9. @Regex
- 4.10. @Parser
- 4.11. @Suggestions
- 4.12. Injections
- 4.13. Builder Modifiers
- 4.14. Annotation Mappers
- 4.15. Pre-processor Mappers
- 4.16. @CommandContainer
- 5. Kotlin DSL
- 6. Platforms
- Glossary
|
Caution
|
The Cloud documentation is still a work in progress. |
Cloud is a command manager and dispatcher for the JVM. Cloud allows you to define commands in several ways, most notably using command builders, or annotations. Cloud has platform implementations for many platforms, including Minecraft server software such as Bukkit or Discord bot frameworks such as JDA.
Cloud allows you to customize the command execution pipeline by injecting custom behaviour along the entire execution path. All of this will be covered in this document.
This document will first introduce different Cloud concepts using the builder pattern API. Section 4 will expand upon this by introducing the annotation (declarative) API, which offers another way of declaring commands.
This document will not cover every specific detail of Cloud. Instead, the purpose of this document is to give an introduction to various Cloud concepts and explain how they can be used to build useful commands. It is very recommended to read the JavaDocs and use them as the primary source of information.
Cloud is available through Maven Central.
Maven
<dependency>
<groupId>cloud.commandframework</groupId>
<artifactId>cloud-core</artifactId>
<version>1.8.4</version>
</dependency>Gradle (Groovy)
implementation 'cloud.commandframework:cloud-core:1.8.4'Gradle (Kotlin)
implementation("cloud.commandframework:cloud-core:1.8.4")If you want to use snapshot builds, then they are available the Sonatype snapshots repository:
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>- cloud-core
-
Core Cloud API module.
- cloud-annotations
-
Cloud annotation API.
- cloud-services
-
Cloud service API. Included in Core.
- cloud-tasks
-
Cloud scheduling API.
- cloud-kotlin-extensions
-
Cloud extensions for Kotlin.
- cloud-bukkit
-
Cloud implementation for the Bukkit API.
- cloud-paper
-
Extension of cloud-bukkit for the Paper API.
- cloud-velocity
-
Cloud implementation for the Velocity (1.1.0+) API.
- cloud-brigadier
-
Cloud utilities for Mojang’s Brigadier API.
- cloud-bungee
-
Cloud implementation for the BungeeCord API.
- cloud-fabric
-
Cloud implementation for Minecraft clients and servers using the Fabric mod loader and API.
- cloud-jda
-
Cloud implementation for the JDA API.
- cloud-javacord
-
Cloud implementation for the Javacord API.
- cloud-pircbotx
-
Cloud implementation for the PircBotX framework.
- cloud-sponge7
-
Cloud implementation for Sponge v7.
The core module contains the majority of the API that you will be interacting with when using Cloud.
The first step to any Cloud project is to create a command manager. Each supported platform has its own command manager, but for the most part they look and behave very similarly. It is possible to support multiple platforms in the same project.
All command managers have a generic type argument for the command sender type. Most platforms have their own "native" command sender type, but Cloud allows you to use whatever sender you want, by supplying a mapping function to the command manager. This sender type will be included in the command context, which you will be interacting with a lot when using Cloud.
This particular example uses cloud-bukkit, though most concepts transfer over to the other command mangers.
CommandManager<CommandSender> manager = new BukkitCommandManager<>(
/* Owning plugin */ this,
CommandExecutionCoordinator.simpleCoordinator(), (1)
Function.identity(), (2)
Function.identity(), (3)
);-
The execution coordinator handles the coordination of command parsing and execution. You can read more about this in section 3.6.
-
Function that maps the platform command sender to your command sender.
-
Function that maps your command sender to the platform command sender.
The command manager is used to register commands, create builders, change command settings, etc. More information can be found in the CommandManager JavaDoc.
Commands consist of chains of arguments that are parsed from user input. These arguments can be either static literals or variables. Variable arguments are parsed into different types using argument parsers. Variable arguments may be either required, or they can be optional. Optional arguments may have default values.
/foo bar one
/foo bar two <arg>
/foo <arg> (1)-
When a variable argument is present next to literals, it will be allowed to catch any input that isn’t caught by the literals. Only one variable may exist at any level, but there may be many literals.
This example contains three unique commands.
Literals are fixed strings and can be used to create "subcommands". You may use however many command literals you want at any level of a command. Command literals may have additional aliases that correspond to the same argument.
A literal can be created directly in the command builder:
builder = builder.literal(
"main", (1)
"alias1", "alias2", "alias3" (2)
);-
Any literal must have a main "alias".
-
You may also specify additional aliases. These are optional.
You can also attach a description to your node, which is used in the command help system:
builder = builder.literal(
"main",
Description.of("Your Description")
);Literals may also be created using the StaticArgument class.
Cloud has built in support for all primitive types, as well as some other commonly used argument types.
There are three different types of string arguments:
- single
-
A single string without any blank spaces.
- greedy
-
Consumes all remaining input.
- quoted
-
Consumes either a single string, or a string surrounded by
"or'.
String arguments can be constructed using:
-
StringArgument.of(name): Required single string argument -
StringArgument.of(name, mode): Required string argument of specified type -
StringArgument.optional(name): Optional single string argument -
StringArgument.optional(name, mode): Optional string argument of specified type
Furthermore, a string argument builder can be constructed using StringArgument.newBuilder(name).
This allows you to provide a custom suggestion generator, using StringArgument.Builder#withSuggestionsProvider(BiFunction<CommandContext<C>, List<String>>).
There are four different integer argument types:
-
byte
-
short
-
int
-
long
All integer types are created the same way, the only difference is the class. These examples will use IntegerArgument, but the same
methods are available in ByteArgument, ShortArgument, and LongArgument.
Integer arguments can be constructed using:
-
IntegerArgument.of(name): Required integer argument without a range -
IntegerArgument.optional(name): Optional integer argument without a range -
IntegerArgument.optional(name, default): Optional integer argument without a range, with a default value
Furthermore, an integer argument builder can be constructed using IntegerArgument.newBuilder(name). This allows you to provide a custom suggestion generator, using IntegerArgument.Builder#withSuggestionsProvider(BiFunction<CommandContext<C>, List<String>>), and set minimum and maximum values.
There are two different floating point argument types:
-
float
-
double
All floating point types are created the same way, the only difference is the class. These examples will use FloatArgument, but the same
methods are available in DoubleArgument.
Floating point arguments can be constructed using:
-
FloatArgument.of(name): Required float argument without a range -
FloatArgument.optional(name): Optional float argument without a range -
FloatArgument.optional(name, default): Optional float argument without a range, with a default value
Furthermore, a floating-point argument builder can be constructed using FloatArgument.newBuilder(name). This allows you to provide a custom suggestion generator, using FloatArgument.Builder#withSuggestionsProvider(BiFunction<CommandContext<C>, List<String>>), and set minimum and maximum values.
The enum argument type allows you to create a command argument using any enum type. They can be created using EnumArgument.of
and EnumArgument.optional. The parser accepts case independent values and suggestions will be created for you.
The boolean argument type is very simple. It parses boolean-like values from the input. There are two different modes:
- liberal
-
Accepts truthy values ("true", "yes", "on") and falsy values ("false", "no", off")
- non-liberal
-
Accepts only "true" and "false"
Compound arguments are a special type of arguments that consists of multiple other arguments. By default, 2 or 3 arguments may be used in a compound argument.
The methods for creating compounds arguments can be found in CommandManager, or in the ArgumentPair or ArgumentTriplet classes.
In general, they need a tuple of names, and a tuple of argument types. They can also take in a mapping function which maps the value to a more user-friendly type.
commandBuilder.argumentTriplet(
"coords",
TypeToken.get(Vector.class),
Triplet.of("x", "y", "z"),
Triplet.of(Integer.class, Integer.class, Integer.class),
(sender, triplet) -> new Vector(triplet.getFirst(), triplet.getSecond(),
triplet.getThird()
),
Description.of("Coordinates")
)Cloud allows you to create custom argument parsers. The easiest way to achieve this
is by extending CommandArgument<C, YourType>. This is recommended if you are creating
arguments that will be exposed in some kind of library. For inspiration on how
to achieve this, it is recommended to check out the standard Cloud arguments.
If you don’t need to expose your parser as a part of an API, you may simply
implement ArgumentParser<C, YourType>. The method you will be working with
looks like:
public @NonNull ArgumentParseResult<YourType> parse(
@NonNull CommandContext<@NonNull C> commandContext,
@NonNull Queue<@NonNull String> inputQueue (1)
) {
// ...
}-
Queue containing (remaining) user input.
When reading an argument you should do the following:
-
Peek the queue.
-
Attempt to parse your object.
-
If the object could not be parsed, return
ArgumentParseResult.failure(exception)
-
-
If the object was parsed successfully, pop a string from the queue.
-
Return
ArgumentParseResult.success(object).
|
Warning
|
If the read string(s) isn’t popped from the queue, then the command engine will assume that the syntax is wrong and send an error message to the command sender. |
It is highly recommended to make use of ParserException when returning a failed result. This allows for integration with the Cloud caption system (refer to the section on Exception Handling for more information).
You should - in most cases - register your parser to the ParserRegistry which you can access using CommandManager#getParserRegistry(). If you are registering a parser that shouldn’t be the default for the argument type, then it is recommended to register a named parser. If your parser is not registered to the parser registry, it will not be usable in annotated command methods.
When registering a command parser, you’re actually registering a function that will generate a parser based on parser parameters. These parameters can be used together with the annotation system to differentiate between different parsers and also change parser settings. In order to create these parameters you can create an annotation mapper using ParserRegistry#registerAnnotationMapper.
Here’s an example of how a UUID parser can be created and registered:
This example is taken from UUIDArgument.java , which also includes a custom exception and argument builder. It’s a good reference class for custom arguments, as it does not contain any complicated logic.
public final class UUIDParser<C> implements ArgumentParser<C, UUID> {
@Override
public @NonNull ArgumentParseResult<UUID> parse(
final String input = inputQueue.peek();
if (input == null) {
return ArgumentParseResult.failure(new NoInputProvidedException(
UUIDParser.class,
commandContext
));
}
try {
UUID uuid = UUID.fromString(input);
inputQueue.remove();
return ArgumentParseResult.success(uuid);
} catch(final IllegalArgumentException e) {
return ArgumentParseResult.failure(new UUIDParseException(input, commandContext));
}
)
}It is then registered to the parser registry using
parserRegistry.registerParserSupplier(
TypeToken.get(UUID.class),
options -> new UUIDParser<>()
);Flags are named optional values that can either have an associated argument (value flag) or have the value evaluated by whether the flag is present (presence flag). These flags are registered much the same way as normal arguments, only that you use .flag methods in the command builder instead.
Flags are always optional. You cannot have required flags. If you need required values, then they should be part of a deterministic command chain. Flags must also necessarily be placed at the tail of a command chain, and you cannot put any arguments (required, or optional) after the flags. This is enforced by the command builder.
Flags can have aliases alongside their full names. When referring to the full name of a flag, you use --name whereas an alias
uses syntax similar to -a. You can chain the aliases of multiple presence flags together, such that -a -b -c is equivalent
to -abc.
manager.command(
manager.commandBuilder("cp")
.argument(StringArgument.of("source"), ArgumentDescription.of("Source path"))
.argument(StringArgument.of("destination"), ArgumentDescription.of("Destination path"))
.flag(
manager.flagBuilder("recursive")
.withAliases("r")
.withDescription(ArgumentDescription.of("Recursive copy"))
).handler(context -> {
boolean recursive = context.flags().isPresent("recursive");
// ...
})
);In this example the flag is constructed outside the command builder, and referenced using the flag object itself. Flag objects are also reusable across multiple commands (unlike command arguments).
final CommandFlag<Float> yawFlag = CommandFlag
.newBuilder("yaw")
.withArgument(FloatArgument.of("yaw"))
.build();
manager.command(
manager.commandBuilder("teleport")
.argumentTriplet(
"vector",
Triplet.of("x", "y", "z"),
Triplet.of(Double.class, Double.class, Double.class),
ArgumentDescription.of("The position to teleport to")
)
.flag(yawFlag)
.handler(context -> {
// ...
final float yaw = context.flags().getValue(yawFlag, 0f);
// ...
})
);An argument preprocessor is a function that gets to act on command input before it’s given to a command. This allows you to inject custom verification behaviour into existing parsers, or register annotations that add extra verification to your custom annotations.
RegexPreprocessor.java is a good example of a preprocessor that allows you to add regular expression checking to your arguments.
Argument preprocessors can be applied to created arguments using CommandArgument#addPreprocessor.
Many platforms support command suggestions. You can add command suggestions to your command parser, by overriding the suggestion method:
@Override
public @NonNull List<@NonNull String> suggestions(
final @NonNull CommandContext<C> commandContext,
final @NonNull String input
) {
final List<String> completions = new ArrayList<>();
for (Material value : Material.values()) {
completions.add(value.name().toLowerCase());
}
return completions;
}or by specifying a suggestion function in a command argument builder using CommandArgument.Builder#withSuggestionProvider.
You also register a standalone suggestions to the parser registry,
using
ParserRegistry#registerSuggestionProvider.
Registering a named suggestion provider allows it to be used in
annotated command methods, or retrieved using ParserRegistry#getSuggestionProvider.
When a command is entered by a command sender, it goes through the following stages:
-
It is turned into string tokens. This is used to create the input queue.
-
A command context is created for the input queue combined with the command sender.
-
The command is passed to the preprocessors, which may alter the input queue or write to the context.
-
If a preprocessor causes an interrupt using
ConsumerService.interrupt()then the context will be filtered out and the command will not be parsed. -
If no preprocessor filters out the context, the context and input will be ready to be parsed into an executable command.
-
-
The input is parsed into a command chain and components are written to the context.
-
If the command does not fit any existing command chains, the sender is notified and the parsing is cancelled.
-
If the command is valid, it will be sent to the postprocessors.
-
-
The command postprocessors get to act on the command can alter the command context. they may now postpone command execution, such is the case with the command confirmation postprocessor.
-
If a postprocessor causes an interrupt using
ConsumerService.interrupt()the command will not be executed. -
If no postprocessor interrupts during the post-processing stage, the command will be sent to the executor.
-
-
The command is executed using the command executor.
Command preprocessing happens before the input has been pasted to the command tree for parsing. To register a preprocessor, implement cloud.commandframework.execution.preprocessor.CommandPreProcessor:
public class YourPreProcessor<C> implements CommandPreprocessor<C> {
@Override
public void accept(final CommandPreprocessingContext<C> context) {
/* Act on the context */
if (yourCondition) {
/* Filter out the context so that it is never passed to the parser */
ConsumerService.interrupt();
}
}
}Then register the preprocessor using CommandManager#registerCommandPreProcessor(CommandPreprocessor<C>).
Command postprocessing happen after the input has been parsed into a command chain, but before the command is executed. To register a postprocessor, implement cloud.commandframework.execution.postprocessor.CommandPostProcessor:
public class YourPostprocessor<C> implements CommandPostprocessor<C> {
@Override
public void accept(final CommandPostprocessingContext<C> context) {
/* Act on the context */
if (yourCondition) {
/* Filter out the context so that it is never passed to the executor */
ConsumerService.interrupt();
}
}
}Then register the postprocessor using CommandManager#registerCommandPostProcessor(CommandPostprocessor<C>).
Command proxying is a feature that allows you to forward a command chain to another command chain. More specifically, a "proxy" of a command is a command which has all the same required arguments in the same order as in the original command. Essentially, they can be thought of as more powerful command aliases.
It is easier understood by an example. Imagine you have a warp command in a game,
let’s call it /game warp me <warp>, but you feel like it’s a little too verbose for common use. You may then choose to introduce
a /warpme <warp> command proxy that gets forwarded to the original command.
To create a command proxy you can use Command.Builder#proxies(Command). Please note the documentation of the method, which reads:
Make the current command be a proxy of the supplied command. This means that all of the proxied command’s variable command arguments will be inserted into this builder instance, in the order they are declared in the proxied command. Furthermore, the proxied command’s command handler will be shown by the command that is currently being built. If the current command builder does not have a permission node set, this too will be copied.
In general, it is up to each platform manager to handle command exceptions. Command exceptions are thrown whenever a command cannot be executed normally.
This can be for several reasons, such as:
-
The command sender does not have the required permission (NoPermissionException)
-
The command sender is of the wrong type (InvalidCommandSenderException)
-
The requested command does not exist (NoSuchCommandException)
-
The provided command input is invalid (InvalidSyntaxException)
-
The input provided to a command argument cannot be parsed (ArgumentParseException)
Generally, the command managers are highly encouraged to make use of CommandManager#handleException, in which case you may override the exception handling by using CommandManager#registerExceptionHandler.
ArgumentParseException is a special case which makes use of the internal caption system. (Nearly) all argument parsers in cloud will throw ParserException on invalid input, in which case you are able to override the exception message by configuring the manager’s CaptionRegistry. By default, cloud uses a FactoryDelegatingCaptionRegistry, which allows you to override the exception handling per caption key. All standard caption keys can be found in StandardCaptionKeys. Some platform adapters have their own caption key classes as well.
The caption keys have JavaDoc that list their replacement variables. The message
registered for the caption will have those variables replaced with values
specific to the parsing instance. {input} is accepted by all parser captions,
and will be replaced with the argument input that caused the exception to be thrown.
final CaptionRegistry<YourSenderType> registry = manager.getCaptionRegistry();
if (registry instanceof FactoryDelegatingCaptionRegistry) {
final FactoryDelegatingCaptionRegistry<YourSenderType> factoryRegistry = (FactoryDelegatingCaptionRegistry<YourSenderType>) registry;
factoryRegistry.registerMessageFactory(
StandardCaptionKeys.ARGUMENT_PARSE_FAILURE_BOOLEAN,
(context, key) -> "'{input}' är inte ett tillåtet booelskt värde"
);
}Cloud has built in support for commands that require confirmation by the sender. It essentially postpones command execution until an additional command has been dispatched.
You first have to create a command confirmation manager:
CommandConfirmationManager<YourSender> confirmationManager = new CommandConfirmationManager<>(
30L, (1)
TimeUnit.SECONDS,
context -> context.getCommandContext().getSender().sendMessage("Confirmation required!"), (2)
sender -> sender.sendMessage("You don't have any pending commands") (3)
);-
The amount (in the selected time unit) before the pending command expires.
-
Action to run when the confirmation manager requires action from the sender.
-
Action to run when the confirmation command is ran by a sender without any pending commands.
The confirmation manager needs to be registered to the command manager. This is as easy as
confirmationManager.registerConfirmationProcessor(manager).
You also need a confirmation command. The recommended way to create this is by doing:
manager.command(
builder.literal("confirm"))
.meta(CommandMeta.DESCRIPTION, "Confirm a pending command")
.handler(confirmationManager.createConfirmationExecutionHandler())
);The important part is that the generated execution handler is used in your command. All commands
that require confirmation needs .meta(CommandConfirmationManager.META_CONFIRMATION_REQUIRED, true)
or a @Confirmation annotation.
Annotations allow for an alternative way of declaring commands in cloud. Instead of constructing commands using builders, commands consist of annotated instance methods. Command arguments will be bound to the method parameters, instead of being retrieved through the command context.
|
Note
|
Since version 1.7.0, cloud-annotations will now also perform
annotation processing, verifying that @CommandMethod annotated methods
have valid signatures, etc.
|
In order to use the annotation parser & annotation processor, you need to
add cloud-annotations as a dependency:
Maven
<dependency>
<groupId>cloud.commandframework</groupId>
<artifactId>cloud-annotations</artifactId>
<version>1.8.4</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>cloud.commandframework</groupId>
<artifactId>cloud-annotations</artifactId>
<version>1.8.4</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>Gradle (Groovy)
implementation 'cloud.commandframework:cloud-annotations:1.8.4'
annotationProcessor 'cloud.commandframework:cloud-annotations:1.8.4'Gradle (Kotlin)
implementation("cloud.commandframework:cloud-annotations:1.8.4")
annotationProcessor("cloud.commandframework:cloud-annotations:1.8.4")In order to work with annotated command methods you need to construct an annotation parser. Fortunately this is very easy:
AnnotationParser<C> annotationParser = new AnnotationParser<>(
manager, (1)
parameters -> SimpleCommandMeta.empty() (2)
);-
Your command manager instance. Commands parsed by the parser will be automatically registered to this manager.
-
A mapping function that maps parser parameters to a command meta instance.
In order to parse commands in a class, simply call annotationParser.parse(yourInstance) where yourInstance is
an instance of the class you wish to parse.
All command methods must be annotated with @CommandMethod. The value of the annotation is the command
structure, using the following syntax:
-
literal:
name -
required argument:
<name> -
optional argument:
[name]
@CommandMethod("command <foo> [bar]") would be equivalent to
builder.literal("command")
.argument(SomeArgument.of("foo"))
.argument(SomeArgument.optional("bar"));@CommandMethod cannot be put on static methods.
In order to map command arguments to command parameters you need to annotate your parameters with
@Argument. The value of the annotation is the name of the argument, and should correspond to
the name used in the command syntax in @CommandMethod.
Ordering of the methods arguments does not matter, instead Cloud will match arguments based on the names supplied to the annotation. This also means that Cloud doesn’t care about the names of the method parameters.
You may also specify a named argument parser, named suggestions provider, default value
and description using the @Argument annotation.
Flags can be used in annotated command methods by decorating the method parameter with
@Flag. Similarly to @Argument, this annotation can be used to specify suggestion
providers, parsers, etc.
If a boolean is annotated with @Flag, the flag will become a presence flag. Otherwise
it will become a value flag, with the parameter type as the type of the flag value.
|
Warning
|
@Flag should NOT be used together with @Argument. Nor should flags be included
in the @CommandMethod syntax string.
|
@CommandDescription can be put on command methods to specify the description of the command.
@CommandPermission can be put on either a command method or a class containing command methods
in order to specify the permission required to use the command.
@ProxiedBy lets you define command proxies on top of command methods. Unlike
the builder method, this annotation creates a proxy of the annotated method.
rather than making the target a proxy.
@ProxiedBy("warpme")
@CommandMethod("game warp me <warp>")
public void warpMe(final @NonNull GamePlayer player, final @NonNull @Argument("warp") Warp warp) {
player.teleport(warp);
}This method will generate two commands: /game warp me <warp> and /warpme, with identical
functionality.
@Regex can be used on command arguments to apply a regex argument
pre-processor.
@Argument("money") @Regex(
value = "(?=.*?\\d)^\\$?(([1-9]\\d{0,2}(,\\d{3})*)|\\d+)?(\\.\\d{1,2})?$",
failureCaption = "regex.money"
) String money@Parser can be used to create argument parsers from instance methods.
The annotation value is the name of the parser. If no name is supplied,
the parser will be registered as the default parser for the method’s
return type.
The signature of the method should be:
@Parser("name")
public ParsedType methodName(CommandContext<YourSender> sender, Queue<String> input) {
}The method can throw exceptions, and the thrown exceptions will automatically be wrapped in an argument parse result.
It is also possibly to specify the suggestions provider that should be used by
default by the generated parser. This is done by specifying a name in the annotation,
such as @Parser(suggestions="yourSuggestionsProvider"). For this to work
the suggestion provider must be registered in the parser registry.
@Suggestions can be used to create suggestion provider from instance methods.
The annotation value is the name of the suggestions provider.
The signature of the method should be:
@Suggestions("name")
public List<String> methodName(CommandContext<YourSender> sender, String input) {
}@Suggestions
generated suggestion providers will be automatically registered to the parser registry.
Command methods may have parameters that are not arguments. A very common example would be the command sender object, or the command object. Command method parameters that aren’t arguments are referred to as injected values.
Injected values can be registered in the ParameterInjectorRegistry, which is available in the command manager. You register a parameter injector for a specific type (class), which is essentially a function mapping the command context and an annotation accessor to an injectable value.
The following is an example from cloud-annotations that injects the raw command input
into string arrays annotated with @RawArgs.
this.getParameterInjectorRegistry().registerInjector(
String[].class, (1)
(context, annotations) -> annotations.annotation(RawArgs.class) == null
? null (2)
: context.getRawInput().toArray(new String[0])
);-
Type to inject.
-
If no value can be injected, it is fine to return
null.
By default, the CommandContext, @RawArgs String[] and the command sender are injectable.
It is possible to register injection services that delegate injections to a custom, or existing
dependency injection system. In version 1.4.0, a GuiceInjectionService was added which can be
used to delegate injection requests to a Guice injector.
All you need is to create an injection service:
public class YourInjectionService<C> implements InjectionService<C> {
@Override
public Object handle(CommandContext<C> context, Class<?> clazz) {
return yourInjectionSystem.injectInstance(clazz);
}
}and then register it to the parameter injection registry using
manager.parameterInjectionRegistry().registerInjectionService(new YourInjectionService<>()).
Builder modifiers allow you to register annotations that can effect how a
@CommandMethod based command is generated.
Builder modifiers are allowed to act on command builders after all arguments have been added to the builder. This allows for modifications to the builder instance before the command is registered to the command manager.
Builder modifiers are registered to the annotation parser:
annotationParser.registerBuilderModifier(
YourAnnotation.class, (1)
(yourAnnotation, builder) -> builder.meta("key", "value") (2)
);-
The modifier receives the instance of the method annotation.
-
The modifier method must necessarily return the modified builder. Command builders immutable, so the modifier should return the instance of the command builder that is returned as the result of any operations on the builder.
Annotation mappers allow you to register custom annotations that will modify the parser parameters for a command argument. This allows you to modify how the command parser is generated for a command based on the annotation.
Annotation mappers are registered to the annotation parser:
annotationParser.registerAnnotationMapper(
YourAnnotation.class,
(yourAnnotation) -> ParserParameters.single(StandardParameters.RANGE_MIN, 10)
);It is possible to register annotations that will bind a given argument pre-processor to the annotated argument.
Pre-processor mappers are registered to the annotation parser:
annotationParser.registerPreprocessorMapper(
YourAnnotation.class,
annotation -> yourPreProcessor
);Bukkit mappings for cloud. If commodore is present on the classpath and the server is running at least version 1.13+, Brigadier mappings will be available.
To setup a Bukkit command manager, simply do:
final BukkitCommandManager<YourSender> bukkitCommandManager = new BukkitCommandManager<>(
yourPlugin,
yourExecutionCoordinator,
forwardMapper, (1)
backwardsMapper (2)
);-
The
forwardMapperis a function that maps your chosen sender type to Bukkit’s CommandSender. -
The
backwardMapperdoes the opposite of theforwardMapper.
|
Note
|
In the case that you don’t need a custom sender type, you can simply use CommandSender as the generic type and pass
Function.identity() as the forward and backward mappers.
|
To use commodore, include it as a dependency:
maven:
<dependency>
<groupId>me.lucko</groupId>
<artifactId>commodore</artifactId>
<version>1.9</version>
</dependency>gradle (groovy)
dependencies {
implementation 'me.lucko:commodore:1.9'
}Then initialise the commodore mappings using:
try {
bukkitCommandManager.registerBrigadier();
} catch (final Exception e) {
plugin.getLogger().warning("Failed to initialize Brigadier support: " + e.getMessage());
}The mappings will then be created and registered automatically whenever a new command is registered.
|
Note
|
The mapper must be initialized before any commands are registered. |
You can check whether the running server supports Brigadier, by using bukkitCommandManager.queryCapability(…). When shading Commodore into your plugin, remember to relocate it’s classes.
cloud-bukkit has plenty of Bukkit-specific parsers. These are easiest found
via the JavaDocs:
Many of these are pre-mapped to serializable Brigadier argument types.
cloud-paper works on all Bukkit derivatives and has graceful fallbacks for cases where Paper specific features are missing.
It is initialized the same way as cloud-bukkit, except PaperCommandManager is used in place of BukkitCommandManager.
When using Paper 1.15+ Brigadier mappings are available even without commodore present.
An example plugin using the cloud-paper API can be found
here.
cloud-paper supports asynchronous completions when running on Paper.
First check if the capability is present, by using paperCommandManager.queryCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)
and then initialize the asynchronous completion listener by using paperCommandManager.registerAsynchronousCompletions().
The cloud-minecraft-extras module contains additional opinionated features for the cloud minecraft platforms, taking advantage of the Kyori adventure api for sending text components to minecraft users. On platforms like Sponge and Velocity which include adventure-api as the standard text and user interface library, minecraft-extras can be used with no additional dependencies. On platforms that do not natively support adventure, like Bukkit and BungeeCord for example, a platform adapter must be used. Learn more about adventure platform adapters at the adventure docs.
Included in minecraft-extras are prebuilt handlers for ArgumentParseException, InvalidCommandSenderException, InvalidSyntaxException, and NoPermissionException. These handlers provide improved aesthetics on exception messages, and allow for custom decoration of the messages, for example with a prefix.
Use these exception handlers by creating a new instance of
MinecraftExceptionHandler,
applying the handlers and decorator you wish to use, and then applying the handlers to the manager.
new MinecraftExceptionHandler<CommandSender>()
.withArgumentParsingHandler()
.withInvalidSenderHandler()
.withInvalidSyntaxHandler()
.withNoPermissionHandler()
.withCommandExecutionHandler()
.withDecorator(message -> myPrefixComponent.append(Component.space()).append(message))
.apply(commandManager, bukkitAudiences::sender); (1)-
bukkitAudiences::senderis simply a method mapping the CommandSender to an Audience
minecraft-extras includes a utility for generating attractive help menus for your minecraft projects. These help menus include hover and click elements, pagination of results, and customization of colors and text.
To use the minecraft-extras help menu, first create an instance of
MinecraftHelp,
like so:
new MinecraftHelp<CommandSender>(
"/myplugin help", (1)
bukkitAudiences::sender, (2)
commandManager
);-
The command which this help menu will be bound to
-
Function mapping your CommandSender type to an adventure Audience
To query help and display the results to a user, use the MinecraftHelp#queryCommands(String, C) method in the handler for
your help command. Continuing with the above example, our help command might look something like this:
manager.command(
manager.commandBuilder("myplugin")
.literal("help")
.argument(StringArgument.optional("query", StringArgument.StringMode.GREEDY))
.handler(context -> {
minecraftHelp.queryCommands(context.getOrDefault("query", ""), context.getSender());
})
);Something developers may find desirable as well is to use a custom suggestion provider for the query argument, and to suggest syntax strings gotten from a blank query to CommandHelpHandler#queryHelp
(see
CommandManager#getCommandHelpHandler
and
CommandHelpHandler#queryHelp
).
Brigadier is Mojang’s command parser and dispatcher for Minecraft: Java Edition. It was released in version 1.13, and is available in Notchian servers and clients released since. The most notable feature of Brigadier is the real-time argument parsing and feedback system, which allows you to see whether your argument is valid, while writing it. This feature works for the primitive Java types, and some serializable types in the Minecraft: Java Edition client.
Cloud has Brigadier hooks for: Velocity 1.10+, Paper 1.15+ (1.13+ using commodore), Spigot 1.13 using commodore, and Sponge v8+. When using Paper/Spigot, this feature is opt-in (refer to the platform documentation for more information).
Cloud will try to hook into the Mojang (net.minecraft.server) serializable
types. In most cases this works when using the platform specific argument types,
such as Location. You can also create your own mappings. See the platform adapter
JavaDoc for more information.
- sender
-
A thing that is able to produce input that gets parsed into commands.
- argument
-
An argument is something that can be parsed from a string.
- required argument
-
A required argument is an argument that must be supplied by the sender.
- optional argument
-
An optional argument is an argument that can be omitted by the sender. It may have a default value.
- literal
-
A fixed string.
- command
-
A command is a chain of arguments combined with a handler that acts on the parsed arguments.
- command tree
-
A structure that contains all recognized commands, and that is used when parsing command sender input.

