diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandResult.java b/api/src/main/java/com/velocitypowered/api/command/CommandResult.java new file mode 100644 index 000000000..88a409eb4 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/command/CommandResult.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020-2021 Velocity Contributors + * + * The Velocity API is licensed under the terms of the MIT License. For more details, + * reference the LICENSE file in the api top-level directory. + */ + +package com.velocitypowered.api.command; + +/** + * The result of a command invocation attempt. + */ +public enum CommandResult { + /** + * The command was successfully executed by the proxy. + */ + EXECUTED, + /** + * The command was forwarded to the backend server. + * The command may be successfully executed or not + */ + FORWARDED, + /** + * The provided command input contained syntax errors. + */ + SYNTAX_ERROR, + /** + * An unexpected exception occurred while executing the command in the proxy. + */ + EXCEPTION +} diff --git a/api/src/main/java/com/velocitypowered/api/event/command/PostCommandInvocationEvent.java b/api/src/main/java/com/velocitypowered/api/event/command/PostCommandInvocationEvent.java new file mode 100644 index 000000000..feeb9e050 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/command/PostCommandInvocationEvent.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020-2023 Velocity Contributors + * + * The Velocity API is licensed under the terms of the MIT License. For more details, + * reference the LICENSE file in the api top-level directory. + */ + +package com.velocitypowered.api.event.command; + +import com.google.common.base.Preconditions; +import com.velocitypowered.api.command.CommandResult; +import com.velocitypowered.api.command.CommandSource; +import org.jetbrains.annotations.NotNull; + +/** + * This event is fired when velocity executed a command. This event is called after the event + * is handled. + * + *

Commands can be cancelled or forwarded to backend servers in {@link CommandExecuteEvent}. + * This will prevent firing this event.

+ * + * @since 3.3.0 + */ +public final class PostCommandInvocationEvent { + + private final CommandSource commandSource; + private final String command; + private final CommandResult result; + + /** + * Constructs a PostCommandInvocationEvent. + * + * @param commandSource the source executing the command + * @param command the command being executed without first slash + * @param result the result of this command + */ + public PostCommandInvocationEvent( + final @NotNull CommandSource commandSource, + final @NotNull String command, + final @NotNull CommandResult result + ) { + this.commandSource = Preconditions.checkNotNull(commandSource, "commandSource"); + this.command = Preconditions.checkNotNull(command, "command"); + this.result = Preconditions.checkNotNull(result, "result"); + } + + /** + * Get the source of this executed command. + * + * @return the source + */ + public @NotNull CommandSource getCommandSource() { + return commandSource; + } + + /** + * Gets the original command line executed without the first slash. + * + * @return the original command + * @see CommandExecuteEvent#getCommand() + */ + public @NotNull String getCommand() { + return command; + } + + /** + * Returns the result of the command execution. + * + * @return the execution result + */ + public @NotNull CommandResult getResult() { + return result; + } + + @Override + public String toString() { + return "PostCommandInvocationEvent{" + + "commandSource=" + commandSource + + ", command=" + command + + '}'; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index c4cacaf5f..80fef3d4e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -31,9 +31,10 @@ import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandMeta; +import com.velocitypowered.api.command.CommandResult; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.command.CommandExecuteEvent; -import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult; +import com.velocitypowered.api.event.command.PostCommandInvocationEvent; import com.velocitypowered.proxy.command.registrar.BrigadierCommandRegistrar; import com.velocitypowered.proxy.command.registrar.CommandRegistrar; import com.velocitypowered.proxy.command.registrar.RawCommandRegistrar; @@ -220,23 +221,30 @@ public class VelocityCommandManager implements CommandManager { Preconditions.checkNotNull(cmdLine, "cmdLine"); final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true); + CommandResult result = CommandResult.EXCEPTION; try { // The parse can fail if the requirement predicates throw final ParseResults parse = this.parse(normalizedInput, source); - return dispatcher.execute(parse) != BrigadierCommand.FORWARD; + boolean executed = dispatcher.execute(parse) != BrigadierCommand.FORWARD; + result = executed ? CommandResult.EXECUTED : CommandResult.FORWARDED; + return executed; } catch (final CommandSyntaxException e) { boolean isSyntaxError = !e.getType().equals( CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()); if (isSyntaxError) { source.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); + result = com.velocitypowered.api.command.CommandResult.SYNTAX_ERROR; // This is, of course, a lie, but the API will need to change... return true; } else { + result = CommandResult.FORWARDED; return false; } } catch (final Throwable e) { // Ugly, ugly swallowing of everything Throwable, because plugins are naughty. throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + source, e); + } finally { + eventManager.fireAndForget(new PostCommandInvocationEvent(source, cmdLine, result)); } } @@ -246,7 +254,7 @@ public class VelocityCommandManager implements CommandManager { Preconditions.checkNotNull(cmdLine, "cmdLine"); return callCommandEvent(source, cmdLine).thenApplyAsync(event -> { - CommandResult commandResult = event.getResult(); + CommandExecuteEvent.CommandResult commandResult = event.getResult(); if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { return false; }