Minestom/src/main/java/net/minestom/server/command/CommandManager.java

207 lines
7.5 KiB
Java
Raw Normal View History

2020-04-24 03:25:58 +02:00
package net.minestom.server.command;
2020-04-05 10:15:21 +02:00
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandDispatcher;
import net.minestom.server.command.builder.CommandResult;
2022-07-25 19:34:40 +02:00
import net.minestom.server.command.builder.ParsedCommand;
2020-04-24 03:25:58 +02:00
import net.minestom.server.entity.Player;
2021-06-04 03:48:51 +02:00
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.PlayerCommandEvent;
2020-04-24 03:25:58 +02:00
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
2020-11-02 04:13:43 +01:00
import net.minestom.server.utils.callback.CommandCallback;
2020-05-23 04:20:01 +02:00
import net.minestom.server.utils.validate.Check;
2020-10-24 16:58:27 +02:00
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
2020-04-05 10:15:21 +02:00
2022-07-25 19:34:40 +02:00
import java.util.*;
2020-10-11 18:35:32 +02:00
/**
2021-03-18 02:47:10 +01:00
* Manager used to register {@link Command commands}.
2020-10-11 18:35:32 +02:00
* <p>
* It is also possible to simulate a command using {@link #execute(CommandSender, String)}.
*/
public final class CommandManager {
2020-04-05 10:15:21 +02:00
public static final String COMMAND_PREFIX = "/";
2021-02-08 03:42:35 +01:00
private final ServerSender serverSender = new ServerSender();
2020-10-22 12:55:53 +02:00
private final ConsoleSender consoleSender = new ConsoleSender();
2022-07-25 19:34:40 +02:00
private final CommandParser parser = CommandParser.parser();
private final CommandDispatcher dispatcher = new CommandDispatcher(this);
private final Map<String, Command> commandMap = new HashMap<>();
private final Set<Command> commands = new HashSet<>();
2020-04-05 10:15:21 +02:00
2020-11-02 04:13:43 +01:00
private CommandCallback unknownCommandCallback;
2020-06-21 14:01:03 +02:00
public CommandManager() {
}
2020-07-21 18:04:02 +02:00
/**
2020-10-15 21:16:31 +02:00
* Registers a {@link Command}.
2020-07-21 18:04:02 +02:00
*
* @param command the command to register
2020-11-14 07:06:46 +01:00
* @throws IllegalStateException if a command with the same name already exists
2020-07-21 18:04:02 +02:00
*/
public synchronized void register(@NotNull Command command) {
Check.stateCondition(commandExists(command.getName()),
"A command with the name " + command.getName() + " is already registered!");
2021-02-09 15:26:53 +01:00
if (command.getAliases() != null) {
for (String alias : command.getAliases()) {
Check.stateCondition(commandExists(alias),
"A command with the name " + alias + " is already registered!");
}
}
2022-07-25 19:34:40 +02:00
commands.add(command);
for (String name : command.getNames()) {
commandMap.put(name, command);
}
2020-04-05 10:15:21 +02:00
}
/**
* Removes a command from the currently registered commands.
* Does nothing if the command was not registered before
*
* @param command the command to remove
*/
public void unregister(@NotNull Command command) {
2022-07-25 19:34:40 +02:00
commands.remove(command);
for (String name : command.getNames()) {
commandMap.remove(name);
}
}
/**
2020-10-15 21:16:31 +02:00
* Gets the {@link Command} registered by {@link #register(Command)}.
*
* @param commandName the command name
* @return the command associated with the name, null if not any
*/
public @Nullable Command getCommand(@NotNull String commandName) {
2022-07-25 19:34:40 +02:00
return commandMap.get(commandName.toLowerCase(Locale.ROOT));
}
/**
2021-11-06 18:33:48 +01:00
* Gets if a command with the name {@code commandName} already exists or not.
*
* @param commandName the command name to check
* @return true if the command does exist
*/
public boolean commandExists(@NotNull String commandName) {
2022-07-25 19:34:40 +02:00
return getCommand(commandName) != null;
}
2020-07-21 18:04:02 +02:00
/**
2021-11-06 18:33:48 +01:00
* Executes a command for a {@link CommandSender}.
2020-07-21 18:04:02 +02:00
*
* @param sender the sender of the command
* @param command the raw command string (without the command prefix)
* @return the execution result
2020-07-21 18:04:02 +02:00
*/
public @NotNull CommandResult execute(@NotNull CommandSender sender, @NotNull String command) {
2022-07-25 19:34:40 +02:00
command = command.trim();
2020-10-12 02:56:30 +02:00
// Command event
if (sender instanceof Player player) {
2020-06-21 14:01:03 +02:00
PlayerCommandEvent playerCommandEvent = new PlayerCommandEvent(player, command);
2021-06-04 03:48:51 +02:00
EventDispatcher.call(playerCommandEvent);
2020-06-21 14:01:03 +02:00
if (playerCommandEvent.isCancelled())
return CommandResult.of(CommandResult.Type.CANCELLED, command);
2020-06-21 14:01:03 +02:00
command = playerCommandEvent.getCommand();
}
2020-10-12 02:56:30 +02:00
// Process the command
2022-07-25 19:34:40 +02:00
final CommandParser.Result parsedCommand = parseCommand(command);
final ExecutableCommand executable = parsedCommand.executable();
final ExecutableCommand.Result executeResult = executable.execute(sender);
final CommandResult result = resultConverter(executable, executeResult, command);
if (result.getType() == CommandResult.Type.UNKNOWN) {
if (unknownCommandCallback != null) {
this.unknownCommandCallback.apply(sender, command);
}
2020-04-05 10:15:21 +02:00
}
return result;
2020-04-05 10:15:21 +02:00
}
2021-02-08 03:42:35 +01:00
/**
2021-11-06 18:33:48 +01:00
* Executes the command using a {@link ServerSender}. This can be used
* to run a silent command (nothing is printed to console).
2021-02-08 04:08:22 +01:00
*
* @see #execute(CommandSender, String)
2021-02-08 03:42:35 +01:00
*/
public @NotNull CommandResult executeServerCommand(@NotNull String command) {
2021-02-08 03:42:35 +01:00
return execute(serverSender, command);
}
public @NotNull CommandDispatcher getDispatcher() {
2021-02-11 02:51:42 +01:00
return dispatcher;
}
2020-11-02 04:13:43 +01:00
/**
* Gets the callback executed once an unknown command is run.
*
* @return the unknown command callback, null if not any
*/
public @Nullable CommandCallback getUnknownCommandCallback() {
2020-11-02 04:13:43 +01:00
return unknownCommandCallback;
}
/**
* Sets the callback executed once an unknown command is run.
*
* @param unknownCommandCallback the new unknown command callback,
* setting it to null mean that nothing will be executed
*/
public void setUnknownCommandCallback(@Nullable CommandCallback unknownCommandCallback) {
this.unknownCommandCallback = unknownCommandCallback;
}
2020-07-21 18:04:02 +02:00
/**
2020-10-15 21:16:31 +02:00
* Gets the {@link ConsoleSender} (which is used as a {@link CommandSender}).
2020-07-21 18:04:02 +02:00
*
2020-10-11 18:35:32 +02:00
* @return the {@link ConsoleSender}
2020-07-21 18:04:02 +02:00
*/
public @NotNull ConsoleSender getConsoleSender() {
2020-06-21 14:01:03 +02:00
return consoleSender;
}
2020-07-21 18:04:02 +02:00
/**
2020-10-15 21:16:31 +02:00
* Gets the {@link DeclareCommandsPacket} for a specific player.
2020-07-21 18:04:02 +02:00
* <p>
* Can be used to update a player auto-completion list.
2020-07-21 18:04:02 +02:00
*
* @param player the player to get the commands packet
* @return the {@link DeclareCommandsPacket} for {@code player}
*/
public @NotNull DeclareCommandsPacket createDeclareCommandsPacket(@NotNull Player player) {
2022-07-25 19:34:40 +02:00
return GraphConverter.createPacket(getGraph(), player);
}
public @NotNull Set<@NotNull Command> getCommands() {
return Collections.unmodifiableSet(commands);
}
/**
* Parses the command based on the registered commands
*
* @param input commands string without prefix
* @return the parsing result
*/
public CommandParser.Result parseCommand(String input) {
return parser.parse(getGraph(), input);
}
private Graph getGraph() {
//todo cache
return Graph.merge(commands);
}
private static CommandResult resultConverter(ExecutableCommand executable,
ExecutableCommand.Result newResult,
String input) {
return CommandResult.of(switch (newResult.type()) {
case SUCCESS -> CommandResult.Type.SUCCESS;
case CANCELLED, PRECONDITION_FAILED, EXECUTOR_EXCEPTION -> CommandResult.Type.CANCELLED;
case INVALID_SYNTAX -> CommandResult.Type.INVALID_SYNTAX;
case UNKNOWN -> CommandResult.Type.UNKNOWN;
}, input, ParsedCommand.fromExecutable(executable), newResult.commandData());
2020-07-10 18:12:29 +02:00
}
2020-04-05 10:15:21 +02:00
}