Scoreboard arguments
The CommandAPI uses two classes to provide information about a scoreboard:
- The
ScoreHolderArgument
class represents score holder - a player's name or an entity's UUID that has scores in an objective. This is described in more detail on the Minecraft Wiki. - The
ScoreboardSlotArgument
class represents a display slot (sidebar, list or belowName) as well as the team color if the display is the sidebar. This is described in more detail on the Minecraft Wiki.
Score holder argument
The score holder argument can accept either a single entity or a collection of multiple entities. In order to specify which one to use, you must use the ScoreHolderArgument.Single
or ScoreHolderArgument.Multiple
constructor respectively:
new ScoreHolderArgument.Single(nodeName);
new ScoreHolderArgument.Multiple(nodeName);
Depending on which constructor is used, the cast type changes. If you use ScoreHolderArgument.Single
, the argument must be casted to a String
. Otherwise, if you use ScoreHolderArgument.Multiple
, the argument must be casted to a Collection<String>
.
Example - Rewarding players with scoreboard objectives
Say we want to reward all players that fit a certain criteria. We want a command with the following syntax:
/reward <players>
Since we could have multiple players that fit a certain criterion, we want to use ScoreHolderArgument.Multiple
constructor.
To give this example a bit more context, let's say we want to reward all players that have died less than 10 times in the server. To do this, we will use the following command:
/reward @e[type=player,scores={deaths=..9}]
Note how we use ..9
to represent 9 or less deaths (since ranges are inclusive). Also note how we restrict our input to players via the command using type=player
. We can now implement our command:
new CommandAPICommand("reward")
// We want multiple players, so we use ScoreHolderType.MULTIPLE in the constructor
.withArguments(new ScoreHolderArgument.Multiple("players"))
.executes((sender, args) -> {
// Get player names by casting to Collection<String>
@SuppressWarnings("unchecked")
Collection<String> players = (Collection<String>) args.get("players");
for (String playerName : players) {
Bukkit.getPlayer(playerName).getInventory().addItem(new ItemStack(Material.DIAMOND, 3));
}
})
.register();
CommandAPICommand("reward")
// We want multiple players, so we use the ScoreHolderArgument.Multiple constructor
.withArguments(ScoreHolderArgument.Multiple("players"))
.executes(CommandExecutor { _, args ->
// Get player names by casting to Collection<String>
val players = args["players"] as Collection<String>
for (playerName in players) {
Bukkit.getPlayer(playerName)?.inventory!!.addItem(ItemStack(Material.DIAMOND, 3))
}
})
.register()
commandAPICommand("reward") {
// We want multiple players, so we use the scoreHolderArgumentMultiple method
scoreHolderArgumentMultiple("player")
anyExecutor { _, args ->
// Get player names by casting to Collection<String>
val players = args["player"] as Collection<String>
for (playerName in players) {
Bukkit.getPlayer(playerName)?.inventory!!.addItem(ItemStack(Material.DIAMOND, 3))
}
}
}
Developer's Note:
In the example above, we have our user use the
@e[type=player]
entity selector to restrict theCollection<String>
so it only returns player names, which allows us to useBukkit.getPlayer(playerName)
. In practice, we cannot guarantee that such a selector will be used, so we could update the code to accept both entities and players. For example, we can differentiate between players and entities by using theUUID.fromString(String)
method:Collection<String> entitiesAndPlayers = (Collection<String>) args.get(0); for(String str : entitiesAndPlayers) { try { UUID uuid = UUID.fromString(str); // Is a UUID, so it must by an entity Bukkit.getEntity(uuid); } catch(IllegalArgumentException exception) { // Not a UUID, so it must be a player name Bukkit.getPlayer(str); } }
Scoreboard slot argument
The ScoreboardSlotArgument
represents where scoreboard information is displayed. Since the Bukkit scoreboard DisplaySlot
is not able to represent the case where team colors are provided, the CommandAPI uses the ScoreboardSlot
wrapper class as the representation of the ScoreboardSlotArgument
.
ScoreboardSlot
wrapper
The ScoreboardSlot
wrapper class has 3 main methods:
enum ScoreboardSlot {
PLAYER_LIST,
SIDEBAR, // Unused, use the other SIDEBAR_TEAM_### values below
BELOW_NAME,
SIDEBAR_TEAM_BLACK,
SIDEBAR_TEAM_DARK_BLUE,
SIDEBAR_TEAM_DARK_GREEN,
SIDEBAR_TEAM_DARK_AQUA,
SIDEBAR_TEAM_DARK_RED,
SIDEBAR_TEAM_DARK_PURPLE,
SIDEBAR_TEAM_GOLD,
SIDEBAR_TEAM_GRAY,
SIDEBAR_TEAM_DARK_GRAY,
SIDEBAR_TEAM_BLUE,
SIDEBAR_TEAM_GREEN,
SIDEBAR_TEAM_AQUA,
SIDEBAR_TEAM_RED,
SIDEBAR_TEAM_LIGHT_PURPLE,
SIDEBAR_TEAM_YELLOW,
SIDEBAR_TEAM_WHITE;
public DisplaySlot getDisplaySlot();
public ChatColor getTeamColor();
public boolean hasTeamColor();
public NamespacedKey getKey();
}
The getDisplaySlot()
method returns the display slot that was chosen. Sidebar scoreboard colors can be accessed via ScoreboardSlot.SIDEBAR_TEAM_###
. You can also retrieve the color using the getTeamColor()
method.
Example - Clearing objectives in a scoreboard slot
Say we want to clear all objectives in a specific scoreboard slot. In this example, we will use the main server scoreboard, which is accessed using Bukkit.getScoreboardManager.getMainScoreboard()
. We want a command with the following syntax:
/clearobjectives <slot>
We implement this simply by using the ScoreboardSlotArgument
as our argument, and then we can clear the slot using the scoreboard clearSlot(DisplaySlot)
method.
new CommandAPICommand("clearobjectives")
.withArguments(new ScoreboardSlotArgument("slot"))
.executes((sender, args) -> {
Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
DisplaySlot slot = ((ScoreboardSlot) args.get("slot")).getDisplaySlot();
scoreboard.clearSlot(slot);
})
.register();
CommandAPICommand("clearobjectives")
.withArguments(ScoreboardSlotArgument("slot"))
.executes(CommandExecutor { _, args ->
val scoreboard = Bukkit.getScoreboardManager().mainScoreboard
val slot = (args["slot"] as ScoreboardSlot).displaySlot
scoreboard.clearSlot(slot)
})
.register()
commandAPICommand("clearobjectives") {
scoreboardSlotArgument("slot")
anyExecutor { _, args ->
val scoreboard = Bukkit.getScoreboardManager().mainScoreboard
val slot = (args["slot"] as ScoreboardSlot).displaySlot
scoreboard.clearSlot(slot)
}
}