String argument suggestions
Suggestions with a String Array
The first method, replaceSuggestions(ArgumentSuggestions suggestions)
, allows you to replace the suggestions normally associated with that argument. This can be replaced with an array of strings by using the strings(String... suggestions)
method.
Example - Teleport to worlds by replacing suggestions
Say we're creating a plugin with the ability to teleport to different warps on the server. If we were to retrieve a list of warps, we would be able to replace the suggestions of a typical StringArgument
to teleport to that warp. Let's create a command with the following syntax:
/warp <warp>
We then implement our warp teleporting command using replaceSuggestions()
on the StringArgument
to provide a list of warps to teleport to:
List<Argument<?>> arguments = new ArrayList<>();
arguments.add(new StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(
"northland", "eastland", "southland", "westland"
)));
new CommandAPICommand("warp")
.withArguments(arguments)
.executesPlayer((player, args) -> {
String warp = (String) args.get("world");
player.teleport(warps.get(warp)); // Look up the warp in a map, for example
})
.register();
val arguments = listOf<Argument<*>>(
StringArgument("world").replaceSuggestions(ArgumentSuggestions.strings(
"northland", "eastland", "southland", "westland"
))
)
CommandAPICommand("warp")
.withArguments(arguments)
.executesPlayer(PlayerCommandExecutor { player, args ->
val warp = args["world"] as String
player.teleport(warps[warp]!!) // Look up the warp in a map, for example
})
.register()
The second method, includeSuggestions(ArgumentSuggestions suggestions)
, allows you to include additional suggestions in combination with the list of existing suggestions for a command.
Suggestions depending on a command sender
The strings(Function<SuggestionInfo, String[]> suggestions)
method in ArgumentSuggestions
allows you to modify suggestions normally associated with that argument with an array of strings that are evaluated dynamically using information about the command sender, using the sender()
method.
Example - Friend list by replacing suggestions
Say you have a plugin which has a "friend list" for players. If you want to teleport to a friend in that list, you could use a PlayerArgument
, which has the list of suggestions replaced with the list of friends that that player has. Since the list of friends depends on the sender, we can use the function to determine what our suggestions should be. Let's use the following command to teleport to a friend from our friend list:
/friendtp <friend>
Let's say we have a simple class to get the friends of a command sender:
public class Friends {
static Map<UUID, String[]> friendsMap = new HashMap<>();
public static String[] getFriends(CommandSender sender) {
if (sender instanceof Player player) {
// Look up friends in a database or file
return friendsMap.get(player.getUniqueId());
} else {
return new String[0];
}
}
}
class Friends {
companion object {
val friends = mutableMapOf<UUID, Array<String>>()
fun getFriends(sender: CommandSender): Array<String> {
if (sender is Player) {
// Look up friends in a database or file
return friends[sender.uniqueId]!!
} else {
return arrayOf<String>()
}
}
}
}
We can then use this to generate our suggested list of friends:
List<Argument<?>> arguments = new ArrayList<>();
arguments.add(new PlayerArgument("friend").replaceSuggestions(ArgumentSuggestions.strings(info ->
Friends.getFriends(info.sender())
)));
new CommandAPICommand("friendtp")
.withArguments(arguments)
.executesPlayer((player, args) -> {
Player target = (Player) args.get("friend");
player.teleport(target);
})
.register();
val arguments = listOf<Argument<*>>(
PlayerArgument("friend").replaceSuggestions(ArgumentSuggestions.strings { info ->
Friends.getFriends(info.sender())
} )
)
CommandAPICommand("friendtp")
.withArguments(arguments)
.executesPlayer(PlayerCommandExecutor { player, args ->
val target = args["friend"] as Player
player.teleport(target)
})
.register()
Suggestions depending on previous arguments
The strings(Function<SuggestionInfo, String[]>)
method also has the capability to suggest arguments based on the values of previously inputted arguments, using the previousArgs()
method in SuggestionInfo
. This previousArgs()
method returns a list of previous arguments which are parsed exactly like any regular CommandAPI command argument.
Note:
The ability to use previously declared arguments does not work via redirects. This means that any command that comes before it that leads into a command that uses suggestions depending on previous arguments will not work. For example, if we had a command /mycommand <arg1> <arg2> <arg3>
and ran it as normal, it would work as normal:
/mycommand arg1 arg2 arg3
However, if we redirect execution via the /execute
command to have the following:
/execute run mycommand <suggestions>
This won't work, because we make use of a redirect:
\(\texttt{/execute run} \xrightarrow{redirect} \texttt{mycommand arg1 arg2 arg3}\)
It is not possible to access the CommandArguments
of previously declared arguments. If a command occurs via a redirect, the CommandArguments
of previously declared arguments will be null.
Example - Sending a message to a nearby player
Say we wanted to create a command that lets you send a message to a specific player in a given radius. (This is a bit of a contrived example, but let's roll with it). To do this, we'll use the following command syntax:
/localmsg <radius> <target> <message>
When run, this command will send a message to a target player within the provided radius. To help identify which players are within a radius, we can replace the suggestions on the <target>
argument to include a list of players within the provided radius. We do this with the following code:
// Declare our arguments as normal
List<Argument<?>> commandArgs = new ArrayList<>();
commandArgs.add(new IntegerArgument("radius"));
// Replace the suggestions for the PlayerArgument.
// info.sender() refers to the command sender that is running this command
// info.previousArgs() refers to the Object[] of previously declared arguments (in this case, the IntegerArgument radius)
commandArgs.add(new PlayerArgument("target").replaceSuggestions(ArgumentSuggestions.strings(info -> {
// Cast the first argument (radius, which is an IntegerArgument) to get its value
int radius = (int) info.previousArgs().get("radius");
// Get nearby entities within the provided radius
Player player = (Player) info.sender();
Collection<Entity> entities = player.getWorld().getNearbyEntities(player.getLocation(), radius, radius, radius);
// Get player names within that radius
return entities.stream()
.filter(e -> e.getType() == EntityType.PLAYER)
.map(Entity::getName)
.toArray(String[]::new);
})));
commandArgs.add(new GreedyStringArgument("message"));
// Declare our command as normal
new CommandAPICommand("localmsg")
.withArguments(commandArgs)
.executesPlayer((player, args) -> {
Player target = (Player) args.get("target");
String message = (String) args.get("message");
target.sendMessage(message);
})
.register();
// Declare our arguments as normal
val commandArgs = mutableListOf<Argument<*>>()
commandArgs.add(IntegerArgument("radius"))
// Replace the suggestions for the PlayerArgument.
// info.sender() refers to the command sender that is running this command
// info.previousArgs() refers to the Object[] of previously declared arguments (in this case, the IntegerArgument radius)
commandArgs.add(PlayerArgument("target").replaceSuggestions(ArgumentSuggestions.strings { info: SuggestionInfo<CommandSender> ->
// Cast the first argument (radius, which is an IntegerArgument) to get its value
val radius = (info.previousArgs()["radius"] as Int).toDouble()
// Get nearby entities within the provided radius
val player = info.sender() as Player
val entities = player.world.getNearbyEntities(player.location, radius, radius, radius)
// Get player names within that radius
entities
.filter { it.type == EntityType.PLAYER }
.map { it.name }
.toTypedArray()
}))
commandArgs.add(GreedyStringArgument("message"))
// Declare our command as normal
CommandAPICommand("localmsg")
.withArguments(*commandArgs.toTypedArray())
.executesPlayer(PlayerCommandExecutor { _, args ->
val target = args["target"] as Player
val message = args["message"] as String
target.sendMessage(message)
})
.register()
As shown in this code, we use the previousArgs()
method access the previously declared arguments. In this example, info.previousArgs()
will be { int }
, where this int
refers to the radius. Note how this object array only has the previously declared arguments (and not for example { int, Player, String }
).