Using the DSL
Defining a simple message command
As a first example and to take a first look at the Kotlin DSL syntax, we will first create a simple command to send messages to a player.
Example - Sending a message to a player using the Kotlin DSL
We want to create a command that lets us send a message to a player. To do this, we want to register a command with the following syntax:
/sendmessageto <player> <msg>
We can then use the following command registration:
commandTree("sendmessageto") {
playerArgument("player") { // Defines a new PlayerArgument("player")
greedyStringArgument("msg") { // Defines a new GreedyStringArgument("msg")
anyExecutor { _, args -> // Command can be executed by anyone and anything (such as entities, the console, etc.)
val player: Player = args["player"] as Player
val message: String = args["msg"] as String
player.sendMessage(message)
}
}
}
}
commandAPICommand("sendmessageto") {
playerArgument("player") // Defines a new PlayerArgument("player")
greedyStringArgument("msg") // Defines a new GreedyStringArgument("msg")
anyExecutor { _, args -> // Command can be executed by anyone and anything (such as entities, the console, etc.)
val player: Player = args["player"] as Player
val message: String = args["msg"] as String
player.sendMessage(message)
}
}
Here you can see some interesting things:
- You do not need to call the
.register()
method when using the DSL - You do not need to initialise any arguments
Executors
The Kotlin DSL also provides executors to execute your command. You've seen the anyExecutor
in the example above.
To find out, which DSL executor corresponds to "normal" executors, you can refer to the table below:
DSL normal executor | DSL resulting executor | DSL normal execution info | DSL resulting execution info | "normal" Executor |
---|---|---|---|---|
anyExecutor() | anyResultingExecutor() | anyExecutionInfo() | anyResultingExecutionInfo | executes() |
playerExecutor() | playerResultingExecutor() | playerExecutionInfo() | playerResultingExecutionInfo() | executesPlayer() |
entityExecutor() | entityResultingExecutor() | entityExecutionInfo() | entityResultingExecutionInfo() | executesEntity() |
consoleExecutor() | consoleResultingExecutor() | consoleExecutionInfo() | consoleResultingExecutionInfo() | executesConsole() |
commandBlockExecutor() | commandBlockResultingExecutor() | commandBlockExecutionInfo() | commandBlockResultingExecutionInfo() | executesCommandBlock() |
proxyExecutor() | proxyResultingExecutor() | proxyExecutionInfo() | proxyResultingExecutionInfo() | executesProxy() |
nativeExecutor() | nativeResultingExecutor() | nativeExecutionInfo() | nativeResultingExecutionInfo() | executesNative() |
remoteConsoleExecutor() | remoteConsoleResultingExecutor() | remoteConsoleExecutionInfo() | remoteConsoleResultingExecutionInfo() | executesRemoteConsole() |
Arguments
The DSL implements almost every argument with a method. You've seen the playerArgument()
and the greedyStringArgument()
method in the example at the top of this page.
The way arguments are implemented is pretty straight forward: It's basically the argument class' name, but as a method. So if you wanted to use a ItemStackArgument
in your command, you would use the itemStackArgument()
method of the DSL.
One thing to note is that the DSL also features every existing constructor. This means if you want to use an IntegerArgument
with a minimum of 0
and a maximum of 10
, you normally would implement it like this:
new IntegerArgument("integer", 0, 10)
However, when using this DSL it is implemented like this:
integerArgument("integer", 0, 10)
Developer's Note:
There are a few arguments not having a method which directly corresponds to their respective argument.
These arguments most likely use a builder pattern and because of that require further implementation by the user.
To use these arguments, the DSL also provides the argument()
method which takes in any argument as a parameter.
Editing arguments
When using the DSL, you might want to modify the behaviour of certain arguments by adding requirements or suggestions to them.
To give you a general idea how you could accomplish that, the sendMessageTo
command is adding a broadcast option which should only be executed by server operators.
commandTree("sendMessageTo") {
playerArgument("player") {
greedyStringArgument("msg") {
playerExecutor { _, args ->
val player: Player = args["player"] as Player
val message: String = args["msg"] as String
player.sendMessage(message)
}
}
}
literalArgument("broadcast") {
withRequirement { sender: CommandSender -> sender.isOp } // Applies the requirement to the broadcast literal argument
/* add more methods here that modify argument behaviour */
greedyStringArgument("msg") {
playerExecutor { _, args ->
val message: String = args["msg"] as String
Bukkit.broadcastMessage(message)
}
}
}
}
commandAPICommand("sendMessageTo") {
playerArgument("player")
greedyStringArgument("msg")
playerExecutor { _, args ->
val player: Player = args["player"] as Player
val message: String = args["msg"] as String
player.sendMessage(message)
}
}
commandAPICommand("sendMessageTo") {
literalArgument("broadcast") {
withRequirement { sender: CommandSender -> sender.isOp } // Applies the requirement to the broadcast literal argument
/* add more methods here that modify argument behaviour */
}
greedyStringArgument("msg")
playerExecutor { _, args ->
val message: String = args["msg"] as String
Bukkit.broadcastMessage(message)
}
}
Notice how you can just add the requirement in a CommandTree by adding it to the argument block where you also define the next arguments and the executor.
However, when modifying the behaviour of an argument in a CommandAPICommand you have to add an extra block where you can implement the additional behaviour.
Adding requirements to commands
Expanding on the previous example where we added a requirement to a single argument, we now also want to add a requirement to a whole command.
This works similar to how argument behaviour is modified in a CommandTree:
commandTree("commandRequirement") {
withRequirement { sender: CommandSender -> sender.isOp}
playerExecutor { player, _ ->
player.sendMessage("This command can only be executed by players who are server operators.")
}
}
commandAPICommand("commandRequirement") {
withRequirement { sender: CommandSender -> sender.isOp}
playerExecutor { player, _ ->
player.sendMessage("This command can only be executed by players who are server operators.")
}
}
More examples
Now, a few more examples are shown to demonstrate the use of this DSL a little more:
Example - Implementing optional arguments with the Kotlin DSL
We want to create a /give
command with the following syntax:
/optionalArgument give <item>
/optionalArgument give <item> <amount>
To declare an argument as optional you need to set the optional
value to true
:
commandTree("optionalArgument") {
literalArgument("give") {
itemStackArgument("item") {
integerArgument("amount", optional = true) {
playerExecutor { player, args ->
// This command will let you execute:
// "/optionalArgument give minecraft:stick"
// "/optionalArgument give minecraft:stick 5"
val itemStack: ItemStack = args["item"] as ItemStack
val amount: Int = args.getOptional("amount").orElse(1) as Int
itemStack.amount = amount
player.inventory.addItem(itemStack)
}
}
}
}
}
commandAPICommand("optionalArgument") {
literalArgument("give")
itemStackArgument("item")
integerArgument("amount", optional = true) // This sets the argument as optional
playerExecutor { player, args ->
// This command will let you execute:
// "/optionalArgument give minecraft:stick"
// "/optionalArgument give minecraft:stick 5"
val itemStack: ItemStack = args["item"] as ItemStack
val amount: Int = args.getOptional("amount").orElse(1) as Int
itemStack.amount = amount
player.inventory.addItem(itemStack)
}
}
Example - Replacing suggestions using the Kotlin DSL
We want to create a command with the following syntax to demonstrate replacing suggestions using the Kotlin DSL:
/replaceSuggestions <strings>
Replacing suggestions works similar to how you would add a requirement to an argument as shown in Editing arguments.
You just have to use the replaceSuggestions
method this time:
commandTree("replaceSuggestions") {
stringArgument("strings") {
replaceSuggestions(ArgumentSuggestions.strings("one", "two", "three")) // Replaces the suggestions for the "strings" StringArgument
playerExecutor { player, args ->
player.sendMessage("You chose option ${args["strings"] as String}!")
}
}
}
commandAPICommand("replaceSuggestions") {
stringArgument("strings") {
replaceSuggestions(ArgumentSuggestions.strings("one", "two", "three")) // Replaces the suggestions for the "strings" StringArgument
}
playerExecutor { player, args ->
player.sendMessage("You chose option ${args["strings"] as String}!")
}
}