diff --git a/paper-api/src/main/java/org/bukkit/command/Command.java b/paper-api/src/main/java/org/bukkit/command/Command.java index ec502af595..b0b7f21f1c 100644 --- a/paper-api/src/main/java/org/bukkit/command/Command.java +++ b/paper-api/src/main/java/org/bukkit/command/Command.java @@ -1,12 +1,19 @@ package org.bukkit.command; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Server; +import org.bukkit.entity.Player; import org.bukkit.permissions.Permissible; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; /** * Represents a Command, which executes various tasks upon user input @@ -48,21 +55,48 @@ public abstract class Command { public abstract boolean execute(CommandSender sender, String commandLabel, String[] args); /** - * Executed on tab completion for this command, returning a list of options - * the player can tab through. - *

- * By returning null, you tell Bukkit to generate a list of players to send - * to the sender. - * By returning an empty list, no options will be sent. - * - * @param sender Source object which is executing this command - * @param args All arguments passed to the command, split via ' ' - * @return null to generate a Player list, otherwise a list of options + * @deprecated This method is not supported and returns null */ + @Deprecated public List tabComplete(CommandSender sender, String[] args) { return null; } + /** + * Executed on tab completion for this command, returning a list of options + * the player can tab through. + * + * @param sender Source object which is executing this command + * @param alias the alias being used + * @param args All arguments passed to the command, split via ' ' + * @return a list of tab-completions for the specified arguments. This will never be null. List may be immutable. + * @throws IllegalArgumentException if sender, alias, or args is null + */ + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (!(sender instanceof Player) || args.length == 0) { + return ImmutableList.of(); + } + + String lastWord = args[args.length - 1]; + + Player senderPlayer = (Player) sender; + + ArrayList matchedPlayers = new ArrayList(); + for (Player player : sender.getServer().getOnlinePlayers()) { + String name = player.getName(); + if (senderPlayer.canSee(player) && StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + + Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); + return matchedPlayers; + } + /** * Returns the name of this command * diff --git a/paper-api/src/main/java/org/bukkit/command/CommandMap.java b/paper-api/src/main/java/org/bukkit/command/CommandMap.java index 5524da2767..e7fad28755 100644 --- a/paper-api/src/main/java/org/bukkit/command/CommandMap.java +++ b/paper-api/src/main/java/org/bukkit/command/CommandMap.java @@ -62,4 +62,16 @@ public interface CommandMap { * @return Command with the specified name or null if a command with that label doesn't exist */ public Command getCommand(String name); + + + /** + * Looks for the requested command and executes an appropriate tab-completer if found. This method will also tab-complete partial commands. + * + * @param sender The command's sender. + * @param cmdLine The entire command string to tab-complete, excluding initial slash. + * @return a list of possible tab-completions. This list may be immutable. Will be null if no matching command of which sender has permission. + * @throws CommandException Thrown when the tab-completer for the given command fails with an unhandled exception + * @throws IllegalArgumentException if either sender or cmdLine are null + */ + public List tabComplete(CommandSender sender, String cmdLine) throws IllegalArgumentException; } diff --git a/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java b/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java index b1b83b1f6b..3a666d16d8 100644 --- a/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java +++ b/paper-api/src/main/java/org/bukkit/command/MultipleCommandAlias.java @@ -10,7 +10,7 @@ public class MultipleCommandAlias extends Command { super(name); this.commands = commands; } - + public Command[] getCommands() { return commands; } diff --git a/paper-api/src/main/java/org/bukkit/command/PluginCommand.java b/paper-api/src/main/java/org/bukkit/command/PluginCommand.java index 05975c7f63..f82e3ed368 100644 --- a/paper-api/src/main/java/org/bukkit/command/PluginCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/PluginCommand.java @@ -1,5 +1,8 @@ package org.bukkit.command; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.plugin.Plugin; /** @@ -8,6 +11,7 @@ import org.bukkit.plugin.Plugin; public final class PluginCommand extends Command implements PluginIdentifiableCommand { private final Plugin owningPlugin; private CommandExecutor executor; + private TabCompleter completer; protected PluginCommand(String name, Plugin owner) { super(name); @@ -69,6 +73,26 @@ public final class PluginCommand extends Command implements PluginIdentifiableCo return executor; } + /** + * Sets the {@link TabCompleter} to run when tab-completing this command. + * If no TabCompleter is specified, and the command's executor implements + * TabCompleter, then the executor will be used for tab completion. + * + * @param completer New tab completer + */ + public void setTabCompleter(TabCompleter completer) { + this.completer = completer; + } + + /** + * Gets the {@link TabCompleter} associated with this command. + * + * @return TabCompleter object linked to this command + */ + public TabCompleter getTabCompleter() { + return completer; + } + /** * Gets the owner of this PluginCommand * @@ -77,4 +101,47 @@ public final class PluginCommand extends Command implements PluginIdentifiableCo public Plugin getPlugin() { return owningPlugin; } + + /** + * {@inheritDoc}
+ *
+ * Delegates to the tab completer if present.
+ * If it is not present or returns null, will delegate to the current command + * executor if it implements {@link TabCompleter}. If a non-null list has not + * been found, will default to standard player name completion in + * {@link Command#tabComplete(CommandSender, String, String[])}.
+ *
+ * This method does not consider permissions. + * @throws CommandException if the completer or executor throw an exception during the process of tab-completing. + * @throws IllegalArgumentException if sender, alias, or args is null + */ + @Override + public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws CommandException, IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + List completions = null; + try { + if (completer != null) { + completions = completer.onTabComplete(sender, this, alias, args); + } + if (completions == null && executor instanceof TabCompleter) { + completions = ((TabCompleter) executor).onTabComplete(sender, this, alias, args); + } + } catch (Throwable ex) { + StringBuilder message = new StringBuilder(); + message.append("Unhandled exception during tab completion for command '/").append(alias).append(' '); + for (String arg : args) { + message.append(arg).append(' '); + } + message.deleteCharAt(message.length() - 1).append("' in plugin ").append(owningPlugin.getDescription().getFullName()); + throw new CommandException(message.toString(), ex); + } + + if (completions == null) { + return super.tabComplete(sender, alias, args); + } + return completions; + } } diff --git a/paper-api/src/main/java/org/bukkit/command/SimpleCommandMap.java b/paper-api/src/main/java/org/bukkit/command/SimpleCommandMap.java index 384a01ec25..a44504ef1a 100644 --- a/paper-api/src/main/java/org/bukkit/command/SimpleCommandMap.java +++ b/paper-api/src/main/java/org/bukkit/command/SimpleCommandMap.java @@ -1,13 +1,25 @@ package org.bukkit.command; -import org.bukkit.command.defaults.*; - -import java.util.*; - -import org.bukkit.Server; import static org.bukkit.util.Java15Compat.Arrays_copyOfRange; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; +import org.bukkit.Server; +import org.bukkit.command.defaults.*; +import org.bukkit.util.StringUtil; + public class SimpleCommandMap implements CommandMap { + private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL); protected final Map knownCommands = new HashMap(); protected final Set aliases = new HashSet(); private final Server server; @@ -81,7 +93,7 @@ public class SimpleCommandMap implements CommandMap { Iterator iterator = command.getAliases().iterator(); while (iterator.hasNext()) { - if (!register((String) iterator.next(), fallbackPrefix, command, true)) { + if (!register(iterator.next(), fallbackPrefix, command, true)) { iterator.remove(); } } @@ -150,7 +162,7 @@ public class SimpleCommandMap implements CommandMap { * {@inheritDoc} */ public boolean dispatch(CommandSender sender, String commandLine) throws CommandException { - String[] args = commandLine.split(" "); + String[] args = PATTERN_ON_SPACE.split(commandLine); if (args.length == 0) { return false; @@ -186,13 +198,82 @@ public class SimpleCommandMap implements CommandMap { } public Command getCommand(String name) { - Command target = knownCommands.get(name.toLowerCase()); + Command target = knownCommands.get(name.toLowerCase()); if (target == null) { target = getFallback(name); } return target; } + public List tabComplete(CommandSender sender, String cmdLine) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(cmdLine, "Command line cannot null"); + + int spaceIndex = cmdLine.indexOf(' '); + + if (spaceIndex == -1) { + ArrayList completions = new ArrayList(); + Map knownCommands = this.knownCommands; + + for (VanillaCommand command : fallbackCommands) { + String name = command.getName(); + + if (!command.testPermissionSilent(sender)) { + continue; + } + if (knownCommands.containsKey(name)) { + // Don't let a vanilla command override a command added below + // This has to do with the way aliases work + continue; + } + if (!StringUtil.startsWithIgnoreCase(name, cmdLine)) { + continue; + } + + completions.add('/' + name); + } + + for (Map.Entry commandEntry : knownCommands.entrySet()) { + Command command = commandEntry.getValue(); + + if (!command.testPermissionSilent(sender)) { + continue; + } + + String name = commandEntry.getKey(); // Use the alias, not command name + + if (StringUtil.startsWithIgnoreCase(name, cmdLine)) { + completions.add('/' + name); + } + } + + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); + return completions; + } + + String commandName = cmdLine.substring(0, spaceIndex); + Command target = getCommand(commandName); + + if (target == null) { + return null; + } + + if (!target.testPermissionSilent(sender)) { + return null; + } + + String argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length()); + String[] args = PATTERN_ON_SPACE.split(argLine, -1); + + try { + return target.tabComplete(sender, commandName, args); + } catch (CommandException ex) { + throw ex; + } catch (Throwable ex) { + throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex); + } + } + public Collection getCommands() { return knownCommands.values(); } diff --git a/paper-api/src/main/java/org/bukkit/command/TabCommandExecutor.java b/paper-api/src/main/java/org/bukkit/command/TabCommandExecutor.java index b2ee58c525..95efbd4089 100644 --- a/paper-api/src/main/java/org/bukkit/command/TabCommandExecutor.java +++ b/paper-api/src/main/java/org/bukkit/command/TabCommandExecutor.java @@ -4,7 +4,11 @@ import java.util.List; /** * Represents a class which can handle command tab completion and commands + * @deprecated Remains for plugins that would have implemented it even without functionality + * @see TabExecutor */ +@Deprecated public interface TabCommandExecutor extends CommandExecutor { public List onTabComplete(); + } diff --git a/paper-api/src/main/java/org/bukkit/command/TabCompleter.java b/paper-api/src/main/java/org/bukkit/command/TabCompleter.java new file mode 100644 index 0000000000..3ba64fe8cb --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/command/TabCompleter.java @@ -0,0 +1,20 @@ +package org.bukkit.command; + +import java.util.List; + +/** + * Represents a class which can suggest tab completions for commands. + */ +public interface TabCompleter { + + /** + * Requests a list of possible completions for a command argument. + * + * @param sender Source of the command + * @param command Command which was executed + * @param alias The alias used + * @param args The arguments passed to the command, including final partial argument to be completed and command label + * @return A List of possible completions for the final argument, or null to default to the command executor + */ + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args); +} diff --git a/paper-api/src/main/java/org/bukkit/command/TabExecutor.java b/paper-api/src/main/java/org/bukkit/command/TabExecutor.java new file mode 100644 index 0000000000..67b3503dbc --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/command/TabExecutor.java @@ -0,0 +1,7 @@ +package org.bukkit.command; + +/** + * This class is provided as a convenience to implement both TabCompleter and CommandExecutor. + */ +public interface TabExecutor extends TabCompleter, CommandExecutor { +} diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/BanCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/BanCommand.java index c4d3a54c73..c436652487 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/BanCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/BanCommand.java @@ -1,12 +1,16 @@ package org.bukkit.command.defaults; import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class BanCommand extends VanillaCommand { public BanCommand() { super("ban"); @@ -35,13 +39,20 @@ public class BanCommand extends VanillaCommand { return true; } - @Override - public List tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; - } - @Override public boolean matches(String input) { return input.equalsIgnoreCase("ban"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java index b104973938..8ea0c35685 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/BanIpCommand.java @@ -2,12 +2,16 @@ package org.bukkit.command.defaults; import java.util.List; import java.util.regex.Pattern; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class BanIpCommand extends VanillaCommand { public static final Pattern ipValidity = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); @@ -43,11 +47,6 @@ public class BanIpCommand extends VanillaCommand { return true; } - @Override - public List tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; - } - private void processIPBan(String ip, CommandSender sender) { // TODO: Kick on ban Bukkit.banIP(ip); @@ -59,4 +58,16 @@ public class BanIpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("ban-ip"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java index 668a487737..08c5be9a48 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/BanListCommand.java @@ -1,11 +1,19 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class BanListCommand extends VanillaCommand { + private static final List BANLIST_TYPES = ImmutableList.of("ips", "players"); + public BanListCommand() { super("banlist"); this.description = "View all players banned from this server"; @@ -38,8 +46,15 @@ public class BanListCommand extends VanillaCommand { } @Override - public List tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], BANLIST_TYPES, new ArrayList(BANLIST_TYPES.size())); + } + return ImmutableList.of(); } @Override diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java index d3de7c8bfe..23c8580091 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/BukkitCommand.java @@ -1,10 +1,10 @@ package org.bukkit.command.defaults; -import org.bukkit.command.Command; - import java.util.List; -public abstract class BukkitCommand extends Command{ +import org.bukkit.command.Command; + +public abstract class BukkitCommand extends Command { protected BukkitCommand(String name) { super(name); } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java index 63390b1591..1611d413e6 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/DefaultGameModeCommand.java @@ -1,11 +1,20 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class DefaultGameModeCommand extends VanillaCommand { + private static final List GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival"); + public DefaultGameModeCommand() { super("defaultgamemode"); this.description = "Set the default gamemode"; @@ -51,4 +60,17 @@ public class DefaultGameModeCommand extends VanillaCommand { return true; } + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], GAMEMODE_NAMES, new ArrayList(GAMEMODE_NAMES.size())); + } else if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java index 291c049772..1ea6cadbf4 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/DeopCommand.java @@ -1,11 +1,17 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class DeopCommand extends VanillaCommand { public DeopCommand() { @@ -34,13 +40,27 @@ public class DeopCommand extends VanillaCommand { return true; } - @Override - public List tabComplete(CommandSender sender, String[] args) { - return args.length >= 1 ? null : EMPTY_LIST; - } - @Override public boolean matches(String input) { return input.equalsIgnoreCase("deop"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getOfflinePlayers()) { + String playerName = player.getName(); + if (player.isOp() && StringUtil.startsWithIgnoreCase(playerName, args[0])) { + completions.add(playerName); + } + } + return completions; + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java index 981cfb7b4e..791d2e1428 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/ExpCommand.java @@ -1,11 +1,16 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class ExpCommand extends VanillaCommand { public ExpCommand() { super("xp"); @@ -44,4 +49,16 @@ public class ExpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("xp"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java index ad44516030..8721e20b38 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/GameModeCommand.java @@ -1,13 +1,22 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; import org.bukkit.GameMode; import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class GameModeCommand extends VanillaCommand { + private static final List GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival"); + public GameModeCommand() { super("gamemode"); this.description = "Changes the player to a specific game mode"; @@ -77,4 +86,18 @@ public class GameModeCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("gamemode"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], GAMEMODE_NAMES, new ArrayList(GAMEMODE_NAMES.size())); + } else if (args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java index eeac739aad..6010ac86cc 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/GiveCommand.java @@ -1,5 +1,10 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -7,8 +12,21 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class GiveCommand extends VanillaCommand { + private static List materials; + static { + ArrayList materialList = new ArrayList(); + for (Material material : Material.values()) { + materialList.add(material.name()); + } + Collections.sort(materialList); + materials = ImmutableList.copyOf(materialList); + } + public GiveCommand() { super("give"); this.description = "Gives the specified player a certain amount of items"; @@ -60,4 +78,45 @@ public class GiveCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("give"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return super.tabComplete(sender, alias, args); + } + if (args.length == 2) { + final String arg = args[1]; + final List materials = GiveCommand.materials; + List completion = null; + + final int size = materials.size(); + int i = Collections.binarySearch(materials, arg, String.CASE_INSENSITIVE_ORDER); + + if (i < 0) { + // Insertion (start) index + i = -1 - i; + } + + for ( ; i < size; i++) { + String material = materials.get(i); + if (StringUtil.startsWithIgnoreCase(material, arg)) { + if (completion == null) { + completion = new ArrayList(); + } + completion.add(material); + } else { + break; + } + } + + if (completion != null) { + return completion; + } + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java index 4c002eb147..db3d92d01e 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/HelpCommand.java @@ -1,7 +1,15 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; import org.apache.commons.lang.math.NumberUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -13,7 +21,7 @@ import org.bukkit.help.HelpTopicComparator; import org.bukkit.help.IndexHelpTopic; import org.bukkit.util.ChatPaginator; -import java.util.*; +import com.google.common.collect.ImmutableList; public class HelpCommand extends VanillaCommand { public HelpCommand() { @@ -106,6 +114,27 @@ public class HelpCommand extends VanillaCommand { return input.equalsIgnoreCase("help") || input.equalsIgnoreCase("?"); } + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List matchedTopics = new ArrayList(); + String searchString = args[0]; + for (HelpTopic topic : Bukkit.getServer().getHelpMap().getHelpTopics()) { + String trimmedTopic = topic.getName().startsWith("/") ? topic.getName().substring(1) : topic.getName(); + + if (trimmedTopic.startsWith(searchString)) { + matchedTopics.add(trimmedTopic); + } + } + return matchedTopics; + } + return ImmutableList.of(); + } + protected HelpTopic findPossibleMatches(String searchString) { int maxDistance = (searchString.length() / 5) + 3; Set possibleMatches = new TreeSet(HelpTopicComparator.helpTopicComparatorInstance()); diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/KickCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/KickCommand.java index 1f747d8c1b..7b890b1ae7 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/KickCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/KickCommand.java @@ -1,11 +1,16 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class KickCommand extends VanillaCommand { public KickCommand() { super("kick"); @@ -44,4 +49,16 @@ public class KickCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("kick"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/KillCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/KillCommand.java index d3049f7122..84f783f25e 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/KillCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/KillCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent; +import com.google.common.collect.ImmutableList; + public class KillCommand extends VanillaCommand { public KillCommand() { super("kill"); @@ -38,4 +43,13 @@ public class KillCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("kill"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/ListCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/ListCommand.java index 489edd2889..e38ae80609 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/ListCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/ListCommand.java @@ -1,9 +1,14 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class ListCommand extends VanillaCommand { public ListCommand() { super("list"); @@ -41,4 +46,13 @@ public class ListCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("list"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/OpCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/OpCommand.java index 4774c7e349..d7874bcf84 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/OpCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/OpCommand.java @@ -1,10 +1,19 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class OpCommand extends VanillaCommand { public OpCommand() { @@ -33,4 +42,39 @@ public class OpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("op"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + if (!(sender instanceof Player)) { + return ImmutableList.of(); + } + + String lastWord = args[0]; + if (lastWord.length() == 0) { + return ImmutableList.of(); + } + + Player senderPlayer = (Player) sender; + + ArrayList matchedPlayers = new ArrayList(); + for (Player player : sender.getServer().getOnlinePlayers()) { + String name = player.getName(); + if (!senderPlayer.canSee(player) || player.isOp()) { + continue; + } + if (StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + + Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); + return matchedPlayers; + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java index 29da200c63..a737ff0913 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/PardonCommand.java @@ -1,9 +1,17 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class PardonCommand extends VanillaCommand { public PardonCommand() { @@ -30,4 +38,23 @@ public class PardonCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("pardon"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getBannedPlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[0])) { + completions.add(name); + } + } + return completions; + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java index d97421100c..4571eeb99f 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/PardonIpCommand.java @@ -1,9 +1,16 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class PardonIpCommand extends VanillaCommand { public PardonIpCommand() { @@ -35,4 +42,16 @@ public class PardonIpCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("pardon-ip"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], Bukkit.getIPBans(), new ArrayList()); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java index 8cb45cce23..b888da13ad 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/PluginsCommand.java @@ -1,8 +1,9 @@ package org.bukkit.command.defaults; import java.util.Arrays; -import org.bukkit.ChatColor; + import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java index 9b090edcd0..7f820b3b3a 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/ReloadCommand.java @@ -1,8 +1,9 @@ package org.bukkit.command.defaults; import java.util.Arrays; -import org.bukkit.ChatColor; + import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; public class ReloadCommand extends BukkitCommand { diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java index 08d8edf9a8..77839c1c7c 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/SaveCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class SaveCommand extends VanillaCommand { public SaveCommand() { super("save-all"); @@ -34,4 +39,13 @@ public class SaveCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("save-all"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java index d46cdd3615..c081e5e106 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/SaveOffCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class SaveOffCommand extends VanillaCommand { public SaveOffCommand() { super("save-off"); @@ -29,4 +34,13 @@ public class SaveOffCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("save-off"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java index 4c1bfec633..d0561c36a9 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/SaveOnCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class SaveOnCommand extends VanillaCommand { public SaveOnCommand() { super("save-on"); @@ -29,4 +34,13 @@ public class SaveOnCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("save-on"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/SayCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/SayCommand.java index 44dc7ae0d9..651b60c0b1 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/SayCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/SayCommand.java @@ -1,10 +1,15 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class SayCommand extends VanillaCommand { public SayCommand() { super("say"); @@ -43,4 +48,15 @@ public class SayCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("say"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + + if (args.length >= 1) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java index 4fac2678de..b64fd4051a 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/SeedCommand.java @@ -1,9 +1,14 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class SeedCommand extends VanillaCommand { public SeedCommand() { super("seed"); @@ -24,4 +29,13 @@ public class SeedCommand extends VanillaCommand { sender.sendMessage("Seed: " + seed); return true; } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/StopCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/StopCommand.java index 2b8ee08250..c6a470b646 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/StopCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/StopCommand.java @@ -1,9 +1,14 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import com.google.common.collect.ImmutableList; + public class StopCommand extends VanillaCommand { public StopCommand() { super("stop"); @@ -26,4 +31,13 @@ public class StopCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("stop"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java index ad2a5527fc..55be9da355 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/TeleportCommand.java @@ -1,5 +1,8 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -8,6 +11,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import com.google.common.collect.ImmutableList; + public class TeleportCommand extends VanillaCommand { public TeleportCommand() { super("tp"); @@ -66,4 +71,16 @@ public class TeleportCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("tp"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1 || args.length == 2) { + return super.tabComplete(sender, alias, args); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java index 9866877d25..011d32db2f 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/TimeCommand.java @@ -1,12 +1,22 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class TimeCommand extends VanillaCommand { + private static final List TABCOMPLETE_ADD_SET = ImmutableList.of("add", "set"); + private static final List TABCOMPLETE_DAY_NIGHT = ImmutableList.of("day", "night"); + public TimeCommand() { super("time"); this.description = "Changes the time on each world"; @@ -66,4 +76,18 @@ public class TimeCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("time"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], TABCOMPLETE_ADD_SET, new ArrayList(TABCOMPLETE_ADD_SET.size())); + } else if (args.length == 2 && args[0].equalsIgnoreCase("set")) { + return StringUtil.copyPartialMatches(args[1], TABCOMPLETE_DAY_NIGHT, new ArrayList(TABCOMPLETE_DAY_NIGHT.size())); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java index b9c8fc6630..29ebbe050b 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/TimingsCommand.java @@ -3,18 +3,25 @@ package org.bukkit.command.defaults; import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredListener; import org.bukkit.plugin.TimedRegisteredListener; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class TimingsCommand extends BukkitCommand { + private static final List TIMINGS_SUBCOMMANDS = ImmutableList.of("merged", "reset", "separate"); + public TimingsCommand(String name) { super(name); this.description = "Records timings for all plugin events"; @@ -99,4 +106,16 @@ public class TimingsCommand extends BukkitCommand { } return true; } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, new ArrayList(TIMINGS_SUBCOMMANDS.size())); + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java index e81710e128..08ed667f4b 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/ToggleDownfallCommand.java @@ -1,5 +1,8 @@ package org.bukkit.command.defaults; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -7,6 +10,8 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.collect.ImmutableList; + public class ToggleDownfallCommand extends VanillaCommand { public ToggleDownfallCommand() { super("toggledownfall"); @@ -44,4 +49,13 @@ public class ToggleDownfallCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("toggledownfall"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java index bf6df75039..1fdcc81ee0 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/VanillaCommand.java @@ -1,13 +1,11 @@ package org.bukkit.command.defaults; -import java.util.ArrayList; import java.util.List; + import org.bukkit.command.Command; import org.bukkit.command.CommandSender; public abstract class VanillaCommand extends Command { - static final List EMPTY_LIST = new ArrayList(0); - protected VanillaCommand(String name) { super(name); } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java index 4d3ba15b09..902f9d1585 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/VersionCommand.java @@ -1,13 +1,18 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.bukkit.ChatColor; +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class VersionCommand extends BukkitCommand { public VersionCommand(String name) { @@ -102,4 +107,23 @@ public class VersionCommand extends BukkitCommand { return result.toString(); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + List completions = new ArrayList(); + String toComplete = args[0].toLowerCase(); + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + if (StringUtil.startsWithIgnoreCase(plugin.getName(), toComplete)) { + completions.add(plugin.getName()); + } + } + return completions; + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java b/paper-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java index d0fd85786f..70786d11e6 100644 --- a/paper-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java +++ b/paper-api/src/main/java/org/bukkit/command/defaults/WhitelistCommand.java @@ -1,12 +1,21 @@ package org.bukkit.command.defaults; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + +import com.google.common.collect.ImmutableList; public class WhitelistCommand extends VanillaCommand { + private static final List WHITELIST_SUBCOMMANDS = ImmutableList.of("add", "remove", "on", "off", "list", "reload"); + public WhitelistCommand() { super("whitelist"); this.description = "Prevents the specified player from using this server"; @@ -88,4 +97,36 @@ public class WhitelistCommand extends VanillaCommand { public boolean matches(String input) { return input.equalsIgnoreCase("whitelist"); } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + if (args.length == 1) { + return StringUtil.copyPartialMatches(args[0], WHITELIST_SUBCOMMANDS, new ArrayList(WHITELIST_SUBCOMMANDS.size())); + } else if (args.length == 2) { + if (args[0].equalsIgnoreCase("add")) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getOfflinePlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[1]) && !player.isWhitelisted()) { + completions.add(name); + } + } + return completions; + } else if (args[0].equalsIgnoreCase("remove")) { + List completions = new ArrayList(); + for (OfflinePlayer player : Bukkit.getWhitelistedPlayers()) { + String name = player.getName(); + if (StringUtil.startsWithIgnoreCase(name, args[1])) { + completions.add(name); + } + } + return completions; + } + } + return ImmutableList.of(); + } } diff --git a/paper-api/src/main/java/org/bukkit/plugin/Plugin.java b/paper-api/src/main/java/org/bukkit/plugin/Plugin.java index 6b75c3e7a6..c266114c5e 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/Plugin.java +++ b/paper-api/src/main/java/org/bukkit/plugin/Plugin.java @@ -5,7 +5,7 @@ import java.io.InputStream; import java.util.logging.Logger; import org.bukkit.Server; -import org.bukkit.command.CommandExecutor; +import org.bukkit.command.TabExecutor; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.generator.ChunkGenerator; @@ -16,7 +16,7 @@ import com.avaje.ebean.EbeanServer; *

* The use of {@link PluginBase} is recommended for actual Implementation */ -public interface Plugin extends CommandExecutor { +public interface Plugin extends TabExecutor { /** * Returns the folder that the plugin data's files are located in. The * folder may not yet exist. diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java index cdc6e75175..d48355476a 100644 --- a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java +++ b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java @@ -298,6 +298,13 @@ public abstract class JavaPlugin extends PluginBase { return false; } + /** + * {@inheritDoc} + */ + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return null; + } + /** * Gets the command with the given name, specific to this plugin * diff --git a/paper-api/src/main/java/org/bukkit/util/StringUtil.java b/paper-api/src/main/java/org/bukkit/util/StringUtil.java new file mode 100644 index 0000000000..12bc58d4db --- /dev/null +++ b/paper-api/src/main/java/org/bukkit/util/StringUtil.java @@ -0,0 +1,49 @@ +package org.bukkit.util; + +import java.util.Collection; +import org.apache.commons.lang.Validate; + +public class StringUtil { + + /** + * Copies all elements from the iterable collection of originals to the collection provided. + * + * @param token String to search for + * @param originals An iterable collection of strings to filter. + * @param collection The collection to add matches to + * @return the collection provided that would have the elements copied into + * @throws UnsupportedOperationException if the collection is immutable and originals contains a string which starts with the specified search string. + * @throws IllegalArgumentException if any parameter is is null + * @throws IllegalArgumentException if originals contains a null element. Note: the collection may be modified before this is thrown + */ + public static > T copyPartialMatches(final String token, final Iterable originals, final T collection) throws UnsupportedOperationException, IllegalArgumentException { + Validate.notNull(token, "Search token cannot be null"); + Validate.notNull(collection, "Collection cannot be null"); + Validate.notNull(originals, "Originals cannot be null"); + + for (String string : originals) { + if (startsWithIgnoreCase(string, token)) { + collection.add(string); + } + } + + return collection; + } + + /** + * This method uses a substring to check case-insensitive equality. This means the internal array does not need to be copied like a toLowerCase() call would. + * + * @param string String to check + * @param prefix Prefix of string to compare + * @return true if provided string starts with, ignoring case, the prefix provided + * @throws NullPointerException if prefix is null + * @throws IllegalArgumentException if string is null + */ + public static boolean startsWithIgnoreCase(final String string, final String prefix) throws IllegalArgumentException, NullPointerException { + Validate.notNull(string, "Cannot check a null string for a match"); + if (string.length() < prefix.length()) { + return false; + } + return string.substring(0, prefix.length()).equalsIgnoreCase(prefix); + } +} diff --git a/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java b/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java index a435771aed..7e098925cd 100644 --- a/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java +++ b/paper-api/src/test/java/org/bukkit/plugin/TestPlugin.java @@ -2,6 +2,7 @@ package org.bukkit.plugin; import java.io.File; import java.io.InputStream; +import java.util.List; import org.bukkit.Server; import org.bukkit.command.Command; @@ -103,4 +104,8 @@ public class TestPlugin extends PluginBase { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { throw new UnsupportedOperationException("Not supported."); } + + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } }