From 04c31262f720e83c293e4bf3eaa7709b3dbd7813 Mon Sep 17 00:00:00 2001 From: Albert Pham Date: Tue, 18 Jun 2013 14:50:46 -0700 Subject: [PATCH 01/45] Added a new command dispatcher that injects different parameters dynamically. This reduces the boilerplate code needed to parse arguments in each command, and reduces the need to maintain command documentation with @Command. Example: @Command(aliases = "/set", desc = "Set all the blocks inside the selection to a block") @CommandPermissions("worldedit.region.set") @Logging(REGION) void setBlocks(LocalPlayer player, EditSession editSession, @Selection Region region, Pattern replaceWith) { // Perform command } --- pom.xml | 65 +++ .../util/commands/CommandContext.java | 78 ++- .../util/commands/CommandException.java | 35 ++ .../util/commands/CommandLocals.java | 49 ++ .../util/commands/SuggestionContext.java | 49 ++ .../sk89q/rebar/command/CommandCallable.java | 63 ++ .../sk89q/rebar/command/CommandMapping.java | 78 +++ .../com/sk89q/rebar/command/Description.java | 69 +++ .../com/sk89q/rebar/command/Dispatcher.java | 112 ++++ .../rebar/command/InvalidUsageException.java | 48 ++ .../command/MissingParameterException.java | 30 + .../com/sk89q/rebar/command/Parameter.java | 65 +++ .../rebar/command/SimpleDescription.java | 128 +++++ .../sk89q/rebar/command/SimpleDispatcher.java | 127 +++++ .../command/SimpleDispatcherCommand.java | 99 ++++ .../sk89q/rebar/command/SimpleParameter.java | 130 +++++ .../command/UnconsumedParameterException.java | 41 ++ .../command/binding/PrimitiveBindings.java | 258 +++++++++ .../sk89q/rebar/command/binding/Range.java | 49 ++ .../command/binding/StandardBindings.java | 45 ++ .../sk89q/rebar/command/binding/Switch.java | 43 ++ .../com/sk89q/rebar/command/binding/Text.java | 40 ++ .../sk89q/rebar/command/binding/Validate.java | 44 ++ .../rebar/command/fluent/CommandGraph.java | 83 +++ .../rebar/command/fluent/DispatcherNode.java | 141 +++++ .../parametric/AbstractInvokeListener.java | 35 ++ .../command/parametric/ArgumentStack.java | 77 +++ .../rebar/command/parametric/Binding.java | 91 +++ .../command/parametric/BindingBehavior.java | 51 ++ .../command/parametric/BindingHelper.java | 223 ++++++++ .../command/parametric/BindingMatch.java | 70 +++ .../parametric/ContextArgumentStack.java | 177 ++++++ .../parametric/ExceptionConverter.java | 51 ++ .../parametric/ExceptionConverterHelper.java | 108 ++++ .../command/parametric/ExceptionMatch.java | 33 ++ .../command/parametric/InvokeHandler.java | 81 +++ .../command/parametric/InvokeListener.java | 57 ++ .../parametric/LegacyCommandsHandler.java | 96 ++++ .../rebar/command/parametric/Optional.java | 40 ++ .../command/parametric/ParameterData.java | 193 +++++++ .../parametric/ParameterException.java | 44 ++ .../command/parametric/ParametricBuilder.java | 204 +++++++ .../parametric/ParametricCallable.java | 537 ++++++++++++++++++ .../parametric/ParametricException.java | 27 + .../parametric/PermissionsHandler.java | 66 +++ .../parametric/StringArgumentStack.java | 127 +++++ 46 files changed, 4350 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/sk89q/minecraft/util/commands/CommandLocals.java create mode 100644 src/main/java/com/sk89q/minecraft/util/commands/SuggestionContext.java create mode 100644 src/main/java/com/sk89q/rebar/command/CommandCallable.java create mode 100644 src/main/java/com/sk89q/rebar/command/CommandMapping.java create mode 100644 src/main/java/com/sk89q/rebar/command/Description.java create mode 100644 src/main/java/com/sk89q/rebar/command/Dispatcher.java create mode 100644 src/main/java/com/sk89q/rebar/command/InvalidUsageException.java create mode 100644 src/main/java/com/sk89q/rebar/command/MissingParameterException.java create mode 100644 src/main/java/com/sk89q/rebar/command/Parameter.java create mode 100644 src/main/java/com/sk89q/rebar/command/SimpleDescription.java create mode 100644 src/main/java/com/sk89q/rebar/command/SimpleDispatcher.java create mode 100644 src/main/java/com/sk89q/rebar/command/SimpleDispatcherCommand.java create mode 100644 src/main/java/com/sk89q/rebar/command/SimpleParameter.java create mode 100644 src/main/java/com/sk89q/rebar/command/UnconsumedParameterException.java create mode 100644 src/main/java/com/sk89q/rebar/command/binding/PrimitiveBindings.java create mode 100644 src/main/java/com/sk89q/rebar/command/binding/Range.java create mode 100644 src/main/java/com/sk89q/rebar/command/binding/StandardBindings.java create mode 100644 src/main/java/com/sk89q/rebar/command/binding/Switch.java create mode 100644 src/main/java/com/sk89q/rebar/command/binding/Text.java create mode 100644 src/main/java/com/sk89q/rebar/command/binding/Validate.java create mode 100644 src/main/java/com/sk89q/rebar/command/fluent/CommandGraph.java create mode 100644 src/main/java/com/sk89q/rebar/command/fluent/DispatcherNode.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/AbstractInvokeListener.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ArgumentStack.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/Binding.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/BindingBehavior.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/BindingHelper.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/BindingMatch.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ContextArgumentStack.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverter.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverterHelper.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ExceptionMatch.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/InvokeHandler.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/InvokeListener.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/LegacyCommandsHandler.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/Optional.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ParameterData.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ParameterException.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ParametricBuilder.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ParametricCallable.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/ParametricException.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/PermissionsHandler.java create mode 100644 src/main/java/com/sk89q/rebar/command/parametric/StringArgumentStack.java diff --git a/pom.xml b/pom.xml index d01870078..55acf6510 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,13 @@ http://repo.bukkit.org/content/groups/public + + + + sk89q-repo + http://maven.sk89q.com/repo/ + + @@ -112,6 +119,12 @@ jar true + + + com.thoughtworks.paranamer + paranamer + 2.5.2 + org.bukkit @@ -191,6 +204,25 @@ 1.6 + + + com.thoughtworks.paranamer + paranamer-maven-plugin-largestack + 2.5.5-SNAPSHOT + + + run + compile + + ${project.build.sourceDirectory} + ${project.build.outputDirectory} + + + generate + + + + org.apache.maven.plugins @@ -244,6 +276,7 @@ com.sk89q:jchronic + com.thoughtworks.paranamer:paranamer @@ -302,8 +335,40 @@ + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + com.thoughtworks.paranamer + paranamer-maven-plugin-largestack + [2.5.5-SNAPSHOT,) + + generate + + + + + + + + + + + + + diff --git a/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java b/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java index 0a36b7e98..6b583682a 100644 --- a/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java +++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java @@ -27,15 +27,22 @@ import java.util.Map; import java.util.Set; public class CommandContext { + protected final String command; protected final List parsedArgs; protected final List originalArgIndices; protected final String[] originalArgs; protected final Set booleanFlags = new HashSet(); protected final Map valueFlags = new HashMap(); + protected final SuggestionContext suggestionContext; + protected final CommandLocals locals; + + public static String[] split(String args) { + return args.split(" ", -1); + } public CommandContext(String args) throws CommandException { - this(args.split(" "), null); + this(args.split(" ", -1), null); } public CommandContext(String[] args) throws CommandException { @@ -43,28 +50,50 @@ public class CommandContext { } public CommandContext(String args, Set valueFlags) throws CommandException { - this(args.split(" "), valueFlags); + this(args.split(" ", -1), valueFlags); + } + + public CommandContext(String args, Set valueFlags, boolean allowHangingFlag) + throws CommandException { + this(args.split(" ", -1), valueFlags, allowHangingFlag, new CommandLocals()); + } + + public CommandContext(String[] args, Set valueFlags) throws CommandException { + this(args, valueFlags, false, null); } /** - * @param args An array with arguments. Empty strings outside quotes will be removed. - * @param valueFlags A set containing all value flags. Pass null to disable value flag parsing. - * @throws CommandException This is thrown if flag fails for some reason. + * Parse the given array of arguments. + * + *

Empty arguments are removed from the list of arguments.

+ * + * @param args an array with arguments + * @param valueFlags a set containing all value flags (pass null to disable value flag parsing) + * @param allowHangingFlag true if hanging flags are allowed + * @param locals the locals, null to create empty one + * @throws CommandException thrown on a parsing error */ - public CommandContext(String[] args, Set valueFlags) throws CommandException { + public CommandContext(String[] args, Set valueFlags, + boolean allowHangingFlag, CommandLocals locals) throws CommandException { if (valueFlags == null) { valueFlags = Collections.emptySet(); } originalArgs = args; command = args[0]; + this.locals = locals != null ? locals : new CommandLocals(); + boolean isHanging = false; + SuggestionContext suggestionContext = SuggestionContext.hangingValue(); // Eliminate empty args and combine multiword args first List argIndexList = new ArrayList(args.length); List argList = new ArrayList(args.length); for (int i = 1; i < args.length; ++i) { + isHanging = false; + String arg = args[i]; if (arg.length() == 0) { + isHanging = true; continue; } @@ -112,9 +141,14 @@ public class CommandContext { for (int nextArg = 0; nextArg < argList.size(); ) { // Fetch argument String arg = argList.get(nextArg++); + suggestionContext = SuggestionContext.hangingValue(); // Not a flag? if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z]+$")) { + if (!isHanging) { + suggestionContext = SuggestionContext.lastValue(); + } + originalArgIndices.add(argIndexList.get(nextArg - 1)); parsedArgs.add(arg); continue; @@ -139,16 +173,30 @@ public class CommandContext { } if (nextArg >= argList.size()) { - throw new CommandException("No value specified for the '-" + flagName + "' flag."); + if (allowHangingFlag) { + suggestionContext = SuggestionContext.flag(flagName); + break; + } else { + throw new CommandException("No value specified for the '-" + flagName + "' flag."); + } } // If it is a value flag, read another argument and add it this.valueFlags.put(flagName, argList.get(nextArg++)); + if (!isHanging) { + suggestionContext = SuggestionContext.flag(flagName); + } } else { booleanFlags.add(flagName); } } } + + this.suggestionContext = suggestionContext; + } + + public SuggestionContext getSuggestionContext() { + return suggestionContext; } public String getCommand() { @@ -175,6 +223,18 @@ public class CommandContext { } return buffer.toString(); } + + public String getRemainingString(int start) { + return getString(start, parsedArgs.size() - 1); + } + + public String getString(int start, int end) { + StringBuilder buffer = new StringBuilder(parsedArgs.get(start)); + for (int i = start + 1; i < end + 1; ++i) { + buffer.append(" ").append(parsedArgs.get(i)); + } + return buffer.toString(); + } public int getInteger(int index) throws NumberFormatException { return Integer.parseInt(parsedArgs.get(index)); @@ -258,4 +318,8 @@ public class CommandContext { public int argsLength() { return parsedArgs.size(); } + + public CommandLocals getLocals() { + return locals; + } } diff --git a/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java b/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java index 6a3e7e1a4..577b9a7ff 100644 --- a/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java +++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandException.java @@ -19,8 +19,14 @@ package com.sk89q.minecraft.util.commands; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + public class CommandException extends Exception { + private static final long serialVersionUID = 870638193072101739L; + private List commandStack = new ArrayList(); public CommandException() { super(); @@ -30,8 +36,37 @@ public class CommandException extends Exception { super(message); } + public CommandException(String message, Throwable t) { + super(message, t); + } + public CommandException(Throwable t) { super(t); } + public void prependStack(String name) { + commandStack.add(name); + } + + public String toStackString(String prefix, String spacedSuffix) { + StringBuilder builder = new StringBuilder(); + if (prefix != null) { + builder.append(prefix); + } + ListIterator li = commandStack.listIterator(commandStack.size()); + while (li.hasPrevious()) { + if (li.previousIndex() != commandStack.size() - 1) { + builder.append(" "); + } + builder.append(li.previous()); + } + if (spacedSuffix != null) { + if (builder.length() > 0) { + builder.append(" "); + } + builder.append(spacedSuffix); + } + return builder.toString(); + } + } diff --git a/src/main/java/com/sk89q/minecraft/util/commands/CommandLocals.java b/src/main/java/com/sk89q/minecraft/util/commands/CommandLocals.java new file mode 100644 index 000000000..368036c5f --- /dev/null +++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandLocals.java @@ -0,0 +1,49 @@ +// $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.minecraft.util.commands; + +import java.util.HashMap; +import java.util.Map; + +public class CommandLocals { + + private final Map locals = new HashMap(); + + public boolean containsKey(Object key) { + return locals.containsKey(key); + } + + public boolean containsValue(Object value) { + return locals.containsValue(value); + } + + public Object get(Object key) { + return locals.get(key); + } + + @SuppressWarnings("unchecked") + public T get(Class key) { + return (T) locals.get(key); + } + + public Object put(Object key, Object value) { + return locals.put(key, value); + } + +} diff --git a/src/main/java/com/sk89q/minecraft/util/commands/SuggestionContext.java b/src/main/java/com/sk89q/minecraft/util/commands/SuggestionContext.java new file mode 100644 index 000000000..425588417 --- /dev/null +++ b/src/main/java/com/sk89q/minecraft/util/commands/SuggestionContext.java @@ -0,0 +1,49 @@ +package com.sk89q.minecraft.util.commands; + +public class SuggestionContext { + + private static final SuggestionContext FOR_LAST = new SuggestionContext(null, true); + private static final SuggestionContext FOR_HANGING = new SuggestionContext(null, false); + + private final Character flag; + private final boolean forLast; + + private SuggestionContext(Character flag, boolean forLast) { + this.flag = flag; + this.forLast = forLast; + } + + public boolean forHangingValue() { + return flag == null && !forLast; + } + + public boolean forLastValue() { + return flag == null && forLast; + } + + public boolean forFlag() { + return flag != null; + } + + public Character getFlag() { + return flag; + } + + @Override + public String toString() { + return forFlag() ? ("-" + getFlag()) : (forHangingValue() ? "hanging" : "last"); + } + + public static SuggestionContext flag(Character flag) { + return new SuggestionContext(flag, false); + } + + public static SuggestionContext lastValue() { + return FOR_LAST; + } + + public static SuggestionContext hangingValue() { + return FOR_HANGING; + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/CommandCallable.java b/src/main/java/com/sk89q/rebar/command/CommandCallable.java new file mode 100644 index 000000000..2c3bd6750 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/CommandCallable.java @@ -0,0 +1,63 @@ +// $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.rebar.command; + +import java.util.Collection; +import java.util.Set; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; + +/** + * A command that can be executed. + */ +public interface CommandCallable { + + /** + * Get a list of value flags used by this command. + * + * @return a list of value flags + */ + Set getValueFlags(); + + /** + * Execute the command. + * + * @param context the user input + * @throws CommandException thrown on any sort of command exception + */ + void call(CommandContext context) throws CommandException; + + /** + * Get a list of suggestions. + * + * @param context the user input + * @return a list of suggestions + * @throws CommandException + */ + Collection getSuggestions(CommandContext context) throws CommandException; + + /** + * Get an object describing this command. + * + * @return the command description + */ + Description getDescription(); + +} diff --git a/src/main/java/com/sk89q/rebar/command/CommandMapping.java b/src/main/java/com/sk89q/rebar/command/CommandMapping.java new file mode 100644 index 000000000..53f03811b --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/CommandMapping.java @@ -0,0 +1,78 @@ +// $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.rebar.command; + + +/** + * Tracks a command registration. + */ +public class CommandMapping { + + private final String[] aliases; + private final CommandCallable callable; + + /** + * Create a new instance. + * + * @param callable the command callable + * @param alias a list of all aliases, where the first one is the primary one + */ + public CommandMapping(CommandCallable callable, String... alias) { + super(); + this.aliases = alias; + this.callable = callable; + } + + /** + * Get the primary alias. + * + * @return the primary alias + */ + public String getPrimaryAlias() { + return aliases[0]; + } + + /** + * Get a list of all aliases. + * + * @return aliases + */ + public String[] getAllAliases() { + return aliases; + } + + /** + * Get the callable + * + * @return the callable + */ + public CommandCallable getCallable() { + return callable; + } + + /** + * Get the {@link Description} form the callable. + * + * @return the description + */ + public Description getDescription() { + return getCallable().getDescription(); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/Description.java b/src/main/java/com/sk89q/rebar/command/Description.java new file mode 100644 index 000000000..ff08ea43b --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/Description.java @@ -0,0 +1,69 @@ +// $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.rebar.command; + +import java.util.List; + +/** + * A description of a command. + */ +public interface Description { + + /** + * Get the list of parameters for this command. + * + * @return a list of parameters + */ + List getParameters(); + + /** + * Get a short one-line description of this command. + * + * @return a description, or null if no description is available + */ + String getDescription(); + + /** + * Get a longer help text about this command. + * + * @return a help text, or null if no help is available + */ + String getHelp(); + + /** + * Get the usage string of this command. + * + *

A usage string may look like + * [-w <world>] <var1> <var2>.

+ * + * @return a usage string + */ + String getUsage(); + + /** + * Get a list of permissions that the player may have to have permission. + * + *

Permission data may or may not be available. This is only useful as a + * potential hint.

+ * + * @return the list of permissions + */ + List getPermissions(); + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/Dispatcher.java b/src/main/java/com/sk89q/rebar/command/Dispatcher.java new file mode 100644 index 000000000..5427dede1 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/Dispatcher.java @@ -0,0 +1,112 @@ +// $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.rebar.command; + +import java.util.Collection; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; + +/** + * Executes a command based on user input. + */ +public interface Dispatcher { + + /** + * Register a command with this dispatcher. + * + * @param callable the command executor + * @param alias a list of aliases, where the first alias is the primary name + */ + void register(CommandCallable callable, String... alias); + + /** + * Get a list of command registrations. + * + *

The returned collection cannot be modified.

+ * + * @return a list of registrations + */ + Collection getCommands(); + + /** + * Get a list of primary aliases. + * + *

The returned collection cannot be modified.

+ * + * @return a list of aliases + */ + Collection getPrimaryAliases(); + + /** + * Get a list of all the command aliases. + * + *

A command may have more than one alias assigned to it. The returned + * collection cannot be modified.

+ * + * @return a list of aliases + */ + Collection getAllAliases(); + + /** + * Get the {@link CommandCallable} associated with an alias. + * + * @param alias the alias + * @return the command mapping + */ + CommandMapping get(String alias); + + /** + * Returns whether the dispatcher contains a registered command for the given alias. + * + * @param alias the alias + * @return true if a registered command exists + */ + boolean contains(String alias); + + /** + * Execute the correct command based on the input. + * + * @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 + */ + CommandMapping call(String arguments, CommandLocals locals) throws CommandException; + + /** + * Execute the correct command based on the input. + * + * @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 + */ + CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException; + + /** + * Get a list of suggestions based on input. + * + * @param arguments the arguments entered up to this point + * @return a list of suggestions + * @throws CommandException thrown if there was a parsing error + */ + Collection getSuggestions(String arguments) throws CommandException; + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/InvalidUsageException.java b/src/main/java/com/sk89q/rebar/command/InvalidUsageException.java new file mode 100644 index 000000000..f07404e19 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/InvalidUsageException.java @@ -0,0 +1,48 @@ +// $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.rebar.command; + +import com.sk89q.minecraft.util.commands.CommandException; + +/** + * Thrown when a command is not used properly. + */ +public class InvalidUsageException extends CommandException { + + private static final long serialVersionUID = -3222004168669490390L; + private final Description description; + + public InvalidUsageException(Description description) { + this.description = description; + } + + public InvalidUsageException(String message, Description description) { + super(message); + this.description = description; + } + + public Description getDescription() { + return description; + } + + public String getUsage(String prefix) { + return toStackString(prefix, getDescription().getUsage()); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/MissingParameterException.java b/src/main/java/com/sk89q/rebar/command/MissingParameterException.java new file mode 100644 index 000000000..cf7d8581f --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/MissingParameterException.java @@ -0,0 +1,30 @@ +// $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.rebar.command; + +import com.sk89q.rebar.command.parametric.ParameterException; + +/** + * Thrown when there is a missing parameter. + */ +public class MissingParameterException extends ParameterException { + + private static final long serialVersionUID = 2169299987926950535L; + +} diff --git a/src/main/java/com/sk89q/rebar/command/Parameter.java b/src/main/java/com/sk89q/rebar/command/Parameter.java new file mode 100644 index 000000000..16ee33560 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/Parameter.java @@ -0,0 +1,65 @@ +// $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.rebar.command; + +/** + * Describes a parameter. + * + * @see Description + */ +public interface Parameter { + + /** + * The name of the parameter. + * + * @return the name + */ + String getName(); + + /** + * Get the flag associated with this parameter. + * + * @return the flag, or null if there is no flag associated + * @see #isValueFlag() + */ + Character getFlag(); + + /** + * Return whether the flag is a value flag. + * + * @return true if the flag is a value flag + * @see #getFlag() + */ + boolean isValueFlag(); + + /** + * Get whether this parameter is optional. + * + * @return true if the parameter does not have to be specified + */ + boolean isOptional(); + + /** + * Get the default value as a string to be parsed by the binding. + * + * @return a default value, or null if none is set + */ + public String[] getDefaultValue(); + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/SimpleDescription.java b/src/main/java/com/sk89q/rebar/command/SimpleDescription.java new file mode 100644 index 000000000..1367ff915 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/SimpleDescription.java @@ -0,0 +1,128 @@ +// $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.rebar.command; + +import java.util.Collections; +import java.util.List; + +/** + * A simple implementation of {@link Description} which has setters. + */ +public class SimpleDescription implements Description { + + private List parameters = Collections.emptyList(); + private List permissions = Collections.emptyList(); + private String description; + private String help; + private String overrideUsage; + + @Override + public List getParameters() { + return parameters; + } + + /** + * Set the list of parameters. + * + * @param parameters the list of parameters + * @see #getParameters() + */ + public void setParameters(List parameters) { + this.parameters = Collections.unmodifiableList(parameters); + } + + @Override + public String getDescription() { + return description; + } + + /** + * Set the description of the command. + * + * @param description the description + * @see #getDescription() + */ + public void setDescription(String description) { + this.description = description; + } + + @Override + public String getHelp() { + return help; + } + + /** + * Set the help text of the command. + * + * @param help the help text + * @see #getHelp() + */ + public void setHelp(String help) { + this.help = help; + } + + @Override + public List getPermissions() { + return permissions; + } + + /** + * Set the permissions of this command. + * + * @param permissions the permissions + */ + public void setPermissions(List permissions) { + this.permissions = Collections.unmodifiableList(permissions); + } + + /** + * Override the usage string returned with a given one. + * + * @param usage usage string, or null + */ + public void overrideUsage(String usage) { + this.overrideUsage = usage; + } + + @Override + public String getUsage() { + if (overrideUsage != null) { + return overrideUsage; + } + + StringBuilder builder = new StringBuilder(); + boolean first = true; + + for (Parameter parameter : parameters) { + if (!first) { + builder.append(" "); + } + builder.append(parameter.toString()); + first = false; + } + + return builder.toString(); + } + + @Override + public String toString() { + return getUsage(); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/SimpleDispatcher.java b/src/main/java/com/sk89q/rebar/command/SimpleDispatcher.java new file mode 100644 index 000000000..90b9200bf --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/SimpleDispatcher.java @@ -0,0 +1,127 @@ +// $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.rebar.command; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; +import com.sk89q.minecraft.util.commands.WrappedCommandException; + +/** + * A simple implementation of {@link Dispatcher}. + */ +public class SimpleDispatcher implements Dispatcher { + + private final Map commands = new HashMap(); + + @Override + public void register(CommandCallable callable, String... alias) { + CommandMapping mapping = new CommandMapping(callable, alias); + + // Check for replacements + for (String a : alias) { + String lower = a.toLowerCase(); + if (commands.containsKey(lower)) { + throw new IllegalArgumentException( + "Replacing commands is currently undefined behavior"); + } + } + + for (String a : alias) { + String lower = a.toLowerCase(); + commands.put(lower, mapping); + } + } + + @Override + public Collection getCommands() { + return Collections.unmodifiableCollection(commands.values()); + } + + @Override + public Set getAllAliases() { + return Collections.unmodifiableSet(commands.keySet()); + } + + @Override + public Set getPrimaryAliases() { + Set aliases = new HashSet(); + for (CommandMapping mapping : getCommands()) { + aliases.add(mapping.getPrimaryAlias()); + } + return Collections.unmodifiableSet(aliases); + } + + @Override + public boolean contains(String alias) { + return commands.containsKey(alias.toLowerCase()); + } + + @Override + public CommandMapping get(String alias) { + return commands.get(alias.toLowerCase()); + } + + @Override + public CommandMapping call(String arguments, CommandLocals locals) throws CommandException { + return call(CommandContext.split(arguments), locals); + } + + @Override + public CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException { + CommandContext dummyContext = new CommandContext(arguments); + CommandMapping mapping = get(dummyContext.getCommand()); + if (mapping != null) { + CommandCallable c = mapping.getCallable(); + CommandContext context = + new CommandContext(arguments, c.getValueFlags(), false, locals); + try { + c.call(context); + } catch (CommandException e) { + e.prependStack(context.getCommand()); + throw e; + } catch (Throwable t) { + throw new WrappedCommandException(t); + } + } + return mapping; + } + + @Override + public Collection getSuggestions(String arguments) throws CommandException { + CommandContext dummyContext = new CommandContext(arguments); + CommandMapping mapping = get(dummyContext.getCommand()); + if (mapping != null) { + CommandCallable c = mapping.getCallable(); + CommandContext context = + new CommandContext(arguments, c.getValueFlags(), true); + return c.getSuggestions(context); + } + return new ArrayList(); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/SimpleDispatcherCommand.java b/src/main/java/com/sk89q/rebar/command/SimpleDispatcherCommand.java new file mode 100644 index 000000000..99fff7727 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/SimpleDispatcherCommand.java @@ -0,0 +1,99 @@ +// $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.rebar.command; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; + +/** + * A combination {@link Dispatcher} and {@link CommandCallable} that is backed by + * a {@link SimpleDispatcher}. + * + *

The primary use of this is to make nested commands.

+ */ +public class SimpleDispatcherCommand extends SimpleDispatcher implements CommandCallable { + + private final SimpleDescription description = new SimpleDescription(); + + @Override + public Set getValueFlags() { + return Collections.emptySet(); + } + + @Override + public void call(CommandContext context) throws CommandException { + if (context.argsLength() >= 1) { + super.call(context.getRemainingString(0), context.getLocals()); + } else { + Set aliases = getPrimaryAliases(); + + if (aliases.size() == 0) { + throw new InvalidUsageException( + "This command is supposed to have sub-commands, " + + "but it has no sub-commands.", + getDescription()); + } + + StringBuilder builder = new StringBuilder(); + for (String alias : getPrimaryAliases()) { + builder.append("\n- ").append(alias); + } + + if (aliases.size() == 1) { + builder.append(" (there is only one)"); + } + + throw new InvalidUsageException( + "Select one of these subcommand(s):" + builder.toString(), + getDescription()); + } + } + + @Override + public SimpleDescription getDescription() { + return description; + } + + @Override + public Collection getSuggestions(CommandContext context) throws CommandException { + if (context.argsLength() == 0) { + return super.getAllAliases(); + } else if (context.argsLength() == 1 && + context.getSuggestionContext().forLastValue()) { + String prefix = context.getString(0).toLowerCase(); + List suggestions = new ArrayList(); + for (String alias : super.getAllAliases()) { + if (alias.startsWith(prefix)) { + suggestions.add(alias); + } + } + return suggestions; + } + + return super.getSuggestions( + context.argsLength() > 1 ? context.getRemainingString(1) : ""); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/SimpleParameter.java b/src/main/java/com/sk89q/rebar/command/SimpleParameter.java new file mode 100644 index 000000000..91d9f0ce5 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/SimpleParameter.java @@ -0,0 +1,130 @@ +// $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.rebar.command; + +/** + * A simple implementation of {@link Parameter} that has setters. + */ +public class SimpleParameter implements Parameter { + + private String name; + private Character flag; + private boolean isValue; + private boolean isOptional; + private String[] defaultValue; + + /** + * Create a new parameter with no name defined yet. + */ + public SimpleParameter() { + } + + /** + * Create a new parameter of the given name. + * + * @param name the name + */ + public SimpleParameter(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + /** + * Set the name of the parameter. + * + * @param name the parameter name + */ + public void setName(String name) { + this.name = name; + } + + @Override + public Character getFlag() { + return flag; + } + + @Override + public boolean isValueFlag() { + return flag != null && isValue; + } + + /** + * Set the flag used by this parameter. + * + * @param flag the flag, or null if there is no flag + * @param isValue true if the flag is a value flag + */ + public void setFlag(Character flag, boolean isValue) { + this.flag = flag; + this.isValue = isValue; + } + + @Override + public boolean isOptional() { + return isOptional || getFlag() != null; + } + + /** + * Set whether this parameter is optional. + * + * @param isOptional true if this parameter is optional + */ + public void setOptional(boolean isOptional) { + this.isOptional = isOptional; + } + + @Override + public String[] getDefaultValue() { + return defaultValue; + } + + /** + * Set the default value. + * + * @param defaultValue a default value, or null if none + */ + public void setDefaultValue(String[] defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (getFlag() != null) { + if (isValueFlag()) { + builder.append("[-") + .append(getFlag()).append(" <").append(getName()).append(">]"); + } else { + builder.append("[-").append(getFlag()).append("]"); + } + } else { + if (isOptional()) { + builder.append("[<").append(getName()).append(">]"); + } else { + builder.append("<").append(getName()).append(">"); + } + } + return builder.toString(); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/UnconsumedParameterException.java b/src/main/java/com/sk89q/rebar/command/UnconsumedParameterException.java new file mode 100644 index 000000000..3e7673a63 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/UnconsumedParameterException.java @@ -0,0 +1,41 @@ +// $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.rebar.command; + +import com.sk89q.rebar.command.parametric.ParameterException; + +/** + * Thrown when there are leftover parameters that were not consumed, particular in the + * case of the user providing too many parameters. + */ +public class UnconsumedParameterException extends ParameterException { + + private static final long serialVersionUID = 4449104854894946023L; + + private String unconsumed; + + public UnconsumedParameterException(String unconsumed) { + this.unconsumed = unconsumed; + } + + public String getUnconsumed() { + return unconsumed; + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/binding/PrimitiveBindings.java b/src/main/java/com/sk89q/rebar/command/binding/PrimitiveBindings.java new file mode 100644 index 000000000..f938218c5 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/binding/PrimitiveBindings.java @@ -0,0 +1,258 @@ +// $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.rebar.command.binding; + +import java.lang.annotation.Annotation; + +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; + +/** + * Handles basic Java types such as {@link String}s, {@link Byte}s, etc. + * + *

Handles both the object and primitive types.

+ */ +public final class PrimitiveBindings extends BindingHelper { + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @param text the text annotation + * @param modifiers a list of modifiers + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(classifier = Text.class, + type = String.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = -1, + provideModifiers = true) + public String getText(ArgumentStack context, Text text, Annotation[] modifiers) + throws ParameterException { + String v = context.remaining(); + validate(v, modifiers); + return v; + } + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @param modifiers a list of modifiers + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(type = String.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1, + provideModifiers = true) + public String getString(ArgumentStack context, Annotation[] modifiers) + throws ParameterException { + String v = context.next(); + validate(v, modifiers); + return v; + } + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(type = { Boolean.class, boolean.class }, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public Boolean getBoolean(ArgumentStack context) throws ParameterException { + return context.nextBoolean(); + } + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @param modifiers a list of modifiers + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(type = { Integer.class, int.class }, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1, + provideModifiers = true) + public Integer getInteger(ArgumentStack context, Annotation[] modifiers) + throws ParameterException { + Integer v = context.nextInt(); + if (v != null) { + validate(v, modifiers); + } + return v; + } + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @param modifiers a list of modifiers + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(type = { Short.class, short.class }, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1, + provideModifiers = true) + public Short getShort(ArgumentStack context, Annotation[] modifiers) + throws ParameterException { + Integer v = getInteger(context, modifiers); + if (v != null) { + return v.shortValue(); + } + return null; + } + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @param modifiers a list of modifiers + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(type = { Double.class, double.class }, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1, + provideModifiers = true) + public Double getDouble(ArgumentStack context, Annotation[] modifiers) + throws ParameterException { + Double v = context.nextDouble(); + if (v != null) { + validate(v, modifiers); + } + return v; + } + + /** + * Gets a type from a {@link ArgumentStack}. + * + * @param context the context + * @param modifiers a list of modifiers + * @return the requested type + * @throws ParameterException on error + */ + @BindingMatch(type = { Float.class, float.class }, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1, + provideModifiers = true) + public Float getFloat(ArgumentStack context, Annotation[] modifiers) + throws ParameterException { + Double v = getDouble(context, modifiers); + if (v != null) { + return v.floatValue(); + } + return null; + } + + /** + * Validate a number value using relevant modifiers. + * + * @param number the number + * @param modifiers the list of modifiers to scan + * @throws ParameterException on a validation error + */ + private static void validate(double number, Annotation[] modifiers) + throws ParameterException { + for (Annotation modifier : modifiers) { + if (modifier instanceof Range) { + Range range = (Range) modifier; + if (number < range.min()) { + throw new ParameterException( + String.format( + "A valid value is greater than or equal to %s " + + "(you entered %s)", range.min(), number)); + } else if (number > range.max()) { + throw new ParameterException( + String.format( + "A valid value is less than or equal to %s " + + "(you entered %s)", range.max(), number)); + } + } + } + } + + /** + * Validate a number value using relevant modifiers. + * + * @param number the number + * @param modifiers the list of modifiers to scan + * @throws ParameterException on a validation error + */ + private static void validate(int number, Annotation[] modifiers) + throws ParameterException { + for (Annotation modifier : modifiers) { + if (modifier instanceof Range) { + Range range = (Range) modifier; + if (number < range.min()) { + throw new ParameterException( + String.format( + "A valid value is greater than or equal to %s " + + "(you entered %s)", range.min(), number)); + } else if (number > range.max()) { + throw new ParameterException( + String.format( + "A valid value is less than or equal to %s " + + "(you entered %s)", range.max(), number)); + } + } + } + } + + /** + * Validate a string value using relevant modifiers. + * + * @param string the string + * @param modifiers the list of modifiers to scan + * @throws ParameterException on a validation error + */ + private static void validate(String string, Annotation[] modifiers) + throws ParameterException { + if (string == null) { + return; + } + + for (Annotation modifier : modifiers) { + if (modifier instanceof Validate) { + Validate validate = (Validate) modifier; + + if (!validate.regex().isEmpty()) { + if (!string.matches(validate.regex())) { + throw new ParameterException( + String.format( + "The given text doesn't match the right " + + "format (technically speaking, the 'format' is %s)", + validate.regex())); + } + } + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/binding/Range.java b/src/main/java/com/sk89q/rebar/command/binding/Range.java new file mode 100644 index 000000000..67a5eae1b --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/binding/Range.java @@ -0,0 +1,49 @@ +// $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.rebar.command.binding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies a range of values for numbers. + * + * @see PrimitiveBindings a user of this annotation as a modifier + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Range { + + /** + * The minimum value that the number can be at, inclusive. + * + * @return the minimum value + */ + double min() default Double.MIN_VALUE; + + /** + * The maximum value that the number can be at, inclusive. + * + * @return the maximum value + */ + double max() default Double.MAX_VALUE; + +} diff --git a/src/main/java/com/sk89q/rebar/command/binding/StandardBindings.java b/src/main/java/com/sk89q/rebar/command/binding/StandardBindings.java new file mode 100644 index 000000000..96c82cd22 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/binding/StandardBindings.java @@ -0,0 +1,45 @@ +// $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.rebar.command.binding; + +import com.sk89q.minecraft.util.commands.CommandContext; +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.ArgumentStack; + +/** + * Standard bindings that should be available to most configurations. + */ +public final class StandardBindings extends BindingHelper { + + /** + * Gets a {@link CommandContext} from a {@link ArgumentStack}. + * + * @param context the context + * @return a selection + */ + @BindingMatch(type = CommandContext.class, + behavior = BindingBehavior.PROVIDES) + public CommandContext getCommandContext(ArgumentStack context) { + context.markConsumed(); // Consume entire stack + return context.getContext(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/binding/Switch.java b/src/main/java/com/sk89q/rebar/command/binding/Switch.java new file mode 100644 index 000000000..21f8857cb --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/binding/Switch.java @@ -0,0 +1,43 @@ +// $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.rebar.command.binding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a command flag, such as /command -f. + * + *

If used on a boolean type, then the flag will be a non-value flag. If + * used on any other type, then the flag will be a value flag.

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Switch { + + /** + * The flag character. + * + * @return the flag character (A-Z a-z 0-9 is acceptable) + */ + char value(); + +} diff --git a/src/main/java/com/sk89q/rebar/command/binding/Text.java b/src/main/java/com/sk89q/rebar/command/binding/Text.java new file mode 100644 index 000000000..e8c23e86a --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/binding/Text.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.rebar.command.binding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.sk89q.rebar.command.parametric.ArgumentStack; + +/** + * Indicates a {@link String} parameter will call {@link ArgumentStack#remaining()} and + * therefore consume all remaining arguments. + * + *

This should only be used at the end of a list of parameters (of parameters that + * need to consume from the stack of arguments), otherwise following parameters will + * have no values left to consume.

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Text { + +} diff --git a/src/main/java/com/sk89q/rebar/command/binding/Validate.java b/src/main/java/com/sk89q/rebar/command/binding/Validate.java new file mode 100644 index 000000000..854e3a930 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/binding/Validate.java @@ -0,0 +1,44 @@ +// $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.rebar.command.binding; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.regex.Pattern; + +/** + * Used to validate a string. + * + * @see PrimitiveBindings where this validation is used + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Validate { + + /** + * An optional regular expression that must match the string. + * + * @see Pattern regular expression class + * @return the pattern + */ + String regex() default ""; + +} diff --git a/src/main/java/com/sk89q/rebar/command/fluent/CommandGraph.java b/src/main/java/com/sk89q/rebar/command/fluent/CommandGraph.java new file mode 100644 index 000000000..14d57e75f --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/fluent/CommandGraph.java @@ -0,0 +1,83 @@ +// $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.rebar.command.fluent; + +import com.sk89q.rebar.command.Dispatcher; +import com.sk89q.rebar.command.SimpleDispatcher; +import com.sk89q.rebar.command.parametric.ParametricBuilder; + +/** + * A fluent interface to creating a command graph. + * + *

A command graph may have multiple commands, and multiple sub-commands below that, + * and possibly below that.

+ */ +public class CommandGraph { + + private final DispatcherNode rootDispatcher; + private ParametricBuilder builder; + + /** + * Create a new command graph. + */ + public CommandGraph() { + SimpleDispatcher dispatcher = new SimpleDispatcher(); + rootDispatcher = new DispatcherNode(this, null, dispatcher); + } + + /** + * Get the root dispatcher node. + * + * @return the root dispatcher node + */ + public DispatcherNode commands() { + return rootDispatcher; + } + + /** + * Get the {@link ParametricBuilder}. + * + * @return the builder, or null. + */ + public ParametricBuilder getBuilder() { + return builder; + } + + /** + * Set the {@link ParametricBuilder} used for calls to + * {@link DispatcherNode#build(Object)}. + * + * @param builder the builder, or null + * @return this object + */ + public CommandGraph builder(ParametricBuilder builder) { + this.builder = builder; + return this; + } + + /** + * Get the root dispatcher. + * + * @return the root dispatcher + */ + public Dispatcher getDispatcher() { + return rootDispatcher.getDispatcher(); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/fluent/DispatcherNode.java b/src/main/java/com/sk89q/rebar/command/fluent/DispatcherNode.java new file mode 100644 index 000000000..a17e70964 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/fluent/DispatcherNode.java @@ -0,0 +1,141 @@ +// $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.rebar.command.fluent; + +import com.sk89q.rebar.command.CommandCallable; +import com.sk89q.rebar.command.Dispatcher; +import com.sk89q.rebar.command.SimpleDispatcher; +import com.sk89q.rebar.command.SimpleDispatcherCommand; +import com.sk89q.rebar.command.parametric.ParametricBuilder; + +/** + * A collection of commands. + */ +public class DispatcherNode { + + private final CommandGraph graph; + private final DispatcherNode parent; + private final SimpleDispatcher dispatcher; + + /** + * Create a new instance. + * + * @param graph the root fluent graph object + * @param parent the parent node, or null + * @param dispatcher the dispatcher for this node + */ + DispatcherNode(CommandGraph graph, DispatcherNode parent, + SimpleDispatcher dispatcher) { + this.graph = graph; + this.parent = parent; + this.dispatcher = dispatcher; + } + + /** + * Set the description. + * + *

This can only be used on {@link DispatcherNode}s returned by + * {@link #group(String...)}.

+ * + * @param description the description + * @return this object + */ + public DispatcherNode describe(String description) { + if (dispatcher instanceof SimpleDispatcherCommand) { + ((SimpleDispatcherCommand) dispatcher).getDescription() + .setDescription(description); + } + return this; + } + + /** + * Register a command with this dispatcher. + * + * @param callable the executor + * @param alias the list of aliases, where the first alias is the primary one + */ + public void register(CommandCallable callable, String... alias) { + dispatcher.register(callable, alias); + } + + /** + * Build and register a command with this dispatcher using the + * {@link ParametricBuilder} assigned on the root {@link CommandGraph}. + * + * @param object the object provided to the {@link ParametricBuilder} + * @return this object + * @see ParametricBuilder#register(com.sk89q.rebar.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); + return this; + } + + /** + * Create a new command that will contain sub-commands. + * + *

The object returned by this method can be used to add sub-commands. To + * return to this "parent" context, use {@link DispatcherNode#graph()}.

+ * + * @param alias the list of aliases, where the first alias is the primary one + * @return an object to place sub-commands + */ + public DispatcherNode group(String... alias) { + SimpleDispatcherCommand command = new SimpleDispatcherCommand(); + getDispatcher().register(command, alias); + return new DispatcherNode(graph, this, command); + } + + /** + * Return the parent node. + * + * @return the parent node + * @throws RuntimeException if there is no parent node. + */ + public DispatcherNode parent() { + if (parent != null) { + return parent; + } + + throw new RuntimeException("This node does not have a parent"); + } + + /** + * Get the root command graph. + * + * @return the root command graph + */ + public CommandGraph graph() { + return graph; + } + + /** + * Get the underlying dispatcher of this object. + * + * @return the dispatcher + */ + public Dispatcher getDispatcher() { + return dispatcher; + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/AbstractInvokeListener.java b/src/main/java/com/sk89q/rebar/command/parametric/AbstractInvokeListener.java new file mode 100644 index 000000000..a03550b6d --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/AbstractInvokeListener.java @@ -0,0 +1,35 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Method; + +import com.sk89q.rebar.command.SimpleDescription; + +/** + * An abstract listener. + */ +public abstract class AbstractInvokeListener implements InvokeListener { + + @Override + public void updateDescription(Object object, Method method, + ParameterData[] parameters, SimpleDescription description) { + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ArgumentStack.java b/src/main/java/com/sk89q/rebar/command/parametric/ArgumentStack.java new file mode 100644 index 000000000..221002e39 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ArgumentStack.java @@ -0,0 +1,77 @@ +// $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.rebar.command.parametric; + +import com.sk89q.minecraft.util.commands.CommandContext; + +public interface ArgumentStack { + + /** + * Get the next string, which may come from the stack or a value flag. + * + * @return the value + * @throws ParameterException on a parameter error + */ + String next() throws ParameterException; + + /** + * Get the next integer, which may come from the stack or a value flag. + * + * @return the value + * @throws ParameterException on a parameter error + */ + Integer nextInt() throws ParameterException; + + /** + * Get the next double, which may come from the stack or a value flag. + * + * @return the value + * @throws ParameterException on a parameter error + */ + Double nextDouble() throws ParameterException; + + /** + * Get the next boolean, which may come from the stack or a value flag. + * + * @return the value + * @throws ParameterException on a parameter error + */ + Boolean nextBoolean() throws ParameterException; + + /** + * Get all remaining string values, which will consume the rest of the stack. + * + * @return the value + * @throws ParameterException on a parameter error + */ + String remaining() throws ParameterException; + + /** + * Set as completely consumed. + */ + void markConsumed(); + + /** + * Get the underlying context. + * + * @return the context + */ + CommandContext getContext(); + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/parametric/Binding.java b/src/main/java/com/sk89q/rebar/command/parametric/Binding.java new file mode 100644 index 000000000..6234a6db3 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/Binding.java @@ -0,0 +1,91 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Type; +import java.util.List; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.rebar.command.binding.PrimitiveBindings; +import com.sk89q.rebar.command.binding.StandardBindings; + +/** + * Used to parse user input for a command, based on available method types + * and annotations. + * + *

A binding can be used to handle several types at once. For a binding to be + * called, it must be registered with a {@link ParametricBuilder} with + * {@link ParametricBuilder#addBinding(Binding, java.lang.reflect.Type...)}.

+ * + * @see PrimitiveBindings an example of primitive bindings + * @see StandardBindings standard bindings + */ +public interface Binding { + + /** + * Get the types that this binding handles. + * + * @return the types + */ + Type[] getTypes(); + + /** + * Get how this binding consumes from a {@link ArgumentStack}. + * + * @param parameter information about the parameter + * @return the behavior + */ + BindingBehavior getBehavior(ParameterData parameter); + + /** + * Get the number of arguments that this binding will consume, if this + * information is available. + * + *

This method must return -1 for binding behavior types that are not + * {@link BindingBehavior#CONSUMES}.

+ * + * @param parameter information about the parameter + * @return the number of consumed arguments, or -1 if unknown or irrelevant + */ + int getConsumedCount(ParameterData parameter); + + /** + * Attempt to consume values (if required) from the given {@link ArgumentStack} + * in order to instantiate an object for the given parameter. + * + * @param parameter information about the parameter + * @param scoped the arguments the user has input + * @param onlyConsume true to only consume arguments + * @return an object parsed for the given parameter + * @throws ParameterException thrown if the parameter could not be formulated + * @throws CommandException on a command exception + */ + Object bind(ParameterData parameter, ArgumentStack scoped, boolean onlyConsume) + throws ParameterException, CommandException; + + /** + * Get a list of suggestions for the given parameter and user arguments. + * + * @param parameter information about the parameter + * @param prefix what the user has typed so far (may be an empty string) + * @return a list of suggestions + */ + List getSuggestions(ParameterData parameter, String prefix); + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/BindingBehavior.java b/src/main/java/com/sk89q/rebar/command/parametric/BindingBehavior.java new file mode 100644 index 000000000..b15b5bd03 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/BindingBehavior.java @@ -0,0 +1,51 @@ +// $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.rebar.command.parametric; + +import com.sk89q.minecraft.util.commands.CommandLocals; +import com.sk89q.rebar.command.binding.Switch; + +/** + * Determines the type of binding. + */ +public enum BindingBehavior { + + /** + * Always consumes from a {@link ArgumentStack}. + */ + CONSUMES, + + /** + * Sometimes consumes from a {@link ArgumentStack}. + * + *

Bindings that exhibit this behavior must be defined as a {@link Switch} + * by commands utilizing the given binding.

+ */ + INDETERMINATE, + + /** + * Never consumes from a {@link ArgumentStack}. + * + *

Bindings that exhibit this behavior generate objects from other sources, + * such as from a {@link CommandLocals}. These are "magic" bindings that inject + * variables.

+ */ + PROVIDES; + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/BindingHelper.java b/src/main/java/com/sk89q/rebar/command/parametric/BindingHelper.java new file mode 100644 index 000000000..b3bc4aad0 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/BindingHelper.java @@ -0,0 +1,223 @@ +// $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.rebar.command.parametric; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.sk89q.minecraft.util.commands.CommandException; + +/** + * A binding helper that uses the {@link BindingMatch} annotation to make + * writing bindings extremely easy. + * + *

Methods must have the following and only the following parameters:

+ * + *
    + *
  • A {@link ArgumentStack}
  • + *
  • A {@link Annotation} if there is a classifier set
  • + *
  • A {@link Annotation}[] + * if there {@link BindingMatch#provideModifiers()} is true
  • + *
+ * + *

Methods may throw any exception. Exceptions may be converted using a + * {@link ExceptionConverter} registered with the {@link ParametricBuilder}.

+ */ +public class BindingHelper implements Binding { + + private final List bindings; + private final Type[] types; + + /** + * Create a new instance. + */ + public BindingHelper() { + List bindings = new ArrayList(); + List types = new ArrayList(); + + for (Method method : this.getClass().getMethods()) { + BindingMatch info = method.getAnnotation(BindingMatch.class); + if (info != null) { + Class classifier = null; + + // Set classifier + if (!info.classifier().equals(Annotation.class)) { + classifier = (Class) info.classifier(); + types.add(classifier); + } + + for (Type t : info.type()) { + Type type = null; + + // Set type + if (!t.equals(Class.class)) { + type = t; + if (classifier == null) { + types.add(type); // Only if there is no classifier set! + } + } + + // Check to see if at least one is set + if (type == null && classifier == null) { + throw new RuntimeException( + "A @BindingMatch needs either a type or classifier set"); + } + + BoundMethod handler = new BoundMethod(info, type, classifier, method); + bindings.add(handler); + } + } + } + + Collections.sort(bindings); + + this.bindings = bindings; + + Type[] typesArray = new Type[types.size()]; + types.toArray(typesArray); + this.types = typesArray; + + } + + /** + * Match a {@link BindingMatch} according to the given parameter. + * + * @param parameter the parameter + * @return a binding + */ + private BoundMethod match(ParameterData parameter) { + for (BoundMethod binding : bindings) { + Annotation classifer = parameter.getClassifier(); + Type type = parameter.getType(); + + if (binding.classifier != null) { + if (classifer != null && classifer.annotationType().equals(binding.classifier)) { + if (binding.type == null || binding.type.equals(type)) { + return binding; + } + } + } else if (binding.type.equals(type)) { + return binding; + } + } + + throw new RuntimeException("Unknown type"); + } + + @Override + public Type[] getTypes() { + return types; + } + + @Override + public int getConsumedCount(ParameterData parameter) { + return match(parameter).annotation.consumedCount(); + } + + @Override + public BindingBehavior getBehavior(ParameterData parameter) { + return match(parameter).annotation.behavior(); + } + + @Override + public Object bind(ParameterData parameter, ArgumentStack scoped, + boolean onlyConsume) throws ParameterException, CommandException { + BoundMethod binding = match(parameter); + List args = new ArrayList(); + args.add(scoped); + + if (binding.classifier != null) { + args.add(parameter.getClassifier()); + } + + if (binding.annotation.provideModifiers()) { + args.add(parameter.getModifiers()); + } + + if (onlyConsume && binding.annotation.behavior() == BindingBehavior.PROVIDES) { + return null; // Nothing to consume, nothing to do + } + + Object[] argsArray = new Object[args.size()]; + args.toArray(argsArray); + + try { + return binding.method.invoke(this, argsArray); + } catch (IllegalArgumentException e) { + throw new RuntimeException( + "Processing of classifier " + parameter.getClassifier() + + " and type " + parameter.getType() + " failed for method\n" + + binding.method + "\nbecause the parameters for that method are wrong", e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof ParameterException) { + throw (ParameterException) e.getCause(); + } else if (e.getCause() instanceof CommandException) { + throw (CommandException) e.getCause(); + } + throw new ParameterException(e.getCause()); + } + } + + @Override + public List getSuggestions(ParameterData parameter, String prefix) { + return new ArrayList(); + } + + private static class BoundMethod implements Comparable { + private final BindingMatch annotation; + private final Type type; + private final Class classifier; + private final Method method; + + BoundMethod(BindingMatch annotation, Type type, + Class classifier, Method method) { + this.annotation = annotation; + this.type = type; + this.classifier = classifier; + this.method = method; + } + + @Override + public int compareTo(BoundMethod o) { + if (classifier != null && o.classifier == null) { + return -1; + } else if (classifier == null && o.classifier != null) { + return 1; + } else if (classifier != null && o.classifier != null) { + if (type != null && o.type == null) { + return -1; + } else if (type == null && o.type != null) { + return 1; + } else { + return 0; + } + } else { + return 0; + } + } + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/BindingMatch.java b/src/main/java/com/sk89q/rebar/command/parametric/BindingMatch.java new file mode 100644 index 000000000..5cbc34ffb --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/BindingMatch.java @@ -0,0 +1,70 @@ +// $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.rebar.command.parametric; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes a match of a binding. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface BindingMatch { + + /** + * The classifier. + * + * @return the classifier, or {@link Annotation} if not set + */ + Class classifier() default Annotation.class; + + /** + * The type. + * + * @return the type, or {@link Class} if not set + */ + Class[] type() default Class.class; + + /** + * The binding behavior. + * + * @return the behavior + */ + BindingBehavior behavior(); + + /** + * Get the number of arguments that this binding consumes. + * + * @return -1 if unknown or irrelevant + */ + int consumedCount() default -1; + + /** + * Set whether an array of modifier annotations is provided in the list of + * arguments. + * + * @return true to provide modifiers + */ + boolean provideModifiers() default false; + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ContextArgumentStack.java b/src/main/java/com/sk89q/rebar/command/parametric/ContextArgumentStack.java new file mode 100644 index 000000000..182ecf6c0 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ContextArgumentStack.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.rebar.command.parametric; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.rebar.command.MissingParameterException; + +/** + * Makes an instance of a {@link CommandContext} into a stack of arguments + * that can be consumed. + * + * @see ParametricBuilder a user of this class + */ +public class ContextArgumentStack implements ArgumentStack { + + private final CommandContext context; + private int index = 0; + private int markedIndex = 0; + + /** + * Create a new instance using the given context. + * + * @param context the context + */ + public ContextArgumentStack(CommandContext context) { + this.context = context; + } + + @Override + public String next() throws ParameterException { + try { + return context.getString(index++); + } catch (IndexOutOfBoundsException e) { + throw new MissingParameterException(); + } + } + + @Override + public Integer nextInt() throws ParameterException { + try { + return Integer.parseInt(next()); + } catch (NumberFormatException e) { + throw new ParameterException( + "Expected a number, got '" + context.getString(index - 1) + "'"); + } + } + + @Override + public Double nextDouble() throws ParameterException { + try { + return Double.parseDouble(next()); + } catch (NumberFormatException e) { + throw new ParameterException( + "Expected a number, got '" + context.getString(index - 1) + "'"); + } + } + + @Override + public Boolean nextBoolean() throws ParameterException { + try { + return next().equalsIgnoreCase("true"); + } catch (IndexOutOfBoundsException e) { + throw new MissingParameterException(); + } + } + + @Override + public String remaining() throws ParameterException { + try { + String value = context.getJoinedStrings(index); + index = context.argsLength(); + return value; + } catch (IndexOutOfBoundsException e) { + throw new MissingParameterException(); + } + } + + /** + * Get the unconsumed arguments left over, without touching the stack. + * + * @return the unconsumed arguments + */ + public String getUnconsumed() { + if (index >= context.argsLength()) { + return null; + } + + return context.getJoinedStrings(index); + } + + @Override + public void markConsumed() { + index = context.argsLength(); + } + + /** + * Return the current position. + * + * @return the position + */ + public int position() { + return index; + } + + /** + * Mark the current position of the stack. + * + *

The marked position initially starts at 0.

+ */ + public void mark() { + markedIndex = index; + } + + /** + * Reset to the previously {@link #mark()}ed position of the stack, and return + * the arguments that were consumed between this point and that previous point. + * + *

The marked position initially starts at 0.

+ * + * @return the consumed arguments + */ + public String reset() { + String value = context.getString(markedIndex, index); + index = markedIndex; + return value; + } + + /** + * Return whether any arguments were consumed between the marked position + * and the current position. + * + *

The marked position initially starts at 0.

+ * + * @return true if values were consumed. + */ + public boolean wasConsumed() { + return markedIndex != index; + } + + /** + * Return the arguments that were consumed between this point and that marked point. + * + *

The marked position initially starts at 0.

+ * + * @return the consumed arguments + */ + public String getConsumed() { + return context.getString(markedIndex, index); + } + + /** + * Get the underlying context. + * + * @return the context + */ + @Override + public CommandContext getContext() { + return context; + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverter.java b/src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverter.java new file mode 100644 index 000000000..d40a643db --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverter.java @@ -0,0 +1,51 @@ +// $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.rebar.command.parametric; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.WrappedCommandException; + +/** + * Used to convert a recognized {@link Throwable} into an appropriate + * {@link CommandException}. + * + *

Methods (when invoked by a {@link ParametricBuilder}-created command) may throw + * relevant exceptions that are not caught by the command manager, but translate + * into reasonable exceptions for an application. However, unknown exceptions are + * normally simply wrapped in a {@link WrappedCommandException} and bubbled up. Only + * normal {@link CommandException}s will be printed correctly, so a converter translates + * one of these unknown exceptions into an appropriate {@link CommandException}.

+ * + *

This also allows the code calling the command to not need be aware of these + * application-specific exceptions, as they will all be converted to + * {@link CommandException}s that are handled normally.

+ */ +public interface ExceptionConverter { + + /** + * Attempt to convert the given throwable into a {@link CommandException}. + * + *

If the exception is not recognized, then nothing should be thrown.

+ * + * @param t the throwable + * @throws CommandException a command exception + */ + void convert(Throwable t) throws CommandException; + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverterHelper.java b/src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverterHelper.java new file mode 100644 index 000000000..a0e84185f --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ExceptionConverterHelper.java @@ -0,0 +1,108 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.WrappedCommandException; + +/** + * An implementation of an {@link ExceptionConverter} that automatically calls + * the correct method defined on this object. + * + *

Only public methods will be used. Methods will be called in order of decreasing + * levels of inheritance (between classes where one inherits the other). For two + * different inheritance branches, the order between them is undefined.

+ */ +public abstract class ExceptionConverterHelper implements ExceptionConverter { + + private final List handlers; + + @SuppressWarnings("unchecked") + public ExceptionConverterHelper() { + List handlers = new ArrayList(); + + for (Method method : this.getClass().getMethods()) { + if (method.getAnnotation(ExceptionMatch.class) == null) { + continue; + } + + Class[] parameters = method.getParameterTypes(); + if (parameters.length == 1) { + Class cls = parameters[0]; + if (Throwable.class.isAssignableFrom(cls)) { + handlers.add(new ExceptionHandler( + (Class) cls, method)); + } + } + } + + Collections.sort(handlers); + + this.handlers = handlers; + } + + @Override + public void convert(Throwable t) throws CommandException { + Class throwableClass = t.getClass(); + for (ExceptionHandler handler : handlers) { + if (handler.cls.isAssignableFrom(throwableClass)) { + try { + handler.method.invoke(this, t); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof CommandException) { + throw (CommandException) e.getCause(); + } + throw new WrappedCommandException(e); + } catch (IllegalArgumentException e) { + throw new WrappedCommandException(e); + } catch (IllegalAccessException e) { + throw new WrappedCommandException(e); + } + } + } + } + + private static class ExceptionHandler implements Comparable { + final Class cls; + final Method method; + + public ExceptionHandler(Class cls, Method method) { + this.cls = cls; + this.method = method; + } + + @Override + public int compareTo(ExceptionHandler o) { + if (cls.equals(o.cls)) { + return 0; + } else if (cls.isAssignableFrom(o.cls)) { + return 1; + } else { + return -1; + } + } + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ExceptionMatch.java b/src/main/java/com/sk89q/rebar/command/parametric/ExceptionMatch.java new file mode 100644 index 000000000..5c3dae02d --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ExceptionMatch.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.rebar.command.parametric; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes a match of an exception. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExceptionMatch { + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/InvokeHandler.java b/src/main/java/com/sk89q/rebar/command/parametric/InvokeHandler.java new file mode 100644 index 000000000..f27ac1759 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/InvokeHandler.java @@ -0,0 +1,81 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Method; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; + +/** + * Called before and after a command is invoked for commands executed by a command + * created using {@link ParametricBuilder}. + * + *

Invocation handlers are created by {@link InvokeListener}s. Multiple + * listeners and handlers can be registered, and all be run. However, if one handler + * throws an exception, future handlers will not execute and the command will + * not execute (if thrown in + * {@link #preInvoke(Object, Method, ParameterData[], Object[], CommandContext)}).

+ * + * @see InvokeListener the factory + */ +public interface InvokeHandler { + + /** + * Called before parameters are processed. + * + * @param object the object + * @param method the method + * @param parameters the list of parameters + * @param context the context + * @throws CommandException can be thrown for an error, which will stop invocation + * @throws ParameterException on parameter error + */ + void preProcess(Object object, Method method, ParameterData[] parameters, + CommandContext context) throws CommandException, ParameterException; + + /** + * Called before the parameter is invoked. + * + * @param object the object + * @param method the method + * @param parameters the list of parameters + * @param args the arguments to be given to the method + * @param context the context + * @throws CommandException can be thrown for an error, which will stop invocation + * @throws ParameterException on parameter error + */ + void preInvoke(Object object, Method method, ParameterData[] parameters, + Object[] args, CommandContext context) throws CommandException, ParameterException; + + /** + * Called after the parameter is invoked. + * + * @param object the object + * @param method the method + * @param parameters the list of parameters + * @param args the arguments to be given to the method + * @param context the context + * @throws CommandException can be thrown for an error + * @throws ParameterException on parameter error + */ + void postInvoke(Object object, Method method, ParameterData[] parameters, + Object[] args, CommandContext context) throws CommandException, ParameterException; + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/InvokeListener.java b/src/main/java/com/sk89q/rebar/command/parametric/InvokeListener.java new file mode 100644 index 000000000..2e0898ab1 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/InvokeListener.java @@ -0,0 +1,57 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Method; + +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.rebar.command.CommandCallable; +import com.sk89q.rebar.command.SimpleDescription; + +/** + * Listens to events related to {@link ParametricBuilder}. + */ +public interface InvokeListener { + + /** + * Create a new invocation handler. + * + *

An example use of an {@link InvokeHandler} would be to verify permissions + * added by the {@link CommandPermissions} annotation.

+ * + *

For simple {@link InvokeHandler}, an object can implement both this + * interface and {@link InvokeHandler}.

+ * + * @return a new invocation handler + */ + InvokeHandler createInvokeHandler(); + + /** + * During creation of a {@link CommandCallable} by a {@link ParametricBuilder}, + * this will be called in case the description needs to be updated. + * + * @param object the object + * @param method the method + * @param parameters a list of parameters + * @param description the description to be updated + */ + void updateDescription(Object object, Method method, ParameterData[] parameters, + SimpleDescription description); + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/LegacyCommandsHandler.java b/src/main/java/com/sk89q/rebar/command/parametric/LegacyCommandsHandler.java new file mode 100644 index 000000000..944077fb3 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/LegacyCommandsHandler.java @@ -0,0 +1,96 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Method; + +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.rebar.command.MissingParameterException; +import com.sk89q.rebar.command.SimpleDescription; +import com.sk89q.rebar.command.UnconsumedParameterException; + +/** + * Handles legacy properties on {@link Command} such as {@link Command#min()} and + * {@link Command#max()}. + */ +public class LegacyCommandsHandler 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 { + } + + @Override + public void preInvoke(Object object, Method method, + ParameterData[] parameters, Object[] args, CommandContext context) + throws ParameterException { + Command annotation = method.getAnnotation(Command.class); + + if (annotation != null) { + if (context.argsLength() < annotation.min()) { + throw new MissingParameterException(); + } + + if (annotation.max() != -1 && context.argsLength() > annotation.max()) { + throw new UnconsumedParameterException( + context.getRemainingString(annotation.max())); + } + } + } + + @Override + public void postInvoke(Object object, Method method, + ParameterData[] parameters, Object[] args, CommandContext context) { + + } + + @Override + public void updateDescription(Object object, Method method, + ParameterData[] parameters, SimpleDescription description) { + Command annotation = method.getAnnotation(Command.class); + + // Handle the case for old commands where no usage is set and all of its + // parameters are provider bindings, so its usage information would + // be blank and would imply that there were no accepted parameters + if (annotation != null && annotation.usage().isEmpty() + && (annotation.min() > 0 || annotation.max() > 0)) { + boolean hasUserParameters = false; + + for (ParameterData parameter : parameters) { + if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) { + hasUserParameters = true; + return; + } + } + + if (!hasUserParameters) { + description.overrideUsage("(unknown usage information)"); + } + } + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/Optional.java b/src/main/java/com/sk89q/rebar/command/parametric/Optional.java new file mode 100644 index 000000000..7e0c075af --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/Optional.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.rebar.command.parametric; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates an optional parameter. + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface Optional { + + /** + * The default value to use if no value is set. + * + * @return a string value, or an empty list + */ + String[] value() default {}; + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ParameterData.java b/src/main/java/com/sk89q/rebar/command/parametric/ParameterData.java new file mode 100644 index 000000000..42038051e --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ParameterData.java @@ -0,0 +1,193 @@ +// $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.rebar.command.parametric; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import com.sk89q.rebar.command.SimpleParameter; +import com.sk89q.rebar.command.binding.PrimitiveBindings; +import com.sk89q.rebar.command.binding.Range; +import com.sk89q.rebar.command.binding.Text; + +/** + * Describes a parameter in detail. + */ +public class ParameterData extends SimpleParameter { + + private Binding binding; + private Annotation classifier; + private Annotation[] modifiers; + private Type type; + + /** + * Get the binding associated with this parameter. + * + * @return the binding + */ + public Binding getBinding() { + return binding; + } + + /** + * Set the binding associated with this parameter. + * + * @param binding the binding + */ + void setBinding(Binding binding) { + this.binding = binding; + } + + /** + * Set the main type of this parameter. + * + *

The type is normally that is used to determine which binding is used + * for a particular method's parameter.

+ * + * @return the main type + * @see #getClassifier() which can override the type + */ + public Type getType() { + return type; + } + + /** + * Set the main type of this parameter. + * + * @param type the main type + */ + void setType(Type type) { + this.type = type; + } + + /** + * Get the classifier annotation. + * + *

Normally, the type determines what binding is called, but classifiers + * take precedence if one is found (and registered with + * {@link ParametricBuilder#addBinding(Binding, Type...)}). + * An example of a classifier annotation is {@link Text}.

+ * + * @return the classifier annotation, null is possible + */ + public Annotation getClassifier() { + return classifier; + } + + /** + * Set the classifier annotation. + * + * @param classifier the classifier annotation, null is possible + */ + void setClassifier(Annotation classifier) { + this.classifier = classifier; + } + + /** + * Get a list of modifier annotations. + * + *

Modifier annotations are not considered in the process of choosing a binding + * for a method parameter, but they can be used to modify the behavior of a binding. + * An example of a modifier annotation is {@link Range}, which can restrict + * numeric values handled by {@link PrimitiveBindings} to be within a range. The list + * of annotations may contain a classifier and other unrelated annotations.

+ * + * @return a list of annotations + */ + public Annotation[] getModifiers() { + return modifiers; + } + + /** + * Set the list of modifiers. + * + * @param modifiers a list of annotations + */ + void setModifiers(Annotation[] modifiers) { + this.modifiers = modifiers; + } + + /** + * Return the number of arguments this binding consumes. + * + * @return -1 if unknown or unavailable + */ + int getConsumedCount() { + return getBinding().getConsumedCount(this); + } + + /** + * Get whether this parameter is entered by the user. + * + * @return true if this parameter is entered by the user. + */ + boolean isUserInput() { + return getBinding().getBehavior(this) != BindingBehavior.PROVIDES; + } + + /** + * Get whether this parameter consumes non-flag arguments. + * + * @return true if this parameter consumes non-flag arguments + */ + boolean isNonFlagConsumer() { + return getBinding().getBehavior(this) != BindingBehavior.PROVIDES && !isValueFlag(); + } + + /** + * Validate this parameter and its binding. + */ + void validate(Method method, int parameterIndex) throws ParametricException { + // We can't have indeterminate consumers without @Switches otherwise + // it may screw up parameter processing for later bindings + BindingBehavior behavior = getBinding().getBehavior(this); + boolean indeterminate = (behavior == BindingBehavior.INDETERMINATE); + if (!isValueFlag() && indeterminate) { + throw new ParametricException( + "@Switch missing for indeterminate consumer\n\n" + + "Notably:\nFor the type " + type + ", the binding " + + getBinding().getClass().getCanonicalName() + + "\nmay or may not consume parameters (isIndeterminateConsumer(" + type + ") = true)" + + "\nand therefore @Switch(flag) is required for parameter #" + parameterIndex + " of \n" + + method.toGenericString()); + } + + // getConsumedCount() better return -1 if the BindingBehavior is not CONSUMES + if (behavior != BindingBehavior.CONSUMES && binding.getConsumedCount(this) != -1) { + throw new ParametricException( + "getConsumedCount() does not return -1 for binding " + + getBinding().getClass().getCanonicalName() + + "\neven though its behavior type is " + behavior.name() + + "\nfor parameter #" + parameterIndex + " of \n" + + method.toGenericString()); + } + + // getConsumedCount() should not return 0 if the BindingBehavior is not PROVIDES + if (behavior != BindingBehavior.PROVIDES && binding.getConsumedCount(this) == 0) { + throw new ParametricException( + "getConsumedCount() must not return 0 for binding " + + getBinding().getClass().getCanonicalName() + + "\nwhen its behavior type is " + behavior.name() + " and not PROVIDES " + + "\nfor parameter #" + parameterIndex + " of \n" + + method.toGenericString()); + } + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ParameterException.java b/src/main/java/com/sk89q/rebar/command/parametric/ParameterException.java new file mode 100644 index 000000000..6851d36a1 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ParameterException.java @@ -0,0 +1,44 @@ +// $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.rebar.command.parametric; + +/** + * Thrown if there is an error with a parameter. + */ +public class ParameterException extends Exception { + + private static final long serialVersionUID = -8255175019708245673L; + + public ParameterException() { + super(); + } + + public ParameterException(String message) { + super(message); + } + + public ParameterException(Throwable cause) { + super(cause); + } + + public ParameterException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ParametricBuilder.java b/src/main/java/com/sk89q/rebar/command/parametric/ParametricBuilder.java new file mode 100644 index 000000000..4faa4c6f4 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ParametricBuilder.java @@ -0,0 +1,204 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ImmutableBiMap.Builder; +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.rebar.command.CommandCallable; +import com.sk89q.rebar.command.Dispatcher; +import com.sk89q.rebar.command.binding.PrimitiveBindings; +import com.sk89q.rebar.command.binding.StandardBindings; +import com.sk89q.rebar.command.binding.Switch; +import com.thoughtworks.paranamer.CachingParanamer; +import com.thoughtworks.paranamer.Paranamer; + +/** + * Creates commands using annotations placed on methods and individual parameters of + * such methods. + * + * @see Command defines a command + * @see Switch defines a flag + */ +public class ParametricBuilder { + + private final Map bindings = new HashMap(); + private final Paranamer paranamer = new CachingParanamer(); + private final List invokeListeners = new ArrayList(); + private final List exceptionConverters = new ArrayList(); + + /** + * Create a new builder. + * + *

This method will install {@link PrimitiveBindings} and + * {@link StandardBindings} and default bindings.

+ */ + public ParametricBuilder() { + addBinding(new PrimitiveBindings()); + addBinding(new StandardBindings()); + } + + /** + * Add a binding for a given type or classifier (annotation). + * + *

Whenever a method parameter is encountered, a binding must be found for it + * so that it can be called later to consume the stack of arguments provided by + * the user and return an object that is later passed to + * {@link Method#invoke(Object, Object...)}.

+ * + *

Normally, a {@link Type} is used to discern between different bindings, but + * if this is not specific enough, an annotation can be defined and used. This + * makes it a "classifier" and it will take precedence over the base type. For + * example, even if there is a binding that handles {@link String} parameters, + * a special @MyArg annotation can be assigned to a {@link String} + * parameter, which will cause the {@link Builder} to consult the {@link Binding} + * associated with @MyArg rather than with the binding for + * the {@link String} type.

+ * + * @param binding the binding + * @param type a list of types (if specified) to override the binding's types + */ + public void addBinding(Binding binding, Type... type) { + if (type == null || type.length == 0) { + type = binding.getTypes(); + } + + for (Type t : type) { + bindings.put(t, binding); + } + } + + /** + * Attach an invocation listener. + * + *

Invocation handlers are called in order that their listeners are + * registered with a {@link ParametricBuilder}. It is not guaranteed that + * a listener may be called, in the case of a {@link CommandException} being + * thrown at any time before the appropriate listener or handler is called. + * It is possible for a + * {@link InvokeHandler#preInvoke(Object, Method, ParameterData[], Object[], CommandContext)} to + * be called for a invocation handler, but not the associated + * {@link InvokeHandler#postInvoke(Object, Method, ParameterData[], Object[], CommandContext)}.

+ * + *

An example of an invocation listener is one to handle + * {@link CommandPermissions}, by first checking to see if permission is available + * in a {@link InvokeHandler#preInvoke(Object, Method, ParameterData[], Object[], CommandContext)} + * call. If permission is not found, then an appropriate {@link CommandException} + * can be thrown to cease invocation.

+ * + * @param listener the listener + * @see InvokeHandler the handler + */ + public void attach(InvokeListener listener) { + invokeListeners.add(listener); + } + + /** + * Attach an exception converter to this builder in order to wrap unknown + * {@link Throwable}s into known {@link CommandException}s. + * + *

Exception converters are called in order that they are registered.

+ * + * @param converter the converter + * @see ExceptionConverter for an explanation + */ + public void attach(ExceptionConverter converter) { + exceptionConverters.add(converter); + } + + /** + * Build a list of commands from methods specially annotated with {@link Command} + * (and other relevant annotations) and register them all with the given + * {@link Dispatcher}. + * + * @param dispatcher the dispatcher to register commands with + * @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 { + for (Method method : object.getClass().getDeclaredMethods()) { + Command definition = method.getAnnotation(Command.class); + if (definition != null) { + CommandCallable callable = build(object, method, definition); + dispatcher.register(callable, definition.aliases()); + } + } + } + + /** + * Build a {@link CommandCallable} for the given method. + * + * @param object the object to be invoked on + * @param method the method to invoke + * @param definition the command definition annotation + * @return the command executor + * @throws ParametricException thrown on an error + */ + private CommandCallable build(Object object, Method method, Command definition) + throws ParametricException { + return new ParametricCallable(this, object, method, definition); + } + + /** + * Get the object used to get method names on Java versions before 8 (assuming + * that Java 8 is given the ability to reliably reflect method names at runtime). + * + * @return the paranamer + */ + Paranamer getParanamer() { + return paranamer; + } + + /** + * Get the map of bindings. + * + * @return the map of bindings + */ + Map getBindings() { + return bindings; + } + + /** + * Get a list of invocation listeners. + * + * @return a list of invocation listeners + */ + List getInvokeListeners() { + return invokeListeners; + } + + /** + * Get the list of exception converters. + * + * @return a list of exception converters + */ + List getExceptionConverters() { + return exceptionConverters; + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ParametricCallable.java b/src/main/java/com/sk89q/rebar/command/parametric/ParametricCallable.java new file mode 100644 index 000000000..47d76ed4c --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ParametricCallable.java @@ -0,0 +1,537 @@ +// $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.rebar.command.parametric; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + +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.minecraft.util.commands.SuggestionContext; +import com.sk89q.minecraft.util.commands.WrappedCommandException; +import com.sk89q.rebar.command.CommandCallable; +import com.sk89q.rebar.command.InvalidUsageException; +import com.sk89q.rebar.command.MissingParameterException; +import com.sk89q.rebar.command.Parameter; +import com.sk89q.rebar.command.SimpleDescription; +import com.sk89q.rebar.command.UnconsumedParameterException; +import com.sk89q.rebar.command.binding.Switch; + +/** + * The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}. + */ +class ParametricCallable implements CommandCallable { + + private final ParametricBuilder builder; + private final Object object; + private final Method method; + private final ParameterData[] parameters; + private final Set valueFlags = new HashSet(); + private final SimpleDescription description = new SimpleDescription(); + + /** + * Create a new instance. + * + * @param builder the parametric builder + * @param object the object to invoke on + * @param method the method to invoke + * @param definition the command definition annotation + * @throws ParametricException thrown on an error + */ + ParametricCallable( + ParametricBuilder builder, + Object object, Method method, + Command definition) throws ParametricException { + + this.builder = builder; + this.object = object; + this.method = method; + + Annotation[][] annotations = method.getParameterAnnotations(); + String[] names = builder.getParanamer().lookupParameterNames(method, false); + Type[] types = method.getGenericParameterTypes(); + parameters = new ParameterData[types.length]; + List userParameters = new ArrayList(); + + // This helps keep tracks of @Nullables that appear in the middle of a list + // of parameters + int numOptional = 0; + + // Set permission hint + CommandPermissions permHint = method.getAnnotation(CommandPermissions.class); + if (permHint != null) { + description.setPermissions(Arrays.asList(permHint.value())); + } + + // Go through each parameter + for (int i = 0; i < types.length; i++) { + Type type = types[i]; + + ParameterData parameter = new ParameterData(); + parameter.setType(type); + parameter.setModifiers(annotations[i]); + + // Search for annotations + for (Annotation annotation : annotations[i]) { + if (annotation instanceof Switch) { + parameter.setFlag(((Switch) annotation).value(), type != boolean.class); + } else if (annotation instanceof Nullable) { + parameter.setOptional(true); + } else if (annotation instanceof Optional) { + parameter.setOptional(true); + String[] value = ((Optional) annotation).value(); + if (value.length > 0) { + parameter.setDefaultValue(value); + } + // Special annotation bindings + } else if (parameter.getBinding() == null) { + parameter.setBinding(builder.getBindings().get( + annotation.annotationType())); + parameter.setClassifier(annotation); + } + } + + parameter.setName(names.length > 0 ? + names[i] : generateName(type, parameter.getClassifier(), i)); + + // Track all value flags + if (parameter.isValueFlag()) { + valueFlags.add(parameter.getFlag()); + } + + // No special @annotation binding... let's check for the type + if (parameter.getBinding() == null) { + parameter.setBinding(builder.getBindings().get(type)); + + // 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()); + } + } + + // Do some validation of this parameter + parameter.validate(method, i + 1); + + // Keep track of optional parameters + if (parameter.isOptional() && parameter.getFlag() == null) { + numOptional++; + } else { + if (numOptional > 0 && parameter.isNonFlagConsumer()) { + if (parameter.getConsumedCount() < 0) { + throw new ParametricException( + "Found an parameter using the binding " + + parameter.getBinding().getClass().getCanonicalName() + + "\nthat does not know how many arguments it consumes, but " + + "it follows an optional parameter\nMethod: " + + method.toGenericString()); + } + } + } + + parameters[i] = parameter; + + // Make a list of "real" parameters + if (parameter.isUserInput()) { + userParameters.add(parameter); + } + } + + // Finish description + description.setDescription(!definition.desc().isEmpty() ? definition.desc() : null); + description.setHelp(!definition.help().isEmpty() ? definition.help() : null); + description.overrideUsage(!definition.usage().isEmpty() ? definition.usage() : null); + + for (InvokeListener listener : builder.getInvokeListeners()) { + listener.updateDescription(object, method, parameters, description); + } + + // Set parameters + description.setParameters(userParameters); + } + + @Override + public void call(CommandContext context) throws CommandException { + Object[] args = new Object[parameters.length]; + ContextArgumentStack arguments = new ContextArgumentStack(context); + ParameterData parameter = null; + + try { + // preProcess handlers + List handlers = new ArrayList(); + for (InvokeListener listener : builder.getInvokeListeners()) { + InvokeHandler handler = listener.createInvokeHandler(); + handlers.add(handler); + handler.preProcess(object, method, parameters, context); + } + + // Collect parameters + for (int i = 0; i < parameters.length; i++) { + parameter = parameters[i]; + + if (mayConsumeArguments(i, arguments)) { + // Parse the user input into a method argument + ArgumentStack usedArguments = getScopedContext(parameter, arguments); + + try { + args[i] = parameter.getBinding().bind(parameter, usedArguments, false); + } catch (MissingParameterException e) { + // Not optional? Then we can't execute this command + if (!parameter.isOptional()) { + throw e; + } + + args[i] = getDefaultValue(i, arguments); + } + } else { + args[i] = getDefaultValue(i, arguments); + } + } + + // Check for unused arguments + checkUnconsumed(arguments); + + // preInvoke handlers + for (InvokeHandler handler : handlers) { + handler.preInvoke(object, method, parameters, args, context); + } + + // Execute! + method.invoke(object, args); + + // postInvoke handlers + for (InvokeHandler handler : handlers) { + handler.postInvoke(handler, method, parameters, args, context); + } + } catch (MissingParameterException e) { + throw new InvalidUsageException( + "Too few parameters!", getDescription()); + } catch (UnconsumedParameterException e) { + throw new InvalidUsageException( + "Too many parameters! Unused parameters: " + + e.getUnconsumed(), getDescription()); + } catch (ParameterException e) { + if (e.getCause() != null) { + for (ExceptionConverter converter : builder.getExceptionConverters()) { + converter.convert(e.getCause()); + } + } + + String name = parameter.getName(); + + throw new InvalidUsageException("For parameter '" + name + "': " + + e.getMessage(), getDescription()); + } catch (InvocationTargetException e) { + for (ExceptionConverter converter : builder.getExceptionConverters()) { + converter.convert(e.getCause()); + } + throw new WrappedCommandException(e); + } catch (IllegalArgumentException e) { + throw new WrappedCommandException(e); + } catch (CommandException e) { + throw e; + } catch (Throwable e) { + throw new WrappedCommandException(e); + } + } + + @Override + public List getSuggestions(CommandContext context) throws CommandException { + ContextArgumentStack scoped = new ContextArgumentStack(context); + SuggestionContext suggestable = context.getSuggestionContext(); + + // For /command -f | + // For /command -f flag| + if (suggestable.forFlag()) { + for (int i = 0; i < parameters.length; i++) { + ParameterData parameter = parameters[i]; + + if (parameter.getFlag() == suggestable.getFlag()) { + String prefix = context.getFlag(parameter.getFlag()); + if (prefix == null) { + prefix = ""; + } + + return parameter.getBinding().getSuggestions(parameter, prefix); + } + } + + // This should not happen + return new ArrayList(); + } + + int consumerIndex = 0; + ParameterData lastConsumer = null; + String lastConsumed = null; + + for (int i = 0; i < parameters.length; i++) { + ParameterData parameter = parameters[i]; + + if (parameter.getFlag() != null) { + continue; // We already handled flags + } + + try { + scoped.mark(); + parameter.getBinding().bind(parameter, scoped, true); + if (scoped.wasConsumed()) { + lastConsumer = parameter; + lastConsumed = scoped.getConsumed(); + consumerIndex++; + } + } catch (MissingParameterException e) { + // For /command value1 |value2 + // For /command |value1 value2 + if (suggestable.forHangingValue()) { + return parameter.getBinding().getSuggestions(parameter, ""); + } else { + // For /command value1| value2 + if (lastConsumer != null) { + return lastConsumer.getBinding() + .getSuggestions(lastConsumer, lastConsumed); + // For /command| value1 value2 + // This should never occur + } else { + throw new RuntimeException("Invalid suggestion context"); + } + } + } catch (ParameterException e) { + if (suggestable.forHangingValue()) { + String name = getDescription().getParameters() + .get(consumerIndex).getName(); + + throw new InvalidUsageException("For parameter '" + name + "': " + + e.getMessage(), getDescription()); + } else { + return parameter.getBinding().getSuggestions(parameter, ""); + } + } + } + + // For /command value1 value2 | + if (suggestable.forHangingValue()) { + // There's nothing that we can suggest because there's no more parameters + // to add on, and we can't change the previous parameter + return new ArrayList(); + } else { + // For /command value1 value2| + if (lastConsumer != null) { + return lastConsumer.getBinding() + .getSuggestions(lastConsumer, lastConsumed); + // This should never occur + } else { + throw new RuntimeException("Invalid suggestion context"); + } + + } + } + + @Override + public Set getValueFlags() { + return valueFlags; + } + + @Override + public SimpleDescription getDescription() { + return description; + } + + /** + * Get the right {@link ArgumentStack}. + * + * @param parameter the parameter + * @param existing the existing scoped context + * @return the context to use + */ + private static ArgumentStack getScopedContext(Parameter parameter, ArgumentStack existing) { + if (parameter.getFlag() != null) { + CommandContext context = existing.getContext(); + + if (parameter.isValueFlag()) { + return new StringArgumentStack( + context, context.getFlag(parameter.getFlag()), false); + } else { + String v = context.hasFlag(parameter.getFlag()) ? "true" : "false"; + return new StringArgumentStack(context, v, true); + } + } + + return existing; + } + + /** + * Get whether a parameter is allowed to consume arguments. + * + * @param i the index of the parameter + * @param scoped the scoped context + * @return true if arguments may be consumed + */ + private boolean mayConsumeArguments(int i, ContextArgumentStack scoped) { + CommandContext context = scoped.getContext(); + ParameterData parameter = parameters[i]; + + // Flag parameters: Always consume + // Required non-flag parameters: Always consume + // Optional non-flag parameters: + // - Before required parameters: Consume if there are 'left over' args + // - At the end: Always consumes + + if (parameter.isOptional() && parameter.getFlag() == null) { + int numberFree = context.argsLength() - scoped.position(); + for (int j = i; j < parameters.length; j++) { + if (parameters[j].isNonFlagConsumer() && !parameters[j].isOptional()) { + // We already checked if the consumed count was > -1 + // when we created this object + numberFree -= parameters[j].getConsumedCount(); + } + } + + // Skip this optional parameter + if (numberFree < 1) { + return false; + } + } + + return true; + } + + /** + * Get the default value for a parameter. + * + * @param i the index of the parameter + * @param scoped the scoped context + * @return a value + * @throws ParameterException on an error + * @throws CommandException on an error + */ + private Object getDefaultValue(int i, ContextArgumentStack scoped) + throws ParameterException, CommandException { + CommandContext context = scoped.getContext(); + ParameterData parameter = parameters[i]; + + String[] defaultValue = parameter.getDefaultValue(); + if (defaultValue != null) { + try { + return parameter.getBinding().bind( + parameter, new StringArgumentStack( + context, defaultValue, false), false); + } catch (MissingParameterException e) { + throw new ParametricException( + "The default value of the parameter using the binding " + + parameter.getBinding().getClass() + " in the method\n" + + method.toGenericString() + "\nis invalid"); + } + } + + return null; + } + + + /** + * Check to see if all arguments, including flag arguments, were consumed. + * + * @param scoped the argument scope + * @throws UnconsumedParameterException thrown if parameters were not consumed + */ + private void checkUnconsumed(ContextArgumentStack scoped) + throws UnconsumedParameterException { + CommandContext context = scoped.getContext(); + String unconsumed; + String unconsumedFlags = getUnusedFlags(context); + + if ((unconsumed = scoped.getUnconsumed()) != null) { + throw new UnconsumedParameterException(unconsumed + " " + unconsumedFlags); + } + + if (unconsumedFlags != null) { + throw new UnconsumedParameterException(unconsumedFlags); + } + } + + /** + * Get any unused flag arguments. + * + * @param context the command context + * @param parameters the list of parameters + */ + private String getUnusedFlags(CommandContext context) { + Set unusedFlags = null; + for (char flag : context.getFlags()) { + boolean found = false; + for (int i = 0; i < parameters.length; i++) { + Character paramFlag = parameters[i].getFlag(); + if (paramFlag != null && flag == paramFlag) { + found = true; + break; + } + } + + if (!found) { + if (unusedFlags == null) { + unusedFlags = new HashSet(); + } + unusedFlags.add(flag); + } + } + + if (unusedFlags != null) { + StringBuilder builder = new StringBuilder(); + for (Character flag : unusedFlags) { + builder.append("-").append(flag).append(" "); + } + + return builder.toString().trim(); + } + + return null; + } + + /** + * Generate a name for a parameter. + * + * @param type the type + * @param classifier the classifier + * @param index the index + * @return a generated name + */ + private static String generateName(Type type, Annotation classifier, int index) { + if (classifier != null) { + return classifier.annotationType().getSimpleName().toLowerCase(); + } else { + if (type instanceof Class) { + return ((Class) type).getSimpleName().toLowerCase(); + } else { + return "unknown" + index; + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/rebar/command/parametric/ParametricException.java b/src/main/java/com/sk89q/rebar/command/parametric/ParametricException.java new file mode 100644 index 000000000..24176200c --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/ParametricException.java @@ -0,0 +1,27 @@ +package com.sk89q.rebar.command.parametric; + +/** + * Thrown if the {@link ParametricBuilder} can't build commands from + * an object for whatever reason. + */ +public class ParametricException extends RuntimeException { + + private static final long serialVersionUID = -5426219576099680971L; + + public ParametricException() { + super(); + } + + public ParametricException(String message, Throwable cause) { + super(message, cause); + } + + public ParametricException(String message) { + super(message); + } + + public ParametricException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/PermissionsHandler.java b/src/main/java/com/sk89q/rebar/command/parametric/PermissionsHandler.java new file mode 100644 index 000000000..585799bf3 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/PermissionsHandler.java @@ -0,0 +1,66 @@ +// $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.rebar.command.parametric; + +import java.lang.reflect.Method; + +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; + +/** + * 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); + +} diff --git a/src/main/java/com/sk89q/rebar/command/parametric/StringArgumentStack.java b/src/main/java/com/sk89q/rebar/command/parametric/StringArgumentStack.java new file mode 100644 index 000000000..b81526790 --- /dev/null +++ b/src/main/java/com/sk89q/rebar/command/parametric/StringArgumentStack.java @@ -0,0 +1,127 @@ +// $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.rebar.command.parametric; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.rebar.command.MissingParameterException; +import com.sk89q.util.StringUtil; + +/** + * A virtual scope that does not actually read from the underlying + * {@link CommandContext}. + */ +public class StringArgumentStack implements ArgumentStack { + + private final boolean nonNullBoolean; + private final CommandContext context; + private final String[] arguments; + private int index = 0; + + /** + * Create a new instance using the given context. + * + * @param context the context + * @param arguments a list of arguments + * @param nonNullBoolean true to have {@link #nextBoolean()} return false instead of null + */ + public StringArgumentStack( + CommandContext context, String[] arguments, boolean nonNullBoolean) { + this.context = context; + this.arguments = arguments; + this.nonNullBoolean = nonNullBoolean; + } + + /** + * Create a new instance using the given context. + * + * @param context the context + * @param arguments an argument string to be parsed + * @param nonNullBoolean true to have {@link #nextBoolean()} return false instead of null + */ + public StringArgumentStack( + CommandContext context, String arguments, boolean nonNullBoolean) { + this.context = context; + this.arguments = CommandContext.split(arguments); + this.nonNullBoolean = nonNullBoolean; + } + + @Override + public String next() throws ParameterException { + try { + return arguments[index++]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new MissingParameterException(); + } + } + + @Override + public Integer nextInt() throws ParameterException { + try { + return Integer.parseInt(next()); + } catch (NumberFormatException e) { + throw new ParameterException( + "Expected a number, got '" + context.getString(index - 1) + "'"); + } + } + + @Override + public Double nextDouble() throws ParameterException { + try { + return Double.parseDouble(next()); + } catch (NumberFormatException e) { + throw new ParameterException( + "Expected a number, got '" + context.getString(index - 1) + "'"); + } + } + + @Override + public Boolean nextBoolean() throws ParameterException { + try { + return next().equalsIgnoreCase("true"); + } catch (IndexOutOfBoundsException e) { + if (nonNullBoolean) { // Special case + return false; + } + + throw new MissingParameterException(); + } + } + + @Override + public String remaining() throws ParameterException { + try { + String value = StringUtil.joinString(arguments, " ", index); + markConsumed(); + return value; + } catch (IndexOutOfBoundsException e) { + throw new MissingParameterException(); + } + } + + @Override + public void markConsumed() { + index = arguments.length; + } + + @Override + public CommandContext getContext() { + return context; + } + +} From 142f5c8e5c889ee5098c05ba2fde20b52467c1df Mon Sep 17 00:00:00 2001 From: Albert Pham Date: Tue, 18 Jun 2013 14:51:42 -0700 Subject: [PATCH 02/45] 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()); + } + +} From 38ac5c9ad166987cbda54ade9bbf56e88048d89b Mon Sep 17 00:00:00 2001 From: wizjany Date: Sat, 3 May 2014 23:12:22 -0400 Subject: [PATCH 03/45] Move a bukkit-specific file from #287 to bukkit folder as not to break API/other platform compilation. --- .../com/sk89q/worldedit/bukkit/selections/CylinderSelection.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main => bukkit}/java/com/sk89q/worldedit/bukkit/selections/CylinderSelection.java (100%) diff --git a/src/main/java/com/sk89q/worldedit/bukkit/selections/CylinderSelection.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/selections/CylinderSelection.java similarity index 100% rename from src/main/java/com/sk89q/worldedit/bukkit/selections/CylinderSelection.java rename to src/bukkit/java/com/sk89q/worldedit/bukkit/selections/CylinderSelection.java From 75b9f3edaa82ef841de9377e4eeca85c43b12e02 Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 15 May 2014 14:41:26 -0400 Subject: [PATCH 04/45] [Forge] Add dependency shading via the gradle shadow plugin. --- build.gradle | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cb0a83187..ea1ad00fa 100644 --- a/build.gradle +++ b/build.gradle @@ -7,16 +7,19 @@ buildscript { repositories { mavenCentral() maven { url = "http://files.minecraftforge.net/maven" } + jcenter() } dependencies { classpath 'net.minecraftforge.gradle:ForgeGradle:1.0-SNAPSHOT' + classpath 'com.github.jengelman.gradle.plugins:shadow:0.8' } } apply plugin: 'java' apply plugin: 'maven' apply plugin: 'forge' +apply plugin: 'shadow' group = 'com.sk89q' version = '6.0.0-SNAPSHOT' @@ -76,4 +79,35 @@ processResources { from (sourceSets.main.resources.srcDirs) { exclude 'mcmod.info' } -} \ No newline at end of file +} + +// shade needed runtime dependencies +shadow { + //artifactAttached false + destinationDir "${buildDir}/libs/" + artifactSet { + include '*:jchronic:jar:' + } +} + +task deleteOrig(type: Delete) { + delete "${project.tasks.jar.getArchivePath().getPath()}" +} + +task renameShaded(type: Copy) { + from file("${buildDir}/libs") + into file("${buildDir}/libs") + rename { String fileName -> + fileName.replace('-shadow', '') + } +} + +task deleteShaded(type: Delete) { + delete "${project.tasks.jar.getArchivePath().getPath().replace('.jar', '-shadow.jar')}" +} + +// follow all the steps +build.dependsOn(deleteShaded) +deleteShaded.dependsOn(renameShaded) +renameShaded.dependsOn(deleteOrig) +deleteOrig.dependsOn(shadowJar) From 0e00f0ac9d120f79b391ec66fff9a81385dcb924 Mon Sep 17 00:00:00 2001 From: Wizjany Date: Thu, 26 Jun 2014 17:26:30 -0400 Subject: [PATCH 05/45] [Forge] Move worldedit.properties to defaults folder so it extracts properly --- src/forge/resources/{ => defaults}/worldedit.properties | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/forge/resources/{ => defaults}/worldedit.properties (100%) diff --git a/src/forge/resources/worldedit.properties b/src/forge/resources/defaults/worldedit.properties similarity index 100% rename from src/forge/resources/worldedit.properties rename to src/forge/resources/defaults/worldedit.properties From 900c9b525881233cb0d46751978287686b999c9e Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 26 Jun 2014 16:56:40 -0700 Subject: [PATCH 06/45] Added BlockInteractEvent to replace handleBlockLeftClick(). Needed quite a shim for tools/brushes for now. --- .../worldedit/bukkit/WorldEditListener.java | 4 +- .../sk89q/worldedit/forge/ForgePlayer.java | 2 +- .../sk89q/worldedit/forge/ForgeWorldEdit.java | 4 +- .../java/com/sk89q/worldedit/WorldEdit.java | 40 +------- .../java/com/sk89q/worldedit/WorldVector.java | 26 +++++- .../command/tool/brush/SmoothBrush.java | 2 +- .../event/actor/BlockInteractEvent.java | 91 +++++++++++++++++++ .../event/actor/InteractionType.java | 36 ++++++++ .../extension/platform/PlatformManager.java | 73 ++++++++++++++- .../worldedit/internal/LocalWorldAdapter.java | 2 +- .../ServerInterfaceAdapter.java | 17 +++- .../com/sk89q/worldedit/util/TargetBlock.java | 4 +- 12 files changed, 248 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java create mode 100644 src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java rename src/main/java/com/sk89q/worldedit/{extension/platform => internal}/ServerInterfaceAdapter.java (84%) diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index fbb335543..50e7f9e37 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -125,7 +125,7 @@ public class WorldEditListener implements Listener { Action action = event.getAction(); if (action == Action.LEFT_CLICK_BLOCK) { final Block clickedBlock = event.getClickedBlock(); - final WorldVector pos = new WorldVector(LocalWorldAdapter.wrap(world), clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); + final WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); if (we.handleBlockLeftClick(player, pos)) { event.setCancelled(true); @@ -159,7 +159,7 @@ public class WorldEditListener implements Listener { } else if (action == Action.RIGHT_CLICK_BLOCK) { final Block clickedBlock = event.getClickedBlock(); - final WorldVector pos = new WorldVector(LocalWorldAdapter.wrap(world), clickedBlock.getX(), + final WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); if (we.handleBlockRightClick(player, pos)) { diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java index 01e178f71..a3d441385 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -59,7 +59,7 @@ public class ForgePlayer extends LocalPlayer { } public WorldVector getPosition() { - return new WorldVector(LocalWorldAdapter.wrap(ForgeWorldEdit.inst.getWorld(this.player.worldObj)), this.player.posX, this.player.posY, this.player.posZ); + return new WorldVector(LocalWorldAdapter.adapt(ForgeWorldEdit.inst.getWorld(this.player.worldObj)), this.player.posX, this.player.posY, this.player.posZ); } public com.sk89q.worldedit.world.World getWorld() { diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 283e99a97..1b1b7f621 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -137,7 +137,7 @@ public class ForgeWorldEdit { Action action = event.action; switch (action) { case LEFT_CLICK_BLOCK: { - WorldVector pos = new WorldVector(LocalWorldAdapter.wrap(world), event.x, event.y, event.z); + WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), event.x, event.y, event.z); if (we.handleBlockLeftClick(player, pos)) { event.setCanceled(true); @@ -148,7 +148,7 @@ public class ForgeWorldEdit { } } case RIGHT_CLICK_BLOCK: { - WorldVector pos = new WorldVector(LocalWorldAdapter.wrap(world), event.x, event.y, event.z); + WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), event.x, event.y, event.z); if (we.handleBlockRightClick(player, pos)) { event.setCanceled(true); diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index cf137bb4d..f38966c90 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.CuboidClipboard.FlipDirection; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.command.tool.*; +import com.sk89q.worldedit.event.actor.BlockInteractEvent; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.extension.input.ParserContext; @@ -55,6 +56,7 @@ import java.util.Set; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.event.actor.InteractionType.PRIMARY_INPUT; /** * The entry point and container for a working implementation of WorldEdit. @@ -831,41 +833,9 @@ public class WorldEdit { * @return false if you want the action to go through */ public boolean handleBlockLeftClick(LocalPlayer player, WorldVector clicked) { - LocalSession session = getSession(player); - - if (player.getItemInHand() == getConfiguration().wandItem) { - if (!session.isToolControlEnabled()) { - return false; - } - - if (!player.hasPermission("worldedit.selection.pos")) { - return false; - } - - RegionSelector selector = session.getRegionSelector(player.getWorld()); - if (selector.selectPrimary(clicked)) { - selector.explainPrimarySelection(player, session, clicked); - } - - return true; - } - - if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { - final BlockTool superPickaxe = session.getSuperPickaxe(); - if (superPickaxe != null && superPickaxe.canUse(player)) { - return superPickaxe.actPrimary(getServer(), getConfiguration(), player, session, clicked); - } - } - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof DoubleActionBlockTool) { - if (tool.canUse(player)) { - ((DoubleActionBlockTool) tool).actSecondary(getServer(), getConfiguration(), player, session, clicked); - return true; - } - } - - return false; + BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), PRIMARY_INPUT); + getEventBus().post(event); + return event.isCancelled(); } /** diff --git a/src/main/java/com/sk89q/worldedit/WorldVector.java b/src/main/java/com/sk89q/worldedit/WorldVector.java index 2e901f823..5e3d12b2e 100644 --- a/src/main/java/com/sk89q/worldedit/WorldVector.java +++ b/src/main/java/com/sk89q/worldedit/WorldVector.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit; +import com.sk89q.worldedit.internal.LocalWorldAdapter; + /** * @deprecated Use {@link com.sk89q.worldedit.util.Location} wherever possible */ @@ -81,14 +83,23 @@ public class WorldVector extends Vector { /** * Construct the Vector object. - * - * @param world + * + * @param world */ public WorldVector(LocalWorld world) { super(); this.world = world; } + /** + * Construct the Vector object. + * + * @param location the location + */ + public WorldVector(com.sk89q.worldedit.util.Location location) { + this(LocalWorldAdapter.adapt(location.getWorld()), location.getX(), location.getY(), location.getZ()); + } + /** * Get the world. * @@ -122,4 +133,15 @@ public class WorldVector extends Vector { public BlockWorldVector toWorldBlockVector() { return new BlockWorldVector(this); } + + /** + * Return this object as a new preferred Location + * object. + * + * @return a new location object + */ + public com.sk89q.worldedit.util.Location toLocation() { + return new com.sk89q.worldedit.util.Location(getWorld(), this); + } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java index fe1811c02..d72dc333b 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java @@ -46,7 +46,7 @@ public class SmoothBrush implements Brush { public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { double rad = size; - WorldVector min = new WorldVector(LocalWorldAdapter.wrap(editSession.getWorld()), pos.subtract(rad, rad, rad)); + WorldVector min = new WorldVector(LocalWorldAdapter.adapt(editSession.getWorld()), pos.subtract(rad, rad, rad)); Vector max = pos.add(rad, rad + 10, rad); Region region = new CuboidRegion(editSession.getWorld(), min, max); HeightMap heightMap = new HeightMap(editSession, region, naturalOnly); diff --git a/src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java b/src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java new file mode 100644 index 000000000..cec761cdd --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java @@ -0,0 +1,91 @@ +/* + * 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.actor; + +import com.sk89q.worldedit.event.Cancellable; +import com.sk89q.worldedit.event.Event; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.util.Location; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Called when a block is interacted with. + */ +public class BlockInteractEvent extends Event implements Cancellable { + + private final Actor cause; + private final Location location; + private final InteractionType type; + private boolean cancelled; + + /** + * Create a new event. + * + * @param cause the causing actor + * @param location the location of the block + * @param type the type of interaction + */ + public BlockInteractEvent(Actor cause, Location location, InteractionType type) { + checkNotNull(cause); + checkNotNull(location); + checkNotNull(type); + this.cause = cause; + this.location = location; + this.type = type; + } + + /** + * Get the cause of this event. + * + * @return the cause + */ + public Actor getCause() { + return cause; + } + + /** + * Get the location of the block that was interacted with. + * + * @return the location + */ + public Location getLocation() { + return location; + } + + /** + * Get the type of interaction. + * + * @return the type of interaction + */ + public InteractionType getType() { + return type; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } +} diff --git a/src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java b/src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java new file mode 100644 index 000000000..f965de7c1 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java @@ -0,0 +1,36 @@ +/* + * 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.actor; + +/** + * The type of interaction. + */ +public enum InteractionType { + + /** + * Refers to primary input usage (left click). + */ + PRIMARY_INPUT, + + /** + * Refers to secondary input usage (right click). + */ + SECONDARY_INPUT +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 5f10187ee..e6843853e 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -19,9 +19,16 @@ package com.sk89q.worldedit.extension.platform; -import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.ServerInterface; -import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.command.tool.BlockTool; +import com.sk89q.worldedit.command.tool.DoubleActionBlockTool; +import com.sk89q.worldedit.command.tool.Tool; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.event.actor.BlockInteractEvent; +import com.sk89q.worldedit.internal.ServerInterfaceAdapter; +import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.eventbus.Subscribe; import javax.annotation.Nullable; import java.util.ArrayList; @@ -43,6 +50,7 @@ public class PlatformManager { private final LocalConfiguration defaultConfig = new DefaultConfiguration(); private final List platforms = new ArrayList(); + private final WorldEdit worldEdit; private final CommandManager commandManager; private @Nullable Platform primary = null; @@ -53,7 +61,11 @@ public class PlatformManager { */ public PlatformManager(WorldEdit worldEdit) { checkNotNull(worldEdit); + this.worldEdit = worldEdit; this.commandManager = new CommandManager(worldEdit); + + // Register this instance for events + worldEdit.getEventBus().register(this); } /** @@ -182,13 +194,66 @@ public class PlatformManager { if (platform instanceof ServerInterface) { return (ServerInterface) platform; } else { - return new ServerInterfaceAdapter(platform); + return ServerInterfaceAdapter.adapt(platform); } } else { throw new IllegalStateException("No platform has been registered"); } } + @Subscribe + public void handleBlockInteract(BlockInteractEvent event) { + Actor actor = event.getCause(); + Location location = event.getLocation(); + Vector vector = location.toVector(); + + // At this time, only handle interaction from players + if (actor instanceof Player) { + Player player = (Player) actor; + LocalSession session = worldEdit.getSessionManager().get(actor); + + if (player.getItemInHand() == getConfiguration().wandItem) { + if (!session.isToolControlEnabled()) { + return; + } + + if (!actor.hasPermission("worldedit.selection.pos")) { + return; + } + + RegionSelector selector = session.getRegionSelector(player.getWorld()); + + if (selector.selectPrimary(location.toVector())) { + selector.explainPrimarySelection(actor, session, vector); + } + + event.setCancelled(true); + return; + } + + if (player instanceof LocalPlayer) { // Temporary workaround + LocalPlayer localPlayer = (LocalPlayer) player; + WorldVector worldVector = new WorldVector(location); + + if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { + final BlockTool superPickaxe = session.getSuperPickaxe(); + if (superPickaxe != null && superPickaxe.canUse(localPlayer)) { + event.setCancelled(superPickaxe.actPrimary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector)); + return; + } + } + + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof DoubleActionBlockTool) { + if (tool.canUse(localPlayer)) { + ((DoubleActionBlockTool) tool).actSecondary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector); + event.setCancelled(true); + } + } + } + } + } + /** * A default configuration for when none is set. */ diff --git a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java index 3e061de36..ea509acc3 100644 --- a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java @@ -309,7 +309,7 @@ public class LocalWorldAdapter extends LocalWorld { return world.commit(); } - public static LocalWorldAdapter wrap(World world) { + public static LocalWorldAdapter adapt(World world) { return new LocalWorldAdapter(world); } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java similarity index 84% rename from src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java rename to src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java index 5f1bd80f4..fa71a403a 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java @@ -17,11 +17,12 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.extension.platform; +package com.sk89q.worldedit.internal; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.world.World; import java.util.List; @@ -31,7 +32,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * Adapts {@link Platform}s into the legacy {@link ServerInterface}. */ -class ServerInterfaceAdapter extends ServerInterface { +public class ServerInterfaceAdapter extends ServerInterface { private final Platform platform; @@ -40,7 +41,7 @@ class ServerInterfaceAdapter extends ServerInterface { * * @param platform the platform */ - ServerInterfaceAdapter(Platform platform) { + private ServerInterfaceAdapter(Platform platform) { checkNotNull(platform); this.platform = platform; } @@ -106,4 +107,14 @@ class ServerInterfaceAdapter extends ServerInterface { return platform.getPlatformVersion(); } + /** + * Adapt an {@link Platform} instance into a {@link ServerInterface}. + * + * @param platform the platform + * @return the server interface + */ + public static ServerInterface adapt(Platform platform) { + return new ServerInterfaceAdapter(platform); + } + } diff --git a/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/src/main/java/com/sk89q/worldedit/util/TargetBlock.java index 700dce3f8..2d62e8b37 100644 --- a/src/main/java/com/sk89q/worldedit/util/TargetBlock.java +++ b/src/main/java/com/sk89q/worldedit/util/TargetBlock.java @@ -49,7 +49,7 @@ public class TargetBlock { * @param player player to work with */ public TargetBlock(LocalPlayer player) { - this.world = LocalWorldAdapter.wrap(player.getWorld()); + this.world = LocalWorldAdapter.adapt(player.getWorld()); this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), 300, 1.65, 0.2); } @@ -73,7 +73,7 @@ public class TargetBlock { * @param checkDistance how often to check for blocks, the smaller the more precise */ public TargetBlock(Entity player, int maxDistance, double checkDistance) { - this.world = LocalWorldAdapter.wrap(player.getWorld()); + this.world = LocalWorldAdapter.adapt(player.getWorld()); this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), maxDistance, 1.65, checkDistance); } From ff7d5aad1aa5912f729e026a0f721bfce2ba3f19 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 26 Jun 2014 16:58:54 -0700 Subject: [PATCH 07/45] Only handle primary input in block interact event handler. --- .../extension/platform/PlatformManager.java | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index e6843853e..9a5bd4347 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.command.tool.DoubleActionBlockTool; import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.actor.BlockInteractEvent; +import com.sk89q.worldedit.event.actor.InteractionType; import com.sk89q.worldedit.internal.ServerInterfaceAdapter; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.util.Location; @@ -209,45 +210,47 @@ public class PlatformManager { // At this time, only handle interaction from players if (actor instanceof Player) { - Player player = (Player) actor; - LocalSession session = worldEdit.getSessionManager().get(actor); + if (event.getType() == InteractionType.PRIMARY_INPUT) { + Player player = (Player) actor; + LocalSession session = worldEdit.getSessionManager().get(actor); - if (player.getItemInHand() == getConfiguration().wandItem) { - if (!session.isToolControlEnabled()) { - return; - } - - if (!actor.hasPermission("worldedit.selection.pos")) { - return; - } - - RegionSelector selector = session.getRegionSelector(player.getWorld()); - - if (selector.selectPrimary(location.toVector())) { - selector.explainPrimarySelection(actor, session, vector); - } - - event.setCancelled(true); - return; - } - - if (player instanceof LocalPlayer) { // Temporary workaround - LocalPlayer localPlayer = (LocalPlayer) player; - WorldVector worldVector = new WorldVector(location); - - if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { - final BlockTool superPickaxe = session.getSuperPickaxe(); - if (superPickaxe != null && superPickaxe.canUse(localPlayer)) { - event.setCancelled(superPickaxe.actPrimary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector)); + if (player.getItemInHand() == getConfiguration().wandItem) { + if (!session.isToolControlEnabled()) { return; } + + if (!actor.hasPermission("worldedit.selection.pos")) { + return; + } + + RegionSelector selector = session.getRegionSelector(player.getWorld()); + + if (selector.selectPrimary(location.toVector())) { + selector.explainPrimarySelection(actor, session, vector); + } + + event.setCancelled(true); + return; } - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof DoubleActionBlockTool) { - if (tool.canUse(localPlayer)) { - ((DoubleActionBlockTool) tool).actSecondary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector); - event.setCancelled(true); + if (player instanceof LocalPlayer) { // Temporary workaround + LocalPlayer localPlayer = (LocalPlayer) player; + WorldVector worldVector = new WorldVector(location); + + if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { + final BlockTool superPickaxe = session.getSuperPickaxe(); + if (superPickaxe != null && superPickaxe.canUse(localPlayer)) { + event.setCancelled(superPickaxe.actPrimary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector)); + return; + } + } + + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof DoubleActionBlockTool) { + if (tool.canUse(localPlayer)) { + ((DoubleActionBlockTool) tool).actSecondary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector); + event.setCancelled(true); + } } } } From fc50831cc080287b5d42c8ebda5c5c459a734e3b Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 26 Jun 2014 17:07:08 -0700 Subject: [PATCH 08/45] Changed right click to use the event as well. --- .../java/com/sk89q/worldedit/WorldEdit.java | 37 ++++-------------- .../extension/platform/PlatformManager.java | 38 +++++++++++++++++-- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index f38966c90..e20a6efa9 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -23,7 +23,9 @@ import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.worldedit.CuboidClipboard.FlipDirection; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.command.tool.*; +import com.sk89q.worldedit.command.tool.DoubleActionTraceTool; +import com.sk89q.worldedit.command.tool.Tool; +import com.sk89q.worldedit.command.tool.TraceTool; import com.sk89q.worldedit.event.actor.BlockInteractEvent; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.event.platform.CommandEvent; @@ -38,7 +40,6 @@ import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.pattern.Patterns; import com.sk89q.worldedit.masks.Mask; import com.sk89q.worldedit.patterns.Pattern; -import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.scripting.CraftScriptContext; import com.sk89q.worldedit.scripting.CraftScriptEngine; import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine; @@ -57,6 +58,7 @@ import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.worldedit.event.actor.InteractionType.PRIMARY_INPUT; +import static com.sk89q.worldedit.event.actor.InteractionType.SECONDARY_INPUT; /** * The entry point and container for a working implementation of WorldEdit. @@ -795,34 +797,9 @@ public class WorldEdit { * @return false if you want the action to go through */ public boolean handleBlockRightClick(LocalPlayer player, WorldVector clicked) { - LocalSession session = getSession(player); - - if (player.getItemInHand() == getConfiguration().wandItem) { - if (!session.isToolControlEnabled()) { - return false; - } - - if (!player.hasPermission("worldedit.selection.pos")) { - return false; - } - - RegionSelector selector = session.getRegionSelector(player.getWorld()); - if (selector.selectSecondary(clicked)) { - selector.explainSecondarySelection(player, session, clicked); - } - - return true; - } - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof BlockTool) { - if (tool.canUse(player)) { - ((BlockTool) tool).actPrimary(getServer(), getConfiguration(), player, session, clicked); - return true; - } - } - - return false; + BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), SECONDARY_INPUT); + getEventBus().post(event); + return event.isCancelled(); } /** diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 9a5bd4347..7ddc5e09c 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -210,10 +210,10 @@ public class PlatformManager { // At this time, only handle interaction from players if (actor instanceof Player) { - if (event.getType() == InteractionType.PRIMARY_INPUT) { - Player player = (Player) actor; - LocalSession session = worldEdit.getSessionManager().get(actor); + Player player = (Player) actor; + LocalSession session = worldEdit.getSessionManager().get(actor); + if (event.getType() == InteractionType.PRIMARY_INPUT) { if (player.getItemInHand() == getConfiguration().wandItem) { if (!session.isToolControlEnabled()) { return; @@ -253,6 +253,38 @@ public class PlatformManager { } } } + + } else if (event.getType() == InteractionType.SECONDARY_INPUT) { + if (player.getItemInHand() == getConfiguration().wandItem) { + if (!session.isToolControlEnabled()) { + return; + } + + if (!actor.hasPermission("worldedit.selection.pos")) { + return; + } + + RegionSelector selector = session.getRegionSelector(player.getWorld()); + if (selector.selectSecondary(vector)) { + selector.explainSecondarySelection(actor, session, vector); + } + + event.setCancelled(true); + return; + } + + if (player instanceof LocalPlayer) { // Temporary workaround + LocalPlayer localPlayer = (LocalPlayer) player; + WorldVector worldVector = new WorldVector(location); + + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof BlockTool) { + if (tool.canUse(localPlayer)) { + ((BlockTool) tool).actPrimary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector); + event.setCancelled(true); + } + } + } } } } From 92204ba623fba2e980fdf12290a90554e84e206a Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 26 Jun 2014 17:07:37 -0700 Subject: [PATCH 09/45] [Forge] Fixed switch() fall-through on handling of block interaction. --- .../java/com/sk89q/worldedit/forge/ForgeWorldEdit.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 1b1b7f621..7e21f7134 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -146,6 +146,8 @@ public class ForgeWorldEdit { if (we.handleArmSwing(player)) { event.setCanceled(true); } + + break; } case RIGHT_CLICK_BLOCK: { WorldVector pos = new WorldVector(LocalWorldAdapter.adapt(world), event.x, event.y, event.z); @@ -157,11 +159,15 @@ public class ForgeWorldEdit { if (we.handleRightClick(player)) { event.setCanceled(true); } + + break; } case RIGHT_CLICK_AIR: { if (we.handleRightClick(player)) { event.setCanceled(true); } + + break; } } } From 7827dfea9efcd690d37a487d9626db33097a1c40 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 26 Jun 2014 17:43:25 -0700 Subject: [PATCH 10/45] Put in a tip about setting //limit. --- .../sk89q/worldedit/command/GeneralCommands.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 7f938fcee..285901292 100644 --- a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -57,19 +57,23 @@ public class GeneralCommands { EditSession editSession) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); + boolean mayDisable = player.hasPermission("worldedit.limit.unrestricted"); int limit = Math.max(-1, args.getInteger(0)); - if (!player.hasPermission("worldedit.limit.unrestricted") - && config.maxChangeLimit > -1) { + if (!mayDisable && config.maxChangeLimit > -1) { if (limit > config.maxChangeLimit) { - player.printError("Your maximum allowable limit is " - + config.maxChangeLimit + "."); + player.printError("Your maximum allowable limit is " + config.maxChangeLimit + "."); return; } } session.setBlockChangeLimit(limit); - player.print("Block change limit set to " + limit + "."); + + if (limit != -1) { + player.print("Block change limit set to " + limit + ". (Use //limit -1 to go back to the default.)"); + } else { + player.print("Block change limit set to " + limit + "."); + } } @Command( From d9cea950b0b8e9ee3a401c744d48bd48e43353a3 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 26 Jun 2014 20:07:04 -0700 Subject: [PATCH 11/45] Added events for the other WorldEdit.handle*() methods. --- .../java/com/sk89q/worldedit/WorldEdit.java | 77 +++------------ .../com/sk89q/worldedit/entity/Player.java | 3 +- .../BlockInteractEvent.java | 8 +- .../worldedit/event/platform/InputType.java | 37 ++++++++ .../Interaction.java} | 9 +- .../event/platform/PlayerInputEvent.java | 78 +++++++++++++++ .../extension/platform/PlatformManager.java | 95 +++++++++++++++++-- 7 files changed, 227 insertions(+), 80 deletions(-) rename src/main/java/com/sk89q/worldedit/event/{actor => platform}/BlockInteractEvent.java (94%) create mode 100644 src/main/java/com/sk89q/worldedit/event/platform/InputType.java rename src/main/java/com/sk89q/worldedit/event/{actor/InteractionType.java => platform/Interaction.java} (90%) create mode 100644 src/main/java/com/sk89q/worldedit/event/platform/PlayerInputEvent.java diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index e20a6efa9..6dca280b5 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -23,12 +23,11 @@ import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.worldedit.CuboidClipboard.FlipDirection; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.command.tool.DoubleActionTraceTool; -import com.sk89q.worldedit.command.tool.Tool; -import com.sk89q.worldedit.command.tool.TraceTool; -import com.sk89q.worldedit.event.actor.BlockInteractEvent; +import com.sk89q.worldedit.event.platform.BlockInteractEvent; import com.sk89q.worldedit.event.extent.EditSessionEvent; 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; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.PlatformManager; @@ -57,8 +56,8 @@ import java.util.Set; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.event.actor.InteractionType.PRIMARY_INPUT; -import static com.sk89q.worldedit.event.actor.InteractionType.SECONDARY_INPUT; +import static com.sk89q.worldedit.event.platform.Interaction.HIT; +import static com.sk89q.worldedit.event.platform.Interaction.OPEN; /** * The entry point and container for a working implementation of WorldEdit. @@ -722,35 +721,9 @@ public class WorldEdit { * @return true if the swing was handled */ public boolean handleArmSwing(LocalPlayer player) { - if (player.getItemInHand() == getConfiguration().navigationWand) { - if (getConfiguration().navigationWandMaxDistance <= 0) { - return false; - } - - if (!player.hasPermission("worldedit.navigation.jumpto.tool")) { - return false; - } - - WorldVector pos = player.getSolidBlockTrace(getConfiguration().navigationWandMaxDistance); - if (pos != null) { - player.findFreePosition(pos); - } else { - player.printError("No block in sight (or too far)!"); - } - return true; - } - - LocalSession session = getSession(player); - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof DoubleActionTraceTool) { - if (tool.canUse(player)) { - ((DoubleActionTraceTool) tool).actSecondary(getServer(), getConfiguration(), player, session); - return true; - } - } - - return false; + PlayerInputEvent event = new PlayerInputEvent(player, InputType.PRIMARY); + getEventBus().post(event); + return event.isCancelled(); } /** @@ -760,33 +733,9 @@ public class WorldEdit { * @return true if the right click was handled */ public boolean handleRightClick(LocalPlayer player) { - if (player.getItemInHand() == getConfiguration().navigationWand) { - if (getConfiguration().navigationWandMaxDistance <= 0) { - return false; - } - - if (!player.hasPermission("worldedit.navigation.thru.tool")) { - return false; - } - - if (!player.passThroughForwardWall(40)) { - player.printError("Nothing to pass through!"); - } - - return true; - } - - LocalSession session = getSession(player); - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof TraceTool) { - if (tool.canUse(player)) { - ((TraceTool) tool).actPrimary(getServer(), getConfiguration(), player, session); - return true; - } - } - - return false; + PlayerInputEvent event = new PlayerInputEvent(player, InputType.SECONDARY); + getEventBus().post(event); + return event.isCancelled(); } /** @@ -797,7 +746,7 @@ public class WorldEdit { * @return false if you want the action to go through */ public boolean handleBlockRightClick(LocalPlayer player, WorldVector clicked) { - BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), SECONDARY_INPUT); + BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), OPEN); getEventBus().post(event); return event.isCancelled(); } @@ -810,7 +759,7 @@ public class WorldEdit { * @return false if you want the action to go through */ public boolean handleBlockLeftClick(LocalPlayer player, WorldVector clicked) { - BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), PRIMARY_INPUT); + BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), HIT); getEventBus().post(event); return event.isCancelled(); } diff --git a/src/main/java/com/sk89q/worldedit/entity/Player.java b/src/main/java/com/sk89q/worldedit/entity/Player.java index 4d5a5b601..0098dc2f4 100644 --- a/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -22,12 +22,13 @@ package com.sk89q.worldedit.entity; import com.sk89q.worldedit.PlayerDirection; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; /** * A player. */ -public interface Player extends Entity { +public interface Player extends Entity, Actor { /** * Returns true if the entity is holding a pick axe. diff --git a/src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/BlockInteractEvent.java similarity index 94% rename from src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java rename to src/main/java/com/sk89q/worldedit/event/platform/BlockInteractEvent.java index cec761cdd..2fdb4e443 100644 --- a/src/main/java/com/sk89q/worldedit/event/actor/BlockInteractEvent.java +++ b/src/main/java/com/sk89q/worldedit/event/platform/BlockInteractEvent.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.event.actor; +package com.sk89q.worldedit.event.platform; import com.sk89q.worldedit.event.Cancellable; import com.sk89q.worldedit.event.Event; @@ -33,7 +33,7 @@ public class BlockInteractEvent extends Event implements Cancellable { private final Actor cause; private final Location location; - private final InteractionType type; + private final Interaction type; private boolean cancelled; /** @@ -43,7 +43,7 @@ public class BlockInteractEvent extends Event implements Cancellable { * @param location the location of the block * @param type the type of interaction */ - public BlockInteractEvent(Actor cause, Location location, InteractionType type) { + public BlockInteractEvent(Actor cause, Location location, Interaction type) { checkNotNull(cause); checkNotNull(location); checkNotNull(type); @@ -75,7 +75,7 @@ public class BlockInteractEvent extends Event implements Cancellable { * * @return the type of interaction */ - public InteractionType getType() { + public Interaction getType() { return type; } diff --git a/src/main/java/com/sk89q/worldedit/event/platform/InputType.java b/src/main/java/com/sk89q/worldedit/event/platform/InputType.java new file mode 100644 index 000000000..4f614d719 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/InputType.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * The type of input sent. + */ +public enum InputType { + + /** + * Left click. + */ + PRIMARY, + + /** + * Right click. + */ + SECONDARY + +} diff --git a/src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java b/src/main/java/com/sk89q/worldedit/event/platform/Interaction.java similarity index 90% rename from src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java rename to src/main/java/com/sk89q/worldedit/event/platform/Interaction.java index f965de7c1..91331fa56 100644 --- a/src/main/java/com/sk89q/worldedit/event/actor/InteractionType.java +++ b/src/main/java/com/sk89q/worldedit/event/platform/Interaction.java @@ -17,20 +17,21 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.event.actor; +package com.sk89q.worldedit.event.platform; /** * The type of interaction. */ -public enum InteractionType { +public enum Interaction { /** * Refers to primary input usage (left click). */ - PRIMARY_INPUT, + HIT, /** * Refers to secondary input usage (right click). */ - SECONDARY_INPUT + OPEN + } diff --git a/src/main/java/com/sk89q/worldedit/event/platform/PlayerInputEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/PlayerInputEvent.java new file mode 100644 index 000000000..2bc39ee97 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/PlayerInputEvent.java @@ -0,0 +1,78 @@ +/* + * 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.entity.Player; +import com.sk89q.worldedit.event.Cancellable; +import com.sk89q.worldedit.event.Event; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Raised whenever a player sends input. + */ +public class PlayerInputEvent extends Event implements Cancellable { + + private final Player player; + private final InputType inputType; + private boolean cancelled; + + /** + * Create a new event. + * + * @param player the player + * @param inputType the input type + */ + public PlayerInputEvent(Player player, InputType inputType) { + checkNotNull(player); + checkNotNull(inputType); + this.player = player; + this.inputType = inputType; + } + + /** + * Get the player that sent the input. + * + * @return the player + */ + public Player getPlayer() { + return player; + } + + /** + * Get the type of input sent. + * + * @return the input sent + */ + public InputType getInputType() { + return inputType; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 7ddc5e09c..4a93df9e4 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -20,12 +20,11 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.*; -import com.sk89q.worldedit.command.tool.BlockTool; -import com.sk89q.worldedit.command.tool.DoubleActionBlockTool; -import com.sk89q.worldedit.command.tool.Tool; +import com.sk89q.worldedit.command.tool.*; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.event.actor.BlockInteractEvent; -import com.sk89q.worldedit.event.actor.InteractionType; +import com.sk89q.worldedit.event.platform.BlockInteractEvent; +import com.sk89q.worldedit.event.platform.Interaction; +import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.internal.ServerInterfaceAdapter; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.util.Location; @@ -213,7 +212,7 @@ public class PlatformManager { Player player = (Player) actor; LocalSession session = worldEdit.getSessionManager().get(actor); - if (event.getType() == InteractionType.PRIMARY_INPUT) { + if (event.getType() == Interaction.HIT) { if (player.getItemInHand() == getConfiguration().wandItem) { if (!session.isToolControlEnabled()) { return; @@ -254,7 +253,7 @@ public class PlatformManager { } } - } else if (event.getType() == InteractionType.SECONDARY_INPUT) { + } else if (event.getType() == Interaction.OPEN) { if (player.getItemInHand() == getConfiguration().wandItem) { if (!session.isToolControlEnabled()) { return; @@ -289,6 +288,88 @@ public class PlatformManager { } } + @Subscribe + public void handlePlayerInput(PlayerInputEvent event) { + Player player = event.getPlayer(); + + switch (event.getInputType()) { + case PRIMARY: { + if (player.getItemInHand() == getConfiguration().navigationWand) { + if (getConfiguration().navigationWandMaxDistance <= 0) { + return; + } + + if (!player.hasPermission("worldedit.navigation.jumpto.tool")) { + return; + } + + WorldVector pos = player.getSolidBlockTrace(getConfiguration().navigationWandMaxDistance); + if (pos != null) { + player.findFreePosition(pos); + } else { + player.printError("No block in sight (or too far)!"); + } + + event.setCancelled(true); + return; + } + + LocalSession session = worldEdit.getSessionManager().get(player); + + if (player instanceof LocalPlayer) { // Temporary workaround + LocalPlayer localPlayer = (LocalPlayer) player; + + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof DoubleActionTraceTool) { + if (tool.canUse(localPlayer)) { + ((DoubleActionTraceTool) tool).actSecondary(getServerInterface(), getConfiguration(), localPlayer, session); + event.setCancelled(true); + return; + } + } + } + + break; + } + + case SECONDARY: { + if (player.getItemInHand() == getConfiguration().navigationWand) { + if (getConfiguration().navigationWandMaxDistance <= 0) { + return; + } + + if (!player.hasPermission("worldedit.navigation.thru.tool")) { + return; + } + + if (!player.passThroughForwardWall(40)) { + player.printError("Nothing to pass through!"); + } + + event.setCancelled(true); + return; + } + + LocalSession session = worldEdit.getSessionManager().get(player); + + if (player instanceof LocalPlayer) { // Temporary workaround + LocalPlayer localPlayer = (LocalPlayer) player; + + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof TraceTool) { + if (tool.canUse(localPlayer)) { + ((TraceTool) tool).actPrimary(getServerInterface(), getConfiguration(), localPlayer, session); + event.setCancelled(true); + return; + } + } + } + + break; + } + } + } + /** * A default configuration for when none is set. */ From e52ca6661fe582005b891290aebbba72bd92327f Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 01:11:35 -0700 Subject: [PATCH 12/45] Added support for platforms to declare capabilities. Platforms can declare certain capabilities and a suggested preference for the platform for each capability. WorldEdit can then choose the best platform for a given capability. Examples of capabilities include providing configuration, registering game hooks/events, performing changes to the world, or checking permissions/authorization. --- .../bukkit/BukkitServerInterface.java | 28 ++- .../worldedit/bukkit/WorldEditListener.java | 12 ++ .../worldedit/bukkit/WorldEditPlugin.java | 38 ++-- .../sk89q/worldedit/forge/ForgePlatform.java | 38 +++- .../sk89q/worldedit/forge/ForgeWorldEdit.java | 16 +- .../worldedit/command/WorldEditCommands.java | 10 +- .../event/platform/PlatformReadyEvent.java | 29 +++ .../extension/platform/Capability.java | 81 ++++++++ ...n.java => NoCapablePlatformException.java} | 29 ++- .../extension/platform/Platform.java | 15 ++ .../extension/platform/PlatformManager.java | 175 +++++++++++------- .../extension/platform/Preference.java | 59 ++++++ .../internal/ServerInterfaceAdapter.java | 17 +- 13 files changed, 416 insertions(+), 131 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/platform/Capability.java rename src/main/java/com/sk89q/worldedit/extension/platform/{PlatformRejectionException.java => NoCapablePlatformException.java} (62%) create mode 100644 src/main/java/com/sk89q/worldedit/extension/platform/Preference.java diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 6a34748a9..fdfecd950 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -25,6 +25,8 @@ 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.*; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Preference; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; @@ -32,16 +34,14 @@ import org.bukkit.World; import org.bukkit.entity.EntityType; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; public class BukkitServerInterface extends ServerInterface { public Server server; public WorldEditPlugin plugin; private CommandRegistration dynamicCommands; private BukkitBiomeTypes biomes; + private boolean hookingEvents; public BukkitServerInterface(WorldEditPlugin plugin, Server server) { this.plugin = plugin; @@ -50,6 +50,10 @@ public class BukkitServerInterface extends ServerInterface { dynamicCommands = new CommandRegistration(plugin); } + boolean isHookingEvents() { + return hookingEvents; + } + @Override public int resolveItem(String name) { Material mat = Material.matchMaterial(name); @@ -114,6 +118,11 @@ public class BukkitServerInterface extends ServerInterface { dynamicCommands.register(toRegister); } + @Override + public void registerGameHooks() { + hookingEvents = true; + } + @Override public LocalConfiguration getConfiguration() { return plugin.getLocalConfiguration(); @@ -134,6 +143,17 @@ public class BukkitServerInterface extends ServerInterface { return plugin.getDescription().getVersion(); } + @Override + public Map getCapabilities() { + Map capabilities = new EnumMap(Capability.class); + capabilities.put(Capability.CONFIGURATION, Preference.NORMAL); + capabilities.put(Capability.GAME_HOOKS, Preference.PREFERRED); + capabilities.put(Capability.PERMISSIONS, Preference.PREFERRED); + capabilities.put(Capability.USER_COMMANDS, Preference.PREFERRED); + capabilities.put(Capability.WORLD_EDITING, Preference.PREFER_OTHERS); + return capabilities; + } + public void unregisterCommands() { dynamicCommands.unregisterCommands(); } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index 50e7f9e37..4a60658a3 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java @@ -69,11 +69,19 @@ public class WorldEditListener implements Listener { */ @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + plugin.getWorldEdit().markExpire(plugin.wrapPlayer(event.getPlayer())); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onGamemode(PlayerGameModeChangeEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + // this will automatically refresh their sesssion, we don't have to do anything WorldEdit.getInstance().getSession(plugin.wrapPlayer(event.getPlayer())); } @@ -114,6 +122,10 @@ public class WorldEditListener implements Listener { */ @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + if (event.useItemInHand() == Result.DENY) { return; } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 7efb2b240..03d4383dc 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -26,13 +26,9 @@ import com.sk89q.worldedit.bukkit.selections.CuboidSelection; 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.extension.platform.PlatformRejectionException; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.CylinderRegion; -import com.sk89q.worldedit.regions.Polygonal2DRegion; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.*; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -106,21 +102,19 @@ public class WorldEditPlugin extends JavaPlugin { // Setup interfaces server = new BukkitServerInterface(this, getServer()); controller = WorldEdit.getInstance(); - try { - controller.getPlatformManager().register(server); - api = new WorldEditAPI(this); - getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); - getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL); - // Now we can register events! - getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); + controller.getPlatformManager().register(server); + api = new WorldEditAPI(this); + getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this)); + getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL); + // Now we can register events! + getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); - getServer().getScheduler().runTaskTimerAsynchronously(this, - new SessionTimer(controller, getServer()), 120, 120); - } catch (PlatformRejectionException e) { - throw new RuntimeException( - "WorldEdit rejected the Bukkit implementation of WorldEdit! This is strange and should " + - "not have happened. Please report this error.", e); - } + getServer().getScheduler().runTaskTimerAsynchronously(this, new SessionTimer(controller, getServer()), 120, 120); + + // If we are on MCPC+/Cauldron, then Forge will have already loaded + // Forge WorldEdit and there's (probably) not going to be any other + // platforms to be worried about... at the current time of writing + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); } private void copyNmsBlockClasses(File target) { @@ -357,6 +351,10 @@ public class WorldEditPlugin extends JavaPlugin { return server; } + BukkitServerInterface getInternalPlatform() { + return server; + } + /** * Get WorldEdit. * diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java index f24396222..0e15f1ca8 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -23,6 +23,8 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Preference; import cpw.mods.fml.common.FMLCommonHandler; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommand; @@ -34,21 +36,26 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; class ForgePlatform extends ServerInterface { + private final ForgeWorldEdit mod; private final MinecraftServer server; private final ForgeBiomeTypes biomes; + private boolean hookingEvents = false; - public ForgePlatform(ForgeWorldEdit mod) { + ForgePlatform(ForgeWorldEdit mod) { this.mod = mod; this.server = FMLCommonHandler.instance().getMinecraftServerInstance(); this.biomes = new ForgeBiomeTypes(); } + boolean isHookingEvents() { + return hookingEvents; + } + + @Override public int resolveItem(String name) { if (name == null) return 0; for (Item item : Item.itemsList) { @@ -65,21 +72,26 @@ class ForgePlatform extends ServerInterface { return 0; } + @Override public boolean isValidMobType(String type) { return EntityList.stringToClassMapping.containsKey(type); } + @Override public void reload() { } + @Override public BiomeTypes getBiomes() { return this.biomes; } + @Override public int schedule(long delay, long period, Runnable task) { return -1; } + @Override public List getWorlds() { List worlds = Arrays.asList(DimensionManager.getWorlds()); List ret = new ArrayList(worlds.size()); @@ -125,6 +137,12 @@ class ForgePlatform extends ServerInterface { } } + @Override + public void registerGameHooks() { + // We registered the events already anyway, so we just 'turn them on' + hookingEvents = true; + } + @Override public LocalConfiguration getConfiguration() { return mod.getConfig(); @@ -144,4 +162,16 @@ class ForgePlatform extends ServerInterface { public String getPlatformVersion() { return mod.getInternalVersion(); } + + @Override + public Map getCapabilities() { + Map capabilities = new EnumMap(Capability.class); + capabilities.put(Capability.CONFIGURATION, Preference.PREFER_OTHERS); + capabilities.put(Capability.GAME_HOOKS, Preference.NORMAL); + capabilities.put(Capability.PERMISSIONS, Preference.PREFER_OTHERS); + capabilities.put(Capability.USER_COMMANDS, Preference.NORMAL); + capabilities.put(Capability.WORLD_EDITING, Preference.PREFERRED); + return capabilities; + } + } diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 7e21f7134..72647b26a 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -24,8 +24,8 @@ import com.google.common.io.Closer; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.extension.platform.PlatformRejectionException; import com.sk89q.worldedit.internal.LocalWorldAdapter; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.Mod; @@ -103,11 +103,8 @@ public class ForgeWorldEdit { } this.platform = new ForgePlatform(this); - try { - WorldEdit.getInstance().getPlatformManager().register(platform); - } catch (PlatformRejectionException e) { - throw new RuntimeException("Failed to register with WorldEdit", e); - } + + WorldEdit.getInstance().getPlatformManager().register(platform); } @EventHandler @@ -115,6 +112,11 @@ public class ForgeWorldEdit { WorldEdit.getInstance().getPlatformManager().unregister(platform); } + @EventHandler + public void serverStarted(FMLServerStartedEvent event) { + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); + } + @ForgeSubscribe public void onCommandEvent(CommandEvent event) { if ((event.sender instanceof EntityPlayerMP)) { @@ -128,6 +130,8 @@ public class ForgeWorldEdit { @ForgeSubscribe public void onPlayerInteract(PlayerInteractEvent event) { + if (!platform.isHookingEvents()) return; // We have to be told to catch these events + if (event.useItem == Result.DENY || event.entity.worldObj.isRemote) return; WorldEdit we = WorldEdit.getInstance(); diff --git a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index a4ce0f806..6da2a3ecf 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -56,19 +56,11 @@ public class WorldEditCommands { player.print("https://github.com/sk89q/worldedit/"); PlatformManager pm = we.getPlatformManager(); - Platform primary = pm.getPrimaryPlatform(); player.printDebug(""); player.printDebug("Platforms:"); for (Platform platform : pm.getPlatforms()) { - String prefix = ""; - - if (primary != null && primary.equals(platform)) { - prefix = "[PRIMARY] "; - } - - player.printDebug(String.format("- %s%s v%s (WE v%s)", - prefix, platform.getPlatformName(), platform.getPlatformVersion(), platform.getVersion())); + player.printDebug(String.format("- %s v%s (WE v%s)", platform.getPlatformName(), platform.getPlatformVersion(), platform.getVersion())); } } diff --git a/src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java new file mode 100644 index 000000000..e80e12557 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/PlatformReadyEvent.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** + * Raised when a platform thinks that all the platforms have had a chance to + * register themselves. + */ +public class PlatformReadyEvent extends Event { +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java b/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java new file mode 100644 index 000000000..61ee2bfb8 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java @@ -0,0 +1,81 @@ +/* + * 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.extension.platform; + +/** + * A collection of capabilities that a {@link Platform} may support. + */ +public enum Capability { + + /** + * The capability of registering game hooks to catch events such as + * a player clicking a block. + */ + GAME_HOOKS { + @Override + void initialize(PlatformManager platformManager, Platform platform) { + platform.registerGameHooks(); + } + + @Override + void unload(PlatformManager platformManager, Platform platform) { + } + }, + + /** + * The capability of providing configuration. + */ + CONFIGURATION, + + /** + * The capability of handling user commands entered in chat or console. + */ + USER_COMMANDS { + @Override + void initialize(PlatformManager platformManager, Platform platform) { + platformManager.getCommandManager().register(platform); + } + + @Override + void unload(PlatformManager platformManager, Platform platform) { + platformManager.getCommandManager().unregister(); + } + }, + + /** + * The capability of a platform to assess whether a given + * {@link Actor} has sufficient authorization to perform a task. + */ + PERMISSIONS, + + /** + * The capability of a platform to perform modifications to a world. + */ + WORLD_EDITING; + + void initialize(PlatformManager platformManager, Platform platform) { + + } + + void unload(PlatformManager platformManager, Platform platform) { + + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java b/src/main/java/com/sk89q/worldedit/extension/platform/NoCapablePlatformException.java similarity index 62% rename from src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java rename to src/main/java/com/sk89q/worldedit/extension/platform/NoCapablePlatformException.java index 1810950eb..c3e1019a7 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformRejectionException.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/NoCapablePlatformException.java @@ -19,31 +19,24 @@ package com.sk89q.worldedit.extension.platform; -import com.sk89q.worldedit.WorldEditException; - /** - * Thrown when a platform registration request is rejected, which may - * be because another platform is already registered. + * Thrown when no capable platform is found. */ -public class PlatformRejectionException extends WorldEditException { +public class NoCapablePlatformException extends RuntimeException { - /** - * Create with a message. - * - * @param message the message - */ - public PlatformRejectionException(String message) { + public NoCapablePlatformException() { + } + + public NoCapablePlatformException(String message) { super(message); } - /** - * Create with a message and a cause. - * - * @param message the message - * @param cause the cause - */ - public PlatformRejectionException(String message, Throwable cause) { + public NoCapablePlatformException(String message, Throwable cause) { super(message, cause); } + public NoCapablePlatformException(Throwable cause) { + super(cause); + } + } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index 2d506a07c..5fcfa4941 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.world.World; import java.util.List; +import java.util.Map; /** * Represents a platform that WorldEdit has been implemented for. @@ -82,6 +83,11 @@ public interface Platform { void onCommandRegistration(List commands, CommandsManager manager); + /** + * Register game hooks. + */ + void registerGameHooks(); + /** * Get the configuration from this platform. * @@ -116,4 +122,13 @@ public interface Platform { */ String getPlatformVersion(); + /** + * Get a map of advertised capabilities of this platform, where each key + * in the given map is a supported capability and the respective value + * indicates the preference for this platform for that given capability. + * + * @return a map of capabilities + */ + Map getCapabilities(); + } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 4a93df9e4..7c04b8168 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -20,10 +20,12 @@ package com.sk89q.worldedit.extension.platform; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.command.tool.*; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.BlockInteractEvent; import com.sk89q.worldedit.event.platform.Interaction; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.event.platform.PlayerInputEvent; import com.sk89q.worldedit.internal.ServerInterfaceAdapter; import com.sk89q.worldedit.regions.RegionSelector; @@ -31,8 +33,8 @@ import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.eventbus.Subscribe; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,11 +50,11 @@ public class PlatformManager { private static final Logger logger = Logger.getLogger(PlatformManager.class.getCanonicalName()); - private final LocalConfiguration defaultConfig = new DefaultConfiguration(); - private final List platforms = new ArrayList(); private final WorldEdit worldEdit; private final CommandManager commandManager; - private @Nullable Platform primary = null; + private final List platforms = new ArrayList(); + private final Map preferences = new EnumMap(Capability.class); + private @Nullable String firstSeenVersion; /** * Create a new platform manager. @@ -72,56 +74,128 @@ public class PlatformManager { * Register a platform with WorldEdit. * * @param platform the platform - * @throws PlatformRejectionException thrown if the registration is rejected */ - public synchronized void register(Platform platform) throws PlatformRejectionException { + public synchronized void register(Platform platform) { checkNotNull(platform); + logger.log(Level.FINE, "Got request to register " + platform.getClass() + " with WorldEdit [" + super.toString() + "]"); + + // Just add the platform to the list of platforms: we'll pick favorites + // once all the platforms have been loaded platforms.add(platform); - // Register primary platform - if (this.primary == null) { - commandManager.register(platform); - this.primary = platform; - } else { - // Make sure that versions are in sync - if (!primary.getVersion().equals(platform.getVersion())) { + // Make sure that versions are in sync + if (firstSeenVersion != null) { + if (!firstSeenVersion.equals(platform.getVersion())) { logger.log(Level.WARNING, "\n**********************************************\n" + - "** There is a mismatch in available WorldEdit platforms!\n" + + "** You have WorldEdit installed for multiple platforms in the same \n" + + "** game/program. This is OK except that you have different WorldEdit versions\n" + + "** installed (i.e. {0} and {1}).\n" + "**\n" + - "** {0} v{1} is trying to register WE version v{2}\n" + - "** but the primary platform, {3} v{4}, uses WE version v{5}\n" + + "** WorldEdit has seen both versions {0} and {1}.\n" + "**\n" + "** Things may break! Please make sure that your WE versions are in sync.\n" + "**********************************************\n", new Object[]{ - platform.getClass(), platform.getPlatformVersion(), platform.getVersion(), - primary.getClass(), primary.getPlatformVersion(), primary.getVersion() + firstSeenVersion, platform.getVersion() }); } + } else { + firstSeenVersion = platform.getVersion(); } } /** * Unregister a platform from WorldEdit. + *

+ * If the platform has been chosen for any capabilities, then a new + * platform will be found. * * @param platform the platform */ public synchronized boolean unregister(Platform platform) { checkNotNull(platform); + boolean removed = platforms.remove(platform); + if (removed) { logger.log(Level.FINE, "Unregistering " + platform.getClass().getCanonicalName() + " from WorldEdit"); - if (platform == primary) { - primary = null; - commandManager.unregister(); + boolean choosePreferred = false; + + // Check whether this platform was chosen to be the preferred one + // for any capability and be sure to remove it + Iterator> it = preferences.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + if (entry.getValue().equals(platform)) { + entry.getKey().unload(this, entry.getValue()); + it.remove(); + choosePreferred = true; // Have to choose new favorites + } + } + + if (choosePreferred) { + choosePreferred(); } } + return removed; } + /** + * Get the preferred platform for handling a certain capability. Returns + * null if none is available. + * + * @param capability the capability + * @return the platform + * @throws NoCapablePlatformException thrown if no platform is capable + */ + public synchronized Platform queryCapability(Capability capability) throws NoCapablePlatformException { + Platform platform = preferences.get(checkNotNull(capability)); + if (platform != null) { + return platform; + } else { + throw new NoCapablePlatformException("No platform was found supporting " + capability.name()); + } + } + + /** + * Choose preferred platforms and perform necessary initialization. + */ + private synchronized void choosePreferred() { + for (Capability capability : Capability.values()) { + Platform preferred = findMostPreferred(capability); + if (preferred != null) { + preferences.put(capability, preferred); + capability.initialize(this, preferred); + } + } + } + + /** + * Find the most preferred platform for a given capability from the list of + * platforms. This does not use the map of preferred platforms. + * + * @param capability the capability + * @return the most preferred platform, or null if no platform was found + */ + private synchronized @Nullable Platform findMostPreferred(Capability capability) { + Platform preferred = null; + Preference highest = null; + + for (Platform platform : platforms) { + Preference preference = platform.getCapabilities().get(capability); + if (preference != null && (highest == null || preference.isPreferredOver(highest))) { + preferred = platform; + highest = preference; + } + } + + return preferred; + } + /** * Get a list of loaded platforms. *

@@ -133,15 +207,6 @@ public class PlatformManager { return new ArrayList(platforms); } - /** - * Get the primary platform. - * - * @return the primary platform (may be null) - */ - public @Nullable Platform getPrimaryPlatform() { - return primary; - } - /** * Get the command manager. * @@ -160,26 +225,7 @@ public class PlatformManager { * @return the configuration */ public LocalConfiguration getConfiguration() { - Platform platform = primary; - if (platform != null) { - return platform.getConfiguration(); - } else { - return defaultConfig; - } - } - /** - * Return a {@link Platform}. - * - * @return a {@link Platform} - * @throws IllegalStateException if no platform has been registered - */ - public Platform getPlatform() throws IllegalStateException { - Platform platform = primary; - if (platform != null) { - return platform; - } else { - throw new IllegalStateException("No platform has been registered"); - } + return queryCapability(Capability.CONFIGURATION).getConfiguration(); } /** @@ -188,19 +234,17 @@ public class PlatformManager { * @return a {@link ServerInterface} * @throws IllegalStateException if no platform has been registered */ + @SuppressWarnings("deprecation") public ServerInterface getServerInterface() throws IllegalStateException { - Platform platform = primary; - if (platform != null) { - if (platform instanceof ServerInterface) { - return (ServerInterface) platform; - } else { - return ServerInterfaceAdapter.adapt(platform); - } - } else { - throw new IllegalStateException("No platform has been registered"); - } + return ServerInterfaceAdapter.adapt(queryCapability(Capability.USER_COMMANDS)); } + @Subscribe + public void handlePlatformReady(PlatformReadyEvent event) { + choosePreferred(); + } + + @SuppressWarnings("deprecation") @Subscribe public void handleBlockInteract(BlockInteractEvent event) { Actor actor = event.getCause(); @@ -288,6 +332,7 @@ public class PlatformManager { } } + @SuppressWarnings("deprecation") @Subscribe public void handlePlayerInput(PlayerInputEvent event) { Player player = event.getPlayer(); @@ -370,13 +415,5 @@ public class PlatformManager { } } - /** - * A default configuration for when none is set. - */ - private static class DefaultConfiguration extends LocalConfiguration { - @Override - public void load() { - } - } } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Preference.java b/src/main/java/com/sk89q/worldedit/extension/platform/Preference.java new file mode 100644 index 000000000..f047fa26d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Preference.java @@ -0,0 +1,59 @@ +/* + * 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.extension.platform; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Indicates the preference of a platform for a particular + * {@link Capability}. + */ +public enum Preference { + + /** + * Indicates that the platform should be preferred for a given capability. + */ + PREFERRED, + + /** + * Indicates that preference for a platform is neutral for a given + * capability. + */ + NORMAL, + + /** + * Indicates that there should not be a preference for the platform for + * a given capability. + */ + PREFER_OTHERS; + + /** + * Returns whether this given preference is preferred over the given + * other preference. + * + * @param other the other preference + * @return true if this preference is greater + */ + public boolean isPreferredOver(Preference other) { + checkNotNull(other); + return ordinal() < other.ordinal(); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java index fa71a403a..96cac54b3 100644 --- a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java @@ -21,11 +21,17 @@ package com.sk89q.worldedit.internal; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandsManager; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.BiomeTypes; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.world.World; import java.util.List; +import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; @@ -87,6 +93,10 @@ public class ServerInterfaceAdapter extends ServerInterface { platform.onCommandRegistration(commands, manager); } + @Override + public void registerGameHooks() { + } + @Override public LocalConfiguration getConfiguration() { return platform.getConfiguration(); @@ -107,6 +117,11 @@ public class ServerInterfaceAdapter extends ServerInterface { return platform.getPlatformVersion(); } + @Override + public Map getCapabilities() { + return platform.getCapabilities(); + } + /** * Adapt an {@link Platform} instance into a {@link ServerInterface}. * From 9bb70ad3353b60f6e81aaddde74066557c0e1221 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 13:14:44 -0700 Subject: [PATCH 13/45] Updated events to choose the best platform for certain tasks. --- .../worldedit/bukkit/BukkitCommandSender.java | 3 +- .../sk89q/worldedit/bukkit/BukkitPlayer.java | 2 +- .../bukkit/BukkitServerInterface.java | 24 ++++ .../sk89q/worldedit/bukkit/BukkitWorld.java | 12 +- .../worldedit/bukkit/WorldEditPlugin.java | 2 +- .../sk89q/worldedit/forge/ForgePlatform.java | 31 +++++ .../sk89q/worldedit/forge/ForgePlayer.java | 1 - .../com/sk89q/worldedit/forge/ForgeWorld.java | 16 ++- .../java/com/sk89q/worldedit/LocalPlayer.java | 9 -- .../platform/AbstractPlayerActor.java | 13 -- .../extension/platform/Platform.java | 22 +++ .../extension/platform/PlatformManager.java | 48 ++++++- .../extension/platform/PlayerProxy.java | 125 ++++++++++++++++++ .../internal/ServerInterfaceAdapter.java | 14 ++ 14 files changed, 282 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index e2067fd90..08d4e1342 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -29,8 +29,7 @@ public class BukkitCommandSender extends LocalPlayer { private CommandSender sender; private WorldEditPlugin plugin; - public BukkitCommandSender(WorldEditPlugin plugin, ServerInterface server, CommandSender sender) { - super(server); + public BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) { this.plugin = plugin; this.sender = sender; } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 1499cd09f..637c7c7e8 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -41,7 +41,6 @@ public class BukkitPlayer extends LocalPlayer { private WorldEditPlugin plugin; public BukkitPlayer(WorldEditPlugin plugin, ServerInterface server, Player player) { - super(server); this.plugin = plugin; this.player = player; } @@ -52,6 +51,7 @@ public class BukkitPlayer extends LocalPlayer { return itemStack != null ? itemStack.getTypeId() : 0; } + @Override public BaseBlock getBlockInHand() throws WorldEditException { ItemStack itemStack = player.getItemInHand(); return BukkitUtil.toBlock(getWorld(), itemStack); diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index fdfecd950..03f4510d3 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -25,6 +25,7 @@ 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.*; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Preference; import org.bukkit.Bukkit; @@ -33,6 +34,7 @@ import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.EntityType; +import javax.annotation.Nullable; import java.lang.reflect.Method; import java.util.*; @@ -93,6 +95,28 @@ public class BukkitServerInterface extends ServerInterface { return ret; } + @Nullable + @Override + public Player matchPlayer(Player player) { + if (player instanceof BukkitPlayer) { + return player; + } else { + org.bukkit.entity.Player bukkitPlayer = server.getPlayerExact(player.getName()); + return bukkitPlayer != null ? new BukkitPlayer(plugin, this, bukkitPlayer) : null; + } + } + + @Nullable + @Override + public com.sk89q.worldedit.world.World matchWorld(com.sk89q.worldedit.world.World world) { + if (world instanceof BukkitWorld) { + return world; + } else { + World bukkitWorld = server.getWorld(world.getName()); + return bukkitWorld != null ? new BukkitWorld(bukkitWorld) : null; + } + } + @Override public void onCommandRegistration(List commands, CommandsManager manager) { List toRegister = new ArrayList(); diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index ea784545e..3f32fefda 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -1110,13 +1110,15 @@ public class BukkitWorld extends LocalWorld { @Override public boolean equals(Object other) { - World world = getWorld(); - - if (!(other instanceof BukkitWorld)) { + if (other == null) { + return false; + } else if ((other instanceof BukkitWorld)) { + return ((BukkitWorld) other).getWorld().equals(getWorld()); + } else if (other instanceof com.sk89q.worldedit.world.World) { + return ((com.sk89q.worldedit.world.World) other).getName().equals(getName()); + } else { return false; } - - return ((BukkitWorld) other).getWorld().equals(world); } @Override diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 03d4383dc..e63c1ad80 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -339,7 +339,7 @@ public class WorldEditPlugin extends JavaPlugin { return wrapPlayer((Player) sender); } - return new BukkitCommandSender(this, this.server, sender); + return new BukkitCommandSender(this, sender); } /** diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java index 0e15f1ca8..d983c5885 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -23,19 +23,23 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.world.World; import cpw.mods.fml.common.FMLCommonHandler; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; import net.minecraft.command.ServerCommandManager; import net.minecraft.entity.EntityList; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.Item; import net.minecraft.server.MinecraftServer; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; +import javax.annotation.Nullable; import java.util.*; class ForgePlatform extends ServerInterface { @@ -101,6 +105,33 @@ class ForgePlatform extends ServerInterface { return ret; } + @Nullable + @Override + public Player matchPlayer(Player player) { + if (player instanceof ForgePlayer) { + return player; + } else { + EntityPlayerMP entity = server.getConfigurationManager().getPlayerForUsername(player.getName()); + return entity != null ? new ForgePlayer(entity) : null; + } + } + + @Nullable + @Override + public World matchWorld(World world) { + if (world instanceof ForgeWorld) { + return world; + } else { + for (WorldServer ws : DimensionManager.getWorlds()) { + if (ws.getWorldInfo().getWorldName().equals(world.getName())) { + return new ForgeWorld(ws); + } + } + + return null; + } + } + @Override public void onCommandRegistration(List commands) { if (server == null) return; diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java index a3d441385..21978c270 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -38,7 +38,6 @@ public class ForgePlayer extends LocalPlayer { private EntityPlayerMP player; protected ForgePlayer(EntityPlayerMP player) { - super((ServerInterface) ForgeWorldEdit.inst.getPlatform()); this.player = player; } diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java index 2f64a1f82..9271a1ce2 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -107,7 +107,7 @@ public class ForgeWorld extends AbstractWorld { @Override public String getName() { - return getWorld().provider.getDimensionName(); + return getWorld().getWorldInfo().getWorldName(); } @Override @@ -459,11 +459,15 @@ public class ForgeWorld extends AbstractWorld { @Override public boolean equals(Object o) { - if ((o instanceof ForgeWorld)) { - ForgeWorld other = ((ForgeWorld) o); - World otherWorld = other.worldRef.get(); - World thisWorld = other.worldRef.get(); - return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld); + if (o == null) { + return false; + } else if ((o instanceof ForgeWorld)) { + ForgeWorld other = ((ForgeWorld) o); + World otherWorld = other.worldRef.get(); + World thisWorld = other.worldRef.get(); + return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld); + } else if (o instanceof com.sk89q.worldedit.world.World) { + return ((com.sk89q.worldedit.world.World) o).getName().equals(getName()); } else { return false; } diff --git a/src/main/java/com/sk89q/worldedit/LocalPlayer.java b/src/main/java/com/sk89q/worldedit/LocalPlayer.java index c284c3ffb..49c7e28bc 100644 --- a/src/main/java/com/sk89q/worldedit/LocalPlayer.java +++ b/src/main/java/com/sk89q/worldedit/LocalPlayer.java @@ -31,13 +31,4 @@ import com.sk89q.worldedit.extension.platform.Actor; @Deprecated public abstract class LocalPlayer extends AbstractPlayerActor { - /** - * Construct the object. - * - * @param server A reference to the server this player is on - */ - protected LocalPlayer(ServerInterface server) { - super(server); - } - } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index 2e00f5e7c..9e4c9843c 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -40,19 +40,6 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public abstract class AbstractPlayerActor implements Actor, Player { - private final Platform platform; - - /** - * Create a new instance. - * - * @param platform the platform - */ - protected AbstractPlayerActor(Platform platform) { - checkNotNull(platform); - - this.platform = platform; - } - /** * Returns direction according to rotation. May return null. * diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java index 5fcfa4941..b4f542599 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -24,8 +24,10 @@ import com.sk89q.minecraft.util.commands.CommandsManager; import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; import java.util.List; import java.util.Map; @@ -78,6 +80,26 @@ public interface Platform { List getWorlds(); + /** + * Create a duplicate of the given player. + *

+ * The given player may have been provided by a different platform. + * + * @param player the player to match + * @return a matched player, otherwise null + */ + @Nullable Player matchPlayer(Player player); + + /** + * Create a duplicate of the given world. + *

+ * The given world may have been provided by a different platform. + * + * @param world the world to match + * @return a matched world, otherwise null + */ + @Nullable World matchWorld(World world); + @Deprecated void onCommandRegistration(List commands); diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 7c04b8168..518bbb9f8 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.internal.ServerInterfaceAdapter; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.eventbus.Subscribe; +import com.sk89q.worldedit.world.World; import javax.annotation.Nullable; import java.util.*; @@ -207,6 +208,44 @@ public class PlatformManager { return new ArrayList(platforms); } + /** + * Given a world, possibly return the same world but using a different + * platform preferred for world editing operations. + * + * @param base the world to match + * @return the preferred world, if one was found, otherwise the given world + */ + public World getWorldForEditing(World base) { + checkNotNull(base); + World match = queryCapability(Capability.WORLD_EDITING).matchWorld(base); + return match != null ? match : base; + } + + /** + * Given an actor, return a new one that may use a different platform + * for permissions and world editing. + * + * @param base the base actor to match + * @return a new delegate actor + */ + @SuppressWarnings("unchecked") + public T createProxyActor(T base) { + checkNotNull(base); + + if (base instanceof Player) { + Player player = (Player) base; + + Player permActor = queryCapability(Capability.PERMISSIONS).matchPlayer(player); + if (permActor == null) { + permActor = player; + } + + return (T) new PlayerProxy(player, permActor, getWorldForEditing(base.getWorld())); + } else { + return base; + } + } + /** * Get the command manager. * @@ -247,7 +286,10 @@ public class PlatformManager { @SuppressWarnings("deprecation") @Subscribe public void handleBlockInteract(BlockInteractEvent event) { - Actor actor = event.getCause(); + // Create a proxy actor with a potentially different world for + // making changes to the world + Actor actor = createProxyActor(event.getCause()); + Location location = event.getLocation(); Vector vector = location.toVector(); @@ -335,7 +377,9 @@ public class PlatformManager { @SuppressWarnings("deprecation") @Subscribe public void handlePlayerInput(PlayerInputEvent event) { - Player player = event.getPlayer(); + // Create a proxy actor with a potentially different world for + // making changes to the world + Player player = createProxyActor(event.getPlayer()); switch (event.getInputType()) { case PRIMARY: { diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java new file mode 100644 index 000000000..ce14651be --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -0,0 +1,125 @@ +/* + * 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.extension.platform; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; + +import static com.google.common.base.Preconditions.checkNotNull; + +class PlayerProxy extends AbstractPlayerActor { + + private final Player basePlayer; + private final Actor permActor; + private final World world; + + PlayerProxy(Player basePlayer, Actor permActor, World world) { + checkNotNull(basePlayer); + checkNotNull(permActor); + checkNotNull(world); + this.basePlayer = basePlayer; + this.permActor = permActor; + this.world = world; + } + + @Override + public int getItemInHand() { + return basePlayer.getItemInHand(); + } + + @Override + public void giveItem(int type, int amount) { + basePlayer.giveItem(type, amount); + } + + @Override + public BlockBag getInventoryBlockBag() { + return basePlayer.getInventoryBlockBag(); + } + + @Override + public String getName() { + return basePlayer.getName(); + } + + @Override + public Location getLocation() { + return basePlayer.getLocation(); + } + + @Override + public WorldVector getPosition() { + return basePlayer.getPosition(); + } + + @Override + public double getPitch() { + return basePlayer.getPitch(); + } + + @Override + public double getYaw() { + return basePlayer.getYaw(); + } + + @Override + public void setPosition(Vector pos, float pitch, float yaw) { + basePlayer.setPosition(pos, pitch, yaw); + } + + @Override + public World getWorld() { + return world; + } + + @Override + public void printRaw(String msg) { + basePlayer.printRaw(msg); + } + + @Override + public void printDebug(String msg) { + basePlayer.printDebug(msg); + } + + @Override + public void print(String msg) { + basePlayer.print(msg); + } + + @Override + public void printError(String msg) { + basePlayer.printError(msg); + } + + @Override + public String[] getGroups() { + return permActor.getGroups(); + } + + @Override + public boolean hasPermission(String perm) { + return permActor.hasPermission(perm); + } +} diff --git a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java index 96cac54b3..ab582847f 100644 --- a/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java @@ -25,11 +25,13 @@ import com.sk89q.worldedit.BiomeTypes; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; import java.util.List; import java.util.Map; @@ -82,6 +84,18 @@ public class ServerInterfaceAdapter extends ServerInterface { return platform.getWorlds(); } + @Nullable + @Override + public Player matchPlayer(Player player) { + return platform.matchPlayer(player); + } + + @Nullable + @Override + public World matchWorld(World world) { + return platform.matchWorld(world); + } + @Override @Deprecated public void onCommandRegistration(List commands) { From c9e60f1f6091aec597798811aa19c08f7a71b1fb Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 13:14:55 -0700 Subject: [PATCH 14/45] Updated /we version with platform information. --- .../sk89q/worldedit/command/WorldEditCommands.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index 6da2a3ecf..203d1c915 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -24,6 +24,7 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Console; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.PlatformManager; @@ -57,10 +58,15 @@ public class WorldEditCommands { PlatformManager pm = we.getPlatformManager(); - player.printDebug(""); - player.printDebug("Platforms:"); + player.printDebug("----------- Platforms -----------"); for (Platform platform : pm.getPlatforms()) { - player.printDebug(String.format("- %s v%s (WE v%s)", platform.getPlatformName(), platform.getPlatformVersion(), platform.getVersion())); + player.printDebug(String.format("* %s (%s)", platform.getPlatformName(), platform.getPlatformVersion())); + } + + player.printDebug("----------- Capabilities -----------"); + for (Capability capability : Capability.values()) { + Platform platform = pm.queryCapability(capability); + player.printDebug(String.format("%s: %s", capability.name(), platform != null ? platform.getPlatformName() : "NONE")); } } From b8bc055d21ac693da93cd8ff37145ec26e04bb19 Mon Sep 17 00:00:00 2001 From: rhylos Date: Fri, 27 Jun 2014 16:15:56 -0700 Subject: [PATCH 15/45] [Forge] Add slash to location of defaults folder to allow locating of worldedit.properties file. --- src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 7e21f7134..3de2beceb 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -242,7 +242,7 @@ public class ForgeWorldEdit { checkNotNull(jar); checkNotNull(name); - String path = "defaults/" + name; + String path = "/defaults/" + name; File targetFile = new File(getWorkingDir(), name); Closer closer = Closer.create(); From aaf4c6125103178c1cfefc144e22f9564c834756 Mon Sep 17 00:00:00 2001 From: rhylos Date: Fri, 27 Jun 2014 16:15:56 -0700 Subject: [PATCH 16/45] [Forge] Add slash to location of defaults folder to allow locating of worldedit.properties file. --- src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 72647b26a..1df421583 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -246,7 +246,7 @@ public class ForgeWorldEdit { checkNotNull(jar); checkNotNull(name); - String path = "defaults/" + name; + String path = "/defaults/" + name; File targetFile = new File(getWorkingDir(), name); Closer closer = Closer.create(); From ebe2bc6ae27a5eb00a3bb6fe11f968b3d86967f1 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 17:55:39 -0700 Subject: [PATCH 17/45] Changed command pipeline to use Actor over LocalPlayer. --- .../worldedit/bukkit/BukkitCommandSender.java | 72 +++++++++---------- .../worldedit/bukkit/WorldEditPlugin.java | 14 ++-- .../com/sk89q/worldedit/LocalSession.java | 3 +- .../java/com/sk89q/worldedit/WorldEdit.java | 7 +- .../worldedit/command/GeneralCommands.java | 26 ++++--- .../worldedit/command/SchematicCommands.java | 17 ++--- .../worldedit/command/UtilityCommands.java | 50 ++++++------- .../worldedit/command/WorldEditCommands.java | 34 ++++----- .../event/platform/CommandEvent.java | 20 +++--- .../extension/platform/CommandManager.java | 4 +- .../worldedit/util/WorldEditBinding.java | 18 +++++ 11 files changed, 127 insertions(+), 138 deletions(-) diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index 08d4e1342..9d4c252b7 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -19,17 +19,29 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.PlayerNeededException; +import com.sk89q.worldedit.WorldEditPermissionException; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.internal.cui.CUIEvent; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -public class BukkitCommandSender extends LocalPlayer { +import java.io.File; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +class BukkitCommandSender implements Actor { + private CommandSender sender; private WorldEditPlugin plugin; - public BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) { + BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) { + checkNotNull(plugin); + checkNotNull(sender); + checkArgument(!(sender instanceof Player), "Cannot wrap a player"); + this.plugin = plugin; this.sender = sender; } @@ -67,6 +79,11 @@ public class BukkitCommandSender extends LocalPlayer { } } + @Override + public boolean canDestroyBedrock() { + return true; + } + @Override public String[] getGroups() { return new String[0]; @@ -74,31 +91,30 @@ public class BukkitCommandSender extends LocalPlayer { @Override public boolean hasPermission(String perm) { - if (!plugin.getLocalConfiguration().noOpPermissions && sender.isOp()) { - return true; - } + return true; + } - return plugin.getPermissionsResolver().hasPermission(null, sender.getName(), perm); + @Override + public void checkPermission(String permission) throws WorldEditPermissionException { } @Override public boolean isPlayer() { - return sender instanceof Player; + return false; } @Override - public int getItemInHand() { - throw new PlayerNeededException(); + public File openFileOpenDialog(String[] extensions) { + return null; } @Override - public Location getLocation() { - throw new PlayerNeededException(); + public File openFileSaveDialog(String[] extensions) { + return null; } @Override - public WorldVector getPosition() { - throw new PlayerNeededException(); + public void dispatchCUIEvent(CUIEvent event) { } @Override @@ -106,28 +122,4 @@ public class BukkitCommandSender extends LocalPlayer { throw new PlayerNeededException(); } - @Override - public double getPitch() { - throw new PlayerNeededException(); - } - - @Override - public double getYaw() { - throw new PlayerNeededException(); - } - - @Override - public void giveItem(int type, int amt) { - throw new PlayerNeededException(); - } - - @Override - public void setPosition(Vector pos, float pitch, float yaw) { - throw new PlayerNeededException(); - } - - @Override - public BlockBag getInventoryBlockBag() { - throw new PlayerNeededException(); - } } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index e63c1ad80..b66ee0e94 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -26,7 +26,9 @@ import com.sk89q.worldedit.bukkit.selections.CuboidSelection; 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.PlatformReadyEvent; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.regions.*; import org.bukkit.World; @@ -214,20 +216,16 @@ public class WorldEditPlugin extends JavaPlugin { } } - /** - * Called on WorldEdit command. - */ @Override - public boolean onCommand(CommandSender sender, org.bukkit.command.Command cmd, - String commandLabel, String[] args) { - + public boolean onCommand(CommandSender sender, org.bukkit.command.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(); - controller.handleCommand(wrapCommandSender(sender), split); + CommandEvent event = new CommandEvent(wrapCommandSender(sender), split); + getWorldEdit().getEventBus().post(event); return true; } @@ -334,7 +332,7 @@ public class WorldEditPlugin extends JavaPlugin { return new BukkitPlayer(this, this.server, player); } - public LocalPlayer wrapCommandSender(CommandSender sender) { + public Actor wrapCommandSender(CommandSender sender) { if (sender instanceof Player) { return wrapPlayer((Player) sender); } diff --git a/src/main/java/com/sk89q/worldedit/LocalSession.java b/src/main/java/com/sk89q/worldedit/LocalSession.java index 935980eb9..2efd2fe00 100644 --- a/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -395,8 +395,7 @@ public class LocalSession { * @return position * @throws IncompleteRegionException */ - public Vector getPlacementPosition(LocalPlayer player) - throws IncompleteRegionException { + public Vector getPlacementPosition(Player player) throws IncompleteRegionException { if (!placeAtPos1) { return player.getBlockIn(); } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index 969bdf62d..e52184372 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -29,6 +29,7 @@ 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; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.registry.BlockRegistry; @@ -626,7 +627,7 @@ public class WorldEdit { * @param player the player * @param editSession the edit session */ - public void flushBlockBag(LocalPlayer player, EditSession editSession) { + public void flushBlockBag(Actor actor, EditSession editSession) { BlockBag blockBag = editSession.getBlockBag(); if (blockBag != null) { @@ -635,7 +636,7 @@ public class WorldEdit { Map missingBlocks = editSession.popMissingBlocks(); - if (missingBlocks.size() > 0) { + if (!missingBlocks.isEmpty()) { StringBuilder str = new StringBuilder(); str.append("Missing these blocks: "); int size = missingBlocks.size(); @@ -657,7 +658,7 @@ public class WorldEdit { } } - player.printError(str.toString()); + actor.printError(str.toString()); } } diff --git a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 16e20ab35..61cbe98b3 100644 --- a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -22,9 +22,9 @@ package com.sk89q.worldedit.command; 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.worldedit.*; import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.masks.Mask; /** @@ -151,9 +151,7 @@ public class GeneralCommands { min = 1, max = 1 ) - @Console - public void searchItem(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void searchItem(Actor actor, CommandContext args) throws WorldEditException { String query = args.getString(0).trim().toLowerCase(); boolean blocksOnly = args.hasFlag('b'); @@ -165,9 +163,9 @@ public class GeneralCommands { ItemType type = ItemType.fromID(id); if (type != null) { - player.print("#" + type.getID() + " (" + type.getName() + ")"); + actor.print("#" + type.getID() + " (" + type.getName() + ")"); } else { - player.printError("No item found by ID " + id); + actor.printError("No item found by ID " + id); } return; @@ -175,26 +173,26 @@ public class GeneralCommands { } if (query.length() <= 2) { - player.printError("Enter a longer search string (len > 2)."); + actor.printError("Enter a longer search string (len > 2)."); return; } if (!blocksOnly && !itemsOnly) { - player.print("Searching for: " + query); + actor.print("Searching for: " + query); } else if (blocksOnly && itemsOnly) { - player.printError("You cannot use both the 'b' and 'i' flags simultaneously."); + actor.printError("You cannot use both the 'b' and 'i' flags simultaneously."); return; } else if (blocksOnly) { - player.print("Searching for blocks: " + query); + actor.print("Searching for blocks: " + query); } else { - player.print("Searching for items: " + query); + actor.print("Searching for items: " + query); } int found = 0; for (ItemType type : ItemType.values()) { if (found >= 15) { - player.print("Too many results!"); + actor.print("Too many results!"); break; } @@ -208,7 +206,7 @@ public class GeneralCommands { for (String alias : type.getAliases()) { if (alias.contains(query)) { - player.print("#" + type.getID() + " (" + type.getName() + ")"); + actor.print("#" + type.getID() + " (" + type.getName() + ")"); ++found; break; } @@ -216,7 +214,7 @@ public class GeneralCommands { } if (found == 0) { - player.printError("No items found."); + actor.printError("No items found."); } } diff --git a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 90afb104d..c45a8b7d5 100644 --- a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.*; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.schematic.SchematicFormat; import com.sk89q.worldedit.world.DataException; @@ -207,11 +208,9 @@ public class SchematicCommands { desc = "List available schematic formats", max = 0 ) - @Console @CommandPermissions("worldedit.schematic.formats") - public void formats(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - player.print("Available schematic formats (Name: Lookup names)"); + public void formats(Actor actor) throws WorldEditException { + actor.print("Available schematic formats (Name: Lookup names)"); StringBuilder builder; boolean first = true; for (SchematicFormat format : SchematicFormat.getFormats()) { @@ -225,7 +224,7 @@ public class SchematicCommands { first = false; } first = true; - player.print(builder.toString()); + actor.print(builder.toString()); } } @@ -238,10 +237,8 @@ public class SchematicCommands { " -d sorts by date, oldest first\n" + " -n sorts by date, newest first\n" ) - @Console @CommandPermissions("worldedit.schematic.list") - public void list(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void list(Actor actor, CommandContext args) throws WorldEditException { File dir = we.getWorkingDirectoryFile(we.getConfiguration().saveDir); File[] files = dir.listFiles(new FileFilter(){ @Override @@ -273,8 +270,8 @@ public class SchematicCommands { } }); - player.print("Available schematics (Filename (Format)):"); - player.print(listFiles("", files)); + actor.print("Available schematics (Filename (Format)):"); + actor.print(listFiles("", files)); } private String listFiles(String prefix, File[] files) { diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 8e57def7c..de498d24b 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -23,6 +23,8 @@ import com.sk89q.minecraft.util.commands.*; import com.sk89q.worldedit.*; import com.sk89q.worldedit.LocalWorld.KillFlags; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.regions.CuboidRegion; @@ -30,6 +32,7 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Description; import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.world.World; import java.util.Comparator; @@ -367,9 +370,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.butcher") @Logging(PLACEMENT) - @Console - public void butcher(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void butcher(Actor actor, @Optional Player player, @Optional LocalSession session, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -388,7 +389,7 @@ public class UtilityCommands { } } - FlagContainer flags = new FlagContainer(player); + FlagContainer flags = new FlagContainer(actor); flags.or(KillFlags.FRIENDLY , args.hasFlag('f')); // No permission check here. Flags will instead be filtered by the subsequent calls. flags.or(KillFlags.PETS , args.hasFlag('p'), "worldedit.butcher.pets"); flags.or(KillFlags.NPCS , args.hasFlag('n'), "worldedit.butcher.npcs"); @@ -400,7 +401,7 @@ public class UtilityCommands { // If you add flags here, please add them to com.sk89q.worldedit.commands.BrushCommands.butcherBrush() as well int killed; - if (player.isPlayer()) { + if (player != null) { killed = player.getWorld().killMobs(session.getPlacementPosition(player), radius, flags.flags); } else { killed = 0; @@ -410,16 +411,16 @@ public class UtilityCommands { } if (radius < 0) { - player.print("Killed " + killed + " mobs."); + actor.print("Killed " + killed + " mobs."); } else { - player.print("Killed " + killed + " mobs in a radius of " + radius + "."); + actor.print("Killed " + killed + " mobs in a radius of " + radius + "."); } } public static class FlagContainer { - private final LocalPlayer player; + private final Actor player; public int flags = 0; - public FlagContainer(LocalPlayer player) { + public FlagContainer(Actor player) { this.player = player; } @@ -445,15 +446,13 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.remove") @Logging(PLACEMENT) - @Console - public void remove(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void remove(Actor actor, @Optional Player player, @Optional LocalSession session, CommandContext args) throws WorldEditException { String typeStr = args.getString(0); int radius = args.getInteger(1); if (radius < -1) { - player.printError("Use -1 to remove all entities in loaded chunks"); + actor.printError("Use -1 to remove all entities in loaded chunks"); return; } @@ -483,12 +482,12 @@ public class UtilityCommands { } else if (typeStr.matches("xp")) { type = EntityType.XP_ORBS; } else { - player.printError("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); + actor.printError("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); return; } int removed = 0; - if (player.isPlayer()) { + if (player != null) { Vector origin = session.getPlacementPosition(player); removed = player.getWorld().removeEntities(type, origin, radius); } else { @@ -506,15 +505,12 @@ public class UtilityCommands { min = 0, max = -1 ) - @Console @CommandPermissions("worldedit.help") - public void help(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - help(args, we, session, player, editSession); + public void help(Actor actor, CommandContext args) throws WorldEditException { + help(args, we, actor); } - public static void help(CommandContext args, WorldEdit we, LocalSession session, LocalPlayer player, EditSession editSession) { + public static void help(CommandContext args, WorldEdit we, Actor actor) { final Dispatcher dispatcher = we.getPlatformManager().getCommandManager().getDispatcher(); if (args.argsLength() == 0) { @@ -542,7 +538,7 @@ public class UtilityCommands { first = false; } - player.print(sb.toString()); + actor.print(sb.toString()); return; } @@ -551,22 +547,22 @@ public class UtilityCommands { CommandMapping mapping = dispatcher.get(command); if (mapping == null) { - player.printError("Unknown command '" + command + "'."); + actor.printError("Unknown command '" + command + "'."); return; } Description description = mapping.getDescription(); if (description.getUsage() != null) { - player.printDebug("Usage: " + description.getUsage()); + actor.printDebug("Usage: " + description.getUsage()); } if (description.getHelp() != null) { - player.print(description.getHelp()); + actor.print(description.getHelp()); } else if (description.getDescription() != null) { - player.print(description.getDescription()); + actor.print(description.getDescription()); } else { - player.print("No further help is available."); + actor.print("No further help is available."); } } } diff --git a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index 203d1c915..78d1af9df 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -22,8 +22,8 @@ package com.sk89q.worldedit.command; 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.worldedit.*; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.PlatformManager; @@ -49,24 +49,21 @@ public class WorldEditCommands { min = 0, max = 0 ) - @Console - public void version(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - player.print("WorldEdit version " + WorldEdit.getVersion()); - player.print("https://github.com/sk89q/worldedit/"); + public void version(Actor actor) throws WorldEditException { + actor.print("WorldEdit version " + WorldEdit.getVersion()); + actor.print("https://github.com/sk89q/worldedit/"); PlatformManager pm = we.getPlatformManager(); - player.printDebug("----------- Platforms -----------"); + actor.printDebug("----------- Platforms -----------"); for (Platform platform : pm.getPlatforms()) { - player.printDebug(String.format("* %s (%s)", platform.getPlatformName(), platform.getPlatformVersion())); + actor.printDebug(String.format("* %s (%s)", platform.getPlatformName(), platform.getPlatformVersion())); } - player.printDebug("----------- Capabilities -----------"); + actor.printDebug("----------- Capabilities -----------"); for (Capability capability : Capability.values()) { Platform platform = pm.queryCapability(capability); - player.printDebug(String.format("%s: %s", capability.name(), platform != null ? platform.getPlatformName() : "NONE")); + actor.printDebug(String.format("%s: %s", capability.name(), platform != null ? platform.getPlatformName() : "NONE")); } } @@ -78,12 +75,9 @@ public class WorldEditCommands { max = 0 ) @CommandPermissions("worldedit.reload") - @Console - public void reload(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void reload(Actor actor) throws WorldEditException { we.getServer().reload(); - player.print("Configuration reloaded!"); + actor.print("Configuration reloaded!"); } @Command( @@ -106,7 +100,6 @@ public class WorldEditCommands { min = 1, max = 1 ) - @Console public void tz(CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession) throws WorldEditException { TimeZone tz = TimeZone.getTimeZone(args.getString(0)); @@ -124,10 +117,7 @@ public class WorldEditCommands { max = -1 ) @CommandPermissions("worldedit.help") - @Console - public void help(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - UtilityCommands.help(args, we, session, player, editSession); + public void help(Actor actor, CommandContext args) throws WorldEditException { + UtilityCommands.help(args, we, actor); } } 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 0cba50b7e..a6b7ebdf1 100644 --- a/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java +++ b/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java @@ -19,8 +19,8 @@ package com.sk89q.worldedit.event.platform; -import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.event.AbstractCancellable; +import com.sk89q.worldedit.extension.platform.Actor; import static com.google.common.base.Preconditions.checkNotNull; @@ -29,30 +29,30 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class CommandEvent extends AbstractCancellable { - private final LocalPlayer player; + private final Actor actor; private final String[] args; /** * Create a new instance. * - * @param player the player + * @param actor the player * @param args the arguments */ - public CommandEvent(LocalPlayer player, String[] args) { - checkNotNull(player); + public CommandEvent(Actor actor, String[] args) { + checkNotNull(actor); checkNotNull(args); - this.player = player; + this.actor = actor; this.args = args; } /** - * Get the player. + * Get the actor that issued the command. * - * @return the player + * @return the actor that issued the command */ - public LocalPlayer getPlayer() { - return player; + public Actor getActor() { + return actor; } /** 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 318389579..51b54f9d5 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -192,7 +192,7 @@ public final class CommandManager { public void handleCommand(CommandEvent event) { Request.reset(); - Actor actor = event.getPlayer(); + Actor actor = event.getActor(); String split[] = commandDetection(event.getArguments()); // No command found! @@ -241,7 +241,7 @@ public final class CommandManager { } } - worldEdit.flushBlockBag(event.getPlayer(), editSession); + worldEdit.flushBlockBag(actor, editSession); } } diff --git a/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java index 835d88c8f..725b4b540 100644 --- a/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java +++ b/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java @@ -97,6 +97,24 @@ public class WorldEditBinding extends BindingHelper { return worldEdit.getSessionManager().get(sender); } + /** + * Gets an {@link Actor} from a {@link ArgumentStack}. + * + * @param context the context + * @return a local player + * @throws ParameterException on error + */ + @BindingMatch(type = Actor.class, + behavior = BindingBehavior.PROVIDES) + public Actor getActor(ArgumentStack context) throws ParameterException { + Actor sender = context.getContext().getLocals().get(Actor.class); + if (sender == null) { + throw new ParameterException("Missing 'Actor'"); + } else { + return sender; + } + } + /** * Gets an {@link Player} from a {@link ArgumentStack}. * From b7e0821b97d3b282070bcebbdce72013e4d4a55c Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 17:59:23 -0700 Subject: [PATCH 18/45] Removed command binding classes to internal package. --- .../sk89q/worldedit/extension/platform/CommandManager.java | 4 ++-- .../{util => internal/command}/WorldEditBinding.java | 2 +- .../command}/WorldEditExceptionConverter.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/com/sk89q/worldedit/{util => internal/command}/WorldEditBinding.java (99%) rename src/main/java/com/sk89q/worldedit/{util => internal/command}/WorldEditExceptionConverter.java (99%) 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 51b54f9d5..2910fa273 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -32,8 +32,8 @@ import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.CommandLoggingHandler; import com.sk89q.worldedit.util.CommandPermissionsHandler; -import com.sk89q.worldedit.util.WorldEditBinding; -import com.sk89q.worldedit.util.WorldEditExceptionConverter; +import com.sk89q.worldedit.internal.command.WorldEditBinding; +import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.InvalidUsageException; import com.sk89q.worldedit.util.command.fluent.CommandGraph; diff --git a/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java similarity index 99% rename from src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java rename to src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index 725b4b540..5cd4f5d81 100644 --- a/src/main/java/com/sk89q/worldedit/util/WorldEditBinding.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util; +package com.sk89q.worldedit.internal.command; import com.sk89q.worldedit.*; import com.sk89q.worldedit.entity.Player; diff --git a/src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java similarity index 99% rename from src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java rename to src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java index 69187b5af..520e0d08d 100644 --- a/src/main/java/com/sk89q/worldedit/util/WorldEditExceptionConverter.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util; +package com.sk89q.worldedit.internal.command; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.worldedit.*; From 1431cd2a673c478366a15575553edc463fa931a6 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 18:04:34 -0700 Subject: [PATCH 19/45] Cleaned up BiomeCommands and made better use of new command framework. --- .../worldedit/command/BiomeCommands.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 2f25cf454..39512f6b3 100644 --- a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -24,6 +24,8 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.masks.BiomeTypeMask; import com.sk89q.worldedit.masks.InvertedMask; import com.sk89q.worldedit.masks.Mask; @@ -35,14 +37,24 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +/** + * Implements biome-related commands such as "/biomelist". + */ public class BiomeCommands { - private WorldEdit we; + private final WorldEdit worldEdit; - public BiomeCommands(WorldEdit we) { - this.we = we; + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public BiomeCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -52,9 +64,7 @@ public class BiomeCommands { max = 1 ) @CommandPermissions("worldedit.biome.list") - public void biomeList(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void biomeList(Actor actor, CommandContext args) throws WorldEditException { int page; int offset; int count = 0; @@ -66,14 +76,14 @@ public class BiomeCommands { offset = (page - 1) * 19; } - List biomes = we.getServer().getBiomes().all(); + List biomes = worldEdit.getServer().getBiomes().all(); int totalPages = biomes.size() / 19 + 1; - player.print("Available Biomes (page " + page + "/" + totalPages + ") :"); + actor.print("Available Biomes (page " + page + "/" + totalPages + ") :"); for (BiomeType biome : biomes) { if (offset > 0) { offset--; } else { - player.print(" " + biome.getName()); + actor.print(" " + biome.getName()); if (++count == 19) { break; } @@ -93,9 +103,7 @@ public class BiomeCommands { max = 0 ) @CommandPermissions("worldedit.biome.info") - public void biomeInfo(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void biomeInfo(CommandContext args, Player player, LocalSession session) throws WorldEditException { if (args.hasFlag('t')) { Vector blockPosition = player.getBlockTrace(300); if (blockPosition == null) { @@ -144,10 +152,8 @@ public class BiomeCommands { ) @Logging(REGION) @CommandPermissions("worldedit.biome.set") - public void setBiome(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final BiomeType target = we.getServer().getBiomes().get(args.getString(0)); + public void setBiome(CommandContext args, Player player, LocalSession session, EditSession editSession) throws WorldEditException { + final BiomeType target = worldEdit.getServer().getBiomes().get(args.getString(0)); if (target == null) { player.printError("Biome '" + args.getString(0) + "' does not exist!"); return; @@ -199,4 +205,5 @@ public class BiomeCommands { player.print("Biome changed to " + target.getName() + ". " + affected + " columns affected."); } } + } From 47076b313e0a47257c572c85cf2c0e4787e24c6b Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 18:28:31 -0700 Subject: [PATCH 20/45] Fixed command logging regression caused by merge. --- .../extension/platform/CommandManager.java | 3 +++ .../worldedit/util/CommandLoggingHandler.java | 21 +++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) 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 2910fa273..8734071e1 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -144,9 +144,12 @@ public final class CommandManager { // Register log if (!logging || path.isEmpty()) { dynamicHandler.setHandler(null); + logger.setLevel(Level.OFF); } else { File file = new File(config.getWorkingDirectory(), path); + logger.setLevel(Level.ALL); + logger.log(Level.INFO, "Logging WorldEdit commands to " + file.getAbsolutePath()); try { diff --git a/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java b/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java index f75e02b31..48494a57b 100644 --- a/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java +++ b/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java @@ -23,6 +23,8 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.util.command.parametric.AbstractInvokeListener; import com.sk89q.worldedit.util.command.parametric.InvokeHandler; import com.sk89q.worldedit.util.command.parametric.ParameterData; @@ -72,11 +74,19 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv logMode = loggingAnnotation.value(); } - LocalPlayer sender = context.getLocals().get(LocalPlayer.class); + Actor sender = context.getLocals().get(Actor.class); + Player player; + if (sender == null) { return; } + if (sender instanceof Player) { + player = (Player) sender; + } else { + return; + } + builder.append("WorldEdit: ").append(sender.getName()); if (sender.isPlayer()) { builder.append(" (in \"" + sender.getWorld().getName() + "\")"); @@ -89,13 +99,13 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv } if (logMode != null && sender.isPlayer()) { - Vector position = sender.getPosition(); - LocalSession session = worldEdit.getSession(sender); + Vector position = player.getPosition(); + LocalSession session = worldEdit.getSessionManager().get(player); switch (logMode) { case PLACEMENT: try { - position = session.getPlacementPosition(sender); + position = session.getPlacementPosition(player); } catch (IncompleteRegionException e) { break; } @@ -110,8 +120,7 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv /* FALL-THROUGH */ case ORIENTATION_REGION: - builder.append(" - Orientation: " - + sender.getCardinalDirection().name()); + builder.append(" - Orientation: " + player.getCardinalDirection().name()); /* FALL-THROUGH */ case REGION: From 23ce159ccf7256abd4d9a1d525cca7635b7d2cce Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 18:29:11 -0700 Subject: [PATCH 21/45] Moved other command handler related classes to the internal package. --- .../sk89q/worldedit/extension/platform/CommandManager.java | 4 ++-- .../{util => internal/command}/CommandLoggingHandler.java | 2 +- .../{util => internal/command}/CommandPermissionsHandler.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/com/sk89q/worldedit/{util => internal/command}/CommandLoggingHandler.java (99%) rename src/main/java/com/sk89q/worldedit/{util => internal/command}/CommandPermissionsHandler.java (96%) 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 8734071e1..a92472f0d 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,8 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.*; import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.session.request.Request; -import com.sk89q.worldedit.util.CommandLoggingHandler; -import com.sk89q.worldedit.util.CommandPermissionsHandler; +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.util.command.Dispatcher; diff --git a/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java b/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java similarity index 99% rename from src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java rename to src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java index 48494a57b..2cd72dfaa 100644 --- a/src/main/java/com/sk89q/worldedit/util/CommandLoggingHandler.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util; +package com.sk89q.worldedit.internal.command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; diff --git a/src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java b/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java similarity index 96% rename from src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java rename to src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java index e558dec59..fc36bf1e4 100644 --- a/src/main/java/com/sk89q/worldedit/util/CommandPermissionsHandler.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.util; +package com.sk89q.worldedit.internal.command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.worldedit.util.command.parametric.PermissionsHandler; From 9381beb417ae62fc3f7da401921003f0469d54ae Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 18:34:58 -0700 Subject: [PATCH 22/45] Actor should not have getWorld(). --- .../com/sk89q/worldedit/bukkit/BukkitCommandSender.java | 5 ----- .../com/sk89q/worldedit/extension/platform/Actor.java | 7 ------- .../worldedit/extension/platform/PlatformManager.java | 2 +- .../internal/command/CommandLoggingHandler.java | 4 ++-- .../worldedit/internal/command/WorldEditBinding.java | 9 +++++++-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index 9d4c252b7..a8dc5adc2 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -117,9 +117,4 @@ class BukkitCommandSender implements Actor { public void dispatchCUIEvent(CUIEvent event) { } - @Override - public LocalWorld getWorld() { - throw new PlayerNeededException(); - } - } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java b/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java index 9dfb769bf..f9912de1a 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java @@ -37,13 +37,6 @@ public interface Actor { */ String getName(); - /** - * Get the actor's world. - * - * @return the world - */ - World getWorld(); - /** * Print a message. * diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 518bbb9f8..87ec055da 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -240,7 +240,7 @@ public class PlatformManager { permActor = player; } - return (T) new PlayerProxy(player, permActor, getWorldForEditing(base.getWorld())); + return (T) new PlayerProxy(player, permActor, getWorldForEditing(player.getWorld())); } else { return base; } diff --git a/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java b/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java index 2cd72dfaa..b09a26bee 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java @@ -89,7 +89,7 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv builder.append("WorldEdit: ").append(sender.getName()); if (sender.isPlayer()) { - builder.append(" (in \"" + sender.getWorld().getName() + "\")"); + builder.append(" (in \"" + player.getWorld().getName() + "\")"); } builder.append(": ").append(context.getCommand()); @@ -126,7 +126,7 @@ public class CommandLoggingHandler extends AbstractInvokeListener implements Inv case REGION: try { builder.append(" - Region: ") - .append(session.getSelection(sender.getWorld())); + .append(session.getSelection(player.getWorld())); } catch (IncompleteRegionException e) { break; } diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index 5cd4f5d81..8d881f5a5 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.internal.command; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; @@ -169,7 +170,9 @@ public class WorldEditBinding extends BindingHelper { Actor actor = context.getContext().getLocals().get(Actor.class); ParserContext parserContext = new ParserContext(); parserContext.setActor(context.getContext().getLocals().get(Actor.class)); - parserContext.setWorld(actor.getWorld()); + if (actor instanceof Entity) { + parserContext.setWorld(((Entity) actor).getWorld()); + } parserContext.setSession(worldEdit.getSessionManager().get(actor)); return worldEdit.getPatternRegistry().parseFromInput(context.next(), parserContext); } @@ -189,7 +192,9 @@ public class WorldEditBinding extends BindingHelper { Actor actor = context.getContext().getLocals().get(Actor.class); ParserContext parserContext = new ParserContext(); parserContext.setActor(context.getContext().getLocals().get(Actor.class)); - parserContext.setWorld(actor.getWorld()); + if (actor instanceof Entity) { + parserContext.setWorld(((Entity) actor).getWorld()); + } parserContext.setSession(worldEdit.getSessionManager().get(actor)); return worldEdit.getMaskRegistry().parseFromInput(context.next(), parserContext); } From d96d3cf8bcf6f40222233a3f75e345e6e733e828 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 19:55:38 -0700 Subject: [PATCH 23/45] Changed instances of Pattern, Mask, etc. to newer versions. Most affected is the brush code. --- .../java/com/sk89q/worldedit/EditSession.java | 23 ++- .../worldedit/command/BiomeCommands.java | 2 +- .../worldedit/command/BrushCommands.java | 137 +++++++----------- .../worldedit/command/ToolUtilCommands.java | 18 +-- .../worldedit/command/tool/AreaPickaxe.java | 19 ++- .../command/tool/BlockDataCyler.java | 36 +++-- .../worldedit/command/tool/BlockReplacer.java | 34 +++-- .../worldedit/command/tool/BlockTool.java | 26 +--- .../worldedit/command/tool/BrushTool.java | 50 +++---- .../worldedit/command/tool/DistanceWand.java | 16 +- .../command/tool/DoubleActionBlockTool.java | 24 +-- .../command/tool/DoubleActionTraceTool.java | 19 +-- .../command/tool/FloatingTreeRemover.java | 30 ++-- .../worldedit/command/tool/FloodFillTool.java | 47 +++--- .../command/tool/LongRangeBuildTool.java | 19 ++- .../worldedit/command/tool/QueryTool.java | 28 ++-- .../command/tool/RecursivePickaxe.java | 40 ++--- .../worldedit/command/tool/SinglePickaxe.java | 27 ++-- .../sk89q/worldedit/command/tool/Tool.java | 12 +- .../worldedit/command/tool/TraceTool.java | 23 +-- .../worldedit/command/tool/TreePlanter.java | 18 ++- .../worldedit/command/tool/brush/Brush.java | 5 +- .../command/tool/brush/ButcherBrush.java | 6 +- .../command/tool/brush/ClipboardBrush.java | 7 +- .../command/tool/brush/CylinderBrush.java | 10 +- .../command/tool/brush/GravityBrush.java | 7 +- .../tool/brush/HollowCylinderBrush.java | 11 +- .../command/tool/brush/HollowSphereBrush.java | 11 +- .../command/tool/brush/SmoothBrush.java | 4 +- .../command/tool/brush/SphereBrush.java | 11 +- .../extension/platform/PlatformManager.java | 75 ++++------ .../worldedit/internal/LocalWorldAdapter.java | 3 +- .../sk89q/worldedit/world/AbstractWorld.java | 3 +- .../java/com/sk89q/worldedit/world/World.java | 3 +- .../world/snapshot/SnapshotRestore.java | 2 +- 35 files changed, 361 insertions(+), 445 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index 487395cd1..a641f0089 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -57,7 +57,6 @@ import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.internal.expression.runtime.RValue; -import com.sk89q.worldedit.masks.Mask; import com.sk89q.worldedit.math.interpolation.Interpolation; import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation; import com.sk89q.worldedit.math.interpolation.Node; @@ -275,7 +274,6 @@ public class EditSession implements Extent { * * @return mask, may be null */ - @SuppressWarnings("deprecation") public Mask getMask() { return oldMask; } @@ -285,13 +283,24 @@ public class EditSession implements Extent { * * @param mask mask or null */ - @SuppressWarnings("deprecation") public void setMask(Mask mask) { this.oldMask = mask; if (mask == null) { maskingExtent.setMask(Masks.alwaysTrue()); } else { - maskingExtent.setMask(Masks.wrap(mask, this)); + maskingExtent.setMask(mask); + } + } + + /** + * @deprecated Use {@link #setMask(Mask)} + */ + @Deprecated + public void setMask(com.sk89q.worldedit.masks.Mask mask) { + if (mask == null) { + setMask((Mask) null); + } else { + setMask(Masks.wrap(mask)); } } @@ -786,7 +795,7 @@ public class EditSession implements Extent { checkNotNull(position); checkArgument(apothem >= 1, "apothem >= 1"); - Mask mask = new com.sk89q.worldedit.masks.FuzzyBlockMask(new BaseBlock(blockType, -1)); + Mask mask = new FuzzyBlockMask(this, new BaseBlock(blockType, -1)); Vector adjustment = new Vector(1, 1, 1).multiply(apothem - 1); Region region = new CuboidRegion( getWorld(), // Causes clamping of Y range @@ -855,7 +864,7 @@ public class EditSession implements Extent { */ @SuppressWarnings("deprecation") public int replaceBlocks(Region region, Set filter, Pattern pattern) throws MaxChangedBlocksException { - Mask mask = filter == null ? new com.sk89q.worldedit.masks.ExistingBlockMask() : new com.sk89q.worldedit.masks.FuzzyBlockMask(filter); + Mask mask = filter == null ? new ExistingBlockMask(this) : new FuzzyBlockMask(this, filter); return replaceBlocks(region, mask, pattern); } @@ -876,7 +885,7 @@ public class EditSession implements Extent { checkNotNull(pattern); BlockReplace replace = new BlockReplace(this, Patterns.wrap(pattern)); - RegionMaskingFilter filter = new RegionMaskingFilter(Masks.wrap(mask, this), replace); + RegionMaskingFilter filter = new RegionMaskingFilter(mask, replace); RegionVisitor visitor = new RegionVisitor(region, filter); Operations.completeLegacy(visitor); return visitor.getAffected(); diff --git a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 39512f6b3..a08f70ab3 100644 --- a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -26,9 +26,9 @@ import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.*; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.masks.BiomeTypeMask; import com.sk89q.worldedit.masks.InvertedMask; -import com.sk89q.worldedit.masks.Mask; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; diff --git a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 0b0a62ae3..181b5931f 100644 --- a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -22,46 +22,42 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.worldedit.CuboidClipboard; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.LocalWorld.KillFlags; -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.command.UtilityCommands.FlagContainer; -import com.sk89q.worldedit.masks.BlockMask; -import com.sk89q.worldedit.patterns.Pattern; -import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.command.tool.BrushTool; -import com.sk89q.worldedit.command.tool.brush.ButcherBrush; -import com.sk89q.worldedit.command.tool.brush.ClipboardBrush; -import com.sk89q.worldedit.command.tool.brush.CylinderBrush; -import com.sk89q.worldedit.command.tool.brush.GravityBrush; -import com.sk89q.worldedit.command.tool.brush.HollowCylinderBrush; -import com.sk89q.worldedit.command.tool.brush.HollowSphereBrush; -import com.sk89q.worldedit.command.tool.brush.SmoothBrush; -import com.sk89q.worldedit.command.tool.brush.SphereBrush; +import com.sk89q.worldedit.command.tool.brush.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.pattern.BlockPattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.command.parametric.Optional; + +import static com.google.common.base.Preconditions.checkNotNull; /** - * Brush shape commands. - * - * @author sk89q + * Commands to set brush shape. */ public class BrushCommands { - private final WorldEdit we; - - public BrushCommands(WorldEdit we) { - this.we = we; + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public BrushCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( aliases = { "sphere", "s" }, - usage = " [radius]", + usage = " [radius]", flags = "h", desc = "Choose the sphere brush", help = @@ -71,25 +67,21 @@ public class BrushCommands { max = 2 ) @CommandPermissions("worldedit.brush.sphere") - public void sphereBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { - - double radius = args.argsLength() > 1 ? args.getDouble(1) : 2; - we.checkMaxBrushRadius(radius); + public void sphereBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, + @Optional("2") double radius, @Switch('h') boolean hollow) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand()); - Pattern fill = we.getBlockPattern(player, args.getString(0)); tool.setFill(fill); tool.setSize(radius); - if (args.hasFlag('h')) { + if (hollow) { tool.setBrush(new HollowSphereBrush(), "worldedit.brush.sphere"); } else { tool.setBrush(new SphereBrush(), "worldedit.brush.sphere"); } - player.print(String.format("Sphere brush shape equipped (%.0f).", - radius)); + player.print(String.format("Sphere brush shape equipped (%.0f).", radius)); } @Command( @@ -104,28 +96,22 @@ public class BrushCommands { max = 3 ) @CommandPermissions("worldedit.brush.cylinder") - public void cylinderBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { - - double radius = args.argsLength() > 1 ? args.getDouble(1) : 2; - we.checkMaxBrushRadius(radius); - - int height = args.argsLength() > 2 ? args.getInteger(2) : 1; - we.checkMaxBrushRadius(height); + public void cylinderBrush(Player player, LocalSession session, EditSession editSession, Pattern fill, + @Optional("2") double radius, @Optional("1") int height, @Switch('h') boolean hollow) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); + worldEdit.checkMaxBrushRadius(height); BrushTool tool = session.getBrushTool(player.getItemInHand()); - Pattern fill = we.getBlockPattern(player, args.getString(0)); tool.setFill(fill); tool.setSize(radius); - if (args.hasFlag('h')) { + if (hollow) { tool.setBrush(new HollowCylinderBrush(height), "worldedit.brush.cylinder"); } else { tool.setBrush(new CylinderBrush(height), "worldedit.brush.cylinder"); } - player.print(String.format("Cylinder brush shape equipped (%.0f by %d).", - radius, height)); + player.print(String.format("Cylinder brush shape equipped (%.0f by %d).", radius, height)); } @Command( @@ -140,8 +126,7 @@ public class BrushCommands { max = 0 ) @CommandPermissions("worldedit.brush.clipboard") - public void clipboardBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { + public void clipboardBrush(Player player, LocalSession session, EditSession editSession, @Switch('a') boolean ignoreAir) throws WorldEditException { CuboidClipboard clipboard = session.getClipboard(); @@ -152,12 +137,12 @@ public class BrushCommands { Vector size = clipboard.getSize(); - we.checkMaxBrushRadius(size.getBlockX()); - we.checkMaxBrushRadius(size.getBlockY()); - we.checkMaxBrushRadius(size.getBlockZ()); + worldEdit.checkMaxBrushRadius(size.getBlockX()); + worldEdit.checkMaxBrushRadius(size.getBlockY()); + worldEdit.checkMaxBrushRadius(size.getBlockZ()); BrushTool tool = session.getBrushTool(player.getItemInHand()); - tool.setBrush(new ClipboardBrush(clipboard, args.hasFlag('a')), "worldedit.brush.clipboard"); + tool.setBrush(new ClipboardBrush(clipboard, ignoreAir), "worldedit.brush.clipboard"); player.print("Clipboard brush shape equipped."); } @@ -169,24 +154,20 @@ public class BrushCommands { desc = "Choose the terrain softener brush", help = "Chooses the terrain softener brush.\n" + - "The -n flag makes it only consider naturally occuring blocks.", + "The -n flag makes it only consider naturally occurring blocks.", min = 0, max = 2 ) @CommandPermissions("worldedit.brush.smooth") - public void smoothBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { - - double radius = args.argsLength() > 0 ? args.getDouble(0) : 2; - we.checkMaxBrushRadius(radius); - - int iterations = args.argsLength() > 1 ? args.getInteger(1) : 4; + public void smoothBrush(Player player, LocalSession session, EditSession editSession, + @Optional("2") double radius, @Optional("4") int iterations, @Switch('n') boolean naturalBlocksOnly) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand()); tool.setSize(radius); - tool.setBrush(new SmoothBrush(iterations, args.hasFlag('n')), "worldedit.brush.smooth"); + tool.setBrush(new SmoothBrush(iterations, naturalBlocksOnly), "worldedit.brush.smooth"); - player.print(String.format("Smooth brush equipped (%.0f x %dx, using " + (args.hasFlag('n') ? "natural blocks only" : "any block") + ").", + player.print(String.format("Smooth brush equipped (%.0f x %dx, using " + (naturalBlocksOnly ? "natural blocks only" : "any block") + ").", radius, iterations)); } @@ -198,21 +179,17 @@ public class BrushCommands { max = 1 ) @CommandPermissions("worldedit.brush.ex") - public void extinguishBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { - - double radius = args.argsLength() > 1 ? args.getDouble(1) : 5; - we.checkMaxBrushRadius(radius); + public void extinguishBrush(Player player, LocalSession session, EditSession editSession, @Optional("5") double radius) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand()); - Pattern fill = new SingleBlockPattern(new BaseBlock(0)); + Pattern fill = new BlockPattern(new BaseBlock(0)); tool.setFill(fill); tool.setSize(radius); - tool.setMask(new BlockMask(new BaseBlock(BlockID.FIRE))); + tool.setMask(new BlockMask(editSession, new BaseBlock(BlockID.FIRE))); tool.setBrush(new SphereBrush(), "worldedit.brush.ex"); - player.print(String.format("Extinguisher equipped (%.0f).", - radius)); + player.print(String.format("Extinguisher equipped (%.0f).", radius)); } @Command( @@ -228,15 +205,12 @@ public class BrushCommands { max = 1 ) @CommandPermissions("worldedit.brush.gravity") - public void gravityBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { - - double radius = args.argsLength() > 0 ? args.getDouble(0) : 5; - we.checkMaxBrushRadius(radius); + public void gravityBrush(Player player, LocalSession session, EditSession editSession, @Optional("5") double radius, @Switch('h') boolean fromMaxY) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand()); tool.setSize(radius); - tool.setBrush(new GravityBrush(args.hasFlag('h')), "worldedit.brush.gravity"); + tool.setBrush(new GravityBrush(fromMaxY), "worldedit.brush.gravity"); player.print(String.format("Gravity brush equipped (%.0f).", radius)); @@ -253,10 +227,9 @@ public class BrushCommands { max = 2 ) @CommandPermissions("worldedit.brush.butcher") - public void butcherBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { + public void butcherBrush(CommandContext args, LocalSession session, Player player, EditSession editSession) throws WorldEditException { - LocalConfiguration config = we.getConfiguration(); + LocalConfiguration config = worldEdit.getConfiguration(); double radius = args.argsLength() > 0 ? args.getDouble(0) : 5; double maxRadius = config.maxBrushRadius; diff --git a/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index a1a537b46..dab90dc25 100644 --- a/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -23,13 +23,13 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.*; -import com.sk89q.worldedit.masks.Mask; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.util.command.parametric.Optional; /** * Tool commands. - * - * @author sk89q */ public class ToolUtilCommands { private final WorldEdit we; @@ -77,13 +77,11 @@ public class ToolUtilCommands { max = -1 ) @CommandPermissions("worldedit.brush.options.mask") - public void mask(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - if (args.argsLength() == 0) { + public void mask(Player player, LocalSession session, EditSession editSession, @Optional Mask mask) throws WorldEditException { + if (mask == null) { session.getBrushTool(player.getItemInHand()).setMask(null); player.print("Brush mask disabled."); } else { - Mask mask = we.getBlockMask(player, session, args.getJoinedStrings(0)); session.getBrushTool(player.getItemInHand()).setMask(mask); player.print("Brush mask set."); } @@ -97,9 +95,7 @@ public class ToolUtilCommands { max = 1 ) @CommandPermissions("worldedit.brush.options.material") - public void material(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - Pattern pattern = we.getBlockPattern(player, args.getString(0)); + public void material(Player player, LocalSession session, EditSession editSession, Pattern pattern) throws WorldEditException { session.getBrushTool(player.getItemInHand()).setFill(pattern); player.print("Brush material set."); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java index 40f1e4472..fffa9a0be 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/AreaPickaxe.java @@ -22,13 +22,15 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; /** * A super pickaxe mode that will remove blocks in an area. - * - * @author sk89q */ public class AreaPickaxe implements BlockTool { + private static final BaseBlock air = new BaseBlock(0); private int range; @@ -36,17 +38,17 @@ public class AreaPickaxe implements BlockTool { this.range = range; } - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.superpickaxe.area"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { - LocalWorld world = clicked.getWorld(); + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { int ox = clicked.getBlockX(); int oy = clicked.getBlockY(); int oz = clicked.getBlockZ(); - int initialType = world.getBlockType(clicked); + int initialType = clicked.getWorld().getBlockType(clicked.toVector()); if (initialType == 0) { return true; @@ -68,7 +70,7 @@ public class AreaPickaxe implements BlockTool { continue; } - world.queueBlockBreakEffect(server, pos, initialType, clicked.distanceSq(pos)); + clicked.getWorld().queueBlockBreakEffect(server, pos, initialType, clicked.toVector().distanceSq(pos)); editSession.setBlock(pos, air); } @@ -83,4 +85,5 @@ public class AreaPickaxe implements BlockTool { return true; } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java b/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java index c51958877..252823561 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BlockDataCyler.java @@ -19,29 +19,34 @@ package com.sk89q.worldedit.command.tool; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; /** * A mode that cycles the data values of supported blocks. - * - * @author sk89q */ public class BlockDataCyler implements DoubleActionBlockTool { - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.data-cycler"); } - private boolean handleCycle(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked, boolean forward) { + private boolean handleCycle(Platform server, LocalConfiguration config, + Player player, LocalSession session, Location clicked, boolean forward) { - LocalWorld world = clicked.getWorld(); + World world = clicked.getWorld(); - int type = world.getBlockType(clicked); - int data = world.getBlockData(clicked); + int type = world.getBlockType(clicked.toVector()); + int data = world.getBlockData(clicked.toVector()); - if (config.allowedDataCycleBlocks.size() > 0 + if (!config.allowedDataCycleBlocks.isEmpty() && !player.hasPermission("worldedit.override.data-cycler") && !config.allowedDataCycleBlocks.contains(type)) { player.printError("You are not permitted to cycle the data value of that block."); @@ -54,20 +59,19 @@ public class BlockDataCyler implements DoubleActionBlockTool { if (data < 0) { player.printError("That block's data cannot be cycled!"); } else { - world.setBlockData(clicked, data); + world.setBlockData(clicked.toVector(), data); } return true; } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { return handleCycle(server, config, player, session, clicked, true); } - public boolean actSecondary(ServerInterface server, - LocalConfiguration config, LocalPlayer player, - LocalSession session, WorldVector clicked) { + @Override + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { return handleCycle(server, config, player, session, clicked, false); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java b/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java index 0d4ca9d8c..19abb8ea3 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BlockReplacer.java @@ -20,37 +20,40 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; -import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.world.World; /** * A mode that replaces one block. - * - * @author sk89q */ public class BlockReplacer implements DoubleActionBlockTool { + private BaseBlock targetBlock; public BlockReplacer(BaseBlock targetBlock) { this.targetBlock = targetBlock; } - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.replacer"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { +BlockBag bag = session.getBlockBag(player); - BlockBag bag = session.getBlockBag(player); - - LocalWorld world = clicked.getWorld(); + World world = clicked.getWorld(); EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1, bag, player); try { - editSession.setBlock(clicked, targetBlock); - } catch (MaxChangedBlocksException e) { + editSession.setBlock(clicked.toVector(), targetBlock); + } catch (MaxChangedBlocksException ignored) { } finally { if (bag != null) { bag.flushChanges(); @@ -61,13 +64,12 @@ public class BlockReplacer implements DoubleActionBlockTool { return true; } - public boolean actSecondary(ServerInterface server, - LocalConfiguration config, LocalPlayer player, - LocalSession session, WorldVector clicked) { - LocalWorld world = clicked.getWorld(); + @Override + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + World world = clicked.getWorld(); EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1, player); - targetBlock = (editSession).getBlock(clicked); + targetBlock = (editSession).getBlock(clicked.toVector()); BlockType type = BlockType.fromID(targetBlock.getType()); if (type != null) { diff --git a/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java b/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java index 523f12f51..a4dd8e78f 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BlockTool.java @@ -19,25 +19,13 @@ package com.sk89q.worldedit.command.tool; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.util.Location; -/** - * Represents a tool that uses a block.. - * - * @author sk89q - */ public interface BlockTool extends Tool { - /** - * Perform the action. Should return true to deny the default - * action. - * - * @param server - * @param config - * @param player - * @param session - * @param clicked - * @return true to deny - */ - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked); + + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index 0b8889c93..464999b19 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -20,28 +20,30 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; -import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.masks.CombinedMask; -import com.sk89q.worldedit.masks.Mask; -import com.sk89q.worldedit.patterns.Pattern; -import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.command.tool.brush.SphereBrush; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.pattern.BlockPattern; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.session.request.Request; /** * Builds a shape at the place being looked at. - * - * @author sk89q */ public class BrushTool implements TraceTool { + protected static int MAX_RANGE = 500; protected int range = -1; private Mask mask = null; private Brush brush = new SphereBrush(); - private Pattern material = new SingleBlockPattern(new BaseBlock(BlockID.COBBLESTONE)); + private Pattern material = new BlockPattern(new BaseBlock(BlockID.COBBLESTONE)); private double size = 1; private String permission; @@ -54,14 +56,8 @@ public class BrushTool implements TraceTool { this.permission = permission; } - /** - * Checks to see if the player can still be using this tool (considering - * permissions and such). - * - * @param player - * @return - */ - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission(permission); } @@ -151,22 +147,14 @@ public class BrushTool implements TraceTool { /** * Set the set brush range. * - * @param size + * @param range */ public void setRange(int range) { this.range = range; } - /** - * Perform the action. Should return true to deny the default - * action. - * - * @param player - * @param session - * @return true to deny - */ - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session) { + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { WorldVector target = null; target = player.getBlockTrace(getRange(), true); @@ -180,14 +168,14 @@ public class BrushTool implements TraceTool { EditSession editSession = session.createEditSession(player); Request.request().setEditSession(editSession); if (mask != null) { - mask.prepare(session, player, target); Mask existingMask = editSession.getMask(); + if (existingMask == null) { editSession.setMask(mask); - } else if (existingMask instanceof CombinedMask) { - ((CombinedMask) existingMask).add(mask); + } else if (existingMask instanceof MaskIntersection) { + ((MaskIntersection) existingMask).add(mask); } else { - CombinedMask newMask = new CombinedMask(existingMask); + MaskIntersection newMask = new MaskIntersection(existingMask); newMask.add(mask); editSession.setMask(newMask); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java b/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java index 150a88d81..ad609e508 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/DistanceWand.java @@ -20,12 +20,13 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.regions.RegionSelector; /** * A wand that can be used at a distance. - * - * @author wizjany */ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { @@ -34,12 +35,12 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { } @Override - public boolean canUse(LocalPlayer player) { + public boolean canUse(Actor player) { return player.hasPermission("worldedit.wand"); } - public boolean actSecondary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session) { + @Override + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) { if (session.isToolControlEnabled() && player.hasPermission("worldedit.selection.pos")) { WorldVector target = getTarget(player); if (target == null) return true; @@ -56,8 +57,7 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { } @Override - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session) { + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { if (session.isToolControlEnabled() && player.hasPermission("worldedit.selection.pos")) { WorldVector target = getTarget(player); if (target == null) return true; @@ -72,7 +72,7 @@ public class DistanceWand extends BrushTool implements DoubleActionTraceTool { return false; } - public WorldVector getTarget(LocalPlayer player) { + public WorldVector getTarget(Player player) { WorldVector target = null; if (this.range > -1) { target = player.getBlockTrace(getRange(), true); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java b/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java index 2d8a47ca9..b44c82191 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionBlockTool.java @@ -20,28 +20,16 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.ServerInterface; -import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.util.Location; /** * Represents a block tool that also has a secondary/primary function. - * - * @author sk89q */ public interface DoubleActionBlockTool extends BlockTool { - /** - * Perform the secondary action. Should return true to deny the default - * action. - * - * @param server - * @param config - * @param player - * @param session - * @param clicked - * @return true to deny - */ - public boolean actSecondary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked); + + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked); + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java b/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java index 43b96ec06..708d3d9e9 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/DoubleActionTraceTool.java @@ -20,24 +20,15 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Platform; /** * Represents a trace tool that also has a secondary/primary function. */ public interface DoubleActionTraceTool extends TraceTool { - /** - * Perform the secondary action. Should return true to deny the default - * action. - * - * @param server - * @param config - * @param player - * @param session - * @return true to deny - */ - public boolean actSecondary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session); + + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session); + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java b/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java index 38b3e464c..df92fb5f0 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/FloatingTreeRemover.java @@ -19,18 +19,22 @@ package com.sk89q.worldedit.command.tool; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Set; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; /** * A pickaxe mode that removes floating treetops (logs and leaves not connected * to anything else) - * - * @author Moo0 */ public class FloatingTreeRemover implements BlockTool { private static final BaseBlock AIR = new BaseBlock(BlockID.AIR); @@ -40,16 +44,18 @@ public class FloatingTreeRemover implements BlockTool { rangeSq = 100*100; } - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.deltree"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, + Player player, LocalSession session, Location clicked) { - final LocalWorld world = clicked.getWorld(); + final World world = clicked.getWorld(); - switch (world.getBlockType(clicked)) { + switch (world.getBlockType(clicked.toVector())) { case BlockID.LOG: case BlockID.LOG2: case BlockID.LEAVES: @@ -67,7 +73,7 @@ public class FloatingTreeRemover implements BlockTool { final EditSession editSession = session.createEditSession(player); try { - final Set blockSet = bfs(world, clicked); + final Set blockSet = bfs(world, clicked.toVector()); if (blockSet == null) { player.printError("That's not a floating tree."); return true; @@ -111,7 +117,7 @@ public class FloatingTreeRemover implements BlockTool { * @param origin any point contained in the floating tree * @return a set containing all blocks in the tree/shroom or null if this is not a floating tree/shroom. */ - private Set bfs(LocalWorld world, Vector origin) throws MaxChangedBlocksException { + private Set bfs(World world, Vector origin) throws MaxChangedBlocksException { final Set visited = new HashSet(); final LinkedList queue = new LinkedList(); diff --git a/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java b/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java index 5ad320f1c..4ccead8cf 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java @@ -19,18 +19,23 @@ package com.sk89q.worldedit.command.tool; -import java.util.HashSet; -import java.util.Set; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; + +import java.util.HashSet; +import java.util.Set; /** * A tool that flood fills blocks. - * - * @author sk89q */ public class FloodFillTool implements BlockTool { + private int range; private Pattern pattern; @@ -39,15 +44,16 @@ public class FloodFillTool implements BlockTool { this.pattern = pattern; } - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.flood-fill"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { - LocalWorld world = clicked.getWorld(); + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { + World world = clicked.getWorld(); - int initialType = world.getBlockType(clicked); + int initialType = world.getBlockType(clicked.toVector()); if (initialType == BlockID.AIR) { return true; @@ -60,8 +66,8 @@ public class FloodFillTool implements BlockTool { EditSession editSession = session.createEditSession(player); try { - recurse(server, editSession, world, clicked.toBlockVector(), - clicked, range, initialType, new HashSet()); + recurse(server, editSession, world, clicked.toVector().toBlockVector(), + clicked.toVector(), range, initialType, new HashSet()); } catch (MaxChangedBlocksException e) { player.printError("Max blocks change limit reached."); } finally { @@ -71,23 +77,8 @@ public class FloodFillTool implements BlockTool { return true; } - /** - * Helper method. - * - * @param server - * @param superPickaxeManyDrop - * @param world - * @param pos - * @param origin - * @param size - * @param initialType - * @param visited - */ - private void recurse(ServerInterface server, EditSession editSession, - LocalWorld world, BlockVector pos, - Vector origin, int size, int initialType, - Set visited) - throws MaxChangedBlocksException { + private void recurse(Platform server, EditSession editSession, World world, BlockVector pos, Vector origin, int size, int initialType, + Set visited) throws MaxChangedBlocksException { if (origin.distance(pos) > size || visited.contains(pos)) { return; diff --git a/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java b/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java index 769c99656..974c893e6 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/LongRangeBuildTool.java @@ -22,11 +22,12 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; /** * A tool that can place (or remove) blocks at a distance. - * - * @author wizjany */ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTool { @@ -40,13 +41,12 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo } @Override - public boolean canUse(LocalPlayer player) { + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.lrbuild"); } - public boolean actSecondary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session) { - + @Override + public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) { WorldVectorFace pos = getTargetFace(player); if (pos == null) return false; EditSession eS = session.createEditSession(player); @@ -65,9 +65,7 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo } @Override - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session) { - + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) { WorldVectorFace pos = getTargetFace(player); if (pos == null) return false; EditSession eS = session.createEditSession(player); @@ -84,7 +82,7 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo return false; } - public WorldVectorFace getTargetFace(LocalPlayer player) { + public WorldVectorFace getTargetFace(Player player) { WorldVectorFace target = null; target = player.getBlockTraceFace(getRange(), true); @@ -95,4 +93,5 @@ public class LongRangeBuildTool extends BrushTool implements DoubleActionTraceTo return target; } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java b/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java index b91386dc9..a68031f74 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/QueryTool.java @@ -19,33 +19,39 @@ package com.sk89q.worldedit.command.tool; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.world.World; /** - * Plants a tree. - * - * @author sk89q + * Looks up information about a block. */ public class QueryTool implements BlockTool { - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.info"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { - LocalWorld world = clicked.getWorld(); + World world = clicked.getWorld(); EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, 0, player); - BaseBlock block = (editSession).rawGetBlock(clicked); + BaseBlock block = (editSession).rawGetBlock(clicked.toVector()); BlockType type = BlockType.fromID(block.getType()); - player.print("\u00A79@" + clicked + ": " + "\u00A7e" + player.print("\u00A79@" + clicked.toVector() + ": " + "\u00A7e" + "#" + block.getType() + "\u00A77" + " (" + (type == null ? "Unknown" : type.getName()) + ") " + "\u00A7f" - + "[" + block.getData() + "]" + " (" + world.getBlockLightLevel(clicked) + "/" + world.getBlockLightLevel(clicked.add(0, 1, 0)) + ")"); + + "[" + block.getData() + "]" + " (" + world.getBlockLightLevel(clicked.toVector()) + "/" + world.getBlockLightLevel(clicked.toVector().add(0, 1, 0)) + ")"); if (block instanceof MobSpawnerBlock) { player.printRaw("\u00A7e" + "Mob Type: " diff --git a/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java index 09cbc4340..e551b9f2e 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java @@ -22,6 +22,10 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.world.World; import java.util.HashSet; import java.util.Set; @@ -29,10 +33,9 @@ import java.util.Set; /** * A pickaxe mode that recursively finds adjacent blocks within range of * an initial block and of the same type. - * - * @author sk89q */ public class RecursivePickaxe implements BlockTool { + private static final BaseBlock air = new BaseBlock(0); private double range; @@ -40,15 +43,16 @@ public class RecursivePickaxe implements BlockTool { this.range = range; } - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.superpickaxe.recursive"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { - LocalWorld world = clicked.getWorld(); + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + World world = clicked.getWorld(); - int initialType = world.getBlockType(clicked); + int initialType = world.getBlockType(clicked.toVector()); if (initialType == BlockID.AIR) { return true; @@ -62,8 +66,8 @@ public class RecursivePickaxe implements BlockTool { editSession.getSurvivalExtent().setToolUse(config.superPickaxeManyDrop); try { - recurse(server, editSession, world, clicked.toBlockVector(), - clicked, range, initialType, new HashSet()); + recurse(server, editSession, world, clicked.toVector().toBlockVector(), + clicked.toVector(), range, initialType, new HashSet()); } catch (MaxChangedBlocksException e) { player.printError("Max blocks change limit reached."); } finally { @@ -74,22 +78,8 @@ public class RecursivePickaxe implements BlockTool { return true; } - /** - * Helper method. - * - * @param server - * @param world - * @param pos - * @param origin - * @param size - * @param initialType - * @param visited - */ - private static void recurse(ServerInterface server, EditSession editSession, - LocalWorld world, BlockVector pos, - Vector origin, double size, int initialType, - Set visited) - throws MaxChangedBlocksException { + private static void recurse(Platform server, EditSession editSession, World world, BlockVector pos, + Vector origin, double size, int initialType, Set visited) throws MaxChangedBlocksException { final double distanceSq = origin.distanceSq(pos); if (distanceSq > size*size || visited.contains(pos)) { diff --git a/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java b/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java index e01c73ea9..202c04c95 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/SinglePickaxe.java @@ -19,26 +19,31 @@ package com.sk89q.worldedit.command.tool; -import com.sk89q.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.world.World; /** * A super pickaxe mode that removes one block. - * - * @author sk89q */ public class SinglePickaxe implements BlockTool { - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.superpickaxe"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { - LocalWorld world = clicked.getWorld(); - - final int blockType = world.getBlockType(clicked); + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, com.sk89q.worldedit.util.Location clicked) { + World world = clicked.getWorld(); + final int blockType = world.getBlockType(clicked.toVector()); if (blockType == BlockID.BEDROCK && !player.canDestroyBedrock()) { return true; @@ -48,14 +53,14 @@ public class SinglePickaxe implements BlockTool { editSession.getSurvivalExtent().setToolUse(config.superPickaxeDrop); try { - editSession.setBlock(clicked, new BaseBlock(BlockID.AIR)); + editSession.setBlock(clicked.toVector(), new BaseBlock(BlockID.AIR)); } catch (MaxChangedBlocksException e) { player.printError("Max blocks change limit reached."); } finally { editSession.flushQueue(); } - world.playEffect(clicked, 2001, blockType); + world.playEffect(clicked.toVector(), 2001, blockType); return true; } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/Tool.java b/src/main/java/com/sk89q/worldedit/command/tool/Tool.java index 2777b4c5f..f1001053c 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/Tool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/Tool.java @@ -19,23 +19,21 @@ package com.sk89q.worldedit.command.tool; -import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.extension.platform.Actor; /** * Represents a tool. This interface alone defines nothing. A tool also * has to implement BlockTool or TraceTool. - * - * @author sk89q */ -public abstract interface Tool { +public interface Tool { /** * Checks to see if the player can still be using this tool (considering * permissions and such). * - * @param player - * @return + * @param actor the actor + * @return true if use is permitted */ - public boolean canUse(LocalPlayer player); + public boolean canUse(Actor actor); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java b/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java index c5c11df9e..3728cd862 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/TraceTool.java @@ -20,26 +20,11 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.ServerInterface; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Platform; -/** - * Represents a tool that does not require a block. - * - * @author sk89q - */ public interface TraceTool extends Tool { - /** - * Perform the action. Should return true to deny the default - * action. - * - * @param server - * @param config - * @param player - * @param session - * @return true to deny - */ - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session); + + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java b/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java index 41168e027..56f4bf5ed 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/TreePlanter.java @@ -20,26 +20,30 @@ package com.sk89q.worldedit.command.tool; import com.sk89q.worldedit.*; -import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.util.*; +import com.sk89q.worldedit.util.Location; /** * Plants a tree. - * - * @author sk89q */ public class TreePlanter implements BlockTool { + private TreeGenerator gen; public TreePlanter(TreeGenerator gen) { this.gen = gen; } - public boolean canUse(LocalPlayer player) { + @Override + public boolean canUse(Actor player) { return player.hasPermission("worldedit.tool.tree"); } - public boolean actPrimary(ServerInterface server, LocalConfiguration config, - LocalPlayer player, LocalSession session, WorldVector clicked) { + @Override + public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session, Location clicked) { EditSession editSession = session.createEditSession(player); @@ -47,7 +51,7 @@ public class TreePlanter implements BlockTool { boolean successful = false; for (int i = 0; i < 10; i++) { - if (gen.generate(editSession, clicked.add(0, 1, 0))) { + if (gen.generate(editSession, clicked.toVector().add(0, 1, 0))) { successful = true; break; } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java index bee617569..aba66e9fc 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/Brush.java @@ -22,7 +22,7 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; /** * Represents a brush. @@ -39,6 +39,5 @@ public interface Brush { * @param size * @throws MaxChangedBlocksException */ - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException; + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException; } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java index 6f2445ccc..1a8e4f9cd 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/ButcherBrush.java @@ -22,9 +22,10 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; public class ButcherBrush implements Brush { + private int flags; public ButcherBrush(int flags) { @@ -32,8 +33,7 @@ public class ButcherBrush implements Brush { } @Override - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException { + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { editSession.getWorld().killMobs(pos, size, flags); } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java index 4ae8d2cee..ef5fffbcc 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/ClipboardBrush.java @@ -23,9 +23,10 @@ import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; public class ClipboardBrush implements Brush { + private CuboidClipboard clipboard; private boolean noAir; @@ -34,8 +35,8 @@ public class ClipboardBrush implements Brush { this.noAir = noAir; } - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException { + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { clipboard.place(editSession, pos.subtract(clipboard.getSize().divide(2)), noAir); } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java index 67bc6b545..bed1416a7 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/CylinderBrush.java @@ -22,17 +22,19 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; public class CylinderBrush implements Brush { + private int height; public CylinderBrush(int height) { this.height = height; } - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException { - editSession.makeCylinder(pos, mat, size, size, height, true); + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { + editSession.makeCylinder(pos, Patterns.wrap(mat), size, size, height, true); } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java index c658290e7..7e90e57bc 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java @@ -24,14 +24,12 @@ import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; import java.util.*; -/** - * @author zml2008 - */ public class GravityBrush implements Brush { + private final boolean fullHeight; public GravityBrush(boolean fullHeight) { @@ -65,4 +63,5 @@ public class GravityBrush implements Brush { } } } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowCylinderBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowCylinderBrush.java index 0ed5dd2c0..6d68cee21 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowCylinderBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowCylinderBrush.java @@ -22,17 +22,20 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; public class HollowCylinderBrush implements Brush { + private int height; public HollowCylinderBrush(int height) { this.height = height; } - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException { - editSession.makeCylinder(pos, mat, size, size, height, false); + @Override + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { + editSession.makeCylinder(pos, Patterns.wrap(mat), size, size, height, false); } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowSphereBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowSphereBrush.java index ecfbf2e68..4a469066c 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowSphereBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/HollowSphereBrush.java @@ -22,14 +22,13 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; public class HollowSphereBrush implements Brush { - public HollowSphereBrush() { - } - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException { - editSession.makeSphere(pos, mat, size, size, size, false); + @Override + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { + editSession.makeSphere(pos, Patterns.wrap(mat), size, size, size, false); } } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java index d72dc333b..fa9c625ed 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/SmoothBrush.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.LocalWorldAdapter; import com.sk89q.worldedit.math.convolution.HeightMap; import com.sk89q.worldedit.MaxChangedBlocksException; @@ -27,11 +28,11 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.math.convolution.GaussianKernel; import com.sk89q.worldedit.math.convolution.HeightMapFilter; -import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; public class SmoothBrush implements Brush { + private int iterations; private boolean naturalOnly; @@ -53,4 +54,5 @@ public class SmoothBrush implements Brush { HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); heightMap.applyFilter(filter, iterations); } + } diff --git a/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java b/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java index 80c1c0219..ea79d8251 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/brush/SphereBrush.java @@ -22,14 +22,13 @@ package com.sk89q.worldedit.command.tool.brush; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.patterns.Pattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; public class SphereBrush implements Brush { - public SphereBrush() { - } - public void build(EditSession editSession, Vector pos, Pattern mat, double size) - throws MaxChangedBlocksException { - editSession.makeSphere(pos, mat, size, size, size, true); + @Override + public void build(EditSession editSession, Vector pos, Pattern mat, double size) throws MaxChangedBlocksException { + editSession.makeSphere(pos, Patterns.wrap(mat), size, size, size, true); } } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 87ec055da..ea5db45f6 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -318,24 +318,19 @@ public class PlatformManager { return; } - if (player instanceof LocalPlayer) { // Temporary workaround - LocalPlayer localPlayer = (LocalPlayer) player; - WorldVector worldVector = new WorldVector(location); - - if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { - final BlockTool superPickaxe = session.getSuperPickaxe(); - if (superPickaxe != null && superPickaxe.canUse(localPlayer)) { - event.setCancelled(superPickaxe.actPrimary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector)); - return; - } + if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) { + final BlockTool superPickaxe = session.getSuperPickaxe(); + if (superPickaxe != null && superPickaxe.canUse(player)) { + event.setCancelled(superPickaxe.actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location)); + return; } + } - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof DoubleActionBlockTool) { - if (tool.canUse(localPlayer)) { - ((DoubleActionBlockTool) tool).actSecondary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector); - event.setCancelled(true); - } + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof DoubleActionBlockTool) { + if (tool.canUse(player)) { + ((DoubleActionBlockTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location); + event.setCancelled(true); } } @@ -358,16 +353,11 @@ public class PlatformManager { return; } - if (player instanceof LocalPlayer) { // Temporary workaround - LocalPlayer localPlayer = (LocalPlayer) player; - WorldVector worldVector = new WorldVector(location); - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof BlockTool) { - if (tool.canUse(localPlayer)) { - ((BlockTool) tool).actPrimary(getServerInterface(), getConfiguration(), localPlayer, session, worldVector); - event.setCancelled(true); - } + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof BlockTool) { + if (tool.canUse(player)) { + ((BlockTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location); + event.setCancelled(true); } } } @@ -380,6 +370,7 @@ public class PlatformManager { // Create a proxy actor with a potentially different world for // making changes to the world Player player = createProxyActor(event.getPlayer()); + World world = player.getWorld(); switch (event.getInputType()) { case PRIMARY: { @@ -405,16 +396,12 @@ public class PlatformManager { LocalSession session = worldEdit.getSessionManager().get(player); - if (player instanceof LocalPlayer) { // Temporary workaround - LocalPlayer localPlayer = (LocalPlayer) player; - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof DoubleActionTraceTool) { - if (tool.canUse(localPlayer)) { - ((DoubleActionTraceTool) tool).actSecondary(getServerInterface(), getConfiguration(), localPlayer, session); - event.setCancelled(true); - return; - } + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof DoubleActionTraceTool) { + if (tool.canUse(player)) { + ((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session); + event.setCancelled(true); + return; } } @@ -441,16 +428,12 @@ public class PlatformManager { LocalSession session = worldEdit.getSessionManager().get(player); - if (player instanceof LocalPlayer) { // Temporary workaround - LocalPlayer localPlayer = (LocalPlayer) player; - - Tool tool = session.getTool(player.getItemInHand()); - if (tool != null && tool instanceof TraceTool) { - if (tool.canUse(localPlayer)) { - ((TraceTool) tool).actPrimary(getServerInterface(), getConfiguration(), localPlayer, session); - event.setCancelled(true); - return; - } + Tool tool = session.getTool(player.getItemInHand()); + if (tool != null && tool instanceof TraceTool) { + if (tool.canUse(player)) { + ((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session); + event.setCancelled(true); + return; } } diff --git a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java index ea509acc3..a14d168f1 100644 --- a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.internal; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.regions.Region; @@ -250,7 +251,7 @@ public class LocalWorldAdapter extends LocalWorld { } @Override - public boolean queueBlockBreakEffect(ServerInterface server, Vector position, int blockId, double priority) { + public boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority) { return world.queueBlockBreakEffect(server, position, blockId, priority); } diff --git a/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java b/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java index 29aa34f78..e5876107c 100644 --- a/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java +++ b/src/main/java/com/sk89q/worldedit/world/AbstractWorld.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; @@ -219,7 +220,7 @@ public abstract class AbstractWorld implements World { @SuppressWarnings("deprecation") @Override - public boolean queueBlockBreakEffect(ServerInterface server, Vector position, int blockId, double priority) { + public boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority) { if (taskId == -1) { taskId = server.schedule(0, 1, new Runnable() { @Override diff --git a/src/main/java/com/sk89q/worldedit/world/World.java b/src/main/java/com/sk89q/worldedit/world/World.java index 9f69b122e..8accbe122 100644 --- a/src/main/java/com/sk89q/worldedit/world/World.java +++ b/src/main/java/com/sk89q/worldedit/world/World.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.world; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.regions.Region; @@ -342,7 +343,7 @@ public interface World extends Extent { * @param priority the priority * @return true if the effect was played */ - boolean queueBlockBreakEffect(ServerInterface server, Vector position, int blockId, double priority); + boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority); @Override boolean equals(Object other); diff --git a/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRestore.java b/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRestore.java index 9cd1ef7d9..51db687ce 100644 --- a/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRestore.java +++ b/src/main/java/com/sk89q/worldedit/world/snapshot/SnapshotRestore.java @@ -123,7 +123,7 @@ public class SnapshotRestore { } private void checkAndAddBlock(Vector pos) { - if (editSession.getMask() != null && !editSession.getMask().matches(editSession, pos)) + if (editSession.getMask() != null && !editSession.getMask().test(pos)) return; BlockVector2D chunkPos = ChunkStore.toChunk(pos); From 5347cd4e34da86ca5a9dd7a2d87ae758c446403e Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 20:00:02 -0700 Subject: [PATCH 24/45] Fixed issue where the use of non-existent subcommands had no response. --- .../worldedit/util/command/SimpleDispatcherCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java index ec7b1545e..79b30f064 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java @@ -41,9 +41,9 @@ public class SimpleDispatcherCommand extends SimpleDispatcher implements Command @Override public void call(CommandContext context) throws CommandException { - if (context.argsLength() >= 1) { - super.call(context.getRemainingString(0), context.getLocals()); - } else { + boolean success = context.argsLength() >= 1 && super.call(context.getRemainingString(0), context.getLocals()) != null; + + if (!success) { Set aliases = getPrimaryAliases(); if (aliases.size() == 0) { From 109150f3488187610fcbd53a837f4613a00be393 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 20:09:18 -0700 Subject: [PATCH 25/45] Changed CommandPermissionsHandler to use Actor. --- .../internal/command/CommandPermissionsHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java b/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java index fc36bf1e4..cbf5eb7c5 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/CommandPermissionsHandler.java @@ -20,8 +20,8 @@ package com.sk89q.worldedit.internal.command; import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.util.command.parametric.PermissionsHandler; -import com.sk89q.worldedit.LocalPlayer; public class CommandPermissionsHandler extends PermissionsHandler { @@ -30,9 +30,9 @@ public class CommandPermissionsHandler extends PermissionsHandler { @Override protected boolean hasPermission(CommandContext context, String permission) { - LocalPlayer sender = context.getLocals().get(LocalPlayer.class); + Actor sender = context.getLocals().get(Actor.class); if (sender == null) { - return true; + throw new RuntimeException("Uh oh! No 'Actor' specified so that we can check permissions"); } else { return sender.hasPermission(permission); } From b8b097ce68441c1d53c7c10687c9b71561084045 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 20:10:48 -0700 Subject: [PATCH 26/45] Fixed 'break' being 'return' in LegacyCommandsHandler. --- .../util/command/parametric/LegacyCommandsHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java index 4602be0ab..fbc1794c2 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java @@ -84,7 +84,7 @@ public class LegacyCommandsHandler extends AbstractInvokeListener implements Inv for (ParameterData parameter : parameters) { if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) { hasUserParameters = true; - return; + break; } } From de0f283656b329e114754d86c1c935dc7a40a1d2 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 20:12:44 -0700 Subject: [PATCH 27/45] Call createProxyActor() on command Actors. --- .../sk89q/worldedit/extension/platform/CommandManager.java | 7 +++++-- .../worldedit/extension/platform/PlatformManager.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) 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 a92472f0d..0d068e687 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -62,6 +62,7 @@ public final class CommandManager { private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$"); private final WorldEdit worldEdit; + private final PlatformManager platformManager; private final Dispatcher dispatcher; private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); @@ -70,9 +71,11 @@ public final class CommandManager { * * @param worldEdit the WorldEdit instance */ - CommandManager(final WorldEdit worldEdit) { + CommandManager(final WorldEdit worldEdit, PlatformManager platformManager) { checkNotNull(worldEdit); + checkNotNull(platformManager); this.worldEdit = worldEdit; + this.platformManager = platformManager; // Register this instance for command events worldEdit.getEventBus().register(this); @@ -195,7 +198,7 @@ public final class CommandManager { public void handleCommand(CommandEvent event) { Request.reset(); - Actor actor = event.getActor(); + Actor actor = platformManager.createProxyActor(event.getActor()); String split[] = commandDetection(event.getArguments()); // No command found! diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index ea5db45f6..3e70e2483 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -65,7 +65,7 @@ public class PlatformManager { public PlatformManager(WorldEdit worldEdit) { checkNotNull(worldEdit); this.worldEdit = worldEdit; - this.commandManager = new CommandManager(worldEdit); + this.commandManager = new CommandManager(worldEdit, this); // Register this instance for events worldEdit.getEventBus().register(this); From ec28cd626c77fd65656f06082553d06917e7321e Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 27 Jun 2014 20:24:02 -0700 Subject: [PATCH 28/45] Removed use of @Nullable in ParametricCallable. --- .../parametric/ParametricCallable.java | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) 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 1d1154d13..f97f9b8b1 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 @@ -19,31 +19,15 @@ package com.sk89q.worldedit.util.command.parametric; +import com.sk89q.minecraft.util.commands.*; +import com.sk89q.worldedit.util.command.*; +import com.sk89q.worldedit.util.command.binding.Switch; + import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.annotation.Nullable; - -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.minecraft.util.commands.SuggestionContext; -import com.sk89q.minecraft.util.commands.WrappedCommandException; -import com.sk89q.worldedit.util.command.CommandCallable; -import com.sk89q.worldedit.util.command.InvalidUsageException; -import com.sk89q.worldedit.util.command.MissingParameterException; -import com.sk89q.worldedit.util.command.Parameter; -import com.sk89q.worldedit.util.command.SimpleDescription; -import com.sk89q.worldedit.util.command.UnconsumedParameterException; -import com.sk89q.worldedit.util.command.binding.Switch; +import java.util.*; /** * The implementation of a {@link CommandCallable} for the {@link ParametricBuilder}. @@ -103,8 +87,6 @@ class ParametricCallable implements CommandCallable { for (Annotation annotation : annotations[i]) { if (annotation instanceof Switch) { parameter.setFlag(((Switch) annotation).value(), type != boolean.class); - } else if (annotation instanceof Nullable) { - parameter.setOptional(true); } else if (annotation instanceof Optional) { parameter.setOptional(true); String[] value = ((Optional) annotation).value(); @@ -481,14 +463,14 @@ class ParametricCallable implements CommandCallable { * Get any unused flag arguments. * * @param context the command context - * @param parameters the list of parameters */ private String getUnusedFlags(CommandContext context) { Set unusedFlags = null; for (char flag : context.getFlags()) { boolean found = false; - for (int i = 0; i < parameters.length; i++) { - Character paramFlag = parameters[i].getFlag(); + + for (ParameterData parameter : parameters) { + Character paramFlag = parameter.getFlag(); if (paramFlag != null && flag == paramFlag) { found = true; break; From f64107c2c047d0a1b66d91ae1a1444018492cd42 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 01:01:49 -0700 Subject: [PATCH 29/45] Removed LocalPlayer from commands and removed various uses of old Mask and Pattern classes. --- .../com/sk89q/worldedit/LocalSession.java | 45 ++- .../java/com/sk89q/worldedit/WorldEdit.java | 70 ++-- .../worldedit/command/BrushCommands.java | 7 +- .../worldedit/command/ChunkCommands.java | 33 +- .../worldedit/command/ClipboardCommands.java | 73 ++-- .../worldedit/command/GeneralCommands.java | 40 ++- .../worldedit/command/GenerationCommands.java | 307 ++++++----------- .../worldedit/command/HistoryCommands.java | 42 +-- .../worldedit/command/NavigationCommands.java | 57 ++-- .../worldedit/command/RegionCommands.java | 312 ++++++------------ .../worldedit/command/SchematicCommands.java | 49 +-- .../worldedit/command/ScriptingCommands.java | 48 +-- .../worldedit/command/SelectionCommands.java | 49 +-- .../worldedit/command/SnapshotCommands.java | 31 +- .../command/SnapshotUtilCommands.java | 36 +- .../command/SuperPickaxeCommands.java | 17 +- .../sk89q/worldedit/command/ToolCommands.java | 28 +- .../worldedit/command/ToolUtilCommands.java | 9 +- .../worldedit/command/UtilityCommands.java | 39 +-- .../worldedit/command/WorldEditCommands.java | 7 +- .../internal/command/WorldEditBinding.java | 84 ++++- .../scripting/CraftScriptContext.java | 26 +- .../scripting/CraftScriptEnvironment.java | 13 +- 23 files changed, 622 insertions(+), 800 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/LocalSession.java b/src/main/java/com/sk89q/worldedit/LocalSession.java index 2efd2fe00..df38b58a5 100644 --- a/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -30,13 +30,14 @@ import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.internal.cui.CUIRegion; import com.sk89q.worldedit.internal.cui.SelectionShapeEvent; -import com.sk89q.worldedit.masks.Mask; -import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.snapshot.Snapshot; @@ -141,6 +142,17 @@ public class LocalSession { * @return whether anything was undone */ public EditSession undo(BlockBag newBlockBag, LocalPlayer player) { + return undo(newBlockBag, (Player) player); + } + + /** + * Performs an undo. + * + * @param newBlockBag + * @param player + * @return whether anything was undone + */ + public EditSession undo(BlockBag newBlockBag, Player player) { --historyPointer; if (historyPointer >= 0) { EditSession editSession = history.get(historyPointer); @@ -164,6 +176,17 @@ public class LocalSession { * @return whether anything was redone */ public EditSession redo(BlockBag newBlockBag, LocalPlayer player) { + return redo(newBlockBag, (Player) player); + } + + /** + * Performs a redo + * + * @param newBlockBag + * @param player + * @return whether anything was redone + */ + public EditSession redo(BlockBag newBlockBag, Player player) { if (historyPointer < history.size()) { EditSession editSession = history.get(historyPointer); EditSession newEditSession = WorldEdit.getInstance().getEditSessionFactory() @@ -577,7 +600,7 @@ public class LocalSession { * * @param player */ - public void dispatchCUISetup(LocalPlayer player) { + public void dispatchCUISetup(Player player) { if (selector != null) { dispatchCUISelection(player); } @@ -588,7 +611,7 @@ public class LocalSession { * * @param player */ - public void dispatchCUISelection(LocalPlayer player) { + public void dispatchCUISelection(Player player) { if (!hasCUISupport) { return; } @@ -732,9 +755,6 @@ public class LocalSession { getBlockChangeLimit(), blockBag, player); editSession.setFastMode(fastMode); Request.request().setEditSession(editSession); - if (mask != null && player instanceof LocalPlayer) { - mask.prepare(this, (LocalPlayer) player, null); - } editSession.setMask(mask); return editSession; @@ -775,4 +795,15 @@ public class LocalSession { public void setMask(Mask mask) { this.mask = mask; } + + /** + * Set a mask. + * + * @param mask mask or null + */ + @SuppressWarnings("deprecation") + public void setMask(com.sk89q.worldedit.masks.Mask mask) { + setMask(mask != null ? Masks.wrap(mask) : null); + } + } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index e52184372..a34019da5 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -183,7 +183,7 @@ public class WorldEdit { * @deprecated use {@link #getSessionManager()} */ @Deprecated - public LocalSession getSession(LocalPlayer player) { + public LocalSession getSession(Player player) { return sessions.get(player); } @@ -191,7 +191,7 @@ public class WorldEdit { * @deprecated use {@link #getSessionManager()} */ @Deprecated - public void removeSession(LocalPlayer player) { + public void removeSession(Player player) { sessions.remove(player); } @@ -207,7 +207,7 @@ public class WorldEdit { * @deprecated use {@link #getSessionManager()} */ @Deprecated - public boolean hasSession(LocalPlayer player) { + public boolean hasSession(Player player) { return sessions.contains(player); } @@ -216,7 +216,7 @@ public class WorldEdit { */ @SuppressWarnings("deprecation") @Deprecated - public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed) throws WorldEditException { + public BaseBlock getBlock(Player player, String arg, boolean allAllowed) throws WorldEditException { return getBlock(player, arg, allAllowed, false); } @@ -225,7 +225,7 @@ public class WorldEdit { */ @SuppressWarnings("deprecation") @Deprecated - public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed, boolean allowNoData) throws WorldEditException { + public BaseBlock getBlock(Player player, String arg, boolean allAllowed, boolean allowNoData) throws WorldEditException { ParserContext context = new ParserContext(); context.setActor(player); context.setWorld(player.getWorld()); @@ -240,7 +240,7 @@ public class WorldEdit { */ @SuppressWarnings("deprecation") @Deprecated - public BaseBlock getBlock(LocalPlayer player, String id) throws WorldEditException { + public BaseBlock getBlock(Player player, String id) throws WorldEditException { return getBlock(player, id, false); } @@ -249,7 +249,7 @@ public class WorldEdit { */ @Deprecated @SuppressWarnings("deprecation") - public Set getBlocks(LocalPlayer player, String list, boolean allAllowed, boolean allowNoData) throws WorldEditException { + public Set getBlocks(Player player, String list, boolean allAllowed, boolean allowNoData) throws WorldEditException { String[] items = list.split(","); Set blocks = new HashSet(); for (String id : items) { @@ -263,7 +263,7 @@ public class WorldEdit { */ @Deprecated @SuppressWarnings("deprecation") - public Set getBlocks(LocalPlayer player, String list, boolean allAllowed) throws WorldEditException { + public Set getBlocks(Player player, String list, boolean allAllowed) throws WorldEditException { return getBlocks(player, list, allAllowed, false); } @@ -272,7 +272,7 @@ public class WorldEdit { */ @Deprecated @SuppressWarnings("deprecation") - public Set getBlocks(LocalPlayer player, String list) throws WorldEditException { + public Set getBlocks(Player player, String list) throws WorldEditException { return getBlocks(player, list, false); } @@ -281,7 +281,7 @@ public class WorldEdit { */ @Deprecated @SuppressWarnings("deprecation") - public Set getBlockIDs(LocalPlayer player, String list, boolean allBlocksAllowed) throws WorldEditException { + public Set getBlockIDs(Player player, String list, boolean allBlocksAllowed) throws WorldEditException { String[] items = list.split(","); Set blocks = new HashSet(); for (String s : items) { @@ -295,7 +295,7 @@ public class WorldEdit { */ @Deprecated @SuppressWarnings("deprecation") - public Pattern getBlockPattern(LocalPlayer player, String input) throws WorldEditException { + public Pattern getBlockPattern(Player player, String input) throws WorldEditException { ParserContext context = new ParserContext(); context.setActor(player); context.setWorld(player.getWorld()); @@ -308,7 +308,7 @@ public class WorldEdit { */ @Deprecated @SuppressWarnings("deprecation") - public Mask getBlockMask(LocalPlayer player, LocalSession session, String input) throws WorldEditException { + public Mask getBlockMask(Player player, LocalSession session, String input) throws WorldEditException { ParserContext context = new ParserContext(); context.setActor(player); context.setWorld(player.getWorld()); @@ -330,8 +330,7 @@ public class WorldEdit { * @return a file * @throws FilenameException thrown if the filename is invalid */ - public File getSafeSaveFile(LocalPlayer player, File dir, String filename, String defaultExt, String... extensions) - throws FilenameException { + public File getSafeSaveFile(Player player, File dir, String filename, String defaultExt, String... extensions) throws FilenameException { return getSafeFile(player, dir, filename, defaultExt, extensions, true); } @@ -349,8 +348,7 @@ public class WorldEdit { * @return a file * @throws FilenameException thrown if the filename is invalid */ - public File getSafeOpenFile(LocalPlayer player, File dir, String filename, String defaultExt, String... extensions) - throws FilenameException { + public File getSafeOpenFile(Player player, File dir, String filename, String defaultExt, String... extensions) throws FilenameException { return getSafeFile(player, dir, filename, defaultExt, extensions, false); } @@ -366,9 +364,7 @@ public class WorldEdit { * @return a file * @throws FilenameException thrown if the filename is invalid */ - private File getSafeFile(LocalPlayer player, File dir, String filename, - String defaultExt, String[] extensions, boolean isSave) - throws FilenameException { + private File getSafeFile(Player player, File dir, String filename, String defaultExt, String[] extensions, boolean isSave) throws FilenameException { if (extensions != null && (extensions.length == 1 && extensions[0] == null)) extensions = null; File f; @@ -411,7 +407,7 @@ public class WorldEdit { } } - public int getMaximumPolygonalPoints(LocalPlayer player) { + public int getMaximumPolygonalPoints(Player player) { if (player.hasPermission("worldedit.limit.unrestricted") || getConfiguration().maxPolygonalPoints < 0) { return getConfiguration().defaultMaxPolygonalPoints; } @@ -423,7 +419,7 @@ public class WorldEdit { return Math.min(getConfiguration().defaultMaxPolygonalPoints, getConfiguration().maxPolygonalPoints); } - public int getMaximumPolyhedronPoints(LocalPlayer player) { + public int getMaximumPolyhedronPoints(Player player) { if (player.hasPermission("worldedit.limit.unrestricted") || getConfiguration().maxPolyhedronPoints < 0) { return getConfiguration().defaultMaxPolyhedronPoints; } @@ -586,9 +582,7 @@ public class WorldEdit { * @return a direction vector * @throws UnknownDirectionException thrown if the direction is not known */ - public Vector getDiagonalDirection(LocalPlayer player, String dirStr) - throws UnknownDirectionException { - + public Vector getDiagonalDirection(Player player, String dirStr) throws UnknownDirectionException { return getPlayerDirection(player, dirStr.toLowerCase()).vector(); } @@ -600,8 +594,7 @@ public class WorldEdit { * @return a direction vector * @throws UnknownDirectionException thrown if the direction is not known */ - public FlipDirection getFlipDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException { - + public FlipDirection getFlipDirection(Player player, String dirStr) throws UnknownDirectionException { final PlayerDirection dir = getPlayerDirection(player, dirStr); switch (dir) { case WEST: @@ -624,7 +617,7 @@ public class WorldEdit { /** * Flush a block bag's changes to a player. * - * @param player the player + * @param actor the actor * @param editSession the edit session */ public void flushBlockBag(Actor actor, EditSession editSession) { @@ -668,7 +661,7 @@ public class WorldEdit { * @param player the player */ @Deprecated - public void handleDisconnect(LocalPlayer player) { + public void handleDisconnect(Player player) { forgetPlayer(player); } @@ -677,7 +670,7 @@ public class WorldEdit { * * @param player the player */ - public void markExpire(LocalPlayer player) { + public void markExpire(Player player) { sessions.markforExpiration(player); } @@ -686,7 +679,7 @@ public class WorldEdit { * * @param player the player */ - public void forgetPlayer(LocalPlayer player) { + public void forgetPlayer(Player player) { sessions.remove(player); } @@ -703,7 +696,7 @@ public class WorldEdit { * @param player the player * @return true if the swing was handled */ - public boolean handleArmSwing(LocalPlayer player) { + public boolean handleArmSwing(Player player) { PlayerInputEvent event = new PlayerInputEvent(player, InputType.PRIMARY); getEventBus().post(event); return event.isCancelled(); @@ -715,7 +708,7 @@ public class WorldEdit { * @param player the player * @return true if the right click was handled */ - public boolean handleRightClick(LocalPlayer player) { + public boolean handleRightClick(Player player) { PlayerInputEvent event = new PlayerInputEvent(player, InputType.SECONDARY); getEventBus().post(event); return event.isCancelled(); @@ -728,7 +721,7 @@ public class WorldEdit { * @param clicked the clicked block * @return false if you want the action to go through */ - public boolean handleBlockRightClick(LocalPlayer player, WorldVector clicked) { + public boolean handleBlockRightClick(Player player, WorldVector clicked) { BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), OPEN); getEventBus().post(event); return event.isCancelled(); @@ -741,7 +734,7 @@ public class WorldEdit { * @param clicked the clicked block * @return false if you want the action to go through */ - public boolean handleBlockLeftClick(LocalPlayer player, WorldVector clicked) { + public boolean handleBlockLeftClick(Player player, WorldVector clicked) { BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), HIT); getEventBus().post(event); return event.isCancelled(); @@ -753,7 +746,7 @@ public class WorldEdit { * @param split * @return whether the command was processed */ - public boolean handleCommand(LocalPlayer player, String[] split) { + public boolean handleCommand(Player player, String[] split) { CommandEvent event = new CommandEvent(player, split); getEventBus().post(event); return event.isCancelled(); @@ -771,7 +764,7 @@ public class WorldEdit { * @param args arguments for the script * @throws WorldEditException */ - public void runScript(LocalPlayer player, File f, String[] args) throws WorldEditException { + public void runScript(Player player, File f, String[] args) throws WorldEditException { Request.reset(); String filename = f.getPath(); @@ -809,9 +802,8 @@ public class WorldEdit { return; } - LocalSession session = getSession(player); - CraftScriptContext scriptContext = - new CraftScriptContext(this, getServer(), getConfiguration(), session, player, args); + LocalSession session = getSessionManager().get(player); + CraftScriptContext scriptContext = new CraftScriptContext(this, getServer(), getConfiguration(), session, player, args); CraftScriptEngine engine = null; diff --git a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 181b5931f..f954e26d8 100644 --- a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -160,7 +160,9 @@ public class BrushCommands { ) @CommandPermissions("worldedit.brush.smooth") public void smoothBrush(Player player, LocalSession session, EditSession editSession, - @Optional("2") double radius, @Optional("4") int iterations, @Switch('n') boolean naturalBlocksOnly) throws WorldEditException { + @Optional("2") double radius, @Optional("4") int iterations, @Switch('n') + boolean naturalBlocksOnly) throws WorldEditException { + worldEdit.checkMaxBrushRadius(radius); BrushTool tool = session.getBrushTool(player.getItemInHand()); @@ -227,8 +229,7 @@ public class BrushCommands { max = 2 ) @CommandPermissions("worldedit.brush.butcher") - public void butcherBrush(CommandContext args, LocalSession session, Player player, EditSession editSession) throws WorldEditException { - + public void butcherBrush(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = worldEdit.getConfiguration(); double radius = args.argsLength() > 0 ? args.getDouble(0) : 5; diff --git a/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java b/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java index 6e81ce8ad..0330c58bc 100644 --- a/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java @@ -24,6 +24,7 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.math.MathUtils; import com.sk89q.worldedit.world.storage.LegacyChunkStore; import com.sk89q.worldedit.world.storage.McRegionChunkStore; @@ -33,18 +34,19 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Set; +import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; /** - * Chunk tools. - * - * @author sk89q + * Commands for working with chunks. */ public class ChunkCommands { - private final WorldEdit we; + + private final WorldEdit worldEdit; - public ChunkCommands(WorldEdit we) { - this.we = we; + public ChunkCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -55,9 +57,7 @@ public class ChunkCommands { max = 0 ) @CommandPermissions("worldedit.chunkinfo") - public void chunkInfo(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void chunkInfo(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Vector pos = player.getBlockIn(); int chunkX = (int) Math.floor(pos.getBlockX() / 16.0); int chunkZ = (int) Math.floor(pos.getBlockZ() / 16.0); @@ -81,9 +81,7 @@ public class ChunkCommands { max = 0 ) @CommandPermissions("worldedit.listchunks") - public void listChunks(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void listChunks(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Set chunks = session.getSelection(player.getWorld()).getChunks(); for (Vector2D chunk : chunks) { @@ -100,11 +98,9 @@ public class ChunkCommands { ) @CommandPermissions("worldedit.delchunks") @Logging(REGION) - public void deleteChunks(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void deleteChunks(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { player.print("Note that this command does not yet support the mcregion format."); - LocalConfiguration config = we.getConfiguration(); + LocalConfiguration config = worldEdit.getConfiguration(); Set chunks = session.getSelection(player.getWorld()).getChunks(); FileOutputStream out = null; @@ -139,7 +135,7 @@ public class ChunkCommands { if (out != null) { try { out.close(); - } catch (IOException ie) { } + } catch (IOException ignored) { } } } } else if (config.shellSaveType.equalsIgnoreCase("bash")) { @@ -171,7 +167,7 @@ public class ChunkCommands { if (out != null) { try { out.close(); - } catch (IOException ie) { + } catch (IOException ignored) { } } } @@ -179,4 +175,5 @@ public class ChunkCommands { player.printError("Shell script type must be configured: 'bat' or 'bash' expected."); } } + } diff --git a/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 910064278..ffc765095 100644 --- a/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -19,28 +19,40 @@ package com.sk89q.worldedit.command; -import com.sk89q.minecraft.util.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.Logging; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.world.World; +import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; /** * Clipboard commands. - * - * @author sk89q */ public class ClipboardCommands { - private final WorldEdit we; - public ClipboardCommands(WorldEdit we) { - this.we = we; + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public ClipboardCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -55,9 +67,7 @@ public class ClipboardCommands { max = 0 ) @CommandPermissions("worldedit.clipboard.copy") - public void copy(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void copy(Player player, LocalSession session, EditSession editSession, @Switch('e') boolean copyEntities) throws WorldEditException { Region region = session.getSelection(player.getWorld()); Vector min = region.getMinimumPoint(); Vector max = region.getMaximumPoint(); @@ -73,11 +83,12 @@ public class ClipboardCommands { clipboard.copy(editSession, region); } - if (args.hasFlag('e')) { + if (copyEntities) { for (LocalEntity entity : player.getWorld().getEntities(region)) { clipboard.storeEntity(entity); } } + session.setClipboard(clipboard); player.print("Block(s) copied."); @@ -97,16 +108,9 @@ public class ClipboardCommands { ) @CommandPermissions("worldedit.clipboard.cut") @Logging(REGION) - public void cut(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - BaseBlock block = new BaseBlock(BlockID.AIR); + public void cut(Player player, LocalSession session, EditSession editSession, @Optional("air") BaseBlock block, @Switch('e') boolean copyEntities) throws WorldEditException { World world = player.getWorld(); - if (args.argsLength() > 0) { - block = we.getBlock(player, args.getString(0)); - } - Region region = session.getSelection(world); Vector min = region.getMinimumPoint(); Vector max = region.getMaximumPoint(); @@ -122,13 +126,14 @@ public class ClipboardCommands { clipboard.copy(editSession, region); } - if (args.hasFlag('e')) { + if (copyEntities) { LocalEntity[] entities = world.getEntities(region); for (LocalEntity entity : entities) { clipboard.storeEntity(entity); } world.killEntities(entities); } + session.setClipboard(clipboard); editSession.setBlocks(region, block); @@ -151,8 +156,7 @@ public class ClipboardCommands { ) @CommandPermissions("worldedit.clipboard.paste") @Logging(PLACEMENT) - public void paste(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void paste(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { boolean atOrigin = args.hasFlag('o'); boolean pasteNoAir = args.hasFlag('a'); @@ -194,8 +198,7 @@ public class ClipboardCommands { max = 1 ) @CommandPermissions("worldedit.clipboard.rotate") - public void rotate(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void rotate(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int angle = args.getInteger(0); @@ -221,12 +224,8 @@ public class ClipboardCommands { max = 1 ) @CommandPermissions("worldedit.clipboard.flip") - public void flip(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - CuboidClipboard.FlipDirection dir = we.getFlipDirection(player, - args.argsLength() > 0 ? args.getString(0).toLowerCase() : "me"); - + public void flip(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + CuboidClipboard.FlipDirection dir = worldEdit.getFlipDirection(player, args.argsLength() > 0 ? args.getString(0).toLowerCase() : "me"); CuboidClipboard clipboard = session.getClipboard(); clipboard.flip(dir, args.hasFlag('p')); player.print("Clipboard flipped."); @@ -241,9 +240,8 @@ public class ClipboardCommands { ) @Deprecated @CommandPermissions("worldedit.clipboard.load") - public void load(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - player.printError("This command is no longer used. See //schematic load."); + public void load(Actor actor) { + actor.printError("This command is no longer used. See //schematic load."); } @Command( @@ -255,9 +253,8 @@ public class ClipboardCommands { ) @Deprecated @CommandPermissions("worldedit.clipboard.save") - public void save(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - player.printError("This command is no longer used. See //schematic save."); + public void save(Actor actor) { + actor.printError("This command is no longer used. See //schematic save."); } @Command( @@ -268,9 +265,7 @@ public class ClipboardCommands { max = 0 ) @CommandPermissions("worldedit.clipboard.clear") - public void clearClipboard(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void clearClipboard(Player player, LocalSession session, EditSession editSession) throws WorldEditException { session.setClipboard(null); player.print("Clipboard cleared."); } diff --git a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 61cbe98b3..19cef7ff9 100644 --- a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -24,19 +24,28 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.masks.Mask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.command.parametric.Optional; + +import static com.google.common.base.Preconditions.checkNotNull; /** * General WorldEdit commands. - * - * @author sk89q */ public class GeneralCommands { - private final WorldEdit we; - public GeneralCommands(WorldEdit we) { - this.we = we; + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public GeneralCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -47,10 +56,9 @@ public class GeneralCommands { max = 1 ) @CommandPermissions("worldedit.limit") - public void limit(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void limit(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - LocalConfiguration config = we.getConfiguration(); + LocalConfiguration config = worldEdit.getConfiguration(); boolean mayDisable = player.hasPermission("worldedit.limit.unrestricted"); int limit = Math.max(-1, args.getInteger(0)); @@ -78,8 +86,7 @@ public class GeneralCommands { max = 1 ) @CommandPermissions("worldedit.fast") - public void fast(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void fast(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { String newState = args.getString(0, null); if (session.hasFastMode()) { @@ -109,13 +116,11 @@ public class GeneralCommands { max = -1 ) @CommandPermissions("worldedit.global-mask") - public void gmask(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - if (args.argsLength() == 0) { - session.setMask(null); + public void gmask(Player player, LocalSession session, EditSession editSession, @Optional Mask mask) throws WorldEditException { + if (mask == null) { + session.setMask((Mask) null); player.print("Global mask disabled."); } else { - Mask mask = we.getBlockMask(player, session, args.getJoinedStrings(0)); session.setMask(mask); player.print("Global mask set."); } @@ -128,8 +133,7 @@ public class GeneralCommands { min = 0, max = 0 ) - public void togglePlace(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void togglePlace(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { if (session.togglePlacementPosition()) { player.print("Now placing at pos #1."); diff --git a/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 4a8fcd655..ffce9f7ec 100644 --- a/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -19,41 +19,46 @@ package com.sk89q.worldedit.command; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.PLACEMENT; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; - 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.worldedit.BiomeType; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; +import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.internal.expression.ExpressionException; -import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.TreeGenerator.TreeType; +import com.sk89q.worldedit.util.command.binding.Range; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.command.binding.Text; +import com.sk89q.worldedit.util.command.parametric.Optional; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; /** - * Generation commands. - * - * @author sk89q + * Commands for the generation of shapes and other objects. */ public class GenerationCommands { - private final WorldEdit we; - - public GenerationCommands(WorldEdit we) { - this.we = we; + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public GenerationCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( aliases = { "/hcyl" }, - usage = " [,] [height]", + usage = " [,] [height]", desc = "Generates a hollow cylinder.", help = "Generates a hollow cylinder.\n" + @@ -65,40 +70,14 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public void hcyl(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern block = we.getBlockPattern(player, args.getString(0)); - String[] radiuses = args.getString(1).split(","); - final double radiusX, radiusZ; - switch (radiuses.length) { - case 1: - radiusX = radiusZ = Math.max(1, Double.parseDouble(radiuses[0])); - break; - - case 2: - radiusX = Math.max(1, Double.parseDouble(radiuses[0])); - radiusZ = Math.max(1, Double.parseDouble(radiuses[1])); - break; - - default: - player.printError("You must either specify 1 or 2 radius values."); - return; - } - int height = args.argsLength() > 2 ? args.getInteger(2) : 1; - - we.checkMaxRadius(radiusX); - we.checkMaxRadius(radiusZ); - we.checkMaxRadius(height); - - Vector pos = session.getPlacementPosition(player); - int affected = editSession.makeCylinder(pos, block, radiusX, radiusZ, height, false); - player.print(affected + " block(s) have been created."); + public void hcyl(Player player, LocalSession session, EditSession editSession, Pattern pattern, String radiusString, @Optional("1") int height) throws WorldEditException { + cyl(player, session, editSession, pattern, radiusString, height, true); } @Command( aliases = { "/cyl" }, usage = " [,] [height]", + flags = "h", desc = "Generates a cylinder.", help = "Generates a cylinder.\n" + @@ -110,34 +89,30 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.cylinder") @Logging(PLACEMENT) - public void cyl(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern block = we.getBlockPattern(player, args.getString(0)); - String[] radiuses = args.getString(1).split(","); + public void cyl(Player player, LocalSession session, EditSession editSession, Pattern pattern, String radiusString, @Optional("1") int height, @Switch('h') boolean hollow) throws WorldEditException { + String[] radii = radiusString.split(","); final double radiusX, radiusZ; - switch (radiuses.length) { + switch (radii.length) { case 1: - radiusX = radiusZ = Math.max(1, Double.parseDouble(radiuses[0])); + radiusX = radiusZ = Math.max(1, Double.parseDouble(radii[0])); break; case 2: - radiusX = Math.max(1, Double.parseDouble(radiuses[0])); - radiusZ = Math.max(1, Double.parseDouble(radiuses[1])); + radiusX = Math.max(1, Double.parseDouble(radii[0])); + radiusZ = Math.max(1, Double.parseDouble(radii[1])); break; default: player.printError("You must either specify 1 or 2 radius values."); return; } - int height = args.argsLength() > 2 ? args.getInteger(2) : 1; - we.checkMaxRadius(radiusX); - we.checkMaxRadius(radiusZ); - we.checkMaxRadius(height); + worldEdit.checkMaxRadius(radiusX); + worldEdit.checkMaxRadius(radiusZ); + worldEdit.checkMaxRadius(height); Vector pos = session.getPlacementPosition(player); - int affected = editSession.makeCylinder(pos, block, radiusX, radiusZ, height, true); + int affected = editSession.makeCylinder(pos, Patterns.wrap(pattern), radiusX, radiusZ, height, !hollow); player.print(affected + " block(s) have been created."); } @@ -155,52 +130,14 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.sphere") @Logging(PLACEMENT) - public void hsphere(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final Pattern block = we.getBlockPattern(player, args.getString(0)); - String[] radiuses = args.getString(1).split(","); - final double radiusX, radiusY, radiusZ; - switch (radiuses.length) { - case 1: - radiusX = radiusY = radiusZ = Math.max(1, Double.parseDouble(radiuses[0])); - break; - - case 3: - radiusX = Math.max(1, Double.parseDouble(radiuses[0])); - radiusY = Math.max(1, Double.parseDouble(radiuses[1])); - radiusZ = Math.max(1, Double.parseDouble(radiuses[2])); - break; - - default: - player.printError("You must either specify 1 or 3 radius values."); - return; - } - - we.checkMaxRadius(radiusX); - we.checkMaxRadius(radiusY); - we.checkMaxRadius(radiusZ); - - final boolean raised; - if (args.argsLength() > 2) { - raised = args.getString(2).equalsIgnoreCase("true") || args.getString(2).equalsIgnoreCase("yes"); - } else { - raised = false; - } - - Vector pos = session.getPlacementPosition(player); - if (raised) { - pos = pos.add(0, radiusY, 0); - } - - int affected = editSession.makeSphere(pos, block, radiusX, radiusY, radiusZ, false); - player.findFreePosition(); - player.print(affected + " block(s) have been created."); + public void hsphere(Player player, LocalSession session, EditSession editSession, Pattern pattern, String radiusString, @Optional("false") boolean raised) throws WorldEditException { + sphere(player, session, editSession, pattern, radiusString, raised, true); } @Command( aliases = { "/sphere" }, usage = " [,,] [raised?]", + flags = "h", desc = "Generates a filled sphere.", help = "Generates a filled sphere.\n" + @@ -212,21 +149,18 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.sphere") @Logging(PLACEMENT) - public void sphere(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern block = we.getBlockPattern(player, args.getString(0)); - String[] radiuses = args.getString(1).split(","); + public void sphere(Player player, LocalSession session, EditSession editSession, Pattern pattern, String radiusString, @Optional("false") boolean raised, @Switch('h') boolean hollow) throws WorldEditException { + String[] radii = radiusString.split(","); final double radiusX, radiusY, radiusZ; - switch (radiuses.length) { + switch (radii.length) { case 1: - radiusX = radiusY = radiusZ = Math.max(1, Double.parseDouble(radiuses[0])); + radiusX = radiusY = radiusZ = Math.max(1, Double.parseDouble(radii[0])); break; case 3: - radiusX = Math.max(1, Double.parseDouble(radiuses[0])); - radiusY = Math.max(1, Double.parseDouble(radiuses[1])); - radiusZ = Math.max(1, Double.parseDouble(radiuses[2])); + radiusX = Math.max(1, Double.parseDouble(radii[0])); + radiusY = Math.max(1, Double.parseDouble(radii[1])); + radiusZ = Math.max(1, Double.parseDouble(radii[2])); break; default: @@ -234,23 +168,16 @@ public class GenerationCommands { return; } - we.checkMaxRadius(radiusX); - we.checkMaxRadius(radiusY); - we.checkMaxRadius(radiusZ); - - final boolean raised; - if (args.argsLength() > 2) { - raised = args.getString(2).equalsIgnoreCase("true") || args.getString(2).equalsIgnoreCase("yes"); - } else { - raised = false; - } + worldEdit.checkMaxRadius(radiusX); + worldEdit.checkMaxRadius(radiusY); + worldEdit.checkMaxRadius(radiusZ); Vector pos = session.getPlacementPosition(player); if (raised) { pos = pos.add(0, radiusY, 0); } - int affected = editSession.makeSphere(pos, block, radiusX, radiusY, radiusZ, true); + int affected = editSession.makeSphere(pos, Patterns.wrap(pattern), radiusX, radiusY, radiusZ, !hollow); player.findFreePosition(); player.print(affected + " block(s) have been created."); } @@ -265,22 +192,9 @@ public class GenerationCommands { @CommandPermissions("worldedit.generation.forest") @Logging(POSITION) @SuppressWarnings("deprecation") - public void forestGen(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - int size = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 10; - TreeGenerator.TreeType type = args.argsLength() > 1 ? - TreeGenerator.lookup(args.getString(1)) - : TreeGenerator.TreeType.TREE; - double density = args.argsLength() > 2 ? args.getDouble(2) / 100 : 0.05; - - if (type == null) { - player.printError("Tree type '" + args.getString(1) + "' is unknown."); - return; - } - - int affected = editSession.makeForest(session.getPlacementPosition(player), - size, density, new TreeGenerator(type)); + public void forestGen(Player player, LocalSession session, EditSession editSession, @Optional("10") int size, @Optional("tree") TreeType type, @Optional("5") double density) throws WorldEditException { + density = density / 100; + int affected = editSession.makeForest(session.getPlacementPosition(player), size, density, new TreeGenerator(type)); player.print(affected + " trees created."); } @@ -293,59 +207,38 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.pumpkins") @Logging(POSITION) - public void pumpkins(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - int size = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 10; - - int affected = editSession.makePumpkinPatches(session.getPlacementPosition(player), size); + public void pumpkins(Player player, LocalSession session, EditSession editSession, @Optional("10") int apothem) throws WorldEditException { + int affected = editSession.makePumpkinPatches(session.getPlacementPosition(player), apothem); player.print(affected + " pumpkin patches created."); } + @Command( + aliases = { "/hpyramid" }, + usage = " ", + desc = "Generate a hollow pyramid", + min = 2, + max = 2 + ) + @CommandPermissions("worldedit.generation.pyramid") + @Logging(PLACEMENT) + public void hollowPyramid(Player player, LocalSession session, EditSession editSession, Pattern pattern, @Range(min = 1) int size) throws WorldEditException { + pyramid(player, session, editSession, pattern, size, true); + } + @Command( aliases = { "/pyramid" }, usage = " ", + flags = "h", desc = "Generate a filled pyramid", min = 2, max = 2 ) @CommandPermissions("worldedit.generation.pyramid") @Logging(PLACEMENT) - public void pyramid(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern block = we.getBlockPattern(player, args.getString(0)); - int size = Math.max(1, args.getInteger(1)); + public void pyramid(Player player, LocalSession session, EditSession editSession, Pattern pattern, @Range(min = 1) int size, @Switch('h') boolean hollow) throws WorldEditException { Vector pos = session.getPlacementPosition(player); - - we.checkMaxRadius(size); - - int affected = editSession.makePyramid(pos, block, size, true); - - player.findFreePosition(); - player.print(affected + " block(s) have been created."); - } - - @Command( - aliases = { "/hpyramid" }, - usage = " ", - desc = "Generate a hollow pyramid", - min = 2, - max = 2 - ) - @CommandPermissions("worldedit.generation.pyramid") - @Logging(PLACEMENT) - public void hpyramid(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern block = we.getBlockPattern(player, args.getString(0)); - int size = Math.max(1, args.getInteger(1)); - Vector pos = session.getPlacementPosition(player); - - we.checkMaxRadius(size); - - int affected = editSession.makePyramid(pos, block, size, false); - + worldEdit.checkMaxRadius(size); + int affected = editSession.makePyramid(pos, Patterns.wrap(pattern), size, !hollow); player.findFreePosition(); player.print(affected + " block(s) have been created."); } @@ -371,26 +264,25 @@ public class GenerationCommands { ) @CommandPermissions("worldedit.generation.shape") @Logging(ALL) - public void generate(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final Pattern pattern = we.getBlockPattern(player, args.getString(0)); - final Region region = session.getSelection(player.getWorld()); - - final boolean hollow = args.hasFlag('h'); - - final String expression = args.getJoinedStrings(1); + public void generate(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + Pattern pattern, + @Text String expression, + @Switch('h') boolean hollow, + @Switch('r') boolean useRawCoords, + @Switch('o') boolean offset, + @Switch('c') boolean offsetCenter) throws WorldEditException { final Vector zero; Vector unit; - if (args.hasFlag('r')) { + if (useRawCoords) { zero = Vector.ZERO; unit = Vector.ONE; - } else if (args.hasFlag('o')) { + } else if (offset) { zero = session.getPlacementPosition(player); unit = Vector.ONE; - } else if (args.hasFlag('c')) { + } else if (offsetCenter) { final Vector min = region.getMinimumPoint(); final Vector max = region.getMaximumPoint(); @@ -409,7 +301,7 @@ public class GenerationCommands { } try { - final int affected = editSession.makeShape(region, zero, unit, pattern, expression, hollow); + final int affected = editSession.makeShape(region, zero, unit, Patterns.wrap(pattern), expression, hollow); player.findFreePosition(); player.print(affected + " block(s) have been created."); } catch (ExpressionException e) { @@ -438,26 +330,24 @@ public class GenerationCommands { ) @CommandPermissions({"worldedit.generation.shape", "worldedit.biome.set"}) @Logging(ALL) - public void generateBiome(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final BiomeType target = we.getServer().getBiomes().get(args.getString(0)); - final Region region = session.getSelection(player.getWorld()); - - final boolean hollow = args.hasFlag('h'); - - final String expression = args.getJoinedStrings(1); - + public void generateBiome(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + BiomeType target, + @Text String expression, + @Switch('h') boolean hollow, + @Switch('r') boolean useRawCoords, + @Switch('o') boolean offset, + @Switch('c') boolean offsetCenter) throws WorldEditException { final Vector zero; Vector unit; - if (args.hasFlag('r')) { + if (useRawCoords) { zero = Vector.ZERO; unit = Vector.ONE; - } else if (args.hasFlag('o')) { + } else if (offset) { zero = session.getPlacementPosition(player); unit = Vector.ONE; - } else if (args.hasFlag('c')) { + } else if (offsetCenter) { final Vector min = region.getMinimumPoint(); final Vector max = region.getMaximumPoint(); @@ -483,4 +373,5 @@ public class GenerationCommands { player.printError(e.getMessage()); } } + } diff --git a/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java b/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java index 1a19bf4d1..eda112bbd 100644 --- a/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/HistoryCommands.java @@ -23,17 +23,25 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; + +import static com.google.common.base.Preconditions.checkNotNull; /** - * History little commands. - * - * @author sk89q + * Commands to undo, redo, and clear history. */ public class HistoryCommands { - private final WorldEdit we; - - public HistoryCommands(WorldEdit we) { - this.we = we; + + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public HistoryCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -44,9 +52,7 @@ public class HistoryCommands { max = 2 ) @CommandPermissions("worldedit.history.undo") - public void undo(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void undo(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int times = Math.max(1, args.getInteger(0, 1)); for (int i = 0; i < times; ++i) { EditSession undone; @@ -54,7 +60,7 @@ public class HistoryCommands { undone = session.undo(session.getBlockBag(player), player); } else { player.checkPermission("worldedit.history.undo.other"); - LocalSession sess = we.getSession(args.getString(1)); + LocalSession sess = worldEdit.getSession(args.getString(1)); if (sess == null) { player.printError("Unable to find session for " + args.getString(1)); break; @@ -63,7 +69,7 @@ public class HistoryCommands { } if (undone != null) { player.print("Undo successful."); - we.flushBlockBag(player, undone); + worldEdit.flushBlockBag(player, undone); } else { player.printError("Nothing left to undo."); break; @@ -79,8 +85,7 @@ public class HistoryCommands { max = 2 ) @CommandPermissions("worldedit.history.redo") - public void redo(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void redo(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int times = Math.max(1, args.getInteger(0, 1)); @@ -90,7 +95,7 @@ public class HistoryCommands { redone = session.redo(session.getBlockBag(player), player); } else { player.checkPermission("worldedit.history.redo.other"); - LocalSession sess = we.getSession(args.getString(1)); + LocalSession sess = worldEdit.getSession(args.getString(1)); if (sess == null) { player.printError("Unable to find session for " + args.getString(1)); break; @@ -99,7 +104,7 @@ public class HistoryCommands { } if (redone != null) { player.print("Redo successful."); - we.flushBlockBag(player, redone); + worldEdit.flushBlockBag(player, redone); } else { player.printError("Nothing left to redo."); } @@ -114,10 +119,9 @@ public class HistoryCommands { max = 0 ) @CommandPermissions("worldedit.history.clear") - public void clearHistory(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void clearHistory(Player player, LocalSession session, EditSession editSession) throws WorldEditException { session.clearHistory(); player.print("History cleared."); } + } diff --git a/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java b/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java index fd4bf7e82..ecfeef15d 100644 --- a/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java @@ -19,31 +19,32 @@ package com.sk89q.worldedit.command; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; - 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.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.WorldVector; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.POSITION; /** - * Navigation commands. - * - * @author sk89q + * Commands for moving the player around. */ public class NavigationCommands { - @SuppressWarnings("unused") - private final WorldEdit we; - public NavigationCommands(WorldEdit we) { - this.we = we; + @SuppressWarnings("unused") + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public NavigationCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -54,9 +55,7 @@ public class NavigationCommands { max = 0 ) @CommandPermissions("worldedit.navigation.unstuck") - public void unstuck(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - + public void unstuck(Player player) throws WorldEditException { player.print("There you go!"); player.findFreePosition(); } @@ -69,8 +68,7 @@ public class NavigationCommands { max = 1 ) @CommandPermissions("worldedit.navigation.ascend") - public void ascend(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void ascend(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int levelsToAscend = 0; if (args.argsLength() == 0) { levelsToAscend = 1; @@ -96,8 +94,7 @@ public class NavigationCommands { max = 1 ) @CommandPermissions("worldedit.navigation.descend") - public void descend(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void descend(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int levelsToDescend = 0; if (args.argsLength() == 0) { levelsToDescend = 1; @@ -125,8 +122,7 @@ public class NavigationCommands { ) @CommandPermissions("worldedit.navigation.ceiling") @Logging(POSITION) - public void ceiling(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void ceiling(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { final int clearance = args.argsLength() > 0 ? Math.max(0, args.getInteger(0)) : 0; @@ -147,8 +143,7 @@ public class NavigationCommands { max = 0 ) @CommandPermissions("worldedit.navigation.thru.command") - public void thru(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void thru(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { if (player.passThroughForwardWall(6)) { player.print("Whoosh!"); } else { @@ -164,8 +159,7 @@ public class NavigationCommands { max = 0 ) @CommandPermissions("worldedit.navigation.jumpto.command") - public void jumpTo(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void jumpTo(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { WorldVector pos = player.getSolidBlockTrace(300); if (pos != null) { @@ -186,8 +180,7 @@ public class NavigationCommands { ) @CommandPermissions("worldedit.navigation.up") @Logging(POSITION) - public void up(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void up(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { final int distance = args.getInteger(0); @@ -206,7 +199,7 @@ public class NavigationCommands { * @return true, if glass should always be put under the player */ private boolean getAlwaysGlass(CommandContext args) { - final LocalConfiguration config = we.getConfiguration(); + final LocalConfiguration config = worldEdit.getConfiguration(); final boolean forceFlight = args.hasFlag('f'); final boolean forceGlass = args.hasFlag('g'); diff --git a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index f33af5c10..67e285dcd 100644 --- a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -20,50 +20,61 @@ package com.sk89q.worldedit.command; 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.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.internal.expression.ExpressionException; -import com.sk89q.worldedit.function.mask.ExistingBlockMask; -import com.sk89q.worldedit.function.operation.Operations; -import com.sk89q.worldedit.math.convolution.GaussianKernel; -import com.sk89q.worldedit.math.convolution.HeightMap; -import com.sk89q.worldedit.math.convolution.HeightMapFilter; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.function.GroundFunction; import com.sk89q.worldedit.function.generator.FloraGenerator; import com.sk89q.worldedit.function.generator.ForestGenerator; -import com.sk89q.worldedit.function.visitor.LayerVisitor; -import com.sk89q.worldedit.masks.Mask; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.NoiseFilter2D; -import com.sk89q.worldedit.patterns.Pattern; -import com.sk89q.worldedit.patterns.SingleBlockPattern; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.Patterns; +import com.sk89q.worldedit.function.visitor.LayerVisitor; +import com.sk89q.worldedit.internal.annotation.Direction; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.math.convolution.GaussianKernel; +import com.sk89q.worldedit.math.convolution.HeightMap; +import com.sk89q.worldedit.math.convolution.HeightMapFilter; +import com.sk89q.worldedit.math.noise.RandomNoise; import com.sk89q.worldedit.regions.ConvexPolyhedralRegion; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.util.TreeGenerator; -import com.sk89q.worldedit.math.noise.RandomNoise; +import com.sk89q.worldedit.util.TreeGenerator.TreeType; +import com.sk89q.worldedit.util.command.binding.Range; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.sk89q.worldedit.util.command.binding.Text; +import com.sk89q.worldedit.util.command.parametric.Optional; import java.util.ArrayList; import java.util.List; -import java.util.Set; +import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.minecraft.util.commands.Logging.LogMode.*; import static com.sk89q.worldedit.regions.Regions.*; /** - * Region related commands. - * - * @author sk89q + * Commands that operate on regions. */ public class RegionCommands { - private final WorldEdit we; - public RegionCommands(WorldEdit we) { - this.we = we; + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public RegionCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -75,20 +86,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.set") @Logging(REGION) - public void set(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern pattern = we.getBlockPattern(player, args.getString(0)); - - int affected; - - if (pattern instanceof SingleBlockPattern) { - affected = editSession.setBlocks(session.getSelection(player.getWorld()), - ((SingleBlockPattern) pattern).getBlock()); - } else { - affected = editSession.setBlocks(session.getSelection(player.getWorld()), pattern); - } - + public void set(Player player, LocalSession session, EditSession editSession, Pattern pattern) throws WorldEditException { + int affected = editSession.setBlocks(session.getSelection(player.getWorld()), Patterns.wrap(pattern)); player.print(affected + " block(s) have been changed."); } @@ -107,24 +106,21 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.line") @Logging(REGION) - public void line(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void line(Player player, EditSession editSession, + @Selection Region region, + Pattern pattern, + @Optional("0") @Range(min = 0) int thickness, + @Switch('h') boolean shell) throws WorldEditException { - Region region = session.getSelection(session.getSelectionWorld()); if (!(region instanceof CuboidRegion)) { - player.printError("Invalid region type"); - return; - } - if (args.argsLength() < 2 ? false : args.getDouble(1) < 0) { - player.printError("Invalid thickness. Must not be negative"); + player.printError("//line only works with cuboid selections"); return; } - Pattern pattern = we.getBlockPattern(player, args.getString(0)); CuboidRegion cuboidregion = (CuboidRegion) region; Vector pos1 = cuboidregion.getPos1(); Vector pos2 = cuboidregion.getPos2(); - int blocksChanged = editSession.drawLine(pattern, pos1, pos2, args.argsLength() < 2 ? 0 : args.getDouble(1), !args.hasFlag('h')); + int blocksChanged = editSession.drawLine(Patterns.wrap(pattern), pos1, pos2, thickness, !shell); player.print(blocksChanged + " block(s) have been changed."); } @@ -144,24 +140,20 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.curve") @Logging(REGION) - public void curve(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Region region = session.getSelection(session.getSelectionWorld()); + public void curve(Player player, EditSession editSession, + @Selection Region region, + Pattern pattern, + @Optional("0") @Range(min = 0) int thickness, + @Switch('h') boolean shell) throws WorldEditException { if (!(region instanceof ConvexPolyhedralRegion)) { - player.printError("Invalid region type"); - return; - } - if (args.argsLength() < 2 ? false : args.getDouble(1) < 0) { - player.printError("Invalid thickness. Must not be negative"); + player.printError("//line only works with convex polyhedral selections"); return; } - Pattern pattern = we.getBlockPattern(player, args.getString(0)); ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; List vectors = new ArrayList(cpregion.getVertices()); - int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, args.argsLength() < 2 ? 0 : args.getDouble(1), !args.hasFlag('h')); + int blocksChanged = editSession.drawSpline(Patterns.wrap(pattern), vectors, 0, 0, 0, 10, thickness, !shell); player.print(blocksChanged + " block(s) have been changed."); } @@ -176,27 +168,11 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.replace") @Logging(REGION) - public void replace(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Set from; - Pattern to; - if (args.argsLength() == 1) { - from = null; - to = we.getBlockPattern(player, args.getString(0)); - } else { - from = we.getBlocks(player, args.getString(0), true, !args.hasFlag('f')); - to = we.getBlockPattern(player, args.getString(1)); + public void replace(Player player, EditSession editSession, @Selection Region region, @Optional Mask from, Pattern to) throws WorldEditException { + if (from == null) { + from = new ExistingBlockMask(editSession); } - - final int affected; - if (to instanceof SingleBlockPattern) { - affected = editSession.replaceBlocks(session.getSelection(player.getWorld()), from, - ((SingleBlockPattern) to).getBlock()); - } else { - affected = editSession.replaceBlocks(session.getSelection(player.getWorld()), from, to); - } - + int affected = editSession.replaceBlocks(region, from, Patterns.wrap(to)); player.print(affected + " block(s) have been replaced."); } @@ -209,20 +185,9 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.overlay") @Logging(REGION) - public void overlay(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Pattern pat = we.getBlockPattern(player, args.getString(0)); - - Region region = session.getSelection(player.getWorld()); - int affected = 0; - if (pat instanceof SingleBlockPattern) { - affected = editSession.overlayCuboidBlocks(region, - ((SingleBlockPattern) pat).getBlock()); - } else { - affected = editSession.overlayCuboidBlocks(region, pat); - } - player.print(affected + " block(s) have been overlayed."); + public void overlay(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.overlayCuboidBlocks(region, Patterns.wrap(pattern)); + player.print(affected + " block(s) have been overlaid."); } @Command( @@ -234,12 +199,8 @@ public class RegionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.region.center") - public void center(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - Pattern pattern = we.getBlockPattern(player, args.getString(0)); - Region region = session.getSelection(player.getWorld()); - - int affected = editSession.center(region, pattern); + public void center(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.center(region, Patterns.wrap(pattern)); player.print("Center set ("+ affected + " blocks changed)"); } @@ -252,12 +213,9 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.naturalize") @Logging(REGION) - public void naturalize(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Region region = session.getSelection(player.getWorld()); + public void naturalize(Player player, EditSession editSession, @Selection Region region) throws WorldEditException { int affected = editSession.naturalizeCuboidBlocks(region); - player.print(affected + " block(s) have been naturalized."); + player.print(affected + " block(s) have been made to look more natural."); } @Command( @@ -269,20 +227,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.walls") @Logging(REGION) - public void walls(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final Pattern pattern = we.getBlockPattern(player, args.getString(0)); - final int affected; - final Region region = session.getSelection(player.getWorld()); - if (!(region instanceof CuboidRegion)) { - affected = editSession.makeWalls(region, pattern); - } else if (pattern instanceof SingleBlockPattern) { - affected = editSession.makeCuboidWalls(region, ((SingleBlockPattern) pattern).getBlock()); - } else { - affected = editSession.makeCuboidWalls(region, pattern); - } - + public void walls(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.makeCuboidWalls(region, Patterns.wrap(pattern)); player.print(affected + " block(s) have been changed."); } @@ -295,20 +241,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.faces") @Logging(REGION) - public void faces(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final Pattern pattern = we.getBlockPattern(player, args.getString(0)); - final int affected; - final Region region = session.getSelection(player.getWorld()); - if (!(region instanceof CuboidRegion)) { - affected = editSession.makeFaces(region, pattern); - } else if (pattern instanceof SingleBlockPattern) { - affected = editSession.makeCuboidFaces(region, ((SingleBlockPattern) pattern).getBlock()); - } else { - affected = editSession.makeCuboidFaces(region, pattern); - } - + public void faces(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + int affected = editSession.makeCuboidFaces(region, Patterns.wrap(pattern)); player.print(affected + " block(s) have been changed."); } @@ -325,15 +259,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.smooth") @Logging(REGION) - public void smooth(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - int iterations = 1; - if (args.argsLength() > 0) { - iterations = args.getInteger(0); - } - - HeightMap heightMap = new HeightMap(editSession, session.getSelection(player.getWorld()), args.hasFlag('n')); + public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations, @Switch('n') boolean affectNatural) throws WorldEditException { + HeightMap heightMap = new HeightMap(editSession, region, affectNatural); HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0)); int affected = heightMap.applyFilter(filter, iterations); player.print("Terrain's height map smoothed. " + affected + " block(s) changed."); @@ -354,28 +281,18 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.move") @Logging(ORIENTATION_REGION) - public void move(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void move(Player player, EditSession editSession, LocalSession session, + @Selection Region region, + @Optional("1") @Range(min = 1) int count, + @Optional(Direction.AIM) @Direction Vector direction, + @Optional("air") BaseBlock replace, + @Switch('s') boolean moveSelection) throws WorldEditException { - int count = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 1; - Vector dir = we.getDirection(player, - args.argsLength() > 1 ? args.getString(1).toLowerCase() : "me"); - BaseBlock replace; + int affected = editSession.moveRegion(region, direction, count, true, replace); - // Replacement block argument - if (args.argsLength() > 2) { - replace = we.getBlock(player, args.getString(2)); - } else { - replace = new BaseBlock(BlockID.AIR); - } - - int affected = editSession.moveRegion(session.getSelection(player.getWorld()), - dir, count, true, replace); - - if (args.hasFlag('s')) { + if (moveSelection) { try { - Region region = session.getSelection(player.getWorld()); - region.shift(dir.multiply(count)); + region.shift(direction.multiply(count)); session.getRegionSelector(player.getWorld()).learnChanges(); session.getRegionSelector(player.getWorld()).explainRegionAdjust(player, session); @@ -402,22 +319,19 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.stack") @Logging(ORIENTATION_REGION) - public void stack(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void stack(Player player, EditSession editSession, LocalSession session, + @Selection Region region, + @Optional("1") @Range(min = 1) int count, + @Optional(Direction.AIM) @Direction Vector direction, + @Switch('s') boolean moveSelection, + @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { + int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); - int count = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 1; - Vector dir = we.getDiagonalDirection(player, - args.argsLength() > 1 ? args.getString(1).toLowerCase() : "me"); - - int affected = editSession.stackCuboidRegion(session.getSelection(player.getWorld()), - dir, count, !args.hasFlag('a')); - - if (args.hasFlag('s')) { + if (ignoreAirBlocks) { try { - final Region region = session.getSelection(player.getWorld()); final Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()); - final Vector shiftVector = dir.multiply(count * (Math.abs(dir.dot(size)) + 1)); + final Vector shiftVector = direction.multiply(count * (Math.abs(direction.dot(size)) + 1)); region.shift(shiftVector); session.getRegionSelector(player.getWorld()).learnChanges(); @@ -443,14 +357,14 @@ public class RegionCommands { ) @CommandPermissions("worldedit.regen") @Logging(REGION) - public void regenerateChunk(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - Region region = session.getSelection(player.getWorld()); + public void regenerateChunk(Player player, LocalSession session, EditSession editSession, @Selection Region region) throws WorldEditException { Mask mask = session.getMask(); - session.setMask(null); - player.getWorld().regenerate(region, editSession); - session.setMask(mask); + try { + session.setMask((Mask) null); + player.getWorld().regenerate(region, editSession); + } finally { + session.setMask(mask); + } player.print("Region regenerated."); } @@ -469,20 +383,18 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.deform") @Logging(ALL) - public void deform(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final Region region = session.getSelection(player.getWorld()); - - final String expression = args.getJoinedStrings(0); - + public void deform(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + @Text String expression, + @Switch('r') boolean useRawCoords, + @Switch('o') boolean offset) throws WorldEditException { final Vector zero; Vector unit; - if (args.hasFlag('r')) { + if (useRawCoords) { zero = Vector.ZERO; unit = Vector.ONE; - } else if (args.hasFlag('o')) { + } else if (offset) { zero = session.getPlacementPosition(player); unit = Vector.ONE; } else { @@ -519,14 +431,12 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.hollow") @Logging(REGION) - public void hollow(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - - final int thickness = args.argsLength() >= 1 ? Math.max(1, args.getInteger(0)) : 1; - final Pattern pattern = args.argsLength() >= 2 ? we.getBlockPattern(player, args.getString(1)) : new SingleBlockPattern(new BaseBlock(BlockID.AIR)); - - final int affected = editSession.hollowOutRegion(session.getSelection(player.getWorld()), thickness, pattern); + public void hollow(Player player, EditSession editSession, + @Selection Region region, + @Optional("0") @Range(min = 0) int thickness, + @Optional("air") Pattern pattern) throws WorldEditException { + int affected = editSession.hollowOutRegion(region, thickness, Patterns.wrap(pattern)); player.print(affected + " block(s) have been changed."); } @@ -539,18 +449,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.forest") @Logging(REGION) - public void forest(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { - TreeGenerator.TreeType type = args.argsLength() > 0 ? TreeGenerator.lookup(args.getString(0)) : TreeGenerator.TreeType.TREE; - double density = args.argsLength() > 1 ? args.getDouble(1) / 100 : 0.05; - - if (type == null) { - player.printError("Tree type '" + args.getString(0) + "' is unknown."); - return; - } - - Region region = session.getSelection(player.getWorld()); - + public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, @Optional("5") double density) throws WorldEditException { + density = density / 100; ForestGenerator generator = new ForestGenerator(editSession, new TreeGenerator(type)); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); @@ -569,10 +469,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.flora") @Logging(REGION) - public void flora(CommandContext args, LocalSession session, LocalPlayer player, EditSession editSession) throws WorldEditException { - double density = args.argsLength() > 0 ? args.getDouble(0) / 100 : 0.1; - - Region region = session.getSelection(player.getWorld()); + public void flora(Player player, EditSession editSession, @Selection Region region, @Optional("10") double density) throws WorldEditException { + density = density / 100; FloraGenerator generator = new FloraGenerator(editSession); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground); diff --git a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index c45a8b7d5..61ea46fb8 100644 --- a/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.*; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.schematic.SchematicFormat; import com.sk89q.worldedit.world.DataException; @@ -31,16 +32,23 @@ import java.io.IOException; import java.util.Arrays; import java.util.Comparator; +import static com.google.common.base.Preconditions.checkNotNull; + /** - * Commands related to schematics - * - * @see com.sk89q.worldedit.command.ClipboardCommands#schematic() + * Commands that work with schematic files. */ public class SchematicCommands { - private final WorldEdit we; - public SchematicCommands(WorldEdit we) { - this.we = we; + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public SchematicCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -56,10 +64,9 @@ public class SchematicCommands { max = 2 ) @CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load"}) // TODO: Remove 'clipboard' perm - public void load(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void load(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - LocalConfiguration config = we.getConfiguration(); + LocalConfiguration config = worldEdit.getConfiguration(); String fileName; String formatName; @@ -70,8 +77,8 @@ public class SchematicCommands { formatName = args.getString(0); fileName = args.getString(1); } - File dir = we.getWorkingDirectoryFile(config.saveDir); - File f = we.getSafeOpenFile(player, dir, fileName, "schematic", "schematic"); + File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); + File f = worldEdit.getSafeOpenFile(player, dir, fileName, "schematic", "schematic"); if (!f.exists()) { player.printError("Schematic " + fileName + " does not exist!"); @@ -121,10 +128,9 @@ public class SchematicCommands { max = 2 ) @CommandPermissions({"worldedit.clipboard.save", "worldedit.schematic.save"}) // TODO: Remove 'clipboard' perm - public void save(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException, CommandException { + public void save(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException, CommandException { - LocalConfiguration config = we.getConfiguration(); + LocalConfiguration config = worldEdit.getConfiguration(); SchematicFormat format; if (args.argsLength() == 1) { if (SchematicFormat.getFormats().size() == 1) { @@ -143,8 +149,8 @@ public class SchematicCommands { String filename = args.getString(args.argsLength() - 1); - File dir = we.getWorkingDirectoryFile(config.saveDir); - File f = we.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); + File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); + File f = worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); if (!dir.exists()) { if (!dir.mkdir()) { @@ -181,14 +187,13 @@ public class SchematicCommands { max = 1 ) @CommandPermissions("worldedit.schematic.delete") - public void delete(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void delete(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - LocalConfiguration config = we.getConfiguration(); + LocalConfiguration config = worldEdit.getConfiguration(); String filename = args.getString(0); - File dir = we.getWorkingDirectoryFile(config.saveDir); - File f = we.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); + File dir = worldEdit.getWorkingDirectoryFile(config.saveDir); + File f = worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic"); if (!f.exists()) { player.printError("Schematic " + filename + " does not exist!"); @@ -239,7 +244,7 @@ public class SchematicCommands { ) @CommandPermissions("worldedit.schematic.list") public void list(Actor actor, CommandContext args) throws WorldEditException { - File dir = we.getWorkingDirectoryFile(we.getConfiguration().saveDir); + File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().saveDir); File[] files = dir.listFiles(new FileFilter(){ @Override public boolean accept(File file) { diff --git a/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java b/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java index 1949bc0f2..3cb190e15 100644 --- a/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ScriptingCommands.java @@ -19,24 +19,36 @@ package com.sk89q.worldedit.command; -import java.io.File; 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.worldedit.*; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Player; + +import java.io.File; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.ALL; /** - * Scripting commands. - * - * @author sk89q + * Commands related to scripting. */ public class ScriptingCommands { - private final WorldEdit we; - public ScriptingCommands(WorldEdit we) { - this.we = we; + private final WorldEdit worldEdit; + + /** + * Create a new instance. + * + * @param worldEdit reference to WorldEdit + */ + public ScriptingCommands(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; } @Command( @@ -48,8 +60,7 @@ public class ScriptingCommands { ) @CommandPermissions("worldedit.scripting.execute") @Logging(ALL) - public void execute(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void execute(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { String[] scriptArgs = args.getSlice(1); String name = args.getString(0); @@ -61,10 +72,10 @@ public class ScriptingCommands { session.setLastScript(name); - File dir = we.getWorkingDirectoryFile(we.getConfiguration().scriptsDir); - File f = we.getSafeOpenFile(player, dir, name, "js", "js"); + File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().scriptsDir); + File f = worldEdit.getSafeOpenFile(player, dir, name, "js", "js"); - we.runScript(player, f, scriptArgs); + worldEdit.runScript(player, f, scriptArgs); } @Command( @@ -76,8 +87,7 @@ public class ScriptingCommands { ) @CommandPermissions("worldedit.scripting.execute") @Logging(ALL) - public void executeLast(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void executeLast(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { String lastScript = session.getLastScript(); @@ -93,9 +103,9 @@ public class ScriptingCommands { String[] scriptArgs = args.getSlice(0); - File dir = we.getWorkingDirectoryFile(we.getConfiguration().scriptsDir); - File f = we.getSafeOpenFile(player, dir, lastScript, "js", "js"); + File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().scriptsDir); + File f = worldEdit.getSafeOpenFile(player, dir, lastScript, "js", "js"); - we.runScript(player, f, scriptArgs); + worldEdit.runScript(player, f, scriptArgs); } } diff --git a/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index 3e9f29faa..20cfba116 100644 --- a/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -31,6 +31,7 @@ import com.sk89q.minecraft.util.commands.CommandAlias; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.Logging; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.CuboidClipboard; import com.sk89q.worldedit.EditSession; @@ -76,8 +77,7 @@ public class SelectionCommands { ) @Logging(POSITION) @CommandPermissions("worldedit.selection.pos") - public void pos1(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void pos1(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Vector pos; @@ -111,8 +111,7 @@ public class SelectionCommands { ) @Logging(POSITION) @CommandPermissions("worldedit.selection.pos") - public void pos2(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void pos2(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Vector pos; if (args.argsLength() == 1) { @@ -146,8 +145,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.selection.hpos") - public void hpos1(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void hpos1(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Vector pos = player.getBlockTrace(300); @@ -173,8 +171,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.selection.hpos") - public void hpos2(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void hpos2(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Vector pos = player.getBlockTrace(300); @@ -210,8 +207,7 @@ public class SelectionCommands { ) @Logging(POSITION) @CommandPermissions("worldedit.selection.chunk") - public void chunk(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void chunk(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { final Vector min; final Vector max; @@ -274,8 +270,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.wand") - public void wand(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void wand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { player.giveItem(we.getConfiguration().wandItem, 1); player.print("Left click: select pos #1; Right click: select pos #2"); @@ -289,8 +284,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.wand.toggle") - public void toggleWand(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void toggleWand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setToolControl(!session.isToolControlEnabled()); @@ -310,8 +304,7 @@ public class SelectionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.selection.expand") - public void expand(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void expand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { // Special syntax (//expand vert) to expand the selection between // sky and bedrock. @@ -406,8 +399,7 @@ public class SelectionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.selection.contract") - public void contract(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void contract(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { List dirs = new ArrayList(); int change = args.getInteger(0); @@ -482,8 +474,7 @@ public class SelectionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.selection.shift") - public void shift(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void shift(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { List dirs = new ArrayList(); int change = args.getInteger(0); @@ -531,8 +522,7 @@ public class SelectionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.selection.outset") - public void outset(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void outset(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Region region = session.getSelection(player.getWorld()); region.expand(getChangesForEachDir(args)); session.getRegionSelector(player.getWorld()).learnChanges(); @@ -555,8 +545,7 @@ public class SelectionCommands { ) @Logging(REGION) @CommandPermissions("worldedit.selection.inset") - public void inset(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void inset(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Region region = session.getSelection(player.getWorld()); region.contract(getChangesForEachDir(args)); session.getRegionSelector(player.getWorld()).learnChanges(); @@ -592,8 +581,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.selection.size") - public void size(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { if (args.hasFlag('c')) { CuboidClipboard clipboard = session.getClipboard(); @@ -637,8 +625,7 @@ public class SelectionCommands { max = 1 ) @CommandPermissions("worldedit.analysis.count") - public void count(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void count(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { boolean useData = args.hasFlag('d'); if (args.getString(0).contains(":")) { @@ -668,8 +655,7 @@ public class SelectionCommands { max = 0 ) @CommandPermissions("worldedit.analysis.distr") - public void distr(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void distr(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int size; boolean useData = args.hasFlag('d'); @@ -730,8 +716,7 @@ public class SelectionCommands { min = 0, max = 1 ) - public void select(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void select(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { final World world = player.getWorld(); if (args.argsLength() == 0) { diff --git a/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java b/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java index 74d461d32..35641f38f 100644 --- a/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SnapshotCommands.java @@ -21,6 +21,15 @@ package com.sk89q.worldedit.command; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.world.snapshot.InvalidSnapshotException; +import com.sk89q.worldedit.world.snapshot.Snapshot; +import com.sk89q.worldedit.world.storage.MissingWorldException; + import java.io.File; import java.io.IOException; import java.text.DateFormat; @@ -28,13 +37,6 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.List; import java.util.logging.Logger; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.world.storage.MissingWorldException; -import com.sk89q.worldedit.world.snapshot.InvalidSnapshotException; -import com.sk89q.worldedit.world.snapshot.Snapshot; /** * Snapshot commands. @@ -59,8 +61,7 @@ public class SnapshotCommands { max = 1 ) @CommandPermissions("worldedit.snapshots.list") - public void list(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void list(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -110,8 +111,7 @@ public class SnapshotCommands { max = 1 ) @CommandPermissions("worldedit.snapshots.restore") - public void use(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void use(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -154,8 +154,7 @@ public class SnapshotCommands { max = 1 ) @CommandPermissions("worldedit.snapshots.restore") - public void sel(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void sel(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); if (config.snapshotRepo == null) { @@ -202,8 +201,7 @@ public class SnapshotCommands { max = -1 ) @CommandPermissions("worldedit.snapshots.restore") - public void before(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void before(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); @@ -242,8 +240,7 @@ public class SnapshotCommands { max = -1 ) @CommandPermissions("worldedit.snapshots.restore") - public void after(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void after(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); diff --git a/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java b/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java index b0f978438..d6dd4ba89 100644 --- a/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SnapshotUtilCommands.java @@ -19,30 +19,25 @@ package com.sk89q.worldedit.command; -import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; +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.worldedit.*; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.DataException; +import com.sk89q.worldedit.world.snapshot.InvalidSnapshotException; +import com.sk89q.worldedit.world.snapshot.Snapshot; +import com.sk89q.worldedit.world.snapshot.SnapshotRestore; +import com.sk89q.worldedit.world.storage.ChunkStore; +import com.sk89q.worldedit.world.storage.MissingWorldException; import java.io.File; import java.io.IOException; import java.util.logging.Logger; -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; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.world.storage.ChunkStore; -import com.sk89q.worldedit.world.DataException; -import com.sk89q.worldedit.world.storage.MissingWorldException; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.world.snapshot.InvalidSnapshotException; -import com.sk89q.worldedit.world.snapshot.Snapshot; -import com.sk89q.worldedit.world.snapshot.SnapshotRestore; +import static com.sk89q.minecraft.util.commands.Logging.LogMode.REGION; public class SnapshotUtilCommands { @@ -63,8 +58,7 @@ public class SnapshotUtilCommands { ) @Logging(REGION) @CommandPermissions("worldedit.snapshots.restore") - public void restore(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void restore(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); diff --git a/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java b/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java index 249671732..116d71a81 100644 --- a/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SuperPickaxeCommands.java @@ -22,15 +22,11 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; -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.*; import com.sk89q.worldedit.command.tool.AreaPickaxe; import com.sk89q.worldedit.command.tool.RecursivePickaxe; import com.sk89q.worldedit.command.tool.SinglePickaxe; +import com.sk89q.worldedit.entity.Player; public class SuperPickaxeCommands { private final WorldEdit we; @@ -47,8 +43,7 @@ public class SuperPickaxeCommands { max = 0 ) @CommandPermissions("worldedit.superpickaxe") - public void single(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void single(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setSuperPickaxe(new SinglePickaxe()); session.enableSuperPickAxe(); @@ -63,8 +58,7 @@ public class SuperPickaxeCommands { max = 1 ) @CommandPermissions("worldedit.superpickaxe.area") - public void area(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void area(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); int range = args.getInteger(0); @@ -87,8 +81,7 @@ public class SuperPickaxeCommands { max = 1 ) @CommandPermissions("worldedit.superpickaxe.recursive") - public void recursive(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void recursive(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); double range = args.getDouble(0); diff --git a/src/main/java/com/sk89q/worldedit/command/ToolCommands.java b/src/main/java/com/sk89q/worldedit/command/ToolCommands.java index 4279f0ce6..60a569ff8 100644 --- a/src/main/java/com/sk89q/worldedit/command/ToolCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ToolCommands.java @@ -26,6 +26,7 @@ import com.sk89q.minecraft.util.commands.NestedCommand; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.command.tool.*; import com.sk89q.worldedit.util.TreeGenerator; @@ -44,8 +45,7 @@ public class ToolCommands { min = 0, max = 0 ) - public void none(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void none(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setTool(player.getItemInHand(), null); player.print("Tool unbound from your current item."); @@ -59,8 +59,7 @@ public class ToolCommands { max = 0 ) @CommandPermissions("worldedit.tool.info") - public void info(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void info(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setTool(player.getItemInHand(), new QueryTool()); player.print("Info tool bound to " @@ -76,8 +75,7 @@ public class ToolCommands { ) @CommandPermissions("worldedit.tool.tree") @SuppressWarnings("deprecation") - public void tree(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void tree(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { TreeGenerator.TreeType type = args.argsLength() > 0 ? type = TreeGenerator.lookup(args.getString(0)) @@ -101,8 +99,7 @@ public class ToolCommands { max = 1 ) @CommandPermissions("worldedit.tool.replacer") - public void repl(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void repl(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { BaseBlock targetBlock = we.getBlock(player, args.getString(0)); session.setTool(player.getItemInHand(), new BlockReplacer(targetBlock)); @@ -118,8 +115,7 @@ public class ToolCommands { max = 0 ) @CommandPermissions("worldedit.tool.data-cycler") - public void cycler(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void cycler(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setTool(player.getItemInHand(), new BlockDataCyler()); player.print("Block data cycler tool bound to " @@ -134,8 +130,7 @@ public class ToolCommands { max = 2 ) @CommandPermissions("worldedit.tool.flood-fill") - public void floodFill(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void floodFill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); int range = args.getInteger(1); @@ -159,8 +154,7 @@ public class ToolCommands { max = 0 ) @CommandPermissions("worldedit.tool.deltree") - public void deltree(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void deltree(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setTool(player.getItemInHand(), new FloatingTreeRemover()); player.print("Floating tree remover tool bound to " @@ -175,8 +169,7 @@ public class ToolCommands { max = 0 ) @CommandPermissions("worldedit.tool.farwand") - public void farwand(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void farwand(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setTool(player.getItemInHand(), new DistanceWand()); player.print("Far wand tool bound to " + ItemType.toHeldName(player.getItemInHand()) + "."); @@ -190,8 +183,7 @@ public class ToolCommands { max = 2 ) @CommandPermissions("worldedit.tool.lrbuild") - public void longrangebuildtool(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void longrangebuildtool(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { BaseBlock secondary = we.getBlock(player, args.getString(0)); BaseBlock primary = we.getBlock(player, args.getString(1)); diff --git a/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index dab90dc25..5ba04745f 100644 --- a/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -46,8 +46,7 @@ public class ToolUtilCommands { max = 1 ) @CommandPermissions("worldedit.superpickaxe") - public void togglePickaxe(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void togglePickaxe(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { String newState = args.getString(0, null); if (session.hasSuperPickAxe()) { @@ -108,8 +107,7 @@ public class ToolUtilCommands { max = 1 ) @CommandPermissions("worldedit.brush.options.range") - public void range(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void range(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int range = args.getInteger(0); session.getBrushTool(player.getItemInHand()).setRange(range); player.print("Brush range set."); @@ -123,8 +121,7 @@ public class ToolUtilCommands { max = 1 ) @CommandPermissions("worldedit.brush.options.size") - public void size(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void size(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int radius = args.getInteger(0); we.checkMaxBrushRadius(radius); diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index de498d24b..8b4999e67 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -63,8 +63,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fill") @Logging(PLACEMENT) - public void fill(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void fill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Pattern pattern = we.getBlockPattern(player, args.getString(0)); double radius = Math.max(1, args.getDouble(1)); @@ -92,8 +91,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fill.recursive") @Logging(PLACEMENT) - public void fillr(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void fillr(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { Pattern pattern = we.getBlockPattern(player, args.getString(0)); double radius = Math.max(1, args.getDouble(1)); @@ -121,8 +119,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.drain") @Logging(PLACEMENT) - public void drain(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void drain(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { double radius = Math.max(0, args.getDouble(0)); we.checkMaxRadius(radius); @@ -140,8 +137,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fixlava") @Logging(PLACEMENT) - public void fixLava(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void fixLava(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { double radius = Math.max(0, args.getDouble(0)); we.checkMaxRadius(radius); @@ -159,8 +155,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.fixwater") @Logging(PLACEMENT) - public void fixWater(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void fixWater(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { double radius = Math.max(0, args.getDouble(0)); we.checkMaxRadius(radius); @@ -178,8 +173,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.removeabove") @Logging(PLACEMENT) - public void removeAbove(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void removeAbove(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int size = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 1; we.checkMaxRadius(size); @@ -200,8 +194,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.removebelow") @Logging(PLACEMENT) - public void removeBelow(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void removeBelow(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int size = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) : 1; we.checkMaxRadius(size); @@ -221,8 +214,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.removenear") @Logging(PLACEMENT) - public void removeNear(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void removeNear(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { BaseBlock block = we.getBlock(player, args.getString(0), true); int size = Math.max(1, args.getInteger(1, 50)); @@ -242,8 +234,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.replacenear") @Logging(PLACEMENT) - public void replaceNear(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void replaceNear(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { int size = Math.max(1, args.getInteger(0)); int affected; @@ -279,8 +270,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.snow") @Logging(PLACEMENT) - public void snow(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void snow(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; @@ -297,8 +287,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.thaw") @Logging(PLACEMENT) - public void thaw(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void thaw(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; @@ -316,8 +305,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.green") @Logging(PLACEMENT) - public void green(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void green(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { final double size = args.argsLength() > 0 ? Math.max(1, args.getDouble(0)) : 10; final boolean onlyNormalDirt = !args.hasFlag('f'); @@ -335,8 +323,7 @@ public class UtilityCommands { ) @CommandPermissions("worldedit.extinguish") @Logging(PLACEMENT) - public void extinguish(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void extinguish(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); diff --git a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index 78d1af9df..dfdbf36f1 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -23,6 +23,7 @@ import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; @@ -87,8 +88,7 @@ public class WorldEditCommands { min = 0, max = 0 ) - public void cui(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void cui(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { session.setCUISupport(true); session.dispatchCUISetup(player); } @@ -100,8 +100,7 @@ public class WorldEditCommands { min = 1, max = 1 ) - public void tz(CommandContext args, LocalSession session, LocalPlayer player, - EditSession editSession) throws WorldEditException { + public void tz(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { TimeZone tz = TimeZone.getTimeZone(args.getString(0)); session.setTimezone(tz); player.print("Timezone set for this session to: " + tz.getDisplayName()); diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index 8d881f5a5..9d5210496 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.internal.command; import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.ParserContext; @@ -29,8 +30,12 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.Selection; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.util.command.parametric.*; +import java.util.Arrays; + /** * Binds standard WorldEdit classes such as {@link Player} and {@link LocalSession}. */ @@ -137,22 +142,25 @@ public class WorldEditBinding extends BindingHelper { } /** - * Gets an {@link Player} from a {@link ArgumentStack}. + * Gets an {@link BaseBlock} from a {@link ArgumentStack}. * * @param context the context - * @return a local player + * @return a pattern * @throws ParameterException on error + * @throws WorldEditException on error */ - @SuppressWarnings("deprecation") - @BindingMatch(type = LocalPlayer.class, - behavior = BindingBehavior.PROVIDES) - public Player getLocalPlayer(ArgumentStack context) throws ParameterException { - Player player = getPlayer(context); - if (player instanceof LocalPlayer) { - return (LocalPlayer) player; - } else { - throw new ParameterException("This command/function needs to be updated to take 'Player' rather than 'LocalPlayer'"); + @BindingMatch(type = BaseBlock.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public BaseBlock getBaseBlock(ArgumentStack context) throws ParameterException, WorldEditException { + Actor actor = context.getContext().getLocals().get(Actor.class); + ParserContext parserContext = new ParserContext(); + parserContext.setActor(context.getContext().getLocals().get(Actor.class)); + if (actor instanceof Entity) { + parserContext.setWorld(((Entity) actor).getWorld()); } + parserContext.setSession(worldEdit.getSessionManager().get(actor)); + return worldEdit.getBlockRegistry().parseFromInput(context.next(), parserContext); } /** @@ -218,4 +226,58 @@ public class WorldEditBinding extends BindingHelper { return worldEdit.getDirection(sender, context.next()); } + /** + * Gets an {@link TreeType} from a {@link ArgumentStack}. + * + * @param context the context + * @return a pattern + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @BindingMatch(type = TreeType.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public TreeType getTreeType(ArgumentStack context) throws ParameterException, WorldEditException { + String input = context.next(); + if (input != null) { + TreeType type = TreeGenerator.lookup(input); + if (type != null) { + return type; + } else { + throw new ParameterException( + String.format("Can't recognize tree type '%s' -- choose from: %s", input, Arrays.toString(TreeType.values()))); + } + } else { + return TreeType.TREE; + } + } + + /** + * Gets an {@link BiomeType} from a {@link ArgumentStack}. + * + * @param context the context + * @return a pattern + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @BindingMatch(type = BiomeType.class, + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) + public BiomeType getBiomeType(ArgumentStack context) throws ParameterException, WorldEditException { + String input = context.next(); + if (input != null) { + BiomeType type = worldEdit.getServer().getBiomes().get(input); + if (type != null) { + return type; + } else { + throw new ParameterException( + String.format("Can't recognize biome type '%s' -- use //biomelist to list available types", input)); + } + } else { + throw new ParameterException( + "This command takes a 'default' biome if one is not set, except there is no particular " + + "biome that should be 'default', so the command should not be taking a default biome"); + } + } + } diff --git a/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java b/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java index 827dda8aa..f1cfaf157 100644 --- a/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java +++ b/src/main/java/com/sk89q/worldedit/scripting/CraftScriptContext.java @@ -19,24 +19,18 @@ package com.sk89q.worldedit.scripting; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.command.InsufficientArgumentsException; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.patterns.Pattern; + import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; -import com.sk89q.worldedit.DisallowedItemException; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.FilenameException; -import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.ServerInterface; -import com.sk89q.worldedit.UnknownItemException; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.command.InsufficientArgumentsException; -import com.sk89q.worldedit.patterns.Pattern; /** * The context given to scripts. @@ -48,8 +42,8 @@ public class CraftScriptContext extends CraftScriptEnvironment { private String[] args; public CraftScriptContext(WorldEdit controller, - ServerInterface server, LocalConfiguration config, - LocalSession session, LocalPlayer player, String[] args) { + Platform server, LocalConfiguration config, + LocalSession session, Player player, String[] args) { super(controller, server, config, session, player); this.args = args; } @@ -74,7 +68,7 @@ public class CraftScriptContext extends CraftScriptEnvironment { * * @return */ - public LocalPlayer getPlayer() { + public Player getPlayer() { return player; } diff --git a/src/main/java/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java b/src/main/java/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java index b437bc005..68c145b4c 100644 --- a/src/main/java/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java +++ b/src/main/java/com/sk89q/worldedit/scripting/CraftScriptEnvironment.java @@ -20,24 +20,25 @@ package com.sk89q.worldedit.scripting; import com.sk89q.worldedit.LocalConfiguration; -import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.ServerInterface; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Platform; public abstract class CraftScriptEnvironment { + protected WorldEdit controller; - protected LocalPlayer player; + protected Player player; protected LocalConfiguration config; protected LocalSession session; - protected ServerInterface server; + protected Platform server; - public CraftScriptEnvironment(WorldEdit controller, ServerInterface server, - LocalConfiguration config, LocalSession session, LocalPlayer player) { + public CraftScriptEnvironment(WorldEdit controller, Platform server, LocalConfiguration config, LocalSession session, Player player) { this.controller = controller; this.player = player; this.config = config; this.server = server; this.session = session; } + } From 225851310421a2130f5cd16ca52f994257da476c Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 01:42:59 -0700 Subject: [PATCH 30/45] Made Dispatcher implement CommandCallable. This simplifies things a bit. --- .../extension/platform/CommandManager.java | 5 +- .../util/command/CommandCallable.java | 24 ++-- .../util/command/CommandMapping.java | 9 ++ .../worldedit/util/command/Dispatcher.java | 34 +----- .../util/command/SimpleDispatcher.java | 104 +++++++++++++----- .../util/command/SimpleDispatcherCommand.java | 96 ---------------- .../util/command/fluent/DispatcherNode.java | 8 +- .../parametric/ParametricCallable.java | 30 +++-- 8 files changed, 126 insertions(+), 184 deletions(-) delete mode 100644 src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java 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 0d068e687..a3e506bdb 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.platform; +import com.google.common.base.Joiner; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.minecraft.util.commands.CommandPermissionsException; @@ -29,11 +30,11 @@ 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.session.request.Request; 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; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.InvalidUsageException; import com.sk89q.worldedit.util.command.fluent.CommandGraph; @@ -215,7 +216,7 @@ public final class CommandManager { long start = System.currentTimeMillis(); try { - dispatcher.call(split, locals); + dispatcher.call(Joiner.on(" ").join(split), locals); } catch (CommandPermissionsException e) { actor.printError("You don't have permission to do this."); } catch (InvalidUsageException e) { 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 4c0046ba0..0fd96670d 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java @@ -19,8 +19,8 @@ package com.sk89q.worldedit.util.command; -import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; import java.util.Collection; import java.util.Set; @@ -38,21 +38,23 @@ public interface CommandCallable { Set getValueFlags(); /** - * Execute the command. - * - * @param context the user input - * @throws CommandException thrown on any sort of command exception + * Execute the correct command based on the input. + * + * @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 */ - void call(CommandContext context) throws CommandException; + boolean call(String arguments, CommandLocals locals) throws CommandException; /** - * Get a list of suggestions. - * - * @param context the user input + * Get a list of suggestions based on input. + * + * @param arguments the arguments entered up to this point * @return a list of suggestions - * @throws CommandException + * @throws CommandException thrown if there was a parsing error */ - Collection getSuggestions(CommandContext context) throws CommandException; + Collection getSuggestions(String arguments) throws CommandException; /** * Get an object describing this command. diff --git a/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java index a6f973cb1..6e44bff17 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util.command; +import java.util.Arrays; /** * Tracks a command registration. @@ -76,4 +77,12 @@ public class CommandMapping { return getCallable().getDescription(); } + @Override + public String toString() { + return "CommandMapping{" + + "aliases=" + Arrays.toString(aliases) + + ", callable=" + callable + + '}'; + } + } diff --git a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java index 90beed347..00aa399e6 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java @@ -19,15 +19,12 @@ package com.sk89q.worldedit.util.command; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandLocals; - import java.util.Collection; /** * Executes a command based on user input. */ -public interface Dispatcher { +public interface Dispatcher extends CommandCallable { /** * Register a command with this dispatcher. @@ -81,33 +78,4 @@ public interface Dispatcher { */ boolean contains(String alias); - /** - * Execute the correct command based on the input. - * - * @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 - */ - CommandMapping call(String arguments, CommandLocals locals) throws CommandException; - - /** - * Execute the correct command based on the input. - * - * @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 - */ - CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException; - - /** - * Get a list of suggestions based on input. - * - * @param arguments the arguments entered up to this point - * @return a list of suggestions - * @throws CommandException thrown if there was a parsing error - */ - Collection getSuggestions(String arguments) throws CommandException; - } \ No newline at end of file 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 af379e7b4..888445017 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util.command; +import com.google.common.base.Joiner; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; @@ -32,6 +33,7 @@ import java.util.*; public class SimpleDispatcher implements Dispatcher { private final Map commands = new HashMap(); + private final SimpleDescription description = new SimpleDescription(); @Override public void register(CommandCallable callable, String... alias) { @@ -82,41 +84,93 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public CommandMapping call(String arguments, CommandLocals locals) throws CommandException { - return call(CommandContext.split(arguments), locals); + public Set getValueFlags() { + return Collections.emptySet(); } @Override - public CommandMapping call(String[] arguments, CommandLocals locals) throws CommandException { - CommandContext dummyContext = new CommandContext(arguments); - CommandMapping mapping = get(dummyContext.getCommand()); - if (mapping != null) { - CommandCallable c = mapping.getCallable(); - CommandContext context = - new CommandContext(arguments, c.getValueFlags(), false, locals); - try { - c.call(context); - } catch (CommandException e) { - e.prependStack(context.getCommand()); - throw e; - } catch (Throwable t) { - throw new WrappedCommandException(t); + public boolean call(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) { + String subCommand = split[0]; + CommandMapping mapping = get(subCommand); + String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); + + if (mapping != null) { + try { + mapping.getCallable().call(passedArguments, locals); + } catch (CommandException e) { + e.prependStack(subCommand); + throw e; + } catch (Throwable t) { + throw new WrappedCommandException(t); + } + + return true; } + } - return mapping; + + throw new InvalidUsageException(getSubcommandList(), getDescription()); } @Override public Collection getSuggestions(String arguments) throws CommandException { - CommandContext dummyContext = new CommandContext(arguments); - CommandMapping mapping = get(dummyContext.getCommand()); - if (mapping != null) { - CommandCallable c = mapping.getCallable(); - CommandContext context = - new CommandContext(arguments, c.getValueFlags(), true); - return c.getSuggestions(context); + String[] split = CommandContext.split(arguments); + + if (split.length == 0) { + return getAllAliases(); + } else if (split.length == 1) { + String prefix = split[0]; + List suggestions = new ArrayList(); + + for (String alias : getAllAliases()) { + if (alias.startsWith(prefix)) { + suggestions.add(alias); + } + } + + return suggestions; + } else { + String subCommand = split[0]; + CommandMapping mapping = get(subCommand); + String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); + + if (mapping != null) { + return mapping.getCallable().getSuggestions(passedArguments); + } else { + return Collections.emptyList(); + } } - return new ArrayList(); + } + + @Override + public SimpleDescription getDescription() { + return description; + } + + /** + * Get a list of subcommands for display. + * + * @return a string + */ + private String getSubcommandList() { + Set aliases = getPrimaryAliases(); + + StringBuilder builder = new StringBuilder("Subcommands: "); + for (String alias : getPrimaryAliases()) { + builder.append("\n- ").append(alias); + } + + if (aliases.size() == 1) { + builder.append(" (there is only one)"); + } + + return builder.toString(); } } diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java deleted file mode 100644 index 79b30f064..000000000 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcherCommand.java +++ /dev/null @@ -1,96 +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; - -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; - -import java.util.*; - -/** - * A combination {@link Dispatcher} and {@link CommandCallable} that is backed by - * a {@link SimpleDispatcher}. - * - *

The primary use of this is to make nested commands.

- */ -public class SimpleDispatcherCommand extends SimpleDispatcher implements CommandCallable { - - private final SimpleDescription description = new SimpleDescription(); - - @Override - public Set getValueFlags() { - return Collections.emptySet(); - } - - @Override - public void call(CommandContext context) throws CommandException { - boolean success = context.argsLength() >= 1 && super.call(context.getRemainingString(0), context.getLocals()) != null; - - if (!success) { - Set aliases = getPrimaryAliases(); - - if (aliases.size() == 0) { - throw new InvalidUsageException( - "This command is supposed to have sub-commands, " + - "but it has no sub-commands.", - getDescription()); - } - - StringBuilder builder = new StringBuilder(); - for (String alias : getPrimaryAliases()) { - builder.append("\n- ").append(alias); - } - - if (aliases.size() == 1) { - builder.append(" (there is only one)"); - } - - throw new InvalidUsageException( - "Select one of these subcommand(s):" + builder.toString(), - getDescription()); - } - } - - @Override - public SimpleDescription getDescription() { - return description; - } - - @Override - public Collection getSuggestions(CommandContext context) throws CommandException { - if (context.argsLength() == 0) { - return super.getAllAliases(); - } else if (context.argsLength() == 1 && - context.getSuggestionContext().forLastValue()) { - String prefix = context.getString(0).toLowerCase(); - List suggestions = new ArrayList(); - for (String alias : super.getAllAliases()) { - if (alias.startsWith(prefix)) { - suggestions.add(alias); - } - } - return suggestions; - } - - return super.getSuggestions( - context.argsLength() > 1 ? context.getRemainingString(1) : ""); - } - -} 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 cf0d93d74..8a4f71b6c 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 @@ -22,7 +22,6 @@ package com.sk89q.worldedit.util.command.fluent; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher; -import com.sk89q.worldedit.util.command.SimpleDispatcherCommand; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; /** @@ -58,9 +57,8 @@ public class DispatcherNode { * @return this object */ public DispatcherNode describe(String description) { - if (dispatcher instanceof SimpleDispatcherCommand) { - ((SimpleDispatcherCommand) dispatcher).getDescription() - .setDescription(description); + if (dispatcher instanceof SimpleDispatcher) { + ((SimpleDispatcher) dispatcher).getDescription().setDescription(description); } return this; } @@ -102,7 +100,7 @@ public class DispatcherNode { * @return an object to place sub-commands */ public DispatcherNode group(String... alias) { - SimpleDispatcherCommand command = new SimpleDispatcherCommand(); + SimpleDispatcher command = new SimpleDispatcher(); getDispatcher().register(command, alias); return new DispatcherNode(graph, this, command); } 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 f97f9b8b1..23e2d1b22 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 @@ -162,7 +162,10 @@ class ParametricCallable implements CommandCallable { } @Override - public void call(CommandContext context) throws CommandException { + public boolean call(String stringArguments, CommandLocals locals) throws CommandException { + String[] split = CommandContext.split(stringArguments); + CommandContext context = new CommandContext(split, getValueFlags(), false, locals); + Object[] args = new Object[parameters.length]; ContextArgumentStack arguments = new ContextArgumentStack(context); ParameterData parameter = null; @@ -175,15 +178,15 @@ class ParametricCallable implements CommandCallable { handlers.add(handler); handler.preProcess(object, method, parameters, context); } - + // Collect parameters for (int i = 0; i < parameters.length; i++) { parameter = parameters[i]; - + if (mayConsumeArguments(i, arguments)) { // Parse the user input into a method argument ArgumentStack usedArguments = getScopedContext(parameter, arguments); - + try { args[i] = parameter.getBinding().bind(parameter, usedArguments, false); } catch (MissingParameterException e) { @@ -198,7 +201,7 @@ class ParametricCallable implements CommandCallable { args[i] = getDefaultValue(i, arguments); } } - + // Check for unused arguments checkUnconsumed(arguments); @@ -206,7 +209,7 @@ class ParametricCallable implements CommandCallable { for (InvokeHandler handler : handlers) { handler.preInvoke(object, method, parameters, args, context); } - + // Execute! method.invoke(object, args); @@ -227,7 +230,7 @@ class ParametricCallable implements CommandCallable { converter.convert(e.getCause()); } } - + String name = parameter.getName(); throw new InvalidUsageException("For parameter '" + name + "': " @@ -244,25 +247,28 @@ class ParametricCallable implements CommandCallable { } catch (Throwable e) { throw new WrappedCommandException(e); } + + return true; } @Override - public List getSuggestions(CommandContext context) throws CommandException { + public Collection getSuggestions(String stringArguments) throws CommandException { + String[] split = CommandContext.split(stringArguments); + CommandContext context = new CommandContext(split, getValueFlags()); + ContextArgumentStack scoped = new ContextArgumentStack(context); SuggestionContext suggestable = context.getSuggestionContext(); // For /command -f | // For /command -f flag| if (suggestable.forFlag()) { - for (int i = 0; i < parameters.length; i++) { - ParameterData parameter = parameters[i]; - + for (ParameterData parameter : parameters) { if (parameter.getFlag() == suggestable.getFlag()) { String prefix = context.getFlag(parameter.getFlag()); if (prefix == null) { prefix = ""; } - + return parameter.getBinding().getSuggestions(parameter, prefix); } } From 33e1e0b1f13cb2795cb716407d61939218c60f55 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 16:30:02 -0700 Subject: [PATCH 31/45] 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); - -} From 620a4a21a12fda94e43ad4131c7122cdeda2ab35 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 16:30:22 -0700 Subject: [PATCH 32/45] Fixed -a in //stack being interpreted as -s. --- src/main/java/com/sk89q/worldedit/command/RegionCommands.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 67e285dcd..5534df372 100644 --- a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -327,7 +327,7 @@ public class RegionCommands { @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); - if (ignoreAirBlocks) { + if (moveSelection) { try { final Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()); From 205fee3c365c9b45501c7973e645f609b11b5062 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 16:39:20 -0700 Subject: [PATCH 33/45] Renamed some command manager methods to be more descriptive. --- .../extension/platform/CommandManager.java | 52 +++++++++---------- .../worldedit/util/command/Dispatcher.java | 14 ++--- .../util/command/SimpleDispatcher.java | 10 ++-- .../util/command/fluent/CommandGraph.java | 2 +- .../util/command/fluent/DispatcherNode.java | 12 ++--- .../command/parametric/ParametricBuilder.java | 2 +- 6 files changed, 46 insertions(+), 46 deletions(-) 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 a8e5b7043..77c98a618 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -97,43 +97,43 @@ public final class CommandManager { dispatcher = new CommandGraph() .builder(builder) .commands() - .build(new BiomeCommands(worldEdit)) - .build(new ChunkCommands(worldEdit)) - .build(new ClipboardCommands(worldEdit)) - .build(new GeneralCommands(worldEdit)) - .build(new GenerationCommands(worldEdit)) - .build(new HistoryCommands(worldEdit)) - .build(new NavigationCommands(worldEdit)) - .build(new RegionCommands(worldEdit)) - .build(new ScriptingCommands(worldEdit)) - .build(new SelectionCommands(worldEdit)) - .build(new SnapshotUtilCommands(worldEdit)) - .build(new ToolUtilCommands(worldEdit)) - .build(new ToolCommands(worldEdit)) - .build(new UtilityCommands(worldEdit)) + .registerMethods(new BiomeCommands(worldEdit)) + .registerMethods(new ChunkCommands(worldEdit)) + .registerMethods(new ClipboardCommands(worldEdit)) + .registerMethods(new GeneralCommands(worldEdit)) + .registerMethods(new GenerationCommands(worldEdit)) + .registerMethods(new HistoryCommands(worldEdit)) + .registerMethods(new NavigationCommands(worldEdit)) + .registerMethods(new RegionCommands(worldEdit)) + .registerMethods(new ScriptingCommands(worldEdit)) + .registerMethods(new SelectionCommands(worldEdit)) + .registerMethods(new SnapshotUtilCommands(worldEdit)) + .registerMethods(new ToolUtilCommands(worldEdit)) + .registerMethods(new ToolCommands(worldEdit)) + .registerMethods(new UtilityCommands(worldEdit)) .group("worldedit", "we") - .describe("WorldEdit commands") - .build(new WorldEditCommands(worldEdit)) + .describeAs("WorldEdit commands") + .registerMethods(new WorldEditCommands(worldEdit)) .parent() .group("schematic", "schem", "/schematic", "/schem") - .describe("Schematic commands for saving/loading areas") - .build(new SchematicCommands(worldEdit)) + .describeAs("Schematic commands for saving/loading areas") + .registerMethods(new SchematicCommands(worldEdit)) .parent() .group("snapshot", "snap") - .describe("Schematic commands for saving/loading areas") - .build(new SnapshotCommands(worldEdit)) + .describeAs("Schematic commands for saving/loading areas") + .registerMethods(new SnapshotCommands(worldEdit)) .parent() .group("brush", "br") - .describe("Brushing commands") - .build(new BrushCommands(worldEdit)) + .describeAs("Brushing commands") + .registerMethods(new BrushCommands(worldEdit)) .parent() .group("superpickaxe", "pickaxe", "sp") - .describe("Super-pickaxe commands") - .build(new SuperPickaxeCommands(worldEdit)) + .describeAs("Super-pickaxe commands") + .registerMethods(new SuperPickaxeCommands(worldEdit)) .parent() .group("tool") - .describe("Bind functions to held items") - .build(new ToolCommands(worldEdit)) + .describeAs("Bind functions to held items") + .registerMethods(new ToolCommands(worldEdit)) .parent() .graph() .getDispatcher(); diff --git a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java index 00aa399e6..3ca0419da 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util.command; +import javax.annotation.Nullable; import java.util.Collection; /** @@ -32,7 +33,7 @@ public interface Dispatcher extends CommandCallable { * @param callable the command executor * @param alias a list of aliases, where the first alias is the primary name */ - void register(CommandCallable callable, String... alias); + void registerCommand(CommandCallable callable, String... alias); /** * Get a list of command registrations. @@ -53,22 +54,23 @@ public interface Dispatcher extends CommandCallable { Collection getPrimaryAliases(); /** - * Get a list of all the command aliases. + * Get a list of all the command aliases, which includes the primary alias. * *

A command may have more than one alias assigned to it. The returned * collection cannot be modified.

* * @return a list of aliases */ - Collection getAllAliases(); + Collection getAliases(); /** - * Get the {@link CommandCallable} associated with an alias. + * Get the {@link CommandCallable} associated with an alias. Returns + * null if no command is named by the given alias. * * @param alias the alias - * @return the command mapping + * @return the command mapping (null if not found) */ - CommandMapping get(String alias); + @Nullable CommandMapping get(String alias); /** * Returns whether the dispatcher contains a registered command for the given alias. 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 0d498668b..79a7c672b 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -37,7 +37,7 @@ public class SimpleDispatcher implements Dispatcher { private final SimpleDescription description = new SimpleDescription(); @Override - public void register(CommandCallable callable, String... alias) { + public void registerCommand(CommandCallable callable, String... alias) { CommandMapping mapping = new CommandMapping(callable, alias); // Check for replacements @@ -61,7 +61,7 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public Set getAllAliases() { + public Set getAliases() { return Collections.unmodifiableSet(commands.keySet()); } @@ -124,13 +124,13 @@ public class SimpleDispatcher implements Dispatcher { String[] split = CommandContext.split(arguments); if (split.length == 0) { - return new ArrayList(getAllAliases()); + return new ArrayList(getAliases()); } else if (split.length == 1) { String prefix = split[0]; if (!prefix.isEmpty()) { List suggestions = new ArrayList(); - for (String alias : getAllAliases()) { + for (String alias : getAliases()) { if (alias.startsWith(prefix)) { suggestions.add(alias); } @@ -138,7 +138,7 @@ public class SimpleDispatcher implements Dispatcher { return suggestions; } else { - return new ArrayList(getAllAliases()); + return new ArrayList(getAliases()); } } else { String subCommand = split[0]; diff --git a/src/main/java/com/sk89q/worldedit/util/command/fluent/CommandGraph.java b/src/main/java/com/sk89q/worldedit/util/command/fluent/CommandGraph.java index 6e5b189f7..1b9dacb32 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/fluent/CommandGraph.java +++ b/src/main/java/com/sk89q/worldedit/util/command/fluent/CommandGraph.java @@ -62,7 +62,7 @@ public class CommandGraph { /** * Set the {@link ParametricBuilder} used for calls to - * {@link DispatcherNode#build(Object)}. + * {@link DispatcherNode#registerMethods(Object)}. * * @param builder the builder, or null * @return this object 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 49b53d8b9..ed0d6dae5 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 @@ -56,10 +56,8 @@ public class DispatcherNode { * @param description the description * @return this object */ - public DispatcherNode describe(String description) { - if (dispatcher instanceof SimpleDispatcher) { - ((SimpleDispatcher) dispatcher).getDescription().setDescription(description); - } + public DispatcherNode describeAs(String description) { + dispatcher.getDescription().setDescription(description); return this; } @@ -70,7 +68,7 @@ public class DispatcherNode { * @param alias the list of aliases, where the first alias is the primary one */ public void register(CommandCallable callable, String... alias) { - dispatcher.register(callable, alias); + dispatcher.registerCommand(callable, alias); } /** @@ -81,7 +79,7 @@ public class DispatcherNode { * @return this object * @see ParametricBuilder#registerMethodsAsCommands(com.sk89q.worldedit.util.command.Dispatcher, Object) */ - public DispatcherNode build(Object object) { + public DispatcherNode registerMethods(Object object) { ParametricBuilder builder = graph.getBuilder(); if (builder == null) { throw new RuntimeException("No ParametricBuilder set"); @@ -101,7 +99,7 @@ public class DispatcherNode { */ public DispatcherNode group(String... alias) { SimpleDispatcher command = new SimpleDispatcher(); - getDispatcher().register(command, alias); + getDispatcher().registerCommand(command, alias); return new DispatcherNode(graph, this, command); } 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 f788a695f..dd96c4a2a 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 @@ -151,7 +151,7 @@ public class ParametricBuilder { Command definition = method.getAnnotation(Command.class); if (definition != null) { CommandCallable callable = build(object, method, definition); - dispatcher.register(callable, definition.aliases()); + dispatcher.registerCommand(callable, definition.aliases()); } } } From 10c45fcb223aa8314b4948b8cc4c9f15d1cd8a22 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 16:57:11 -0700 Subject: [PATCH 34/45] Check permissions in command suggestions and Dispatcher. --- .../bukkit/BukkitServerInterface.java | 1 + .../extension/platform/CommandManager.java | 4 +- .../util/command/CommandCallable.java | 3 +- .../worldedit/util/command/Dispatcher.java | 6 +- .../util/command/SimpleDispatcher.java | 59 ++++++++++--------- .../parametric/ParametricCallable.java | 2 +- 6 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 8bf7f874b..bf08bf45a 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -126,6 +126,7 @@ public class BukkitServerInterface extends ServerInterface { 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(); 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 77c98a618..41f1e471f 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -258,7 +258,9 @@ public final class CommandManager { @Subscribe public void handleCommandSuggestion(CommandSuggestionEvent event) { try { - event.setSuggestions(dispatcher.getSuggestions(event.getArguments())); + CommandLocals locals = new CommandLocals(); + locals.put(Actor.class, event.getActor()); + event.setSuggestions(dispatcher.getSuggestions(event.getArguments(), locals)); } catch (CommandException e) { event.getActor().printError(e.getMessage()); } 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 1120dffc8..d8254f4bf 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java @@ -56,10 +56,11 @@ public interface CommandCallable { * Get a list of suggestions based on input. * * @param arguments the arguments entered up to this point + * @param locals the locals * @return a list of suggestions * @throws CommandException thrown if there was a parsing error */ - List getSuggestions(String arguments) throws CommandException; + List getSuggestions(String arguments, CommandLocals locals) throws CommandException; /** * Get an object describing this command. diff --git a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java index 3ca0419da..568db508e 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.util.command; import javax.annotation.Nullable; import java.util.Collection; +import java.util.Set; /** * Executes a command based on user input. @@ -36,13 +37,14 @@ public interface Dispatcher extends CommandCallable { void registerCommand(CommandCallable callable, String... alias); /** - * Get a list of command registrations. + * Get a list of commands. Each command, regardless of how many aliases + * it may have, will only appear once in the returned set. * *

The returned collection cannot be modified.

* * @return a list of registrations */ - Collection getCommands(); + Set getCommands(); /** * Get a list of primary aliases. 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 79a7c672b..2dc778f49 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -20,10 +20,7 @@ package com.sk89q.worldedit.util.command; import com.google.common.base.Joiner; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandLocals; -import com.sk89q.minecraft.util.commands.WrappedCommandException; +import com.sk89q.minecraft.util.commands.*; import javax.annotation.Nullable; import java.util.*; @@ -56,8 +53,8 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public Collection getCommands() { - return Collections.unmodifiableCollection(commands.values()); + public Set getCommands() { + return Collections.unmodifiableSet(new HashSet(commands.values())); } @Override @@ -91,6 +88,11 @@ public class SimpleDispatcher implements Dispatcher { @Override public boolean call(@Nullable String alias, String arguments, CommandLocals locals) throws CommandException { + // We have permission for this command if we have permissions for subcommands + if (!testPermission(locals)) { + throw new CommandPermissionsException(); + } + String[] split = CommandContext.split(arguments); Set aliases = getPrimaryAliases(); @@ -116,37 +118,37 @@ public class SimpleDispatcher implements Dispatcher { } - throw new InvalidUsageException(getSubcommandList(), getDescription()); + throw new InvalidUsageException(getSubcommandList(locals), getDescription()); } @Override - public List getSuggestions(String arguments) throws CommandException { + public List getSuggestions(String arguments, CommandLocals locals) throws CommandException { String[] split = CommandContext.split(arguments); - if (split.length == 0) { - return new ArrayList(getAliases()); - } else if (split.length == 1) { - String prefix = split[0]; - if (!prefix.isEmpty()) { - List suggestions = new ArrayList(); + if (split.length <= 1) { + String prefix = split.length > 0 ? split[0] : ""; - for (String alias : getAliases()) { - if (alias.startsWith(prefix)) { - suggestions.add(alias); + List suggestions = new ArrayList(); + + for (CommandMapping mapping : getCommands()) { + if (mapping.getCallable().testPermission(locals)) { + for (String alias : mapping.getAllAliases()) { + if (prefix.isEmpty() || alias.startsWith(arguments)) { + suggestions.add(mapping.getPrimaryAlias()); + break; + } } } - - return suggestions; - } else { - return new ArrayList(getAliases()); } + + return suggestions; } else { String subCommand = split[0]; CommandMapping mapping = get(subCommand); String passedArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); if (mapping != null) { - return mapping.getCallable().getSuggestions(passedArguments); + return mapping.getCallable().getSuggestions(passedArguments, locals); } else { return Collections.emptyList(); } @@ -174,16 +176,17 @@ public class SimpleDispatcher implements Dispatcher { * * @return a string */ - private String getSubcommandList() { + private String getSubcommandList(CommandLocals locals) { Set aliases = getPrimaryAliases(); StringBuilder builder = new StringBuilder("Subcommands: "); - for (String alias : getPrimaryAliases()) { - builder.append("\n- ").append(alias); - } - if (aliases.size() == 1) { - builder.append(" (there is only one)"); + for (CommandMapping mapping : getCommands()) { + if (mapping.getCallable().testPermission(locals)) { + for (String alias : mapping.getAllAliases()) { + builder.append("\n- ").append(alias); + } + } } return builder.toString(); 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 bada21862..46b8e12ff 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 @@ -252,7 +252,7 @@ class ParametricCallable implements CommandCallable { } @Override - public List getSuggestions(String stringArguments) throws CommandException { + public List getSuggestions(String stringArguments, CommandLocals locals) throws CommandException { String[] split = CommandContext.split(stringArguments); CommandContext context = new CommandContext(split, getValueFlags()); From 3750190f2c17b8ec78ba92d42b246b1a4262b40a Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 17:04:38 -0700 Subject: [PATCH 35/45] Passed a string of parent commands rather than just the current command. --- .../sk89q/worldedit/extension/platform/CommandManager.java | 2 +- .../com/sk89q/worldedit/util/command/CommandCallable.java | 6 ++---- .../com/sk89q/worldedit/util/command/SimpleDispatcher.java | 7 ++++--- .../util/command/parametric/ParametricCallable.java | 6 +++--- 4 files changed, 10 insertions(+), 11 deletions(-) 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 41f1e471f..cc9f26556 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -216,7 +216,7 @@ public final class CommandManager { long start = System.currentTimeMillis(); try { - dispatcher.call(null, Joiner.on(" ").join(split), locals); + dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]); } catch (CommandPermissionsException e) { actor.printError("You don't have permission to do this."); } catch (InvalidUsageException e) { 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 d8254f4bf..9a16a467c 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,6 @@ package com.sk89q.worldedit.util.command; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; -import javax.annotation.Nullable; import java.util.List; import java.util.Set; @@ -43,14 +42,13 @@ public interface CommandCallable { *

* 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 + * @param parentCommands a list of parent commands, with the first most entry being the top-level command * @return the called command, or null if there was no command found * @throws CommandException thrown on a command error */ - boolean call(@Nullable String alias, String arguments, CommandLocals locals) throws CommandException; + boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException; /** * Get a list of suggestions based on input. 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 2dc778f49..12d2ebde5 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -22,7 +22,6 @@ package com.sk89q.worldedit.util.command; import com.google.common.base.Joiner; import com.sk89q.minecraft.util.commands.*; -import javax.annotation.Nullable; import java.util.*; /** @@ -87,7 +86,7 @@ public class SimpleDispatcher implements Dispatcher { } @Override - public boolean call(@Nullable String alias, String arguments, CommandLocals locals) throws CommandException { + public boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException { // We have permission for this command if we have permissions for subcommands if (!testPermission(locals)) { throw new CommandPermissionsException(); @@ -101,11 +100,13 @@ public class SimpleDispatcher implements Dispatcher { } else if (split.length > 0) { String subCommand = split[0]; String subArguments = Joiner.on(" ").join(Arrays.copyOfRange(split, 1, split.length)); + String[] subParents = Arrays.copyOf(parentCommands, parentCommands.length + 1); + subParents[parentCommands.length] = subCommand; CommandMapping mapping = get(subCommand); if (mapping != null) { try { - mapping.getCallable().call(subCommand, subArguments, locals); + mapping.getCallable().call(subArguments, locals, subParents); } catch (CommandException e) { e.prependStack(subCommand); throw e; 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 46b8e12ff..8ba6f32c4 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,7 +23,6 @@ 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; @@ -160,13 +159,14 @@ class ParametricCallable implements CommandCallable { } @Override - public boolean call(@Nullable String alias, String stringArguments, CommandLocals locals) throws CommandException { + public boolean call(String stringArguments, CommandLocals locals, String[] parentCommands) throws CommandException { // Test permission if (!testPermission(locals)) { throw new CommandPermissionsException(); } - String[] split = CommandContext.split(alias + " " + stringArguments); + String calledCommand = parentCommands.length > 0 ? parentCommands[parentCommands.length - 1] : "_"; + String[] split = CommandContext.split(calledCommand + " " + stringArguments); CommandContext context = new CommandContext(split, getValueFlags(), false, locals); Object[] args = new Object[parameters.length]; From c7988c7ad9652e4bc69c7305d967ee0d9066bc71 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 17:32:25 -0700 Subject: [PATCH 36/45] Don't throw a ParameterException for a regular exception. --- .../sk89q/worldedit/util/command/parametric/BindingHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java index a104d6776..b442da6a2 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java @@ -178,7 +178,7 @@ public class BindingHelper implements Binding { } else if (e.getCause() instanceof CommandException) { throw (CommandException) e.getCause(); } - throw new ParameterException(e.getCause()); + throw new RuntimeException(e.getCause()); } } From e261e720b643bf32777fb4e3323ca5622e37a020 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 17:32:48 -0700 Subject: [PATCH 37/45] Removed ParametricCallable's getSuggestions() which isn't quite working. --- .../parametric/ParametricCallable.java | 87 +------------------ 1 file changed, 1 insertion(+), 86 deletions(-) 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 8ba6f32c4..c0317c435 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 @@ -253,92 +253,7 @@ class ParametricCallable implements CommandCallable { @Override public List getSuggestions(String stringArguments, CommandLocals locals) throws CommandException { - String[] split = CommandContext.split(stringArguments); - CommandContext context = new CommandContext(split, getValueFlags()); - - ContextArgumentStack scoped = new ContextArgumentStack(context); - SuggestionContext suggestable = context.getSuggestionContext(); - - // For /command -f | - // For /command -f flag| - if (suggestable.forFlag()) { - for (ParameterData parameter : parameters) { - if (parameter.getFlag() == suggestable.getFlag()) { - String prefix = context.getFlag(parameter.getFlag()); - if (prefix == null) { - prefix = ""; - } - - return parameter.getBinding().getSuggestions(parameter, prefix); - } - } - - // This should not happen - return new ArrayList(); - } - - int consumerIndex = 0; - ParameterData lastConsumer = null; - String lastConsumed = null; - - for (ParameterData parameter : parameters) { - if (parameter.getFlag() != null) { - continue; // We already handled flags - } - - try { - scoped.mark(); - parameter.getBinding().bind(parameter, scoped, true); - if (scoped.wasConsumed()) { - lastConsumer = parameter; - lastConsumed = scoped.getConsumed(); - consumerIndex++; - } - } catch (MissingParameterException e) { - // For /command value1 |value2 - // For /command |value1 value2 - if (suggestable.forHangingValue()) { - return parameter.getBinding().getSuggestions(parameter, ""); - } else { - // For /command value1| value2 - if (lastConsumer != null) { - return lastConsumer.getBinding() - .getSuggestions(lastConsumer, lastConsumed); - // For /command| value1 value2 - // This should never occur - } else { - throw new RuntimeException("Invalid suggestion context"); - } - } - } catch (ParameterException e) { - if (suggestable.forHangingValue()) { - String name = getDescription().getParameters() - .get(consumerIndex).getName(); - - throw new InvalidUsageException("For parameter '" + name + "': " - + e.getMessage(), getDescription()); - } else { - return parameter.getBinding().getSuggestions(parameter, ""); - } - } - } - - // For /command value1 value2 | - if (suggestable.forHangingValue()) { - // There's nothing that we can suggest because there's no more parameters - // to add on, and we can't change the previous parameter - return new ArrayList(); - } else { - // For /command value1 value2| - if (lastConsumer != null) { - return lastConsumer.getBinding() - .getSuggestions(lastConsumer, lastConsumed); - // This should never occur - } else { - throw new RuntimeException("Invalid suggestion context"); - } - - } + return Collections.emptyList(); } @Override From 93a48bbb5508db28ec49be667789cefe5bcf7db3 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 17:37:13 -0700 Subject: [PATCH 38/45] Handle NoMatchException in WorldEditBinding. --- .../internal/command/WorldEditBinding.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index 9d5210496..ed29e8fb5 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.mask.Mask; @@ -160,7 +161,11 @@ public class WorldEditBinding extends BindingHelper { parserContext.setWorld(((Entity) actor).getWorld()); } parserContext.setSession(worldEdit.getSessionManager().get(actor)); - return worldEdit.getBlockRegistry().parseFromInput(context.next(), parserContext); + try { + return worldEdit.getBlockRegistry().parseFromInput(context.next(), parserContext); + } catch (NoMatchException e) { + throw new ParameterException(e.getMessage(), e); + } } /** @@ -182,7 +187,11 @@ public class WorldEditBinding extends BindingHelper { parserContext.setWorld(((Entity) actor).getWorld()); } parserContext.setSession(worldEdit.getSessionManager().get(actor)); - return worldEdit.getPatternRegistry().parseFromInput(context.next(), parserContext); + try { + return worldEdit.getPatternRegistry().parseFromInput(context.next(), parserContext); + } catch (NoMatchException e) { + throw new ParameterException(e.getMessage(), e); + } } /** @@ -204,7 +213,11 @@ public class WorldEditBinding extends BindingHelper { parserContext.setWorld(((Entity) actor).getWorld()); } parserContext.setSession(worldEdit.getSessionManager().get(actor)); - return worldEdit.getMaskRegistry().parseFromInput(context.next(), parserContext); + try { + return worldEdit.getMaskRegistry().parseFromInput(context.next(), parserContext); + } catch (NoMatchException e) { + throw new ParameterException(e.getMessage(), e); + } } /** From a957482ee618d07646480fdfeca94742959fc1a6 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 17:37:47 -0700 Subject: [PATCH 39/45] Ignore cause when handling ParameterException in ParametricCallable. --- .../util/command/parametric/ParametricCallable.java | 6 ------ 1 file changed, 6 deletions(-) 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 c0317c435..9cbecc03e 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 @@ -225,12 +225,6 @@ class ParametricCallable implements CommandCallable { } catch (UnconsumedParameterException e) { throw new InvalidUsageException("Too many parameters! Unused parameters: " + e.getUnconsumed(), getDescription()); } catch (ParameterException e) { - if (e.getCause() != null) { - for (ExceptionConverter converter : builder.getExceptionConverters()) { - converter.convert(e.getCause()); - } - } - assert parameter != null; String name = parameter.getName(); From 2628e80f41978df79ffb0eedd53a0f7e3db6ae13 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 18:42:25 -0700 Subject: [PATCH 40/45] Made SimpleDescription's lists modifiable. --- .../com/sk89q/worldedit/util/command/SimpleDescription.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 111ee68e5..efd5aed95 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.util.command; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -27,8 +28,8 @@ import java.util.List; */ public class SimpleDescription implements Description { - private List parameters = Collections.emptyList(); - private List permissions = Collections.emptyList(); + private List parameters = new ArrayList(); + private List permissions = new ArrayList(); private String description; private String help; private String overrideUsage; From 46c429606ee1b2c56f6a366241145666784123ad Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 18:43:37 -0700 Subject: [PATCH 41/45] Added text formatting classes from the operations branch. --- .../util/formatting/ColorCodeBuilder.java | 275 ++++++++++++++++++ .../util/formatting/CommandListBox.java | 45 +++ .../worldedit/util/formatting/Fragment.java | 92 ++++++ .../worldedit/util/formatting/MessageBox.java | 70 +++++ .../worldedit/util/formatting/Style.java | 274 +++++++++++++++++ .../worldedit/util/formatting/StyleSet.java | 249 ++++++++++++++++ .../util/formatting/StyledFragment.java | 150 ++++++++++ 7 files changed, 1155 insertions(+) create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/CommandListBox.java create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/MessageBox.java create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/Style.java create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java create mode 100644 src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java b/src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java new file mode 100644 index 000000000..c0ea43961 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/ColorCodeBuilder.java @@ -0,0 +1,275 @@ +/* + * 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.formatting; + +import com.google.common.base.Joiner; + +import java.util.LinkedList; +import java.util.List; + +public class ColorCodeBuilder { + + private static final ColorCodeBuilder instance = new ColorCodeBuilder(); + private static final Joiner newLineJoiner = Joiner.on("\n"); + public static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47; + + /** + * Convert a message into color-coded text. + * + * @param message the message + * @return a list of lines + */ + public String[] build(StyledFragment message) { + StringBuilder builder = new StringBuilder(); + buildFragment(builder, message, message.getStyle(), new StyleSet()); + return wordWrap(builder.toString(), GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH); + } + + /** + * Build a fragment. + * + * @param builder the string builder + * @param message the message + * @param parentStyle the parent style + * @param lastStyle the last style + * @return the last style used + */ + private StyleSet buildFragment(StringBuilder builder, StyledFragment message, StyleSet parentStyle, StyleSet lastStyle) { + for (Fragment node : message.getChildren()) { + if (node instanceof StyledFragment) { + StyledFragment fragment = (StyledFragment) node; + lastStyle = buildFragment( + builder, fragment, + parentStyle.extend(message.getStyle()), lastStyle); + } else { + StyleSet style = parentStyle.extend(message.getStyle()); + builder.append(getAdditive(style, lastStyle)); + builder.append(node.toString()); + lastStyle = style; + } + } + + return lastStyle; + } + + /** + * Get the formatting codes. + * + * @param style the style + * @return the color codes + */ + public static String getFormattingCode(StyleSet style) { + StringBuilder builder = new StringBuilder(); + if (style.isBold()) { + builder.append(Style.BOLD); + } + if (style.isItalic()) { + builder.append(Style.ITALIC); + } + if (style.isUnderline()) { + builder.append(Style.UNDERLINE); + } + if (style.isStrikethrough()) { + builder.append(Style.STRIKETHROUGH); + } + return builder.toString(); + } + + /** + * Get the formatting and color codes. + * + * @param style the style + * @return the color codes + */ + public static String getCode(StyleSet style) { + StringBuilder builder = new StringBuilder(); + builder.append(getFormattingCode(style)); + if (style.getColor() != null) { + builder.append(style.getColor().toString()); + } + return builder.toString(); + } + + /** + * Get the additional color codes needed to set the given style when the current + * style is the other given one. + * + * @param resetTo the style to reset to + * @param resetFrom the style to reset from + * @return the color codes + */ + public static String getAdditive(StyleSet resetTo, StyleSet resetFrom) { + if (!resetFrom.hasFormatting() && resetTo.hasFormatting()) { + StringBuilder builder = new StringBuilder(); + builder.append(getFormattingCode(resetTo)); + if (resetFrom.getColor() != resetTo.getColor()) { + builder.append(resetTo.getColor()); + } + return builder.toString(); + } else if (!resetFrom.hasEqualFormatting(resetTo) || + (resetFrom.getColor() != null && resetTo.getColor() == null)) { + StringBuilder builder = new StringBuilder(); + // Have to set reset code and add back all the formatting codes + builder.append(Style.RESET); + builder.append(getCode(resetTo)); + return builder.toString(); + } else { + if (resetFrom.getColor() != resetTo.getColor()) { + return String.valueOf(resetTo.getColor()); + } + } + + return ""; + } + + /** + * Word wrap the given text and maintain color codes throughout lines. + * + *

This is borrowed from Bukkit.

+ * + * @param rawString the raw string + * @param lineLength the maximum line length + * @return a list of lines + */ + private String[] wordWrap(String rawString, int lineLength) { + // A null string is a single line + if (rawString == null) { + return new String[] {""}; + } + + // A string shorter than the lineWidth is a single line + if (rawString.length() <= lineLength && !rawString.contains("\n")) { + return new String[] {rawString}; + } + + char[] rawChars = (rawString + ' ').toCharArray(); // add a trailing space to trigger pagination + StringBuilder word = new StringBuilder(); + StringBuilder line = new StringBuilder(); + List lines = new LinkedList(); + int lineColorChars = 0; + + for (int i = 0; i < rawChars.length; i++) { + char c = rawChars[i]; + + // skip chat color modifiers + if (c == Style.COLOR_CHAR) { + word.append(Style.getByChar(rawChars[i + 1])); + lineColorChars += 2; + i++; // Eat the next character as we have already processed it + continue; + } + + if (c == ' ' || c == '\n') { + if (line.length() == 0 && word.length() > lineLength) { // special case: extremely long word begins a line + String wordStr = word.toString(); + String transformed; + if ((transformed = transform(wordStr)) != null) { + line.append(transformed); + } else { + for (String partialWord : word.toString().split("(?<=\\G.{" + lineLength + "})")) { + lines.add(partialWord); + } + } + } else if (line.length() + word.length() - lineColorChars == lineLength) { // Line exactly the correct length...newline + line.append(' '); + line.append(word); + lines.add(line.toString()); + line = new StringBuilder(); + lineColorChars = 0; + } else if (line.length() + 1 + word.length() - lineColorChars > lineLength) { // Line too long...break the line + String wordStr = word.toString(); + String transformed; + if (word.length() > lineLength && (transformed = transform(wordStr)) != null) { + if (line.length() + 1 + transformed.length() - lineColorChars > lineLength) { + lines.add(line.toString()); + line = new StringBuilder(transformed); + lineColorChars = 0; + } else { + if (line.length() > 0) { + line.append(' '); + } + line.append(transformed); + } + } else { + for (String partialWord : wordStr.split("(?<=\\G.{" + lineLength + "})")) { + lines.add(line.toString()); + line = new StringBuilder(partialWord); + } + lineColorChars = 0; + } + } else { + if (line.length() > 0) { + line.append(' '); + } + line.append(word); + } + word = new StringBuilder(); + + if (c == '\n') { // Newline forces the line to flush + lines.add(line.toString()); + line = new StringBuilder(); + } + } else { + word.append(c); + } + } + + if(line.length() > 0) { // Only add the last line if there is anything to add + lines.add(line.toString()); + } + + // Iterate over the wrapped lines, applying the last color from one line to the beginning of the next + if (lines.get(0).length() == 0 || lines.get(0).charAt(0) != Style.COLOR_CHAR) { + lines.set(0, Style.WHITE + lines.get(0)); + } + for (int i = 1; i < lines.size(); i++) { + final String pLine = lines.get(i-1); + final String subLine = lines.get(i); + + char color = pLine.charAt(pLine.lastIndexOf(Style.COLOR_CHAR) + 1); + if (subLine.length() == 0 || subLine.charAt(0) != Style.COLOR_CHAR) { + lines.set(i, Style.getByChar(color) + subLine); + } + } + + return lines.toArray(new String[lines.size()]); + } + + /** + * Callback for transforming a word, such as a URL. + * + * @param word the word + * @return the transformed value, or null to do nothing + */ + protected String transform(String word) { + return null; + } + + /** + * Convert the given styled fragment into color codes. + * + * @param fragment the fragment + * @return color codes + */ + public static String asColorCodes(StyledFragment fragment) { + return newLineJoiner.join(instance.build(fragment)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/CommandListBox.java b/src/main/java/com/sk89q/worldedit/util/formatting/CommandListBox.java new file mode 100644 index 000000000..03fa0b754 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/CommandListBox.java @@ -0,0 +1,45 @@ +/* + * 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.formatting; + +public class CommandListBox extends MessageBox { + + private boolean first = true; + + /** + * Create a new box. + * + * @param title the title + */ + public CommandListBox(String title) { + super(title); + } + + public CommandListBox appendCommand(String alias, String description) { + if (!first) { + getContents().newLine(); + } + getContents().createFragment(Style.YELLOW_DARK).append(alias).append(": "); + getContents().append(description); + first = false; + return this; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java b/src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java new file mode 100644 index 000000000..3d1add7f4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/Fragment.java @@ -0,0 +1,92 @@ +/* + * 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.formatting; + +/** + * A fragment of text. + */ +public class Fragment { + + private final StringBuilder builder = new StringBuilder(); + + Fragment() { + } + + public Fragment append(String str) { + builder.append(Style.stripColor(str)); + return this; + } + + public Fragment append(Object obj) { + append(String.valueOf(obj)); + return this; + } + + public Fragment append(StringBuffer sb) { + append(String.valueOf(sb)); + return this; + } + + public Fragment append(CharSequence s) { + append(String.valueOf(s)); + return this; + } + + public Fragment append(boolean b) { + append(String.valueOf(b)); + return this; + } + + public Fragment append(char c) { + append(String.valueOf(c)); + return this; + } + + public Fragment append(int i) { + append(String.valueOf(i)); + return this; + } + + public Fragment append(long lng) { + append(String.valueOf(lng)); + return this; + } + + public Fragment append(float f) { + append(String.valueOf(f)); + return this; + } + + public Fragment append(double d) { + append(String.valueOf(d)); + return this; + } + + public Fragment newLine() { + append("\n"); + return this; + } + + @Override + public String toString() { + return builder.toString(); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/MessageBox.java b/src/main/java/com/sk89q/worldedit/util/formatting/MessageBox.java new file mode 100644 index 000000000..66cf81ab3 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/MessageBox.java @@ -0,0 +1,70 @@ +/* + * 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.formatting; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Makes for a box with a border above and below. + */ +public class MessageBox extends StyledFragment { + + private final StyledFragment contents = new StyledFragment(); + + /** + * Create a new box. + */ + public MessageBox(String title) { + checkNotNull(title); + + int leftOver = ColorCodeBuilder.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH - title.length() - 2; + int leftSide = (int) Math.floor(leftOver * 1.0/3); + int rightSide = (int) Math.floor(leftOver * 2.0/3); + if (leftSide > 0) { + createFragment(Style.YELLOW).append(createBorder(leftSide)); + } + append(" "); + append(title); + append(" "); + if (rightSide > 0) { + createFragment(Style.YELLOW).append(createBorder(rightSide)); + } + newLine(); + append(contents); + } + + private String createBorder(int count) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < count; i++) { + builder.append("-"); + } + return builder.toString(); + } + + /** + * Get the internal contents. + * + * @return the contents + */ + public StyledFragment getContents() { + return contents; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/Style.java b/src/main/java/com/sk89q/worldedit/util/formatting/Style.java new file mode 100644 index 000000000..80774b743 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/Style.java @@ -0,0 +1,274 @@ +/* + * 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.formatting; + +import com.google.common.collect.Maps; +import org.apache.commons.lang.Validate; + +import java.util.Map; +import java.util.regex.Pattern; + +/** + * All supported color values for chat. + * + *

From Bukkit.

+ */ +public enum Style { + /** + * Represents black + */ + BLACK('0', 0x00), + /** + * Represents dark blue + */ + BLUE_DARK('1', 0x1), + /** + * Represents dark green + */ + GREEN_DARK('2', 0x2), + /** + * Represents dark blue (aqua) + */ + CYAN_DARK('3', 0x3), + /** + * Represents dark red + */ + RED_DARK('4', 0x4), + /** + * Represents dark purple + */ + PURPLE_DARK('5', 0x5), + /** + * Represents gold + */ + YELLOW_DARK('6', 0x6), + /** + * Represents gray + */ + GRAY('7', 0x7), + /** + * Represents dark gray + */ + GRAY_DARK('8', 0x8), + /** + * Represents blue + */ + BLUE('9', 0x9), + /** + * Represents green + */ + GREEN('a', 0xA), + /** + * Represents aqua + */ + CYAN('b', 0xB), + /** + * Represents red + */ + RED('c', 0xC), + /** + * Represents light purple + */ + PURPLE('d', 0xD), + /** + * Represents yellow + */ + YELLOW('e', 0xE), + /** + * Represents white + */ + WHITE('f', 0xF), + /** + * Represents magical characters that change around randomly + */ + RANDOMIZE('k', 0x10, true), + /** + * Makes the text bold. + */ + BOLD('l', 0x11, true), + /** + * Makes a line appear through the text. + */ + STRIKETHROUGH('m', 0x12, true), + /** + * Makes the text appear underlined. + */ + UNDERLINE('n', 0x13, true), + /** + * Makes the text italic. + */ + ITALIC('o', 0x14, true), + /** + * Resets all previous chat colors or formats. + */ + RESET('r', 0x15); + + /** + * The special character which prefixes all chat color codes. Use this if you need to dynamically + * convert color codes from your custom format. + */ + public static final char COLOR_CHAR = '\u00A7'; + private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf(COLOR_CHAR) + "[0-9A-FK-OR]"); + + private final int intCode; + private final char code; + private final boolean isFormat; + private final String toString; + private final static Map BY_ID = Maps.newHashMap(); + private final static Map BY_CHAR = Maps.newHashMap(); + + private Style(char code, int intCode) { + this(code, intCode, false); + } + + private Style(char code, int intCode, boolean isFormat) { + this.code = code; + this.intCode = intCode; + this.isFormat = isFormat; + this.toString = new String(new char[] {COLOR_CHAR, code}); + } + + /** + * Gets the char value associated with this color + * + * @return A char value of this color code + */ + public char getChar() { + return code; + } + + @Override + public String toString() { + return toString; + } + + /** + * Checks if this code is a format code as opposed to a color code. + * + * @return the if the code is a formatting code + */ + public boolean isFormat() { + return isFormat; + } + + /** + * Checks if this code is a color code as opposed to a format code. + * + * @return the if the code is a color + */ + public boolean isColor() { + return !isFormat && this != RESET; + } + + /** + * Gets the color represented by the specified color code + * + * @param code Code to check + * @return Associative {@link org.bukkit.ChatColor} with the given code, or null if it doesn't exist + */ + public static Style getByChar(char code) { + return BY_CHAR.get(code); + } + + /** + * Gets the color represented by the specified color code + * + * @param code Code to check + * @return Associative {@link org.bukkit.ChatColor} with the given code, or null if it doesn't exist + */ + public static Style getByChar(String code) { + Validate.notNull(code, "Code cannot be null"); + Validate.isTrue(code.length() > 0, "Code must have at least one char"); + + return BY_CHAR.get(code.charAt(0)); + } + + /** + * Strips the given message of all color codes + * + * @param input String to strip of color + * @return A copy of the input string, without any coloring + */ + public static String stripColor(final String input) { + if (input == null) { + return null; + } + + return STRIP_COLOR_PATTERN.matcher(input).replaceAll(""); + } + + /** + * Translates a string using an alternate color code character into a string that uses the internal + * ChatColor.COLOR_CODE color code character. The alternate color code character will only be replaced + * if it is immediately followed by 0-9, A-F, a-f, K-O, k-o, R or r. + * + * @param altColorChar The alternate color code character to replace. Ex: & + * @param textToTranslate Text containing the alternate color code character. + * @return Text containing the ChatColor.COLOR_CODE color code character. + */ + public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { + char[] b = textToTranslate.toCharArray(); + for (int i = 0; i < b.length - 1; i++) { + if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) { + b[i] = Style.COLOR_CHAR; + b[i+1] = Character.toLowerCase(b[i+1]); + } + } + return new String(b); + } + + /** + * Gets the ChatColors used at the end of the given input string. + * + * @param input Input string to retrieve the colors from. + * @return Any remaining ChatColors to pass onto the next line. + */ + public static String getLastColors(String input) { + String result = ""; + int length = input.length(); + + // Search backwards from the end as it is faster + for (int index = length - 1; index > -1; index--) { + char section = input.charAt(index); + if (section == COLOR_CHAR && index < length - 1) { + char c = input.charAt(index + 1); + Style color = getByChar(c); + + if (color != null) { + result = color.toString() + result; + + // Once we find a color or reset we can stop searching + if (color.isColor() || color.equals(RESET)) { + break; + } + } + } + } + + return result; + } + + static { + for (Style color : values()) { + BY_ID.put(color.intCode, color); + BY_CHAR.put(color.code, color); + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java b/src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java new file mode 100644 index 000000000..35663fa9e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/StyleSet.java @@ -0,0 +1,249 @@ +/* + * 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.formatting; + +/** + * Represents set of styles, such as color, bold, etc. + */ +public class StyleSet { + + private Boolean bold; + private Boolean italic; + private Boolean underline; + private Boolean strikethrough; + private Style color; + + /** + * Create a new style set with no properties set. + */ + public StyleSet() { + } + + /** + * Create a new style set with the given styles. + * + *

{@link Style#RESET} will be ignored if provided.

+ * + * @param styles a list of styles + */ + public StyleSet(Style... styles) { + for (Style style : styles) { + if (style.isColor()) { + color = style; + } else if (style == Style.BOLD) { + bold = true; + } else if (style == Style.ITALIC) { + italic = true; + } else if (style == Style.UNDERLINE) { + underline = true; + } else if (style == Style.STRIKETHROUGH) { + strikethrough = true; + } + } + } + + /** + * Get whether this style set is bold. + * + * @return true, false, or null if unset + */ + public Boolean getBold() { + return bold; + } + + /** + * Get whether the text is bold. + * + * @return true if bold + */ + public boolean isBold() { + return getBold() != null && getBold() == true; + } + + /** + * Set whether the text is bold. + * + * @param bold true, false, or null to unset + */ + public void setBold(Boolean bold) { + this.bold = bold; + } + + /** + * Get whether this style set is italicized. + * + * @return true, false, or null if unset + */ + public Boolean getItalic() { + return italic; + } + + /** + * Get whether the text is italicized. + * + * @return true if italicized + */ + public boolean isItalic() { + return getItalic() != null && getItalic() == true; + } + + /** + * Set whether the text is italicized. + * + * @param italic false, or null to unset + */ + public void setItalic(Boolean italic) { + this.italic = italic; + } + + /** + * Get whether this style set is underlined. + * + * @return true, false, or null if unset + */ + public Boolean getUnderline() { + return underline; + } + + /** + * Get whether the text is underlined. + * + * @return true if underlined + */ + public boolean isUnderline() { + return getUnderline() != null && getUnderline() == true; + } + + /** + * Set whether the text is underline. + * + * @param underline false, or null to unset + */ + public void setUnderline(Boolean underline) { + this.underline = underline; + } + + /** + * Get whether this style set is stricken through. + * + * @return true, false, or null if unset + */ + public Boolean getStrikethrough() { + return strikethrough; + } + + /** + * Get whether the text is stricken through. + * + * @return true if there is strikethrough applied + */ + public boolean isStrikethrough() { + return getStrikethrough() != null && getStrikethrough() == true; + } + + /** + * Set whether the text is stricken through. + * + * @param strikethrough false, or null to unset + */ + public void setStrikethrough(Boolean strikethrough) { + this.strikethrough = strikethrough; + } + + /** + * Get the color of the text. + * + * @return true, false, or null if unset + */ + public Style getColor() { + return color; + } + + /** + * Set the color of the text. + * + * @param color the color + */ + public void setColor(Style color) { + this.color = color; + } + + /** + * Return whether text formatting (bold, italics, underline, strikethrough) is set. + * + * @return true if formatting is set + */ + public boolean hasFormatting() { + return getBold() != null || getItalic() != null + || getUnderline() != null || getStrikethrough() != null; + } + + /** + * Return where the text formatting of the given style set is different from + * that assigned to this one. + * + * @param other the other style set + * @return true if there is a difference + */ + public boolean hasEqualFormatting(StyleSet other) { + return getBold() == other.getBold() && getItalic() == other.getItalic() + && getUnderline() == other.getUnderline() && + getStrikethrough() == other.getStrikethrough(); + } + + /** + * Create a new instance with styles inherited from this one but with new styles + * from the given style set. + * + * @param style the style set + * @return a new style set instance + */ + public StyleSet extend(StyleSet style) { + StyleSet newStyle = clone(); + if (style.getBold() != null) { + newStyle.setBold(style.getBold()); + } + if (style.getItalic() != null) { + newStyle.setItalic(style.getItalic()); + } + if (style.getUnderline() != null) { + newStyle.setUnderline(style.getUnderline()); + } + if (style.getStrikethrough() != null) { + newStyle.setStrikethrough(style.getStrikethrough()); + } + if (style.getColor() != null) { + newStyle.setColor(style.getColor()); + } + return newStyle; + } + + @Override + public StyleSet clone() { + StyleSet style = new StyleSet(); + style.setBold(getBold()); + style.setItalic(getItalic()); + style.setUnderline(getUnderline()); + style.setStrikethrough(getStrikethrough()); + style.setColor(getColor()); + return style; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java b/src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java new file mode 100644 index 000000000..7674d9bf8 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/StyledFragment.java @@ -0,0 +1,150 @@ +/* + * 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.formatting; + +import java.util.ArrayList; +import java.util.List; + +/** + * A fragment of text that can be styled. + */ +public class StyledFragment extends Fragment { + + private final List children = new ArrayList(); + private StyleSet style; + private Fragment lastText; + + public StyledFragment() { + style = new StyleSet(); + } + + public StyledFragment(StyleSet style) { + this.style = style; + } + + public StyledFragment(Style... styles) { + this.style = new StyleSet(styles); + } + + public StyleSet getStyle() { + return style; + } + + public void setStyles(StyleSet style) { + this.style = style; + } + + public List getChildren() { + return children; + } + + protected Fragment lastText() { + Fragment text; + if (children.size() > 0) { + text = children.get(children.size() - 1); + if (text == lastText) { + return text; + } + } + + text = new Fragment(); + this.lastText = text; + children.add(text); + return text; + } + + public StyledFragment createFragment(Style... styles) { + StyledFragment fragment = new StyledFragment(styles); + append(fragment); + return fragment; + } + + public StyledFragment append(StyledFragment fragment) { + children.add(fragment); + return this; + } + + @Override + public StyledFragment append(String str) { + lastText().append(str); + return this; + } + + @Override + public StyledFragment append(Object obj) { + append(String.valueOf(obj)); + return this; + } + + @Override + public StyledFragment append(StringBuffer sb) { + append(String.valueOf(sb)); + return this; + } + + @Override + public StyledFragment append(CharSequence s) { + append(String.valueOf(s)); + return this; + } + + @Override + public StyledFragment append(boolean b) { + append(String.valueOf(b)); + return this; + } + + @Override + public StyledFragment append(char c) { + append(String.valueOf(c)); + return this; + } + + @Override + public StyledFragment append(int i) { + append(String.valueOf(i)); + return this; + } + + @Override + public StyledFragment append(long lng) { + append(String.valueOf(lng)); + return this; + } + + @Override + public StyledFragment append(float f) { + append(String.valueOf(f)); + return this; + } + + @Override + public StyledFragment append(double d) { + append(String.valueOf(d)); + return this; + } + + @Override + public StyledFragment newLine() { + append("\n"); + return this; + } + +} From edb6c56aab0ac1581272fffd89413fa47fbde6c6 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 18:43:54 -0700 Subject: [PATCH 42/45] Improved the styling of SimpleDispatcher's list of sub-commands. --- .../util/command/SimpleDispatcher.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) 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 12d2ebde5..a84c6b497 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -21,6 +21,9 @@ package com.sk89q.worldedit.util.command; import com.google.common.base.Joiner; import com.sk89q.minecraft.util.commands.*; +import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; +import com.sk89q.worldedit.util.formatting.CommandListBox; +import com.sk89q.worldedit.util.formatting.StyledFragment; import java.util.*; @@ -32,6 +35,16 @@ public class SimpleDispatcher implements Dispatcher { private final Map commands = new HashMap(); private final SimpleDescription description = new SimpleDescription(); + /** + * Create a new instance. + */ + public SimpleDispatcher() { + description.getParameters().add(new SimpleParameter("subcommand")); + SimpleParameter extraArgs = new SimpleParameter("..."); + extraArgs.setOptional(true); + description.getParameters().add(extraArgs); + } + @Override public void registerCommand(CommandCallable callable, String... alias) { CommandMapping mapping = new CommandMapping(callable, alias); @@ -119,7 +132,7 @@ public class SimpleDispatcher implements Dispatcher { } - throw new InvalidUsageException(getSubcommandList(locals), getDescription()); + throw new InvalidUsageException(ColorCodeBuilder.asColorCodes(getSubcommandList(locals, parentCommands)), getDescription()); } @Override @@ -172,25 +185,16 @@ public class SimpleDispatcher implements Dispatcher { return false; } - /** - * Get a list of subcommands for display. - * - * @return a string - */ - private String getSubcommandList(CommandLocals locals) { - Set aliases = getPrimaryAliases(); - - StringBuilder builder = new StringBuilder("Subcommands: "); - + private StyledFragment getSubcommandList(CommandLocals locals, String[] parentCommands) { + CommandListBox box = new CommandListBox("Subcommands"); + String prefix = parentCommands.length > 0 ? "/" + Joiner.on(" ").join(parentCommands) + " " : ""; for (CommandMapping mapping : getCommands()) { if (mapping.getCallable().testPermission(locals)) { - for (String alias : mapping.getAllAliases()) { - builder.append("\n- ").append(alias); - } + box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getShortDescription()); } } - return builder.toString(); + return box; } } From 723b8b6e63bda32532976cf8046eb6f496386524 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 18:44:09 -0700 Subject: [PATCH 43/45] Shortened and improved some command descriptions. --- .../java/com/sk89q/worldedit/command/UtilityCommands.java | 2 +- .../com/sk89q/worldedit/command/WorldEditCommands.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index f10d74e91..7487cd52e 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -488,7 +488,7 @@ public class UtilityCommands { @Command( aliases = { "/help" }, usage = "[]", - desc = "Displays help for the given command or lists all commands.", + desc = "Displays help for WorldEdit commands", min = 0, max = -1 ) diff --git a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index dfdbf36f1..33070a6b3 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -71,7 +71,7 @@ public class WorldEditCommands { @Command( aliases = { "reload" }, usage = "", - desc = "Reload WorldEdit", + desc = "Reload configuration", min = 0, max = 0 ) @@ -84,7 +84,7 @@ public class WorldEditCommands { @Command( aliases = { "cui" }, usage = "", - desc = "Complete CUI handshake", + desc = "Complete CUI handshake (internal usage)", min = 0, max = 0 ) @@ -96,7 +96,7 @@ public class WorldEditCommands { @Command( aliases = { "tz" }, usage = "[timezone]", - desc = "Set your timezone", + desc = "Set your timezone for snapshots", min = 1, max = 1 ) @@ -111,7 +111,7 @@ public class WorldEditCommands { @Command( aliases = { "help" }, usage = "[]", - desc = "Displays help for the given command or lists all commands.", + desc = "Displays help for WorldEdit commands", min = 0, max = -1 ) From d7d7b2741a4eb26b7ecf7870ff47f3d5f5edebee Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 18:53:06 -0700 Subject: [PATCH 44/45] Added MaxBrushRadiusException to WorldEditExceptionConverter. --- .../internal/command/WorldEditExceptionConverter.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java index 520e0d08d..6b8831d13 100644 --- a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java @@ -90,9 +90,14 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper { + e.getBlockLimit() + ")."); } + @ExceptionMatch + public void convert(MaxBrushRadiusException e) throws CommandException { + throw new CommandException("Maximum brush radius (in configuration): " + worldEdit.getConfiguration().maxBrushRadius); + } + @ExceptionMatch public void convert(MaxRadiusException e) throws CommandException { - throw new CommandException("Maximum radius: " + worldEdit.getConfiguration().maxRadius); + throw new CommandException("Maximum radius (in configuration): " + worldEdit.getConfiguration().maxRadius); } @ExceptionMatch From d86d81ef21f8fc5a80ad00ca3217da227e8b62df Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 28 Jun 2014 19:13:46 -0700 Subject: [PATCH 45/45] Switch Style to Preconditions. --- .../java/com/sk89q/worldedit/util/formatting/Style.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sk89q/worldedit/util/formatting/Style.java b/src/main/java/com/sk89q/worldedit/util/formatting/Style.java index 80774b743..3eafd3c8d 100644 --- a/src/main/java/com/sk89q/worldedit/util/formatting/Style.java +++ b/src/main/java/com/sk89q/worldedit/util/formatting/Style.java @@ -20,11 +20,13 @@ package com.sk89q.worldedit.util.formatting; import com.google.common.collect.Maps; -import org.apache.commons.lang.Validate; import java.util.Map; import java.util.regex.Pattern; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + /** * All supported color values for chat. * @@ -194,8 +196,8 @@ public enum Style { * @return Associative {@link org.bukkit.ChatColor} with the given code, or null if it doesn't exist */ public static Style getByChar(String code) { - Validate.notNull(code, "Code cannot be null"); - Validate.isTrue(code.length() > 0, "Code must have at least one char"); + checkNotNull(code); + checkArgument(!code.isEmpty(), "Code must have at least one character"); return BY_CHAR.get(code.charAt(0)); }