From 33e1e0b1f13cb2795cb716407d61939218c60f55 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 16:30:02 -0700 Subject: [PATCH] Overhauled command handling and suggestion support. --- .../sk89q/bukkit/util/CommandInspector.java | 32 +++++++ .../bukkit/util/DynamicPluginCommand.java | 17 +++- .../util/DynamicPluginCommandHelpTopic.java | 89 +++++++++--------- .../bukkit/BukkitCommandInspector.java | 83 +++++++++++++++++ .../bukkit/BukkitServerInterface.java | 5 +- .../worldedit/bukkit/WorldEditListener.java | 13 +-- .../worldedit/bukkit/WorldEditPlugin.java | 26 +++++- .../sk89q/worldedit/forge/ForgeWorldEdit.java | 7 +- .../java/com/sk89q/worldedit/WorldEdit.java | 17 ---- .../worldedit/command/UtilityCommands.java | 4 +- .../event/platform/CommandEvent.java | 14 +-- .../platform/CommandSuggestionEvent.java | 90 +++++++++++++++++++ .../extension/platform/CommandManager.java | 39 ++++---- ...sionsHandler.java => ActorAuthorizer.java} | 17 ++-- .../sk89q/worldedit/util/auth/Authorizer.java | 38 ++++++++ .../worldedit/util/auth/NullAuthorizer.java | 35 ++++++++ .../util/command/CommandCallable.java | 19 +++- .../worldedit/util/command/Description.java | 2 +- .../util/command/SimpleDescription.java | 4 +- .../util/command/SimpleDispatcher.java | 40 ++++++--- .../util/command/fluent/DispatcherNode.java | 4 +- .../command/parametric/ParametricBuilder.java | 31 ++++++- .../parametric/ParametricCallable.java | 67 ++++++++------ .../parametric/PermissionsHandler.java | 67 -------------- 24 files changed, 536 insertions(+), 224 deletions(-) create mode 100644 src/bukkit/java/com/sk89q/bukkit/util/CommandInspector.java create mode 100644 src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java create mode 100644 src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java rename src/main/java/com/sk89q/worldedit/internal/command/{CommandPermissionsHandler.java => ActorAuthorizer.java} (75%) create mode 100644 src/main/java/com/sk89q/worldedit/util/auth/Authorizer.java create mode 100644 src/main/java/com/sk89q/worldedit/util/auth/NullAuthorizer.java delete mode 100644 src/main/java/com/sk89q/worldedit/util/command/parametric/PermissionsHandler.java diff --git a/src/bukkit/java/com/sk89q/bukkit/util/CommandInspector.java b/src/bukkit/java/com/sk89q/bukkit/util/CommandInspector.java new file mode 100644 index 000000000..a7d213944 --- /dev/null +++ b/src/bukkit/java/com/sk89q/bukkit/util/CommandInspector.java @@ -0,0 +1,32 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.bukkit.util; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public interface CommandInspector { + + String getShortText(Command command); + + String getFullText(Command command); + + boolean testPermission(CommandSender sender, Command command); +} diff --git a/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommand.java b/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommand.java index 1660d987f..c48df659c 100644 --- a/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommand.java +++ b/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommand.java @@ -26,9 +26,12 @@ import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginIdentifiableCommand; +import org.bukkit.command.TabCompleter; import org.bukkit.plugin.Plugin; import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * An implementation of a dynamically registered {@link org.bukkit.command.Command} attached to a plugin @@ -76,6 +79,15 @@ public class DynamicPluginCommand extends org.bukkit.command.Command implements return owningPlugin; } + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + if (owner instanceof TabCompleter) { + return ((TabCompleter) owner).onTabComplete(sender, this, alias, args); + } else { + return Collections.emptyList(); + } + } + @SuppressWarnings("unchecked") @Override public boolean testPermissionSilent(CommandSender sender) { @@ -83,7 +95,10 @@ public class DynamicPluginCommand extends org.bukkit.command.Command implements return true; } - if (registeredWith instanceof CommandsManager) { + if (registeredWith instanceof CommandInspector) { + CommandInspector resolver = (CommandInspector) registeredWith; + return resolver.testPermission(sender, this); + } else if (registeredWith instanceof CommandsManager) { try { for (String permission : permissions) { if (((CommandsManager) registeredWith).hasPermission(sender, permission)) { diff --git a/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java b/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java index 6d0b4f690..fda92fc40 100644 --- a/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java +++ b/src/bukkit/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java @@ -40,55 +40,64 @@ public class DynamicPluginCommandHelpTopic extends HelpTopic { this.cmd = cmd; this.name = "/" + cmd.getName(); - String fullTextTemp = null; - StringBuilder fullText = new StringBuilder(); - - if (cmd.getRegisteredWith() instanceof CommandsManager) { - Map helpText = ((CommandsManager) cmd.getRegisteredWith()).getHelpMessages(); - final String lookupName = cmd.getName().replaceAll("/", ""); - if (helpText.containsKey(lookupName)) { // We have full help text for this command - fullTextTemp = helpText.get(lookupName); - } - // No full help text, assemble help text from info - helpText = ((CommandsManager) cmd.getRegisteredWith()).getCommands(); - if (helpText.containsKey(cmd.getName())) { - final String shortText = helpText.get(cmd.getName()); - if (fullTextTemp == null) { - fullTextTemp = this.name + " " + shortText; - } - this.shortText = shortText; - } + if (cmd.getRegisteredWith() instanceof CommandInspector) { + CommandInspector resolver = (CommandInspector) cmd.getRegisteredWith(); + this.shortText = resolver.getShortText(cmd); + this.fullText = resolver.getFullText(cmd); } else { - this.shortText = cmd.getDescription(); - } + String fullTextTemp = null; + StringBuilder fullText = new StringBuilder(); - // Put the usage in the format: Usage string (newline) Aliases (newline) Help text - String[] split = fullTextTemp == null ? new String[2] : fullTextTemp.split("\n", 2); - fullText.append(ChatColor.BOLD).append(ChatColor.GOLD).append("Usage: ").append(ChatColor.WHITE); - fullText.append(split[0]).append("\n"); - - if (cmd.getAliases().size() > 0) { - fullText.append(ChatColor.BOLD).append(ChatColor.GOLD).append("Aliases: ").append(ChatColor.WHITE); - boolean first = true; - for (String alias : cmd.getAliases()) { - if (!first) { - fullText.append(", "); + if (cmd.getRegisteredWith() instanceof CommandsManager) { + Map helpText = ((CommandsManager) cmd.getRegisteredWith()).getHelpMessages(); + final String lookupName = cmd.getName().replaceAll("/", ""); + if (helpText.containsKey(lookupName)) { // We have full help text for this command + fullTextTemp = helpText.get(lookupName); } - fullText.append(alias); - first = false; + // No full help text, assemble help text from info + helpText = ((CommandsManager) cmd.getRegisteredWith()).getCommands(); + if (helpText.containsKey(cmd.getName())) { + final String shortText = helpText.get(cmd.getName()); + if (fullTextTemp == null) { + fullTextTemp = this.name + " " + shortText; + } + this.shortText = shortText; + } + } else { + this.shortText = cmd.getDescription(); } - fullText.append("\n"); + + // Put the usage in the format: Usage string (newline) Aliases (newline) Help text + String[] split = fullTextTemp == null ? new String[2] : fullTextTemp.split("\n", 2); + fullText.append(ChatColor.BOLD).append(ChatColor.GOLD).append("Usage: ").append(ChatColor.WHITE); + fullText.append(split[0]).append("\n"); + + if (!cmd.getAliases().isEmpty()) { + fullText.append(ChatColor.BOLD).append(ChatColor.GOLD).append("Aliases: ").append(ChatColor.WHITE); + boolean first = true; + for (String alias : cmd.getAliases()) { + if (!first) { + fullText.append(", "); + } + fullText.append(alias); + first = false; + } + fullText.append("\n"); + } + if (split.length > 1) { + fullText.append(split[1]); + } + this.fullText = fullText.toString(); } - if (split.length > 1) { - fullText.append(split[1]); - } - this.fullText = fullText.toString(); } @Override @SuppressWarnings("unchecked") public boolean canSee(CommandSender player) { - if (cmd.getPermissions() != null && cmd.getPermissions().length > 0) { + if (cmd.getRegisteredWith() instanceof CommandInspector) { + CommandInspector resolver = (CommandInspector) cmd.getRegisteredWith(); + return resolver.testPermission(player, cmd); + } else if (cmd.getPermissions() != null && cmd.getPermissions().length > 0) { if (cmd.getRegisteredWith() instanceof CommandsManager) { try { for (String perm : cmd.getPermissions()) { @@ -123,7 +132,7 @@ public class DynamicPluginCommandHelpTopic extends HelpTopic { @Override public String getFullText(CommandSender forWho) { - if (this.fullText == null || this.fullText.length() == 0) { + if (this.fullText == null || this.fullText.isEmpty()) { return getShortText(); } else { return this.fullText; diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java new file mode 100644 index 000000000..9c11224c6 --- /dev/null +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java @@ -0,0 +1,83 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit; + +import com.sk89q.bukkit.util.CommandInspector; +import com.sk89q.minecraft.util.commands.CommandLocals; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.Description; +import com.sk89q.worldedit.util.command.Dispatcher; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +class BukkitCommandInspector implements CommandInspector { + + private static final Logger logger = Logger.getLogger(BukkitCommandInspector.class.getCanonicalName()); + private final WorldEditPlugin plugin; + private final Dispatcher dispatcher; + + BukkitCommandInspector(WorldEditPlugin plugin, Dispatcher dispatcher) { + checkNotNull(plugin); + checkNotNull(dispatcher); + this.plugin = plugin; + this.dispatcher = dispatcher; + } + + @Override + public String getShortText(Command command) { + CommandMapping mapping = dispatcher.get(command.getName()); + if (mapping != null) { + return mapping.getDescription().getShortDescription(); + } else { + logger.warning("BukkitCommandInspector doesn't know how about the command '" + command + "'"); + return "Help text not available"; + } + } + + @Override + public String getFullText(Command command) { + CommandMapping mapping = dispatcher.get(command.getName()); + if (mapping != null) { + Description description = mapping.getDescription(); + return "Usage: " + description.getUsage() + (description.getHelp() != null ? "\n" + description.getHelp() : ""); + } else { + logger.warning("BukkitCommandInspector doesn't know how about the command '" + command + "'"); + return "Help text not available"; + } + } + + @Override + public boolean testPermission(CommandSender sender, Command command) { + CommandMapping mapping = dispatcher.get(command.getName()); + if (mapping != null) { + CommandLocals locals = new CommandLocals(); + locals.put(Actor.class, plugin.wrapCommandSender(sender)); + return mapping.getCallable().testPermission(locals); + } else { + logger.warning("BukkitCommandInspector doesn't know how about the command '" + command + "'"); + return false; + } + } +} diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index d86affe9d..8bf7f874b 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -125,15 +125,14 @@ public class BukkitServerInterface extends ServerInterface { @Override public void registerCommands(Dispatcher dispatcher) { List toRegister = new ArrayList(); + BukkitCommandInspector inspector = new BukkitCommandInspector(plugin, dispatcher); for (CommandMapping command : dispatcher.getCommands()) { Description description = command.getDescription(); List permissions = description.getPermissions(); String[] permissionsArray = new String[permissions.size()]; permissions.toArray(permissionsArray); - toRegister.add(new CommandInfo( - description.getUsage(), description.getDescription(), - command.getAllAliases(), dispatcher, permissionsArray)); + toRegister.add(new CommandInfo(description.getUsage(), description.getShortDescription(), command.getAllAliases(), inspector, permissionsArray)); } dynamicCommands.register(toRegister); diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index 4a60658a3..91a7adcda 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -96,20 +96,21 @@ public class WorldEditListener implements Listener { String[] split = event.getMessage().split(" "); if (split.length > 0) { - split = plugin.getWorldEdit().commandDetection(split); - split[0] = "/" + split[0]; + split[0] = split[0].substring(1); + split = plugin.getWorldEdit().getPlatformManager().getCommandManager().commandDetection(split); } - final String newMessage = StringUtil.joinString(split, " "); + final String newMessage = "/" + StringUtil.joinString(split, " "); if (!newMessage.equals(event.getMessage())) { event.setMessage(newMessage); plugin.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { - if (event.getMessage().length() > 0) { - plugin.getServer().dispatchCommand(event.getPlayer(), - event.getMessage().substring(1)); + if (!event.getMessage().isEmpty()) { + plugin.getServer().dispatchCommand(event.getPlayer(), event.getMessage().substring(1)); } + event.setCancelled(true); } } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index b66ee0e94..9f0beff64 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.google.common.base.Joiner; import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.wepif.PermissionsResolverManager; import com.sk89q.worldedit.*; @@ -27,17 +28,21 @@ import com.sk89q.worldedit.bukkit.selections.CylinderSelection; import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; import com.sk89q.worldedit.bukkit.selections.Selection; import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.regions.*; import org.bukkit.World; +import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import java.io.*; import java.util.Enumeration; +import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; @@ -47,7 +52,7 @@ import java.util.zip.ZipEntry; * * @author sk89q */ -public class WorldEditPlugin extends JavaPlugin { +public class WorldEditPlugin extends JavaPlugin implements TabCompleter { /** * The name of the CUI's plugin channel registration @@ -217,19 +222,32 @@ public class WorldEditPlugin extends JavaPlugin { } @Override - public boolean onCommand(CommandSender sender, org.bukkit.command.Command cmd, String commandLabel, String[] args) { + public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { // Add the command to the array because the underlying command handling // code of WorldEdit expects it String[] split = new String[args.length + 1]; System.arraycopy(args, 0, split, 1, args.length); - split[0] = "/" + cmd.getName(); + split[0] = cmd.getName(); - CommandEvent event = new CommandEvent(wrapCommandSender(sender), split); + CommandEvent event = new CommandEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); getWorldEdit().getEventBus().post(event); return true; } + @Override + public List onTabComplete(CommandSender sender, Command cmd, String commandLabel, String[] args) { + // Add the command to the array because the underlying command handling + // code of WorldEdit expects it + String[] split = new String[args.length + 1]; + System.arraycopy(args, 0, split, 1, args.length); + split[0] = cmd.getName(); + + CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); + getWorldEdit().getEventBus().post(event); + return event.getSuggestions(); + } + /** * Gets the session for the player. * diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 1df421583..dbc53eb1d 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.forge; +import com.google.common.base.Joiner; import com.google.common.io.ByteStreams; import com.google.common.io.Closer; import com.sk89q.worldedit.LocalSession; @@ -123,8 +124,10 @@ public class ForgeWorldEdit { if (((EntityPlayerMP) event.sender).worldObj.isRemote) return; String[] split = new String[event.parameters.length + 1]; System.arraycopy(event.parameters, 0, split, 1, event.parameters.length); - split[0] = ("/" + event.command.getCommandName()); - WorldEdit.getInstance().handleCommand(wrap((EntityPlayerMP) event.sender), split); + split[0] = event.command.getCommandName(); + com.sk89q.worldedit.event.platform.CommandEvent weEvent = + new com.sk89q.worldedit.event.platform.CommandEvent(wrap((EntityPlayerMP) event.sender), Joiner.on(" ").join(split)); + WorldEdit.getInstance().getEventBus().post(weEvent); } } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index a34019da5..1a0d25a5e 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -25,7 +25,6 @@ import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.event.platform.BlockInteractEvent; -import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.event.platform.InputType; import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.extension.input.ParserContext; @@ -740,22 +739,6 @@ public class WorldEdit { return event.isCancelled(); } - /** - * - * @param player - * @param split - * @return whether the command was processed - */ - public boolean handleCommand(Player player, String[] split) { - CommandEvent event = new CommandEvent(player, split); - getEventBus().post(event); - return event.isCancelled(); - } - - public String[] commandDetection(String[] split) { - return getPlatformManager().getCommandManager().commandDetection(split); - } - /** * Executes a WorldEdit script. * diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 8b4999e67..f10d74e91 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -546,8 +546,8 @@ public class UtilityCommands { if (description.getHelp() != null) { actor.print(description.getHelp()); - } else if (description.getDescription() != null) { - actor.print(description.getDescription()); + } else if (description.getShortDescription() != null) { + actor.print(description.getShortDescription()); } else { actor.print("No further help is available."); } diff --git a/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java index a6b7ebdf1..0790787f4 100644 --- a/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java +++ b/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java @@ -30,20 +30,20 @@ import static com.google.common.base.Preconditions.checkNotNull; public class CommandEvent extends AbstractCancellable { private final Actor actor; - private final String[] args; + private final String arguments; /** * Create a new instance. * * @param actor the player - * @param args the arguments + * @param arguments the arguments */ - public CommandEvent(Actor actor, String[] args) { + public CommandEvent(Actor actor, String arguments) { checkNotNull(actor); - checkNotNull(args); + checkNotNull(arguments); this.actor = actor; - this.args = args; + this.arguments = arguments; } /** @@ -60,8 +60,8 @@ public class CommandEvent extends AbstractCancellable { * * @return the arguments */ - public String[] getArguments() { - return args; + public String getArguments() { + return arguments; } } diff --git a/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java new file mode 100644 index 000000000..8fa39c0ff --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/CommandSuggestionEvent.java @@ -0,0 +1,90 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.event.platform; + +import com.sk89q.worldedit.event.Event; +import com.sk89q.worldedit.extension.platform.Actor; + +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Posted when suggestions for auto-completion are requested for command input. + */ +public class CommandSuggestionEvent extends Event { + + private final Actor actor; + private final String arguments; + private List suggestions = Collections.emptyList(); + + /** + * Create a new instance. + * + * @param actor the player + * @param arguments the arguments + */ + public CommandSuggestionEvent(Actor actor, String arguments) { + checkNotNull(actor); + checkNotNull(arguments); + + this.actor = actor; + this.arguments = arguments; + } + + /** + * Get the actor that issued the command. + * + * @return the actor that issued the command + */ + public Actor getActor() { + return actor; + } + + /** + * Get the arguments. + * + * @return the arguments + */ + public String getArguments() { + return arguments; + } + + /** + * Get the list of suggestions that are to be presented. + * + * @return the list of suggestions + */ + public List getSuggestions() { + return suggestions; + } + + /** + * Set the list of suggestions that are to be presented. + * + * @param suggestions the list of suggestions + */ + public void setSuggestions(List suggestions) { + checkNotNull(suggestions); + this.suggestions = suggestions; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index a3e506bdb..a8e5b7043 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -30,8 +30,9 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.*; import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; +import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.command.CommandLoggingHandler; -import com.sk89q.worldedit.internal.command.CommandPermissionsHandler; import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; import com.sk89q.worldedit.session.request.Request; @@ -87,11 +88,11 @@ public final class CommandManager { // Set up the commands manager ParametricBuilder builder = new ParametricBuilder(); + builder.setAuthorizer(new ActorAuthorizer()); builder.addBinding(new WorldEditBinding(worldEdit)); - builder.attach(new CommandPermissionsHandler()); - builder.attach(new WorldEditExceptionConverter(worldEdit)); - builder.attach(new LegacyCommandsHandler()); - builder.attach(new CommandLoggingHandler(worldEdit, logger)); + builder.addExceptionConverter(new WorldEditExceptionConverter(worldEdit)); + builder.addInvokeListener(new LegacyCommandsHandler()); + builder.addInvokeListener(new CommandLoggingHandler(worldEdit, logger)); dispatcher = new CommandGraph() .builder(builder) @@ -171,8 +172,6 @@ public final class CommandManager { } public String[] commandDetection(String[] split) { - split[0] = split[0].substring(1); - // Quick script shortcut if (split[0].matches("^[^/].*\\.js$")) { String[] newSplit = new String[split.length + 1]; @@ -185,13 +184,14 @@ public final class CommandManager { String searchCmd = split[0].toLowerCase(); // Try to detect the command - if (dispatcher.contains(searchCmd)) { - } else if (worldEdit.getConfiguration().noDoubleSlash && dispatcher.contains("/" + searchCmd)) { - split[0] = "/" + split[0]; - } else if (split[0].length() >= 2 && split[0].charAt(0) == '/' - && dispatcher.contains(searchCmd.substring(1))) { - split[0] = split[0].substring(1); + if (!dispatcher.contains(searchCmd)) { + if (worldEdit.getConfiguration().noDoubleSlash && dispatcher.contains("/" + searchCmd)) { + split[0] = "/" + split[0]; + } else if (searchCmd.length() >= 2 && searchCmd.charAt(0) == '/' && dispatcher.contains(searchCmd.substring(1))) { + split[0] = split[0].substring(1); + } } + return split; } @@ -200,7 +200,7 @@ public final class CommandManager { Request.reset(); Actor actor = platformManager.createProxyActor(event.getActor()); - String split[] = commandDetection(event.getArguments()); + String split[] = commandDetection(event.getArguments().split(" ")); // No command found! if (!dispatcher.contains(split[0])) { @@ -216,7 +216,7 @@ public final class CommandManager { long start = System.currentTimeMillis(); try { - dispatcher.call(Joiner.on(" ").join(split), locals); + dispatcher.call(null, Joiner.on(" ").join(split), locals); } catch (CommandPermissionsException e) { actor.printError("You don't have permission to do this."); } catch (InvalidUsageException e) { @@ -255,6 +255,15 @@ public final class CommandManager { event.setCancelled(true); } + @Subscribe + public void handleCommandSuggestion(CommandSuggestionEvent event) { + try { + event.setSuggestions(dispatcher.getSuggestions(event.getArguments())); + } catch (CommandException e) { + event.getActor().printError(e.getMessage()); + } + } + /** * Get the command dispatcher instance. * diff --git a/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java b/src/main/java/com/sk89q/worldedit/internal/command/ActorAuthorizer.java similarity index 75% rename from src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java rename to src/main/java/com/sk89q/worldedit/internal/command/ActorAuthorizer.java index cbf5eb7c5..f2f301ca2 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/ActorAuthorizer.java @@ -19,23 +19,22 @@ package com.sk89q.worldedit.internal.command; -import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.command.parametric.PermissionsHandler; +import com.sk89q.worldedit.util.auth.Authorizer; -public class CommandPermissionsHandler extends PermissionsHandler { - - public CommandPermissionsHandler() { - } +/** + * Implementation of an authorizer that uses {@link Actor#hasPermission(String)}. + */ +public class ActorAuthorizer implements Authorizer { @Override - protected boolean hasPermission(CommandContext context, String permission) { - Actor sender = context.getLocals().get(Actor.class); + public boolean testPermission(CommandLocals locals, String permission) { + Actor sender = locals.get(Actor.class); if (sender == null) { throw new RuntimeException("Uh oh! No 'Actor' specified so that we can check permissions"); } else { return sender.hasPermission(permission); } } - } diff --git a/src/main/java/com/sk89q/worldedit/util/auth/Authorizer.java b/src/main/java/com/sk89q/worldedit/util/auth/Authorizer.java new file mode 100644 index 000000000..adbeafc7c --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/auth/Authorizer.java @@ -0,0 +1,38 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.auth; + +import com.sk89q.minecraft.util.commands.CommandLocals; + +/** + * Tests whether permission is granted. + */ +public interface Authorizer { + + /** + * Tests whether permission is granted for the given context. + * + * @param locals locals + * @param permission the permission string + * @return true if permitted + */ + boolean testPermission(CommandLocals locals, String permission); + +} diff --git a/src/main/java/com/sk89q/worldedit/util/auth/NullAuthorizer.java b/src/main/java/com/sk89q/worldedit/util/auth/NullAuthorizer.java new file mode 100644 index 000000000..346e6b794 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/auth/NullAuthorizer.java @@ -0,0 +1,35 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.util.auth; + +import com.sk89q.minecraft.util.commands.CommandLocals; + +/** + * An implementation of {@link Authorizer} that always returns false for + * tests of permissions. + */ +public class NullAuthorizer implements Authorizer { + + @Override + public boolean testPermission(CommandLocals locals, String permission) { + return false; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java index 0fd96670d..1120dffc8 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java @@ -22,7 +22,8 @@ package com.sk89q.worldedit.util.command; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; -import java.util.Collection; +import javax.annotation.Nullable; +import java.util.List; import java.util.Set; /** @@ -39,13 +40,17 @@ public interface CommandCallable { /** * Execute the correct command based on the input. + *

+ * The implementing class must perform the necessary permission checks. * + * @param alias the alias that was used to invoke this command, + * which may be null if this is a "root" command * @param arguments the arguments * @param locals the locals * @return the called command, or null if there was no command found * @throws CommandException thrown on a command error */ - boolean call(String arguments, CommandLocals locals) throws CommandException; + boolean call(@Nullable String alias, String arguments, CommandLocals locals) throws CommandException; /** * Get a list of suggestions based on input. @@ -54,7 +59,7 @@ public interface CommandCallable { * @return a list of suggestions * @throws CommandException thrown if there was a parsing error */ - Collection getSuggestions(String arguments) throws CommandException; + List getSuggestions(String arguments) throws CommandException; /** * Get an object describing this command. @@ -63,4 +68,12 @@ public interface CommandCallable { */ Description getDescription(); + /** + * Test whether this command can be executed with the given context. + * + * @param locals the locals + * @return true if execution is permitted + */ + boolean testPermission(CommandLocals locals); + } diff --git a/src/main/java/com/sk89q/worldedit/util/command/Description.java b/src/main/java/com/sk89q/worldedit/util/command/Description.java index e45885ed1..72e9fd5f0 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/Description.java +++ b/src/main/java/com/sk89q/worldedit/util/command/Description.java @@ -38,7 +38,7 @@ public interface Description { * * @return a description, or null if no description is available */ - String getDescription(); + String getShortDescription(); /** * Get a longer help text about this command. diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java index 38ab1b799..111ee68e5 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java @@ -49,7 +49,7 @@ public class SimpleDescription implements Description { } @Override - public String getDescription() { + public String getShortDescription() { return description; } @@ -57,7 +57,7 @@ public class SimpleDescription implements Description { * Set the description of the command. * * @param description the description - * @see #getDescription() + * @see #getShortDescription() */ public void setDescription(String description) { this.description = description; diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java index 888445017..0d498668b 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -25,6 +25,7 @@ import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.WrappedCommandException; +import javax.annotation.Nullable; import java.util.*; /** @@ -89,20 +90,20 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public boolean call(String arguments, CommandLocals locals) throws CommandException { + public boolean call(@Nullable String alias, String arguments, CommandLocals locals) throws CommandException { String[] split = CommandContext.split(arguments); Set aliases = getPrimaryAliases(); if (aliases.isEmpty()) { throw new InvalidUsageException("This command has no sub-commands.", getDescription()); - } else if (split.length != 0) { + } else if (split.length > 0) { String subCommand = split[0]; + String subArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); CommandMapping mapping = get(subCommand); - String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); if (mapping != null) { try { - mapping.getCallable().call(passedArguments, locals); + mapping.getCallable().call(subCommand, subArguments, locals); } catch (CommandException e) { e.prependStack(subCommand); throw e; @@ -119,22 +120,26 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public Collection getSuggestions(String arguments) throws CommandException { + public List getSuggestions(String arguments) throws CommandException { String[] split = CommandContext.split(arguments); if (split.length == 0) { - return getAllAliases(); + return new ArrayList(getAllAliases()); } else if (split.length == 1) { String prefix = split[0]; - List suggestions = new ArrayList(); + if (!prefix.isEmpty()) { + List suggestions = new ArrayList(); - for (String alias : getAllAliases()) { - if (alias.startsWith(prefix)) { - suggestions.add(alias); + for (String alias : getAllAliases()) { + if (alias.startsWith(prefix)) { + suggestions.add(alias); + } } - } - return suggestions; + return suggestions; + } else { + return new ArrayList(getAllAliases()); + } } else { String subCommand = split[0]; CommandMapping mapping = get(subCommand); @@ -153,6 +158,17 @@ public class SimpleDispatcher implements Dispatcher { return description; } + @Override + public boolean testPermission(CommandLocals locals) { + for (CommandMapping mapping : getCommands()) { + if (mapping.getCallable().testPermission(locals)) { + return true; + } + } + + return false; + } + /** * Get a list of subcommands for display. * diff --git a/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java b/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java index 8a4f71b6c..49b53d8b9 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java +++ b/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java @@ -79,14 +79,14 @@ public class DispatcherNode { * * @param object the object provided to the {@link ParametricBuilder} * @return this object - * @see ParametricBuilder#register(com.sk89q.worldedit.util.command.Dispatcher, Object) + * @see ParametricBuilder#registerMethodsAsCommands(com.sk89q.worldedit.util.command.Dispatcher, Object) */ public DispatcherNode build(Object object) { ParametricBuilder builder = graph.getBuilder(); if (builder == null) { throw new RuntimeException("No ParametricBuilder set"); } - builder.register(getDispatcher(), object); + builder.registerMethodsAsCommands(getDispatcher(), object); return this; } diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java index be7bc2c1e..f788a695f 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java @@ -24,6 +24,8 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.util.auth.Authorizer; +import com.sk89q.worldedit.util.auth.NullAuthorizer; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; @@ -39,6 +41,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Creates commands using annotations placed on methods and individual parameters of * such methods. @@ -52,6 +56,7 @@ public class ParametricBuilder { private final Paranamer paranamer = new CachingParanamer(); private final List invokeListeners = new ArrayList(); private final List exceptionConverters = new ArrayList(); + private Authorizer authorizer = new NullAuthorizer(); /** * Create a new builder. @@ -115,7 +120,7 @@ public class ParametricBuilder { * @param listener the listener * @see InvokeHandler the handler */ - public void attach(InvokeListener listener) { + public void addInvokeListener(InvokeListener listener) { invokeListeners.add(listener); } @@ -128,7 +133,7 @@ public class ParametricBuilder { * @param converter the converter * @see ExceptionConverter for an explanation */ - public void attach(ExceptionConverter converter) { + public void addExceptionConverter(ExceptionConverter converter) { exceptionConverters.add(converter); } @@ -141,7 +146,7 @@ public class ParametricBuilder { * @param object the object contain the methods * @throws ParametricException thrown if the commands cannot be registered */ - public void register(Dispatcher dispatcher, Object object) throws ParametricException { + public void registerMethodsAsCommands(Dispatcher dispatcher, Object object) throws ParametricException { for (Method method : object.getClass().getDeclaredMethods()) { Command definition = method.getAnnotation(Command.class); if (definition != null) { @@ -201,5 +206,23 @@ public class ParametricBuilder { List getExceptionConverters() { return exceptionConverters; } - + + /** + * Get the authorizer. + * + * @return the authorizer + */ + public Authorizer getAuthorizer() { + return authorizer; + } + + /** + * Set the authorizer. + * + * @param authorizer the authorizer + */ + public void setAuthorizer(Authorizer authorizer) { + checkNotNull(authorizer); + this.authorizer = authorizer; + } } diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java index 23e2d1b22..bada21862 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java @@ -23,6 +23,7 @@ import com.sk89q.minecraft.util.commands.*; import com.sk89q.worldedit.util.command.*; import com.sk89q.worldedit.util.command.binding.Switch; +import javax.annotation.Nullable; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -40,6 +41,7 @@ class ParametricCallable implements CommandCallable { private final ParameterData[] parameters; private final Set valueFlags = new HashSet(); private final SimpleDescription description = new SimpleDescription(); + private final CommandPermissions commandPermissions; /** * Create a new instance. @@ -50,11 +52,7 @@ class ParametricCallable implements CommandCallable { * @param definition the command definition annotation * @throws ParametricException thrown on an error */ - ParametricCallable( - ParametricBuilder builder, - Object object, Method method, - Command definition) throws ParametricException { - + ParametricCallable(ParametricBuilder builder, Object object, Method method, Command definition) throws ParametricException { this.builder = builder; this.object = object; this.method = method; @@ -101,8 +99,7 @@ class ParametricCallable implements CommandCallable { } } - parameter.setName(names.length > 0 ? - names[i] : generateName(type, parameter.getClassifier(), i)); + parameter.setName(names.length > 0 ? names[i] : generateName(type, parameter.getClassifier(), i)); // Track all value flags if (parameter.isValueFlag()) { @@ -115,9 +112,7 @@ class ParametricCallable implements CommandCallable { // Don't know how to parse for this type of value if (parameter.getBinding() == null) { - throw new ParametricException( - "Don't know how to handle the parameter type '" + type + "' in\n" + - method.toGenericString()); + throw new ParametricException("Don't know how to handle the parameter type '" + type + "' in\n" + method.toGenericString()); } } @@ -159,11 +154,19 @@ class ParametricCallable implements CommandCallable { // Set parameters description.setParameters(userParameters); + + // Get permissions annotation + commandPermissions = method.getAnnotation(CommandPermissions.class); } @Override - public boolean call(String stringArguments, CommandLocals locals) throws CommandException { - String[] split = CommandContext.split(stringArguments); + public boolean call(@Nullable String alias, String stringArguments, CommandLocals locals) throws CommandException { + // Test permission + if (!testPermission(locals)) { + throw new CommandPermissionsException(); + } + + String[] split = CommandContext.split(alias + " " + stringArguments); CommandContext context = new CommandContext(split, getValueFlags(), false, locals); Object[] args = new Object[parameters.length]; @@ -218,12 +221,9 @@ class ParametricCallable implements CommandCallable { handler.postInvoke(handler, method, parameters, args, context); } } catch (MissingParameterException e) { - throw new InvalidUsageException( - "Too few parameters!", getDescription()); + throw new InvalidUsageException("Too few parameters!", getDescription()); } catch (UnconsumedParameterException e) { - throw new InvalidUsageException( - "Too many parameters! Unused parameters: " - + e.getUnconsumed(), getDescription()); + throw new InvalidUsageException("Too many parameters! Unused parameters: " + e.getUnconsumed(), getDescription()); } catch (ParameterException e) { if (e.getCause() != null) { for (ExceptionConverter converter : builder.getExceptionConverters()) { @@ -231,10 +231,10 @@ class ParametricCallable implements CommandCallable { } } + assert parameter != null; String name = parameter.getName(); - throw new InvalidUsageException("For parameter '" + name + "': " - + e.getMessage(), getDescription()); + throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), getDescription()); } catch (InvocationTargetException e) { for (ExceptionConverter converter : builder.getExceptionConverters()) { converter.convert(e.getCause()); @@ -252,7 +252,7 @@ class ParametricCallable implements CommandCallable { } @Override - public Collection getSuggestions(String stringArguments) throws CommandException { + public List getSuggestions(String stringArguments) throws CommandException { String[] split = CommandContext.split(stringArguments); CommandContext context = new CommandContext(split, getValueFlags()); @@ -281,13 +281,11 @@ class ParametricCallable implements CommandCallable { ParameterData lastConsumer = null; String lastConsumed = null; - for (int i = 0; i < parameters.length; i++) { - ParameterData parameter = parameters[i]; - + for (ParameterData parameter : parameters) { if (parameter.getFlag() != null) { continue; // We already handled flags } - + try { scoped.mark(); parameter.getBinding().bind(parameter, scoped, true); @@ -306,8 +304,8 @@ class ParametricCallable implements CommandCallable { if (lastConsumer != null) { return lastConsumer.getBinding() .getSuggestions(lastConsumer, lastConsumed); - // For /command| value1 value2 - // This should never occur + // For /command| value1 value2 + // This should never occur } else { throw new RuntimeException("Invalid suggestion context"); } @@ -352,7 +350,22 @@ class ParametricCallable implements CommandCallable { public SimpleDescription getDescription() { return description; } - + + @Override + public boolean testPermission(CommandLocals locals) { + if (commandPermissions != null) { + for (String perm : commandPermissions.value()) { + if (builder.getAuthorizer().testPermission(locals, perm)) { + return true; + } + } + + return false; + } else { + return true; + } + } + /** * Get the right {@link ArgumentStack}. * diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/PermissionsHandler.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/PermissionsHandler.java deleted file mode 100644 index ed11942ba..000000000 --- a/src/main/java/com/sk89q/worldedit/util/command/parametric/PermissionsHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.util.command.parametric; - -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.CommandPermissionsException; - -import java.lang.reflect.Method; - -/** - * A handler for the {@link CommandPermissions} annotation. - */ -public abstract class PermissionsHandler extends AbstractInvokeListener implements InvokeHandler { - - @Override - public InvokeHandler createInvokeHandler() { - return this; - } - - @Override - public void preProcess(Object object, Method method, - ParameterData[] parameters, CommandContext context) - throws CommandException, ParameterException { - CommandPermissions annotation = method.getAnnotation(CommandPermissions.class); - if (annotation != null) { - for (String perm : annotation.value()) { - if (hasPermission(context, perm)) { - return; - } - } - - throw new CommandPermissionsException(); - } - } - - @Override - public void preInvoke(Object object, Method method, ParameterData[] parameters, - Object[] args, CommandContext context) throws CommandException { - } - - @Override - public void postInvoke(Object object, Method method, ParameterData[] parameters, - Object[] args, CommandContext context) throws CommandException { - } - - protected abstract boolean hasPermission(CommandContext context, String permission); - -}