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[0] as Player
val message: String = args[1] 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[0] as Player
val message: String = args[1] 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 Executor | "normal" Executor |
---|---|
anyExecutor() | executes() |
playerExecutor() | executesPlayer() |
consoleExecutor() | executesConsole() |
commandBlockExecutor() | executesCommandBlock() |
proxyExecutor() | executesProxy() |
nativeExecutor() | executesNative() |
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 two arguments not having a method which directly corresponds to their respective argument.
These two arguments are the CustomArgument
and the ListArgument
as they require further implementation by the user.
To use these arguments, the DSL also provides the argument()
method. This takes in any argument as parameter which is why you can also use this method when you need to replace suggestions of any argument or do something else with arguments.
Requirements
When using the DSL, you might want to use requirements to restrict the use of certain arguments to specific players. However, implementing requirements with this DSL works a bit different from when using the "normal" command system.
Below, 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[0] as Player
val message: String = args[1] as String
player.sendMessage(message)
}
}
}
requirement(of("broadcast"), { sender: CommandSender -> sender.isOp }) { // Define a new LiteralArgument("broadcast") that requires the CommandSender to be a player who is a server operator
greedyStringArgument("msg") {
playerExecutor { _, args ->
val message: String = args[0] as String
Bukkit.broadcastMessage(message)
}
}
}
}
commandAPICommand("sendMessageTo") {
playerArgument("player")
greedyStringArgument("msg")
playerExecutor { _, args ->
val player: Player = args[0] as Player
val message: String = args[1] as String
player.sendMessage(message)
}
}
commandAPICommand("sendMessageTo") {
requirement(of("broadcast"), { sender: CommandSender -> sender.isOp }) // Define a new LiteralArgument("broadcast") that requires the CommandSender to be a player who is a server operator
greedyStringArgument("msg")
playerExecutor { _, args ->
val message: String = args[0] as String
Bukkit.broadcastMessage(message)
}
}
Adding requirements to commands
Previously, we've taken a look at how to restrict arguments to certain players by using requirements.
You can also restrict the use of a whole command by using requirements:
commandTree("commandRequirement", {sender: CommandSender -> sender.isOp}) {
playerExecutor { player, _ ->
player.sendMessage("This command can only be executed by players who are server operators.")
}
}
commandAPICommand("commandRequirement", {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>
As with the Java method of registering commands, with a CommandTree you can declare the command tree to include the additional argument <amount>
, and with the CommandAPICommand you have to declare the two branching commands separately:
commandTree("optionalArgument") {
literalArgument("give") {
itemStackArgument("item") {
playerExecutor { player, args -> // This will let you execute "/optionalArgument give minecraft:stick"
val itemStack: ItemStack = args[0] as ItemStack
player.inventory.addItem(itemStack)
}
integerArgument("amount") {
playerExecutor { player, args -> // This will let you execute "/optionalArgument give minecraft:stick 5"
val itemStack: ItemStack = args[0] as ItemStack
val amount: Int = args[1] as Int
itemStack.amount = amount
player.inventory.addItem(itemStack)
}
}
}
}
}
commandAPICommand("optionalArgument") {
literalArgument("give")
itemStackArgument("item")
playerExecutor { player, args -> // This will let you execute "/optionalArgument give minecraft:stick"
val itemStack: ItemStack = args[0] as ItemStack
player.inventory.addItem(itemStack)
}
}
commandAPICommand("optionalArgument") {
literalArgument("give")
itemStackArgument("item")
integerArgument("amount")
playerExecutor { player, args -> // This will let you execute "/optionalArgument give minecraft:stick 5"
val itemStack: ItemStack = args[0] as ItemStack
val amount: Int = args[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>
We make use of the argument()
method to provide an argument implementation and use the replaceSuggestions
method as normal to provide suggestions for the argument:
commandTree("replaceSuggestions") {
argument(StringArgument("strings").replaceSuggestions(ArgumentSuggestions.strings("one", "two", "three"))) { // Implement an argument that has suggestions
playerExecutor { player, args ->
player.sendMessage("You chose option ${args[0] as String}!")
}
}
}
commandAPICommand("replaceSuggestions") {
argument(StringArgument("strings").replaceSuggestions(ArgumentSuggestions.strings("one", "two", "three"))) // Implement an argument that has suggestions
playerExecutor { player, args ->
player.sendMessage("You chose option ${args[0] as String}!")
}
}