Command

Picocli Command

Reactant integrated Picocli into PicocliCommandService. You can create a command class and extend ReactantCommand, then you can declare your own command just like a normal Picocli command. For more usage of Picocli, please refer to Picocli Documentation

Also, you can add internal keyword to the command class to avoid exposing it.

@CommandLine.Command(
name = "poke",
mixinStandardHelpOptions = true, // come with default --help option
description = ["Poke someone"]
)
internal class PokeCommand : ReactantCommand() {
@Option(names = {"-a", "--anonymous"}, description = "Hide your name from the poke message")
var anonymous = false;
@CommandLine.Parameters(arity = "1", paramLabel = "PLAYER_NAME",
description = ["The player you want to poke"])
var playerName: String = "";
override fun run() {
Bukkit.getPlayer(playerName)
?.sendMessage("${ if (anonymous) "Someone" else sender.name } poke you!")
?: stderr.out("Player not found: ${playerName}")
}
}

PicocliCommandService

Now, we can register the command we just created with the PicocliCommandService.

import dev.reactant.reactant.extra.command.PicocliCommandService
@Component
class MyCommandRegister(
private val commandService: PicocliCommandService
) : LifeCycleHook {
override fun onEnable(){
commandService {
command({ PokeCommand() })
}
}
}

Injectables for Command

Since only Component can get the injections of objects, if your command needs to inject other objects, you can inject in the Command Register and pass it through the command class constructor.

@Component
class MyCommandRegister(
private val commandService: PicocliCommandService,
private val someExtraInjectables: SomeObject
) : LifeCycleHook {
override fun onEnable(){
commandService {
command({ PokeCommand(someExtraInjectables) })
}
}
}

Nested Command

Sometimes we might need to create multi-level command, following is an example of how it works.

@CommandLine.Command(name = "main", mixinStandardHelpOptions = true)
internal class MainCommand : ReactantCommand() {
override fun execute() { showUsage() } // show usage when main command being called
}
@CommandLine.Command(name = "sub1", mixinStandardHelpOptions = true)
internal class Sub1Command : ReactantCommand() {
override fun execute() { MyFirstPlugin.log.info("Testing: sub 1") }
}
@CommandLine.Command(name = "sub2", mixinStandardHelpOptions = true)
internal class Sub2Command : ReactantCommand() {
override fun execute() { MyFirstPlugin.log.info("Testing: sub 2") }
}

Then register them all commands in your register component with the correct nesting structure.

override fun onEnable(){
commandService {
command({ MainCommand() }){
command({ Sub1Command() })
command({ Sub2Command() })
}
}
}

You should be able to call the command with /main sub1 and /main sub2 after registered the command.

Permissions

Permission Node

Reactant provided an opinionated tool to manage the permission tree easily, which aim to avoid the typo of permissions and allow autocomplete permission in your editor.

object MyPluginPermissions : PermissionRoot("myplugin") {
object Food : S(prefix) {
object Eat : S(prefix)
object Throw : S(prefix)
object Cook : S(prefix) {
object Salad : S(prefix)
object Hamburger : S(prefix)
object Dessert : S(prefix)
}
}
object Weapon : S(prefix) {
object Throw : S(prefix)
object Attack : S(prefix)
object Create : S(prefix)
}
}

The permission nodes implemented the toString(), and return prefix + simpleClassName.toLowerCase(). Following is an example of using the permission nodes:

// myplugin.food.cook.salad
MyFirstPlugin.log.info(MyPluginPermissions.Food.Cook.Salad)

requirePermission

You can call requirePermission(permission) with PermissionNode or String inside the ReactantCommand, it will throw an exception to stop the execution and warn the command sender.

@CommandLine.Command(name = "eat")
internal class EatFoodCommand : ReactantCommand() {
override fun execute() {
// same as requirePermission("myplugin.food.eat")
requirePermission(MyPluginPermissions.Food.Eat)
// eat the food here...
}
}

requireSenderIs

The behaviour of requireSenderIs(senderClass...) is same as requirePermission, which will also throw an exception. It can be use as a sender type guard.

@CommandLine.Command(name = "eat")
internal class EatFoodCommand : ReactantCommand() {
override fun execute() {
requireSenderIs(Player::class)
requirePermission(MyPluginPermissions.Food.Eat)
// eat the food here...
}
}

You can also use the shorthands: requireSenderIsPlayer(), requireSenderIsConsole()

Custom message