Map arguments
A MapArgument
can be used to provide a map of values. This argument uses an underlying GreedyStringArgument
which means that this argument can only be used at the end of the argument list.
It returns a LinkedHashMap
object.
The MapArgumentBuilder
Similar to the ListArgument
, this argument also uses a builder class to construct it.
\begin{align} &\quad\texttt{Create a MapArgumentBuilder and possibly provide the delimiter} \\ \rightarrow&\quad\texttt{Provide the mapper from a string to an object of the provided key type} \\ \rightarrow&\quad\texttt{Provide the mapper from a string to an object of the provided value type} \\ \rightarrow&\quad\texttt{Provide the list with keys to pull suggestions from} \\ \rightarrow&\quad\texttt{Provide the list with values to pull suggestions from} \\ \rightarrow&\quad\texttt{Build the MapArgument} \end{align}
Building a MapArgument
To start building the argument, you first have to construct a MapArgumentBuilder
parameterized over the types the key and the value are supposed to have.
If you wanted to construct a MapArgument
that returns a LinkedHashMap<String, Integer>
you would construct the MapArgumentBuilder
like this:
new MapArgumentBuilder<String, Integer>
The MapArgumentBuilder
has two possible constructors:
public MapArgumentBuilder<K, V>(String nodeName);
public MapArgumentBuilder<K, V>(String nodeName, char delimiter);
nodeName
: This parameter determines the node name of theMapArgument
delimiter
: This parameter determines the delimiter. This separates a key from a value. When not provided, it defaults to a colon (:
)
$$\downarrow$$
Providing mapper functions
The mapper functions are used to parse the argument when entered. Because a GreedyStringArgument
returns a String
, we need a way to convert a String
into an object specified by the type parameters.
When providing mappers, you first need to provide the key mapper:
public MapArgumentBuilder withKeyMapper(Function<String, K>);
You then have to provide the value mapper:
public MapArgumentBuilder withValueMapper(Function<String, V>);
$$\downarrow$$
Providing suggestions
When providing suggestions you have the choice whether players are allowed to enter any key/value pair or only key/value pairs specified by the MapArgument
.
To accomplish this the MapArgumentBuilder
provides different methods.
Similar to the mappers, you first have to provide the key suggestions:
public MapArgumentBuilder withKeyList(List<String> keyList);
public MapArgumentBuilder withoutKeyList();
Next, you have to provide the value suggestions. In addition to the two possibilities presented for the key suggestions, here you also have the possibility to define whether a value can be written multiple times.
public MapArgumentBuilder withValueList(List<String> valueList);
public MapArgumentBuilder withValueList(List<String> valueList, boolean allowValueDuplicates);
public MapArgumentBuilder withoutValueList();
If you choose to allow a value to be written multiple times you have to set allowValueDuplicates
to true
. By default, it is set to false
and
does not allow values to be written multiple times.
$$\downarrow$$
Building the MapArgument
To finish building the MapArgument
, you have to call the build()
method. This will return a new MapArgument
object.
public MapArgument<K, V> build();
Examples
Example - /sendmessage command
Let's say we want to create a command that we can execute to send multiple players messages without typing the command more than once. For that, we create a command with the following syntax:
/sendmessage <message>
To implement that, we create a command that uses a MapArgument
and use Player
objects as keys and String
objects as values:
new CommandAPICommand("sendmessage")
// Parameter 'delimiter' is missing, delimiter will be a colon
.withArguments(new MapArgumentBuilder<Player, String>("message")
// Providing a key mapper to convert a String into a Player
.withKeyMapper(Bukkit::getPlayer)
// Providing a value mapper to leave the message how it was sent
.withValueMapper(s -> s)
// Providing a list of player names to be used as keys
.withKeyList(Bukkit.getOnlinePlayers().stream().map(Player::getName).toList())
// Don't provide a list of values so messages can be chosen without restrictions
.withoutValueList()
// Build the MapArgument
.build()
)
.executesPlayer((player, args) -> {
// The MapArgument returns a LinkedHashMap
LinkedHashMap<Player, String> map = (LinkedHashMap<Player, String>) args.get("message");
// Sending the messages to the players
for (Player messageRecipient : map.keySet()) {
messageRecipient.sendMessage(map.get(messageRecipient));
}
})
.register();
CommandAPICommand("sendmessage")
// Parameter 'delimiter' is missing, delimiter will be a colon
.withArguments(MapArgumentBuilder<Player, String>("message")
// Providing a key mapper to convert a String into a Player
.withKeyMapper { s: String -> Bukkit.getPlayer(s) }
// Providing a value mapper to leave the message how it was sent
.withValueMapper { s: String -> s }
// Providing a list of player names to be used as keys
.withKeyList(Bukkit.getOnlinePlayers().map { player: Player -> player.name }.toList())
// Don't provide a list of values so messages can be chosen without restrictions
.withoutValueList()
// Build the MapArgument
.build()
)
.executesPlayer(PlayerCommandExecutor { _, args ->
// The MapArgument returns a LinkedHashMap
val map: LinkedHashMap<Player, String> = args["message"] as LinkedHashMap<Player, String>
// Sending the messages to the players
for (messageRecipient in map.keys) {
messageRecipient.sendMessage(map[messageRecipient]!!)
}
})
.register()
commandAPICommand("sendmessage") {
// Parameter 'delimiter' is missing, delimiter will be a colon
argument(MapArgumentBuilder<Player, String>("message")
// Providing a key mapper to convert a String into a Player
.withKeyMapper { s: String -> Bukkit.getPlayer(s) }
// Providing a value mapper to leave the message how it was sent
.withValueMapper { s: String -> s }
// Providing a list of player names to be used as keys
.withKeyList(Bukkit.getOnlinePlayers().map { player: Player -> player.name }.toList())
// Don't provide a list of values so messages can be chosen without restrictions
.withoutValueList()
// Build the MapArgument
.build()
)
playerExecutor { _, args ->
// The MapArgument returns a LinkedHashMap
val map: LinkedHashMap<Player, String> = args["message"] as LinkedHashMap<Player, String>
// Sending the messages to the players
for (messageRecipient in map.keys) {
messageRecipient.sendMessage(map[messageRecipient]!!)
}
}
}
Developer's Note:
The MapArgument
is very strict and doesn't have room for any errors. The syntax for key/value pairs of the MapArgument
is as following:
<key><delimiter>"<value>"
Let's say you are on a server with two players, Player1
and Player2
. We want to send both of them the message Hello, <playerName>!
To do that, we use the previously declared sendmessage
command like this:
/sendmessage Player1:"Hello, Player1!" Player2:"Hello, Player2!"
Here we use a colon as the delimiter because we didn't specify a delimiter in the MapArgumentBuilder
constructor.