Permissions
Permissions let you control which players are allowed to execute which commands. This is handled using the CommandPermission
class, which has the following uses:
Permission | What it does |
---|---|
CommandPermission.OP | Requires OP to execute the command |
CommandPermission.NONE | Anyone can execute the command |
CommandPermission.fromString("my.permission") | Requires a specific permission node to execute the command |
In addition to the CommandPermission
class, there are two different ways to assign permissions (compared to the simple CommandSender.hasPermission()
method that is provided by Bukkit), by using the withPermission
method for arguments or for commands.
The withPermission
method can take two values:
- A
CommandPermission
, which represents a permission such asOP
orNONE
- A
String
, which will be converted automatically to aCommandPermission
usingCommandPermission.fromString()
Adding permissions to commands
To add a permission to a command, you can use the withPermission(CommandPermission)
or withPermission(String)
method when declaring a command.
Example - /god command with permissions
Say we created a command /god
that sets a player as being invulnerable. Since this is a pretty non-survival command, we want to restrict who can run this command. As such, we want our player to have the permission command.god
in order to run this command. To do this, we simply use the withPermission(CommandPermission)
method from our command builder:
// Register the /god command with the permission node "command.god"
new CommandAPICommand("god")
.withPermission(CommandPermission.fromString("command.god"))
.executesPlayer((player, args) -> {
player.setInvulnerable(true);
player.sendMessage("God mode enabled");
})
.register();
// Register the /god command with the permission node "command.god"
CommandAPICommand("god")
.withPermission(CommandPermission.fromString("command.god"))
.executesPlayer(PlayerCommandExecutor { player, _ ->
player.isInvulnerable = true
})
.register()
As stated above, it is possible to assign a permission using a String instead of using CommandPermission.fromString()
:
// Register the /god command with the permission node "command.god", without creating a CommandPermission
new CommandAPICommand("god")
.withPermission("command.god")
.executesPlayer((player, args) -> {
player.setInvulnerable(true);
player.sendMessage("God mode enabled");
})
.register();
// Register the /god command with the permission node "command.god", without creating a CommandPermission
CommandAPICommand("god")
.withPermission("command.god")
.executesPlayer(PlayerCommandExecutor { player, _ ->
player.isInvulnerable = true
})
.register()
Adding permissions to arguments
For further fine-tuning of permission management, the CommandAPI allows you to add permissions to individual arguments. This prevents the user from executing a command with a specific argument if they do not have a specific permission.
This is done by using the withPermission(CommandPermission)
method at the end of an argument.
If a player does not have the required permission:
- The argument hover text which suggests what the command is will not be shown
- The player will receive an error if they try to type something in for that argument
- Suggestions, such as a list of materials or players, will not be shown
Example - /kill command with argument permissions
For example, say we're registering a command /kill
:
/kill - Kills yourself
/kill <target> - Kills a target player
We first declare the command as normal. Nothing fancy is going on here:
// Register /kill command normally. Since no permissions are applied, anyone can run this command
new CommandAPICommand("kill")
.executesPlayer((player, args) -> {
player.setHealth(0);
})
.register();
// Register /kill command normally. Since no permissions are applied, anyone can run this command
CommandAPICommand("kill")
.executesPlayer(PlayerCommandExecutor { player, _ ->
player.health = 0.0
})
.register()
Now we declare our command with arguments. We use a PlayerArgument
and apply the permission to the argument. After that, we register our command as normal:
// Adds the OP permission to the "target" argument. The sender requires OP to execute /kill <target>
new CommandAPICommand("kill")
.withArguments(new PlayerArgument("target").withPermission(CommandPermission.OP))
.executesPlayer((player, args) -> {
((Player) args.get(0)).setHealth(0);
})
.register();
// Adds the OP permission to the "target" argument. The sender requires OP to execute /kill <target>
CommandAPICommand("kill")
.withArguments(PlayerArgument("target").withPermission(CommandPermission.OP))
.executesPlayer(PlayerCommandExecutor { _, args ->
(args[0] as Player).health = 0.0
})
.register()
Developer's Note:
As you can see, there are multiple ways of applying permissions to commands with arguments. In the
/god
command shown above, the permission was applied to the whole command. In the/kill
command shown above, the permission was applied to the argument.There's not really much difference between the two methods, but I personally would use argument permissions as it has greater control over arguments.
Child-based permissions
Child-based permissions allow you to group permissions together.
We achieve this by laying out our permission groups in the plugin.yml
file which Bukkit registers as valid permissions.
When the CommandAPI checks if our player has a permission, Bukkit considers if they have the child of a permission as well.
This not only keeps permissions easier to manage, it also makes your code cleaner and gives you a nice place to lay out all of your permissions,
detailing what they do and what other permissions inherit them.
Example - /economy command with argument permissions
For example, say we're registering a command /economy
:
/economy - shows your own balance | economy.self
/economy <target> - shows you another players balance | economy.other
/economy give <target> <amount> - gives the target a set amount of money | economy.admin.give
/economy reset <target> <amount> - resets the targets balance | economy.admin.reset
We first declare the command as normal. Nothing fancy is going on here:
// /economy - requires the permission "economy.self" to execute
new CommandAPICommand("economy")
.withPermission("economy.self") // The important part of this example
.executesPlayer((player, args) -> {
// send the executor their own balance here.
})
.register();
// /economy <target> - requires the permission "economy.other" to execute
new CommandAPICommand("economy")
.withPermission("economy.other") // The important part of this example
.withArguments(new PlayerArgument("target"))
.executesPlayer((player, args) -> {
Player target = (Player) args.get(0);
// Send a message to the executor with the the target's balance
player.sendMessage(target.getName() + "'s balance: " + Economy.getBalance(target));
})
.register();
// /economy give <target> <amount> - requires the permission "economy.admin.give" to execute
new CommandAPICommand("economy")
.withPermission("economy.admin.give") // The important part of this example
.withArguments(new PlayerArgument("target"))
.withArguments(new DoubleArgument("amount"))
.executesPlayer((player, args) -> {
Player target = (Player) args.get(0);
double amount = (Double) args.get(1);
// Update the target player's balance
Economy.updateBalance(target, amount);
})
.register();
// /economy reset <target> - requires the permission "economy.admin.reset" to execute
new CommandAPICommand("economy")
.withPermission("economy.admin.reset") // The important part of this example
.withArguments(new PlayerArgument("target"))
.executesPlayer((player, args) -> {
Player target = (Player) args.get(0);
// Reset target balance here
Economy.resetBalance(target);
})
.register();
// /economy - requires the permission "economy.self" to exectue
CommandAPICommand("economy")
.withPermission("economy.self")
.executesPlayer(PlayerCommandExecutor { player, _ ->
// send the executor their own balance here.
})
.register()
// /economy <target> - requires the permission "economy.other" to execute
CommandAPICommand("economy")
.withPermission("economy.other") // The important part of this example
.withArguments(PlayerArgument("target"))
.executesPlayer(PlayerCommandExecutor { player, args ->
val target = args[0] as Player
// send the executor the targets balance here.
})
.register()
// /economy give <target> <amount> - requires the permission "economy.admin.give" to execute
CommandAPICommand("economy")
.withPermission("economy.admin.give") // The important part of this example
.withArguments(PlayerArgument("target"))
.withArguments(DoubleArgument("amount"))
.executesPlayer(PlayerCommandExecutor { player, args ->
val target = args[0] as Player
val amount = args[1] as Double
// update the targets balance here
})
.register()
// /economy reset <target> - requires the permission "economy.admin.reset" to execute
CommandAPICommand("economy")
.withPermission("economy.admin.reset") // The important part of this example
.withArguments(PlayerArgument("target"))
.executesPlayer(PlayerCommandExecutor { player, args ->
val target = args[0] as Player
// reset the targets balance here
})
.register()
In our plugin.yml we can also set up our permissions for example...
permissions:
economy.*:
description: Gives the user full access to the economy commands
children:
economy.other: true
economy.admin.*: true
economy.self:
description: Allows the user to view their own balance
economy.other:
description: Allows the user to another players balance
children:
economy.self: true
economy.admin.*:
description: Gives the user access to all of the admin commands
children:
economy.admin.give: true
economy.admin.reset: true
economy.admin.give:
description: Gives the user access to /economy give <target> <amount>
economy.admin.reset:
description: Gives the user access to /economy reset <target>
This setup of children allows us to give a player less permissions, but have them access more features.
Since economy.*
inherits economy.admin.*
which inherits economy.admin.give
, a player with the permission economy.*
will be able to execute /economy give <target> <amount>
without them directly having the economy.admin.give
permission node.
This also works with economy.other
, if a player has economy.other
they will inherit economy
.