diff --git a/api/src/main/java/com/velocitypowered/api/command/RawCommand.java b/api/src/main/java/com/velocitypowered/api/command/RawCommand.java new file mode 100644 index 000000000..268beeeb4 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/command/RawCommand.java @@ -0,0 +1,59 @@ +package com.velocitypowered.api.command; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * A specialized sub-interface of {@code Command} which indicates that the proxy should pass a + * raw command to the command. This is useful for bolting on external command frameworks to + * Velocity. + */ +public interface RawCommand extends Command { + /** + * Executes the command for the specified {@link CommandSource}. + * + * @param source the source of this command + * @param commandLine the full command line after the command name + */ + void execute(CommandSource source, String commandLine); + + default void execute(CommandSource source, String @NonNull [] args) { + execute(source, String.join(" ", args)); + } + + /** + * Provides tab complete suggestions for a command for a specified {@link CommandSource}. + * + * @param source the source to run the command for + * @param currentLine the current, partial command line for this command + * @return tab complete suggestions + */ + default List suggest(CommandSource source, String currentLine) { + return ImmutableList.of(); + } + + @Override + default List suggest(CommandSource source, String @NonNull [] currentArgs) { + return suggest(source, String.join(" ", currentArgs)); + } + + default boolean hasPermission(CommandSource source, String @NonNull [] args) { + return hasPermission(source, String.join(" ", args)); + } + + /** + * Tests to check if the {@code source} has permission to use this command with the provided + * {@code args}. + * + *

If this method returns false, the handling will be forwarded onto + * the players current server.

+ * + * @param source the source of the command + * @param commandLine the arguments for this command + * @return whether the source has permission + */ + default boolean hasPermission(CommandSource source, String commandLine) { + return true; + } +} 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 dcd47339c..c60438c98 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableSet; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.command.RawCommand; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -55,11 +56,20 @@ public class VelocityCommandManager implements CommandManager { @SuppressWarnings("nullness") String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); try { - if (!command.hasPermission(source, actualArgs)) { - return false; + if (command instanceof RawCommand) { + RawCommand rc = (RawCommand) command; + int firstSpace = cmdLine.indexOf(' '); + String line = firstSpace == -1 ? "" : cmdLine.substring(firstSpace + 1); + if (!rc.hasPermission(source, line)) { + return false; + } + rc.execute(source, line); + } else { + if (!command.hasPermission(source, actualArgs)) { + return false; + } + command.execute(source, actualArgs); } - - command.execute(source, actualArgs); return true; } catch (Exception e) { throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + source, e); @@ -112,11 +122,20 @@ public class VelocityCommandManager implements CommandManager { @SuppressWarnings("nullness") String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); try { - if (!command.hasPermission(source, actualArgs)) { - return ImmutableList.of(); + if (command instanceof RawCommand) { + RawCommand rc = (RawCommand) command; + int firstSpace = cmdLine.indexOf(' '); + String line = firstSpace == -1 ? "" : cmdLine.substring(firstSpace + 1); + if (!rc.hasPermission(source, line)) { + return ImmutableList.of(); + } + return ImmutableList.copyOf(rc.suggest(source, line)); + } else { + if (!command.hasPermission(source, actualArgs)) { + return ImmutableList.of(); + } + return ImmutableList.copyOf(command.suggest(source, actualArgs)); } - - return ImmutableList.copyOf(command.suggest(source, actualArgs)); } catch (Exception e) { throw new RuntimeException( "Unable to invoke suggestions for command " + alias + " for " + source, e); @@ -149,7 +168,14 @@ public class VelocityCommandManager implements CommandManager { @SuppressWarnings("nullness") String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); try { - return command.hasPermission(source, actualArgs); + if (command instanceof RawCommand) { + RawCommand rc = (RawCommand) command; + int firstSpace = cmdLine.indexOf(' '); + String line = firstSpace == -1 ? "" : cmdLine.substring(firstSpace + 1); + return rc.hasPermission(source, line); + } else { + return command.hasPermission(source, actualArgs); + } } catch (Exception e) { throw new RuntimeException( "Unable to invoke suggestions for command " + alias + " for " + source, e);