From 142f5c8e5c889ee5098c05ba2fde20b52467c1df Mon Sep 17 00:00:00 2001 From: Albert Pham Date: Tue, 18 Jun 2013 14:51:42 -0700 Subject: [PATCH] Changed WorldEdit to use the new dispatcher. --- .../com/sk89q/worldedit/ServerInterface.java | 10 +- .../java/com/sk89q/worldedit/WorldEdit.java | 414 ++++++------------ .../sk89q/worldedit/annotation/Direction.java | 37 ++ .../sk89q/worldedit/annotation/Selection.java | 33 ++ .../bukkit/BukkitServerInterface.java | 33 +- .../worldedit/bukkit/WorldEditPlugin.java | 26 +- .../worldedit/commands/BiomeCommands.java | 2 +- .../worldedit/commands/ClipboardCommands.java | 22 +- .../worldedit/commands/GeneralCommands.java | 12 +- .../commands/SnapshotUtilCommands.java | 12 +- .../worldedit/commands/ToolCommands.java | 26 +- .../worldedit/commands/ToolUtilCommands.java | 26 +- .../worldedit/commands/UtilityCommands.java | 66 ++- .../worldedit/util/CommandLoggingHandler.java | 164 +++++++ .../util/CommandPermissionsHandler.java | 40 ++ .../worldedit/util/WorldEditBinding.java | 177 ++++++++ .../util/WorldEditExceptionConverter.java | 163 +++++++ 17 files changed, 882 insertions(+), 381 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/annotation/Direction.java create mode 100644 src/main/java/com/sk89q/worldedit/annotation/Selection.java create mode 100644 src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java create mode 100644 src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java create mode 100644 src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java create mode 100644 src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java diff --git a/src/main/java/com/sk89q/worldedit/ServerInterface.java b/src/main/java/com/sk89q/worldedit/ServerInterface.java index 84c25f00f..685f86170 100644 --- a/src/main/java/com/sk89q/worldedit/ServerInterface.java +++ b/src/main/java/com/sk89q/worldedit/ServerInterface.java @@ -19,12 +19,13 @@ package com.sk89q.worldedit; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandsManager; - import java.util.Collections; import java.util.List; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.rebar.command.Dispatcher; + /** * * @author sk89q @@ -83,4 +84,7 @@ public abstract class ServerInterface { public void onCommandRegistration(List commands, CommandsManager manager) { onCommandRegistration(commands); } + + public void registerCommands(Dispatcher dispatcher) { + } } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index a2d0e9c4d..7beb56773 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -24,7 +24,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -32,49 +31,47 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.logging.FileHandler; -import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; import javax.script.ScriptException; import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandPermissionsException; -import com.sk89q.minecraft.util.commands.CommandUsageException; -import com.sk89q.minecraft.util.commands.CommandsManager; -import com.sk89q.minecraft.util.commands.Console; -import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.minecraft.util.commands.MissingNestedCommandException; -import com.sk89q.minecraft.util.commands.SimpleInjector; -import com.sk89q.minecraft.util.commands.UnhandledCommandException; import com.sk89q.minecraft.util.commands.WrappedCommandException; -import com.sk89q.util.StringUtil; +import com.sk89q.rebar.command.Dispatcher; +import com.sk89q.rebar.command.InvalidUsageException; +import com.sk89q.rebar.command.fluent.CommandGraph; +import com.sk89q.rebar.command.parametric.LegacyCommandsHandler; +import com.sk89q.rebar.command.parametric.ParametricBuilder; import com.sk89q.worldedit.CuboidClipboard.FlipDirection; import com.sk89q.worldedit.bags.BlockBag; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.blocks.ClothColor; -import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.NoteBlock; import com.sk89q.worldedit.blocks.SignBlock; import com.sk89q.worldedit.blocks.SkullBlock; import com.sk89q.worldedit.commands.BiomeCommands; +import com.sk89q.worldedit.commands.BrushCommands; import com.sk89q.worldedit.commands.ChunkCommands; import com.sk89q.worldedit.commands.ClipboardCommands; import com.sk89q.worldedit.commands.GeneralCommands; import com.sk89q.worldedit.commands.GenerationCommands; import com.sk89q.worldedit.commands.HistoryCommands; -import com.sk89q.worldedit.commands.InsufficientArgumentsException; import com.sk89q.worldedit.commands.NavigationCommands; import com.sk89q.worldedit.commands.RegionCommands; +import com.sk89q.worldedit.commands.SchematicCommands; import com.sk89q.worldedit.commands.ScriptingCommands; import com.sk89q.worldedit.commands.SelectionCommands; +import com.sk89q.worldedit.commands.SnapshotCommands; import com.sk89q.worldedit.commands.SnapshotUtilCommands; +import com.sk89q.worldedit.commands.SuperPickaxeCommands; import com.sk89q.worldedit.commands.ToolCommands; import com.sk89q.worldedit.commands.ToolUtilCommands; import com.sk89q.worldedit.commands.UtilityCommands; +import com.sk89q.worldedit.commands.WorldEditCommands; import com.sk89q.worldedit.masks.BiomeTypeMask; import com.sk89q.worldedit.masks.BlockMask; import com.sk89q.worldedit.masks.CombinedMask; @@ -99,6 +96,10 @@ import com.sk89q.worldedit.tools.DoubleActionBlockTool; import com.sk89q.worldedit.tools.DoubleActionTraceTool; import com.sk89q.worldedit.tools.Tool; import com.sk89q.worldedit.tools.TraceTool; +import com.sk89q.worldedit.util.CommandLoggingHandler; +import com.sk89q.worldedit.util.CommandPermissionsHandler; +import com.sk89q.worldedit.util.WorldEditBinding; +import com.sk89q.worldedit.util.WorldEditExceptionConverter; /** * This class is the main entry point for WorldEdit. All events are routed @@ -109,186 +110,99 @@ import com.sk89q.worldedit.tools.TraceTool; * @author sk89q */ public class WorldEdit { - /** - * Logger for debugging. - */ + public static final Logger logger = Logger.getLogger("Minecraft.WorldEdit"); - public final Logger commandLogger = Logger.getLogger("Minecraft.WorldEdit.CommandLogger"); - /** - * Holds the current instance of this class, for static access - */ private static WorldEdit instance; - - /** - * Holds WorldEdit's version. - */ private static String version; - - /** - * Interface to the server. - */ - private final ServerInterface server; - - /** - * Configuration. This is a subclass. - */ - private final LocalConfiguration config; - - /** - * List of commands. - */ - private final CommandsManager commands; - /** - * Holds the factory responsible for the creation of edit sessions - */ + private final ServerInterface server; + private final LocalConfiguration config; + private final Dispatcher dispatcher; + private final CommandLoggingHandler commandLogger; + private final HashMap sessions = new HashMap(); private EditSessionFactory editSessionFactory = new EditSessionFactory(); - /** - * Stores a list of WorldEdit sessions, keyed by players' names. Sessions - * persist only for the user's session. On disconnect, the session will be - * removed. Sessions are created only when they are needed and those - * without any WorldEdit abilities or never use WorldEdit in a session will - * not have a session object generated for them. - */ - private final HashMap sessions = new HashMap(); - - /** - * Initialize statically. - */ static { getVersion(); } /** - * Construct an instance of the plugin - * - * @param server - * @param config - */ - public WorldEdit(ServerInterface server, final LocalConfiguration config) { - instance = this; - this.server = server; - this.config = config; - - if (!config.logFile.equals("")) { - try { - FileHandler logFileHandler; - logFileHandler = new FileHandler(new File(config.getWorkingDirectory(), - config.logFile).getAbsolutePath(), true); - logFileHandler.setFormatter(new LogFormat()); - commandLogger.addHandler(logFileHandler); - } catch (IOException e) { - logger.log(Level.WARNING, "Could not use command log file " + config.logFile + ": " - + e.getMessage()); - } - } - - commands = new CommandsManager() { - @Override - protected void checkPermission(LocalPlayer player, Method method) throws CommandException { - if (!player.isPlayer() && !method.isAnnotationPresent(Console.class)) { - throw new UnhandledCommandException(); - } - - super.checkPermission(player, method); - } - - @Override - public boolean hasPermission(LocalPlayer player, String perm) { - return player.hasPermission(perm); - } - - @Override - public void invokeMethod(Method parent, String[] args, - LocalPlayer player, Method method, Object instance, - Object[] methodArgs, int level) throws CommandException { - if (config.logCommands) { - final Logging loggingAnnotation = method.getAnnotation(Logging.class); - - final Logging.LogMode logMode; - if (loggingAnnotation == null) { - logMode = null; - } else { - logMode = loggingAnnotation.value(); - } - - String msg = "WorldEdit: " + player.getName(); - if (player.isPlayer()) { - msg += " (in \"" + player.getWorld().getName() + "\")"; - } - msg += ": " + StringUtil.joinString(args, " "); - if (logMode != null && player.isPlayer()) { - Vector position = player.getPosition(); - final LocalSession session = getSession(player); - switch (logMode) { - case PLACEMENT: - try { - position = session.getPlacementPosition(player); - } catch (IncompleteRegionException e) { - break; - } - /* FALL-THROUGH */ - - case POSITION: - msg += " - Position: " + position; - break; - - case ALL: - msg += " - Position: " + position; - /* FALL-THROUGH */ - - case ORIENTATION_REGION: - msg += " - Orientation: " + player.getCardinalDirection().name(); - /* FALL-THROUGH */ - - case REGION: - try { - msg += " - Region: " + session.getSelection(player.getWorld()); - } catch (IncompleteRegionException e) { - break; - } - break; - } - } - commandLogger.info(msg); - } - super.invokeMethod(parent, args, player, method, instance, methodArgs, level); - } - }; - - commands.setInjector(new SimpleInjector(this)); - - reg(BiomeCommands.class); - reg(ChunkCommands.class); - reg(ClipboardCommands.class); - reg(GeneralCommands.class); - reg(GenerationCommands.class); - reg(HistoryCommands.class); - reg(NavigationCommands.class); - reg(RegionCommands.class); - reg(ScriptingCommands.class); - reg(SelectionCommands.class); - reg(SnapshotUtilCommands.class); - reg(ToolUtilCommands.class); - reg(ToolCommands.class); - reg(UtilityCommands.class); - } - - private void reg(Class clazz) { - server.onCommandRegistration(commands.registerAndReturn(clazz), commands); - } - - /** - * Gets the current instance of this class + * Gets the current instance of this class. * - * @return + *

An instance is available once a plugin/host has initialized WorldEdit.

+ * + * @return an instance */ public static WorldEdit getInstance() { return instance; } + /** + * Construct an instance of the class. + * + * @param server the server interface + * @param config the configuration + */ + public WorldEdit(ServerInterface server, final LocalConfiguration config) { + WorldEdit.instance = this; + this.server = server; + this.config = config; + + ParametricBuilder builder = new ParametricBuilder(); + builder.addBinding(new WorldEditBinding(this)); + builder.attach(new CommandPermissionsHandler()); + builder.attach(new WorldEditExceptionConverter(config)); + builder.attach(new LegacyCommandsHandler()); + builder.attach(commandLogger = new CommandLoggingHandler(this, config)); + + dispatcher = new CommandGraph() + .builder(builder) + .commands() + .build(new BiomeCommands(this)) + .build(new ChunkCommands(this)) + .build(new ClipboardCommands(this)) + .build(new GeneralCommands(this)) + .build(new GenerationCommands(this)) + .build(new HistoryCommands(this)) + .build(new NavigationCommands(this)) + .build(new RegionCommands(this)) + .build(new ScriptingCommands(this)) + .build(new SelectionCommands(this)) + .build(new SnapshotUtilCommands(this)) + .build(new ToolUtilCommands(this)) + .build(new ToolCommands(this)) + .build(new UtilityCommands(this)) + .group("worldedit", "we") + .describe("WorldEdit commands") + .build(new WorldEditCommands(this)) + .parent() + .group("schematic", "schem", "/schematic", "/schem") + .describe("Schematic commands for saving/loading areas") + .build(new SchematicCommands(this)) + .parent() + .group("snapshot", "snap") + .describe("Schematic commands for saving/loading areas") + .build(new SnapshotCommands(this)) + .parent() + .group("brush", "br") + .describe("Brushing commands") + .build(new BrushCommands(this)) + .parent() + .group("superpickaxe", "pickaxe", "sp") + .describe("Super-pickaxe commands") + .build(new SuperPickaxeCommands(this)) + .parent() + .group("tool") + .describe("Bind functions to held items") + .build(new ToolCommands(this)) + .parent() + .graph() + .getDispatcher(); + + server.registerCommands(dispatcher); + } + /** * Gets the LocalSession for a player name if it exists * @@ -302,8 +216,8 @@ public class WorldEdit { /** * Gets the WorldEdit session for a player. * - * @param player - * @return + * @param player the player + * @return the session */ public LocalSession getSession(LocalPlayer player) { LocalSession session; @@ -1149,17 +1063,12 @@ public class WorldEdit { } /** - * @return the commands + * Get the command dispatcher. + * + * @return the command dispatcher */ - public Map getCommands() { - return commands.getCommands(); - } - - /** - * @return the commands - */ - public CommandsManager getCommandsManager() { - return commands; + public Dispatcher getDispatcher() { + return dispatcher; } /** @@ -1367,48 +1276,44 @@ public class WorldEdit { return false; } - private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$"); - /** - * - * @param player - * @param split + * Execute a command. + * + * @param player the local player + * @param split the list of arguments * @return whether the command was processed */ public boolean handleCommand(LocalPlayer player, String[] split) { + split = commandDetection(split); + + // No command found! + if (!dispatcher.contains(split[0])) { + return false; + } + + CommandLocals locals = new CommandLocals(); + locals.put(LocalPlayer.class, player); + + long start = System.currentTimeMillis(); + try { - split = commandDetection(split); - - // No command found! - if (!commands.hasCommand(split[0])) { - return false; - } - - LocalSession session = getSession(player); - EditSession editSession = session.createEditSession(player); - editSession.enableQueue(); - - session.tellVersion(player); - - long start = System.currentTimeMillis(); - - try { - commands.execute(split, player, session, player, editSession); - } catch (CommandPermissionsException e) { - player.printError("You don't have permission to do this."); - } catch (MissingNestedCommandException e) { - player.printError(e.getUsage()); - } catch (CommandUsageException e) { - player.printError(e.getMessage()); - player.printError(e.getUsage()); - } catch (PlayerNeededException e) { - player.printError(e.getMessage()); - } catch (WrappedCommandException e) { - throw e.getCause(); - } catch (UnhandledCommandException e) { - player.printError("Command could not be handled; invalid sender!"); - return false; - } finally { + dispatcher.call(split, locals); + } catch (CommandPermissionsException e) { + player.printError("You don't have permission to do this."); + } catch (InvalidUsageException e) { + player.printError(e.getMessage() + "\nUsage: " + e.getUsage("/")); + } catch (WrappedCommandException e) { + Throwable t = e.getCause(); + player.printError("Please report this error: [See console]"); + player.printRaw(t.getClass().getName() + ": " + t.getMessage()); + t.printStackTrace(); + } catch (CommandException e) { + player.printError(e.getMessage()); + } finally { + EditSession editSession = locals.get(EditSession.class); + + if (editSession != null) { + LocalSession session = getSession(player); session.remember(editSession); editSession.flushQueue(); @@ -1427,50 +1332,6 @@ public class WorldEdit { flushBlockBag(player, editSession); } - } catch (NumberFormatException e) { - final Matcher matcher = numberFormatExceptionPattern.matcher(e.getMessage()); - - if (matcher.matches()) { - player.printError("Number expected; string \"" + matcher.group(1) + "\" given."); - } else { - player.printError("Number expected; string given."); - } - } catch (IncompleteRegionException e) { - player.printError("Make a region selection first."); - } catch (UnknownItemException e) { - player.printError("Block name '" + e.getID() + "' was not recognized."); - } catch (InvalidItemException e) { - player.printError(e.getMessage()); - } catch (DisallowedItemException e) { - player.printError("Block '" + e.getID() + "' not allowed (see WorldEdit configuration)."); - } catch (MaxChangedBlocksException e) { - player.printError("Max blocks changed in an operation reached (" - + e.getBlockLimit() + ")."); - } catch (MaxRadiusException e) { - player.printError("Maximum radius: " + config.maxRadius); - } catch (UnknownDirectionException e) { - player.printError("Unknown direction: " + e.getDirection()); - } catch (InsufficientArgumentsException e) { - player.printError(e.getMessage()); - } catch (EmptyClipboardException e) { - player.printError("Your clipboard is empty. Use //copy first."); - } catch (InvalidFilenameException e) { - player.printError("Filename '" + e.getFilename() + "' invalid: " - + e.getMessage()); - } catch (FilenameResolutionException e) { - player.printError("File '" + e.getFilename() + "' resolution error: " - + e.getMessage()); - } catch (InvalidToolBindException e) { - player.printError("Can't bind tool to " - + ItemType.toHeldName(e.getItemId()) + ": " + e.getMessage()); - } catch (FileSelectionAbortedException e) { - player.printError("File selection aborted."); - } catch (WorldEditException e) { - player.printError(e.getMessage()); - } catch (Throwable excp) { - player.printError("Please report this error: [See console]"); - player.printRaw(excp.getClass().getName() + ": " + excp.getMessage()); - excp.printStackTrace(); } return true; @@ -1491,11 +1352,11 @@ public class WorldEdit { String searchCmd = split[0].toLowerCase(); // Try to detect the command - if (commands.hasCommand(searchCmd)) { - } else if (config.noDoubleSlash && commands.hasCommand("/" + searchCmd)) { + if (dispatcher.contains(searchCmd)) { + } else if (config.noDoubleSlash && dispatcher.contains("/" + searchCmd)) { split[0] = "/" + split[0]; } else if (split[0].length() >= 2 && split[0].charAt(0) == '/' - && commands.hasCommand(searchCmd.substring(1))) { + && dispatcher.contains(searchCmd.substring(1))) { split[0] = split[0].substring(1); } return split; @@ -1599,6 +1460,15 @@ public class WorldEdit { return config; } + /** + * Get the command logger. + * + * @return the logger + */ + public CommandLoggingHandler getCommandLogger() { + return commandLogger; + } + /** * Get the server interface. * diff --git a/src/main/java/com/sk89q/worldedit/annotation/Direction.java b/src/main/java/com/sk89q/worldedit/annotation/Direction.java new file mode 100644 index 000000000..6b1005885 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/annotation/Direction.java @@ -0,0 +1,37 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.sk89q.worldedit.Vector; + +/** + * Annotates a {@link Vector} parameter to inject a direction. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Direction { + + public static final String AIM = "me"; + +} diff --git a/src/main/java/com/sk89q/worldedit/annotation/Selection.java b/src/main/java/com/sk89q/worldedit/annotation/Selection.java new file mode 100644 index 000000000..8612fbb52 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/annotation/Selection.java @@ -0,0 +1,33 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that this value should come from the current selection. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Selection { + +} diff --git a/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index d1ce298a3..9c88f200c 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -23,20 +23,22 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import com.sk89q.bukkit.util.CommandInfo; -import com.sk89q.bukkit.util.CommandRegistration; -import com.sk89q.minecraft.util.commands.Command; - -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.CommandsManager; -import com.sk89q.worldedit.LocalPlayer; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.EntityType; +import com.sk89q.bukkit.util.CommandInfo; +import com.sk89q.bukkit.util.CommandRegistration; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.rebar.command.CommandMapping; +import com.sk89q.rebar.command.Description; +import com.sk89q.rebar.command.Dispatcher; import com.sk89q.worldedit.BiomeTypes; +import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.ServerInterface; @@ -108,6 +110,23 @@ public class BukkitServerInterface extends ServerInterface { dynamicCommands.register(toRegister); } + @Override + public void registerCommands(Dispatcher dispatcher) { + List toRegister = new ArrayList(); + 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)); + } + + dynamicCommands.register(toRegister); + } + public void unregisterCommands() { dynamicCommands.unregisterCommands(); } diff --git a/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index d19de40d7..188d9cde7 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -27,7 +27,6 @@ import java.io.InputStream; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.logging.Handler; import java.util.zip.ZipEntry; import org.bukkit.Bukkit; @@ -61,32 +60,13 @@ import com.sk89q.worldedit.regions.RegionSelector; */ public class WorldEditPlugin extends JavaPlugin { - /** - * The name of the CUI's plugin channel registration - */ public static final String CUI_PLUGIN_CHANNEL = "WECUI"; - - /** - * The server interface that all server-related API goes through. - */ + private BukkitServerInterface server; - /** - * Main WorldEdit instance. - */ private WorldEdit controller; - /** - * Deprecated API. - */ private WorldEditAPI api; - - /** - * Holds the configuration for WorldEdit. - */ private BukkitConfiguration config; - /** - * Called on plugin enable. - */ @Override public void onEnable() { final String pluginYmlVersion = getDescription().getVersion(); @@ -165,9 +145,7 @@ public class WorldEditPlugin extends JavaPlugin { } } controller.clearSessions(); - for (Handler h : controller.commandLogger.getHandlers()) { - h.close(); - } + controller.getCommandLogger().close(); config.unload(); server.unregisterCommands(); this.getServer().getScheduler().cancelTasks(this); diff --git a/src/main/java/com/sk89q/worldedit/commands/BiomeCommands.java b/src/main/java/com/sk89q/worldedit/commands/BiomeCommands.java index f25c25cd8..7ccc6a30c 100644 --- a/src/main/java/com/sk89q/worldedit/commands/BiomeCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/BiomeCommands.java @@ -1,6 +1,6 @@ package com.sk89q.worldedit.commands; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; import java.util.HashSet; import java.util.List; diff --git a/src/main/java/com/sk89q/worldedit/commands/ClipboardCommands.java b/src/main/java/com/sk89q/worldedit/commands/ClipboardCommands.java index 82d8cef98..7e0d0bf1d 100644 --- a/src/main/java/com/sk89q/worldedit/commands/ClipboardCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/ClipboardCommands.java @@ -19,14 +19,21 @@ package com.sk89q.worldedit.commands; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; + import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; - -import com.sk89q.minecraft.util.commands.NestedCommand; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.CuboidClipboard; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalEntity; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.regions.Region; @@ -232,13 +239,6 @@ public class ClipboardCommands { player.printError("This command is no longer used. See //schematic save."); } - @Command( - aliases = { "/schematic", "/schem"}, - desc = "Schematic-related commands" - ) - @NestedCommand(SchematicCommands.class) - public void schematic() {} - @Command( aliases = { "clearclipboard" }, usage = "", diff --git a/src/main/java/com/sk89q/worldedit/commands/GeneralCommands.java b/src/main/java/com/sk89q/worldedit/commands/GeneralCommands.java index 0e0fea5f7..feba74785 100644 --- a/src/main/java/com/sk89q/worldedit/commands/GeneralCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/GeneralCommands.java @@ -23,7 +23,6 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Console; -import com.sk89q.minecraft.util.commands.NestedCommand; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalPlayer; @@ -221,14 +220,5 @@ public class GeneralCommands { player.printError("No items found."); } } - - @Command( - aliases = { "we", "worldedit" }, - desc = "WorldEdit commands" - ) - @NestedCommand(WorldEditCommands.class) - @Console - public void we(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - } + } diff --git a/src/main/java/com/sk89q/worldedit/commands/SnapshotUtilCommands.java b/src/main/java/com/sk89q/worldedit/commands/SnapshotUtilCommands.java index cfeb5e430..e8c667b77 100644 --- a/src/main/java/com/sk89q/worldedit/commands/SnapshotUtilCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/SnapshotUtilCommands.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.commands; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; import java.io.File; import java.io.IOException; @@ -29,7 +29,6 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; -import com.sk89q.minecraft.util.commands.NestedCommand; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalPlayer; @@ -54,15 +53,6 @@ public class SnapshotUtilCommands { this.we = we; } - @Command( - aliases = { "snapshot", "snap" }, - desc = "Snapshot commands" - ) - @NestedCommand(SnapshotCommands.class) - public void snapshot(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - } - @Command( aliases = { "restore", "/restore" }, usage = "[snapshot]", diff --git a/src/main/java/com/sk89q/worldedit/commands/ToolCommands.java b/src/main/java/com/sk89q/worldedit/commands/ToolCommands.java index c3e3662d8..30699044f 100644 --- a/src/main/java/com/sk89q/worldedit/commands/ToolCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/ToolCommands.java @@ -22,12 +22,23 @@ package com.sk89q.worldedit.commands; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.NestedCommand; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldedit.patterns.Pattern; -import com.sk89q.worldedit.tools.*; +import com.sk89q.worldedit.tools.BlockDataCyler; +import com.sk89q.worldedit.tools.BlockReplacer; +import com.sk89q.worldedit.tools.DistanceWand; +import com.sk89q.worldedit.tools.FloatingTreeRemover; +import com.sk89q.worldedit.tools.FloodFillTool; +import com.sk89q.worldedit.tools.LongRangeBuildTool; +import com.sk89q.worldedit.tools.QueryTool; +import com.sk89q.worldedit.tools.TreePlanter; import com.sk89q.worldedit.util.TreeGenerator; public class ToolCommands { @@ -151,15 +162,6 @@ public class ToolCommands { + ItemType.toHeldName(player.getItemInHand()) + "."); } - @Command( - aliases = { "brush", "br" }, - desc = "Brush tool" - ) - @NestedCommand(BrushCommands.class) - public void brush(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - } - @Command( aliases = { "deltree" }, usage = "", diff --git a/src/main/java/com/sk89q/worldedit/commands/ToolUtilCommands.java b/src/main/java/com/sk89q/worldedit/commands/ToolUtilCommands.java index 6981add2b..382b4c09d 100644 --- a/src/main/java/com/sk89q/worldedit/commands/ToolUtilCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/ToolUtilCommands.java @@ -22,8 +22,12 @@ package com.sk89q.worldedit.commands; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.NestedCommand; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.masks.Mask; import com.sk89q.worldedit.patterns.Pattern; @@ -70,24 +74,6 @@ public class ToolUtilCommands { } - @Command( - aliases = { "superpickaxe", "pickaxe", "sp" }, - desc = "Select super pickaxe mode" - ) - @NestedCommand(SuperPickaxeCommands.class) - public void pickaxe(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - } - - @Command( - aliases = {"tool"}, - desc = "Select a tool to bind" - ) - @NestedCommand(ToolCommands.class) - public void tool(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - } - @Command( aliases = { "mask" }, usage = "[mask]", diff --git a/src/main/java/com/sk89q/worldedit/commands/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/commands/UtilityCommands.java index f02d34695..9646f1e8f 100644 --- a/src/main/java/com/sk89q/worldedit/commands/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/commands/UtilityCommands.java @@ -19,9 +19,11 @@ package com.sk89q.worldedit.commands; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -29,9 +31,11 @@ import java.util.TreeSet; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.minecraft.util.commands.Console; import com.sk89q.minecraft.util.commands.Logging; +import com.sk89q.rebar.command.CommandMapping; +import com.sk89q.rebar.command.Description; +import com.sk89q.rebar.command.Dispatcher; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EntityType; import com.sk89q.worldedit.LocalConfiguration; @@ -520,7 +524,7 @@ public class UtilityCommands { } public static void help(CommandContext args, WorldEdit we, LocalSession session, LocalPlayer player, EditSession editSession) { - final CommandsManager commandsManager = we.getCommandsManager(); + Dispatcher dispatcher = we.getDispatcher(); if (args.argsLength() == 0) { SortedSet commands = new TreeSet(new Comparator() { @@ -533,7 +537,7 @@ public class UtilityCommands { return ret; } }); - commands.addAll(commandsManager.getCommands().keySet()); + commands.addAll(dispatcher.getPrimaryAliases()); StringBuilder sb = new StringBuilder(); boolean first = true; @@ -552,14 +556,58 @@ public class UtilityCommands { return; } - String command = args.getJoinedStrings(0).replaceAll("/", ""); - - String helpMessage = commandsManager.getHelpMessages().get(command); - if (helpMessage == null) { + String command = args.getJoinedStrings(0); + + List mappings = new ArrayList(); + + String testCommand = command.replaceAll("/", ""); + for (int i = 0; i < 3; i++) { + CommandMapping mapping = dispatcher.get(testCommand); + if (mapping != null) { + mappings.add(mapping); + } + testCommand = "/" + testCommand; + } + + if (mappings.size() == 0) { player.printError("Unknown command '" + command + "'."); return; } - player.print(helpMessage); + StringBuilder builder = new StringBuilder("\n"); + int index = 0; + for (CommandMapping mapping : mappings) { + if (index != 0) { + builder.append("\n"); + } + + if (mappings.size() > 1) { + builder.append("#").append(index + 1).append(". \n"); + } + + Description desc = mapping.getDescription(); + + builder.append("Aliases: "); + boolean first = true; + for (String alias : mapping.getAllAliases()) { + if (!first) { + builder.append(", "); + } + builder.append("/").append(alias); + first = false; + } + builder.append("\n"); + + builder.append("Usage: ").append(desc.getUsage()).append("\n"); + + if (desc.getHelp() != null) { + builder.append("Help: ").append(desc.getHelp()).append("\n"); + } else if (desc.getDescription() != null) { + builder.append("Description: ").append(desc.getDescription()).append("\n"); + } + index++; + } + + player.print(builder.toString()); } } diff --git a/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java b/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java new file mode 100644 index 000000000..062537c10 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java @@ -0,0 +1,164 @@ +// $Id$ + +package com.sk89q.worldedit.util; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.Logging; +import com.sk89q.rebar.command.parametric.AbstractInvokeListener; +import com.sk89q.rebar.command.parametric.InvokeHandler; +import com.sk89q.rebar.command.parametric.ParameterData; +import com.sk89q.rebar.command.parametric.ParameterException; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.LogFormat; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; + +/** + * Logs called commands to a file. + */ +public class CommandLoggingHandler + extends AbstractInvokeListener + implements InvokeHandler, Closeable { + + private static final Logger logger = + Logger.getLogger(CommandLoggingHandler.class.getCanonicalName()); + + private final WorldEdit worldEdit; + private final LocalConfiguration config; + private final Logger commandLogger = + Logger.getLogger("Minecraft.WorldEdit.CommandLogger"); + + /** + * Create a new instance. + * + * @param worldEdit WorldEdit instance + * @param config the configuration + */ + public CommandLoggingHandler(WorldEdit worldEdit, LocalConfiguration config) { + this.worldEdit = worldEdit; + this.config = config; + + if (!config.logFile.equals("")) { + try { + FileHandler logFileHandler; + File file = new File(config.getWorkingDirectory(), config.logFile); + logFileHandler = new FileHandler(file.getAbsolutePath(), true); + logFileHandler.setFormatter(new LogFormat()); + commandLogger.addHandler(logFileHandler); + } catch (IOException e) { + logger.log(Level.WARNING, "Could not use command log file " + + config.logFile + ": " + e.getMessage()); + } + } + } + + @Override + public void preProcess(Object object, Method method, + ParameterData[] parameters, CommandContext context) + throws CommandException, ParameterException { + } + + @Override + public void preInvoke(Object object, Method method, ParameterData[] parameters, + Object[] args, CommandContext context) throws CommandException { + if (!config.logCommands) { + return; + } + + Logging loggingAnnotation = method.getAnnotation(Logging.class); + Logging.LogMode logMode; + StringBuilder builder = new StringBuilder(); + + if (loggingAnnotation == null) { + logMode = null; + } else { + logMode = loggingAnnotation.value(); + } + + LocalPlayer sender = context.getLocals().get(LocalPlayer.class); + if (sender == null) { + return; + } + + builder.append("WorldEdit: ").append(sender.getName()); + if (sender.isPlayer()) { + builder.append(" (in \"" + sender.getWorld().getName() + "\")"); + } + + builder.append(": ").append(context.getCommand()); + + if (context.argsLength() > 0) { + builder.append(" ").append(context.getJoinedStrings(0)); + } + + if (logMode != null && sender.isPlayer()) { + Vector position = sender.getPosition(); + LocalSession session = worldEdit.getSession(sender); + + switch (logMode) { + case PLACEMENT: + try { + position = session.getPlacementPosition(sender); + } catch (IncompleteRegionException e) { + break; + } + /* FALL-THROUGH */ + + case POSITION: + builder.append(" - Position: " + position); + break; + + case ALL: + builder.append(" - Position: " + position); + /* FALL-THROUGH */ + + case ORIENTATION_REGION: + builder.append(" - Orientation: " + + sender.getCardinalDirection().name()); + /* FALL-THROUGH */ + + case REGION: + try { + builder.append(" - Region: ") + .append(session.getSelection(sender.getWorld())); + } catch (IncompleteRegionException e) { + break; + } + break; + } + } + + commandLogger.info(builder.toString()); + } + + @Override + public void postInvoke(Object object, Method method, ParameterData[] parameters, + Object[] args, CommandContext context) throws CommandException { + } + + @Override + public InvokeHandler createInvokeHandler() { + return this; + } + + @Override + public void close() { + for (Handler h : commandLogger.getHandlers()) { + h.close(); + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java b/src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java new file mode 100644 index 000000000..4ae6976b7 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java @@ -0,0 +1,40 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.rebar.command.parametric.PermissionsHandler; +import com.sk89q.worldedit.LocalPlayer; + +public class CommandPermissionsHandler extends PermissionsHandler { + + public CommandPermissionsHandler() { + } + + @Override + protected boolean hasPermission(CommandContext context, String permission) { + LocalPlayer sender = context.getLocals().get(LocalPlayer.class); + if (sender == null) { + return true; + } else { + return sender.hasPermission(permission); + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java new file mode 100644 index 000000000..b8ba7fd47 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java @@ -0,0 +1,177 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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; + +import com.sk89q.rebar.command.parametric.BindingBehavior; +import com.sk89q.rebar.command.parametric.BindingHelper; +import com.sk89q.rebar.command.parametric.BindingMatch; +import com.sk89q.rebar.command.parametric.ParameterException; +import com.sk89q.rebar.command.parametric.ArgumentStack; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.UnknownDirectionException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.annotation.Direction; +import com.sk89q.worldedit.annotation.Selection; +import com.sk89q.worldedit.masks.Mask; +import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.regions.Region; + +/** + * Binds standard WorldEdit classes such as {@link LocalPlayer} and {@link LocalSession}. + */ +public class WorldEditBinding extends BindingHelper { + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit the WorldEdit instance to bind to + */ + public WorldEditBinding(WorldEdit worldEdit) { + this.worldEdit = worldEdit; + } + + /** + * Gets a selection from a {@link ArgumentStack}. + * + * @param context the context + * @param selection the annotation + * @return a selection + * @throws IncompleteRegionException if no selection is available + * @throws ParameterException on other error + */ + @BindingMatch(classifier = Selection.class, + type = Region.class, + behavior = BindingBehavior.PROVIDES) + public Object getSelection(ArgumentStack context, Selection selection) + throws IncompleteRegionException, ParameterException { + LocalPlayer sender = getLocalPlayer(context); + LocalSession session = worldEdit.getSession(sender); + return session.getSelection(sender.getWorld()); + } + + /** + * Gets an {@link EditSession} from a {@link ArgumentStack}. + * + * @param context the context + * @return an edit session + * @throws ParameterException on other error + */ + @BindingMatch(type = EditSession.class, + behavior = BindingBehavior.PROVIDES) + public EditSession getEditSession(ArgumentStack context) throws ParameterException { + LocalPlayer sender = getLocalPlayer(context); + LocalSession session = worldEdit.getSession(sender); + EditSession editSession = session.createEditSession(sender); + editSession.enableQueue(); + context.getContext().getLocals().put(EditSession.class, editSession); + session.tellVersion(sender); + return editSession; + } + + /** + * Gets an {@link LocalSession} from a {@link ArgumentStack}. + * + * @param context the context + * @return a local session + * @throws ParameterException on error + */ + @BindingMatch(type = LocalSession.class, + behavior = BindingBehavior.PROVIDES) + public LocalSession getLocalSession(ArgumentStack context) throws ParameterException { + LocalPlayer sender = getLocalPlayer(context); + return worldEdit.getSession(sender); + } + + /** + * Gets an {@link LocalPlayer} from a {@link ArgumentStack}. + * + * @param context the context + * @return a local player + * @throws ParameterException on error + */ + @BindingMatch(type = LocalPlayer.class, + behavior = BindingBehavior.PROVIDES) + public LocalPlayer getLocalPlayer(ArgumentStack context) throws ParameterException { + LocalPlayer sender = context.getContext().getLocals().get(LocalPlayer.class); + if (sender == null) { + throw new ParameterException("No player to get a session for"); + } + return sender; + } + + /** + * Gets an {@link Pattern} from a {@link ArgumentStack}. + * + * @param context the context + * @return a pattern + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @BindingMatch(type = Pattern.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public Pattern getPattern(ArgumentStack context) + throws ParameterException, WorldEditException { + return worldEdit.getBlockPattern(getLocalPlayer(context), context.next()); + } + + /** + * Gets an {@link Mask} from a {@link ArgumentStack}. + * + * @param context the context + * @return a pattern + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @BindingMatch(type = Mask.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public Mask getMask(ArgumentStack context) + throws ParameterException, WorldEditException { + return worldEdit.getBlockMask(getLocalPlayer(context), + getLocalSession(context), context.next()); + } + + /** + * Get a direction from the player. + * + * @param context the context + * @param direction the direction annotation + * @return a pattern + * @throws ParameterException on error + * @throws UnknownDirectionException on an unknown direction + */ + @BindingMatch(classifier = Direction.class, + type = Vector.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public Vector getDirection(ArgumentStack context, Direction direction) + throws ParameterException, UnknownDirectionException { + LocalPlayer sender = getLocalPlayer(context); + return worldEdit.getDirection(sender, context.next()); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java b/src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java new file mode 100644 index 000000000..a58a66773 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java @@ -0,0 +1,163 @@ +// $Id$ +/* + * This file is a part of WorldEdit. + * Copyright (c) sk89q + * Copyright (c) the 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 + * (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 + * GNU 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; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.rebar.command.parametric.ExceptionConverterHelper; +import com.sk89q.rebar.command.parametric.ExceptionMatch; +import com.sk89q.worldedit.DisallowedItemException; +import com.sk89q.worldedit.EmptyClipboardException; +import com.sk89q.worldedit.FileSelectionAbortedException; +import com.sk89q.worldedit.FilenameResolutionException; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.InvalidFilenameException; +import com.sk89q.worldedit.InvalidItemException; +import com.sk89q.worldedit.InvalidToolBindException; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.MaxRadiusException; +import com.sk89q.worldedit.PlayerNeededException; +import com.sk89q.worldedit.UnknownDirectionException; +import com.sk89q.worldedit.UnknownItemException; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.commands.InsufficientArgumentsException; +import com.sk89q.worldedit.expression.ExpressionException; +import com.sk89q.worldedit.regions.RegionOperationException; + +/** + * converts WorldEdit exceptions and converts them into {@link CommandException}s. + */ +public class WorldEditExceptionConverter extends ExceptionConverterHelper { + + private static final Pattern numberFormat = Pattern + .compile("^For input string: \"(.*)\"$"); + + private final LocalConfiguration config; + + public WorldEditExceptionConverter(LocalConfiguration config) { + this.config = config; + } + + @ExceptionMatch + public void convert(PlayerNeededException e) throws CommandException { + throw new CommandException(e.getMessage()); + } + + @ExceptionMatch + public void convert(NumberFormatException e) throws CommandException { + final Matcher matcher = numberFormat.matcher(e.getMessage()); + + if (matcher.matches()) { + throw new CommandException("Number expected; string \"" + matcher.group(1) + + "\" given."); + } else { + throw new CommandException("Number expected; string given."); + } + } + + @ExceptionMatch + public void convert(IncompleteRegionException e) throws CommandException { + throw new CommandException("Make a region selection first."); + } + + @ExceptionMatch + public void convert(UnknownItemException e) throws CommandException { + throw new CommandException("Block name '" + e.getID() + "' was not recognized."); + } + + @ExceptionMatch + public void convert(InvalidItemException e) throws CommandException { + throw new CommandException(e.getMessage()); + } + + @ExceptionMatch + public void convert(DisallowedItemException e) throws CommandException { + throw new CommandException("Block '" + e.getID() + + "' not allowed (see WorldEdit configuration)."); + } + + @ExceptionMatch + public void convert(MaxChangedBlocksException e) throws CommandException { + throw new CommandException("Max blocks changed in an operation reached (" + + e.getBlockLimit() + ")."); + } + + @ExceptionMatch + public void convert(MaxRadiusException e) throws CommandException { + throw new CommandException("Maximum radius: " + config.maxRadius); + } + + @ExceptionMatch + public void convert(UnknownDirectionException e) throws CommandException { + throw new CommandException("Unknown direction: " + e.getDirection()); + } + + @ExceptionMatch + public void convert(InsufficientArgumentsException e) throws CommandException { + throw new CommandException(e.getMessage()); + } + + @ExceptionMatch + public void convert(RegionOperationException e) throws CommandException { + throw new CommandException(e.getMessage()); + } + + @ExceptionMatch + public void convert(ExpressionException e) throws CommandException { + throw new CommandException(e.getMessage()); + } + + @ExceptionMatch + public void convert(EmptyClipboardException e) throws CommandException { + throw new CommandException("Your clipboard is empty. Use //copy first."); + } + + @ExceptionMatch + public void convert(InvalidFilenameException e) throws CommandException { + throw new CommandException("Filename '" + e.getFilename() + "' invalid: " + + e.getMessage()); + } + + @ExceptionMatch + public void convert(FilenameResolutionException e) throws CommandException { + throw new CommandException( + "File '" + e.getFilename() + "' resolution error: " + e.getMessage()); + } + + @ExceptionMatch + public void convert(InvalidToolBindException e) throws CommandException { + throw new CommandException("Can't bind tool to " + + ItemType.toHeldName(e.getItemId()) + ": " + e.getMessage()); + } + + @ExceptionMatch + public void convert(FileSelectionAbortedException e) throws CommandException { + throw new CommandException("File selection aborted."); + } + + @ExceptionMatch + public void convert(WorldEditException e) throws CommandException { + throw new CommandException(e.getMessage()); + } + +}