-
Notifications
You must be signed in to change notification settings - Fork 2
Commands
This interface is the base for all commands. It defines some common methods providing base functionality:
-
getChildren
- Returns the direct children of the command
-
getAllChildren
- Returns all children of the command, probably recursive.
-
execute
- Executes the command
-
tabComplete
- Tab-completes the command
-
find
- Find a command
-
hasPermission
- Checks if a sender has the permission to execute it
-
getAcceptedCommandSenders
- Gets the accepted command senders
-
acceptsCommandSender
- Check if it accepts the passed command sender
-
isYourKeyword
- Checks if the String is your keyword
-
getUsage
- Returns the usage of the command
-
getDescription
- Returns the description of the command
-
getKeyword
- Returns the commands keyword. Used in tab completion and the default help command
-
getName
- Returns the name of the command Because this is unpleaseant to deal with, there is a skeleton implementation called
It implements most of the methods above and provides some new ones.
Permission, Collection<CommandSenderType> allowedSenders
Permission, CommandSenderType... allowedSenders
String permission, CommandSenderType... allowedSenders
-
executeGeneral
(protected)- This will be called when the console executes the command or or when ALL is set as the allowed CommandSenders
-
executePlayer
(protected)- This will be called when a player executes the command
-
executeBlock
(procteted)- This will be called when a player executes the command
This class is a CommandNode using a MessageProvider to obtain usage, description, keyword, keyword pattern and name.
It is what you will most likely extend.
Permission, String keywordKey, String keywordRegExKey, String usageKey, String descriptionKey, String nameKey, MessageProvider messageProvider, CommandSenderType... acceptedSenders
Permission permission, String baseKey, MessageProvider messageProvider, CommandSenderType... acceptedSenders
The first one is pretty straightforward. You pass the permission, the keys in the language file to all things, the MessageProvider
to get it from and the allowed CommandSenderType
s.
The second one is a bit trickier, but shorter. It also takes a permission, the MessageProvider
and the CommandSenderType
s, but only one String, the baseKey
. This will be used to construct the others. The format is as follows:
baseKey.keyword
baseKey.keyword.pattern
baseKey.usage
baseKey.description
baseKey.name
Nothing really new there. A method to get the MessagesProvider used, but that is it.
This is the main access point to all commands. It organizes them to a tree and provides a transparent root. This means you can add all your commands to it, and they will appear as top levelcommands (accessable with /).
A default constructor is provided. Nothing else is needed at the moment.
This is a tad more existing. It has the following methods:
-
getAllCommands
- Returns all registered commands
-
addTopLevelChild
- Adds a top level child
-
attachHelp
- Adds the default help node to a command node
-
addTopLevelChildAndRegister
- Adds a top level child AND registers it at runtime, to make it act like a normal command, which was defined in the
plugin.yml
.
- Adds a top level child AND registers it at runtime, to make it act like a normal command, which was defined in the
-
removeTopLevelChild
- Removes a top level child
-
find
- Finds a command based on the users query
-
findAndTabComplete
- Finds a command and tabcompletes it
Unless you want to make your ownCommandExecutor
, you won't need any of the methods exceptaddTopLevelChild
,addTopLevelChildAndRegister
,attachHelp
and mayberemoveTopLevelChild
.
- Finds a command and tabcompletes it
This is an implementation of the CommandExecutor
interface, which uses the CommandTree
. It is what you will probably end up setting as the executors for your top level commands.
-
CommandTree tree, MessageProvider language
- The CommandTree to get the commands from and the
MessageProvider
to use for it's messages.
The messages it uses are:
- The CommandTree to get the commands from and the
-
command.executor.not.found
- If a command was not found
-
No permission: command.executor.no.permission
- If the sender doesn't have sufficient permission
-
Error: command.executor.error.invoking
- If an error occured invoking the command. Typically not the fault of the user
-
Send usage: command.executor.usage
- The usage of the command. Called when the command returns
SEND_USAGE
- The usage of the command. Called when the command returns
Well, there are none.
Same as the CommandExecutor.
-
CommandTree tree
- The CommandTree to use
There are none either.
This is the default help command. It can be attached by either creating it and adding it as a child or by using the CommandTree#attachHelp
method.
Permission permission, MessageProvider messageProvider, CommandTree commandTree
Nothing for you to worry about :)
Needs to be written. Maybe some images too.
package me.ialistannen.bukkittoyaround;
import static com.perceivedev.perceivecore.util.TextUtils.colorize;
import java.util.Collections;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permission;
import com.perceivedev.perceivecore.command.CommandResult;
import com.perceivedev.perceivecore.command.CommandSenderType;
import com.perceivedev.perceivecore.command.TranslatedCommandNode;
import com.perceivedev.perceivecore.util.ArrayUtils;
/**
* A simple me command
*/
public class MeCommand extends TranslatedCommandNode {
public MeCommand() {
// We want the permission "me", the language base key is "command.me" (important for the language later)
// We pass a Language and specify that every sender type (Console, Block, Player) can use it
super(new Permission("me"), "command.me", BukkitToyAround.getInstance().getLanguage(), CommandSenderType.ALL);
}
@Override
public List<String> tabComplete(CommandSender sender, List<String> wholeChat, int relativeIndex) {
// tab completion makes no real sense here, so just disable it by returning an empty list everytime
return Collections.emptyList();
}
// We passed CommandSenderType.ALL in the constructor, so overwrite executeGeneral.
@Override
protected CommandResult executeGeneral(CommandSender sender, String... args) {
// if the user just entered "/me", send the usage again
if(args.length < 1) {
return CommandResult.SEND_USAGE;
}
// Join the array to a string
String concat = ArrayUtils.concat(args, " ");
// Append the player name at the front
concat = "&8[&7" +sender.getName() + "&8]&r " + concat;
// send it to all players
Bukkit.broadcastMessage(colorize(concat));
// Equivalent of returning true, just saying the command was successfully invoked.
return CommandResult.SUCCESSFULLY_INVOKED;
}
}
(Assumes you have a Language already set up)
CommandTree tree = new CommandTree();
MeCommand command = new MeCommand();
// the tree, executor and completer can be used for ALL commands
CommandExecutor executor = new DefaultCommandExecutor(tree, getLanguage());
TabCompleter tabCompleter = new DefaultTabCompleter(tree);
// adding the "/me" command to the CommandTree, to make it availlable to the users
// Register it too, with the alias "meToo". This means the user can enter "/me" and "/meToo"
tree.addTopLevelChildAndRegister(command, executor, tabCompleter, this, "meToo");
// lets just add an help command. Makes no sense here, but it is an example.
tree.attachHelp(command, "me.help", getLanguage());
command.me.name= &6Me
command.me.keyword= me
# The pattern is a RegEx matching all possible keywords.
command.me.keyword.pattern= me|metoo
command.me.description= &7You do
command.me.usage= &c/me &6<message>
# only needed as we added the help command
command.help.name= &6Help
command.help.keyword= help
command.help.keyword.pattern= help
command.help.description= &7Helps you
command.help.usage= &c/me help
This is another cool feature. It basically allows the arguments to the execute method to be dynamic and exactly what you need. Consider a small teleport plugin. The obvious choice would be something like this:
@Override
protected CommandResult executePlayer(Player player, String... args) {
if (args.length < 1) {
return CommandResult.SEND_USAGE;
}
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
player.sendMessage(getMessageProvider().tr("command.teleport.target.offline", (Object) args[0]));
return CommandResult.SUCCESSFULLY_INVOKED;
}
player.teleport(target);
player.sendMessage(getMessageProvider().tr("command.teleport.teleported", (Object) target.getName()));
return CommandResult.SUCCESSFULLY_INVOKED;
}
But we can do better! Look at this:
@ConvertedParams(targetClasses = {Player.class})
public CommandResult onTeleportRequest(Player player, Player target, String[] args, String[] wholeChat) {
if(target == null) {
player.sendMessage(getMessageProvider().tr("command.teleport.target.offline", (Object) wholeChat[0]));
return CommandResult.SUCCESSFULLY_INVOKED;
}
player.teleport(target);
player.sendMessage(getMessageProvider().tr("command.teleport.teleported", (Object) target.getName()));
return CommandResult.SUCCESSFULLY_INVOKED;
}
See what has changed?
- We could name the method how we want, as long as it is public, the first parameter is a CommandSender and the last a String array. AND the
@ConvertedParams
annotation has to be present. It tells the system what to do. In this case, it will convert the player name the user entered to a Player object, passing null if not found. - We no longer need to check the size of
args
, as the command system will do it for us.
The last parameter, wholeChat
contains the whole chat the user entered (it is optional) and args
only the arguments AFTER all parameters were converted.
If the user entered "/tp hello", args
would contain only "hello", wholeChat
" hallo".
This technique uses an ArgumentMapper
. There is one registered for Player
by default, which is why this works. If you want others, you can implement your own too! Here is the sample implementation for Player:
// ==== PLAYER ====
ArgumentMappers.addMapper(new ArgumentMapper<Player>() {
@Override
public Class<Player> getTargetClass() {
return Player.class;
}
@Override
public Optional<? extends Player> map(Queue<String> strings) {
if (strings.isEmpty()) {
return Optional.empty();
}
String name = strings.poll();
return Bukkit.getOnlinePlayers().stream()
.filter(player -> player.getName().equals(name) || player.getDisplayName().equals(name))
.findFirst();
}
});
As you can see you only need two methods. The first returns the class that is being converted, the second converts. You can just poll strings from the queue as needed, what you remove here will NOT be added to args
later and only appear in wholeChat
. You can write your own, more complex wrappers to remove the need to check in every command, if it is really a valid parameter. A !=
check will suffice instead.