diff --git a/pom.xml b/pom.xml index 89e1d0efc..24b512526 100644 --- a/pom.xml +++ b/pom.xml @@ -492,11 +492,23 @@ true + + + org.sk89q.bukkit + bukkit-classloader-check + 1.7.2-R0.3 + runtime + jar + true + + + org.bukkit bukkit - 1.7.2-R0.2 + 1.7.2-R0.3 compile jar true diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index bf08bf45a..1bf4c6a61 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -26,7 +26,9 @@ import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.ServerInterface; 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.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Description; @@ -39,11 +41,12 @@ import org.bukkit.entity.EntityType; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collection; import java.util.EnumMap; import java.util.List; import java.util.Map; -public class BukkitServerInterface extends ServerInterface { +public class BukkitServerInterface extends ServerInterface implements MultiUserPlatform { public Server server; public WorldEditPlugin plugin; private CommandRegistration dynamicCommands; @@ -168,6 +171,7 @@ public class BukkitServerInterface extends ServerInterface { public Map getCapabilities() { Map capabilities = new EnumMap(Capability.class); capabilities.put(Capability.CONFIGURATION, Preference.NORMAL); + capabilities.put(Capability.WORLDEDIT_CUI, Preference.NORMAL); capabilities.put(Capability.GAME_HOOKS, Preference.PREFERRED); capabilities.put(Capability.PERMISSIONS, Preference.PREFERRED); capabilities.put(Capability.USER_COMMANDS, Preference.PREFERRED); @@ -178,4 +182,13 @@ public class BukkitServerInterface extends ServerInterface { public void unregisterCommands() { dynamicCommands.unregisterCommands(); } + + @Override + public Collection getConnectedUsers() { + List users = new ArrayList(); + for (org.bukkit.entity.Player player : Bukkit.getServer().getOnlinePlayers()) { + users.add(new BukkitPlayer(plugin, this, player)); + } + return users; + } } diff --git a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 67d2152b1..458aa8477 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -1312,4 +1312,12 @@ public class BukkitWorld extends LocalWorld { return null; } + /** + * @deprecated Use {@link #setBlock(Vector, BaseBlock, boolean)} + */ + @Deprecated + public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) throws WorldEditException { + return setBlock(pt, (BaseBlock) block, notifyAdjacent); + } + } diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java index 818a14573..62c04afb3 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -23,7 +23,9 @@ 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.Actor; import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Description; @@ -38,13 +40,19 @@ import net.minecraft.entity.EntityList; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.Item; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.management.ServerConfigurationManager; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; -class ForgePlatform extends ServerInterface { +class ForgePlatform extends ServerInterface implements MultiUserPlatform { private final ForgeWorldEdit mod; private final MinecraftServer server; @@ -205,6 +213,7 @@ class ForgePlatform extends ServerInterface { public Map getCapabilities() { Map capabilities = new EnumMap(Capability.class); capabilities.put(Capability.CONFIGURATION, Preference.PREFER_OTHERS); + capabilities.put(Capability.WORLDEDIT_CUI, Preference.NORMAL); capabilities.put(Capability.GAME_HOOKS, Preference.NORMAL); capabilities.put(Capability.PERMISSIONS, Preference.PREFER_OTHERS); capabilities.put(Capability.USER_COMMANDS, Preference.NORMAL); @@ -212,4 +221,16 @@ class ForgePlatform extends ServerInterface { return capabilities; } + @Override + public Collection getConnectedUsers() { + List users = new ArrayList(); + ServerConfigurationManager scm = server.getConfigurationManager(); + for (String name : scm.getAllUsernames()) { + EntityPlayerMP entity = scm.getPlayerForUsername(name); + if (entity != null) { + users.add(new ForgePlayer(entity)); + } + } + return users; + } } diff --git a/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java b/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java index ecfeef15d..4cb581e3d 100644 --- a/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/NavigationCommands.java @@ -23,8 +23,14 @@ 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.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.entity.Player; +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.POSITION; @@ -68,13 +74,7 @@ public class NavigationCommands { max = 1 ) @CommandPermissions("worldedit.navigation.ascend") - public void ascend(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - int levelsToAscend = 0; - if (args.argsLength() == 0) { - levelsToAscend = 1; - } else { - levelsToAscend = args.getInteger(0); - } + public void ascend(Player player, @Optional("1") int levelsToAscend) throws WorldEditException { int ascentLevels = 1; while (player.ascendLevel() && levelsToAscend != ascentLevels) { ++ascentLevels; @@ -94,13 +94,7 @@ public class NavigationCommands { max = 1 ) @CommandPermissions("worldedit.navigation.descend") - public void descend(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - int levelsToDescend = 0; - if (args.argsLength() == 0) { - levelsToDescend = 1; - } else { - levelsToDescend = args.getInteger(0); - } + public void descend(Player player, @Optional("1") int levelsToDescend) throws WorldEditException { int descentLevels = 1; while (player.descendLevel() && levelsToDescend != descentLevels) { ++descentLevels; @@ -181,7 +175,6 @@ public class NavigationCommands { @CommandPermissions("worldedit.navigation.up") @Logging(POSITION) public void up(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - final int distance = args.getInteger(0); final boolean alwaysGlass = getAlwaysGlass(args); diff --git a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index e8dd44277..5f82d1b55 100644 --- a/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -449,7 +449,8 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.forest") @Logging(REGION) - public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, @Optional("5") double density) throws WorldEditException { + public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, + @Optional("5") @Range(min = 0, max = 100) double density) throws WorldEditException { density = density / 100; ForestGenerator generator = new ForestGenerator(editSession, new TreeGenerator(type)); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); @@ -469,7 +470,7 @@ public class RegionCommands { ) @CommandPermissions("worldedit.region.flora") @Logging(REGION) - public void flora(Player player, EditSession editSession, @Selection Region region, @Optional("10") double density) throws WorldEditException { + public void flora(Player player, EditSession editSession, @Selection Region region, @Optional("10") @Range(min = 0, max = 100) double density) throws WorldEditException { density = density / 100; FloraGenerator generator = new FloraGenerator(editSession); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); diff --git a/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index 4a4acaa84..4d8d165dc 100644 --- a/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -20,7 +20,6 @@ package com.sk89q.worldedit.command; import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandAlias; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; @@ -706,7 +705,7 @@ public class SelectionCommands { } @Command( - aliases = { "/sel", ";" }, + aliases = { "/sel", ";", "/desel", "/deselect" }, usage = "[cuboid|extend|poly|ellipsoid|sphere|cyl|convex]", desc = "Choose a region selector", min = 0, @@ -761,9 +760,4 @@ public class SelectionCommands { session.dispatchCUISelection(player); } - @Command(aliases = {"/desel", "/deselect"}, desc = "Deselect the current selection") - @CommandAlias("/sel") - public void deselect() { - - } } diff --git a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index d57bedd28..d1e0852d8 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -22,6 +22,7 @@ package com.sk89q.worldedit.command; import com.google.common.base.Joiner; 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.Logging; import com.sk89q.worldedit.EditSession; @@ -36,6 +37,9 @@ 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.CommandManager; +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; import com.sk89q.worldedit.patterns.Pattern; import com.sk89q.worldedit.patterns.SingleBlockPattern; import com.sk89q.worldedit.regions.CuboidRegion; @@ -44,6 +48,7 @@ import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.CommandMapping; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.PrimaryAliasComparator; +import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.util.command.parametric.Optional; import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; import com.sk89q.worldedit.util.formatting.Style; @@ -504,6 +509,24 @@ public class UtilityCommands { player.print("Marked " + removed + " entit(ies) for removal."); } + @Command( + aliases = { "/calc", "/calculate", "/eval", "/evaluate", "/solve" }, + usage = "", + desc = "Evaluate a mathematical expression" + ) + public void calc(Actor actor, @Text String input) throws CommandException { + try { + Expression expression = Expression.compile(input); + actor.print("= " + expression.evaluate()); + } catch (EvaluationException e) { + actor.printError(String.format( + "'%s' could not be parsed as a valid expression", input)); + } catch (ExpressionException e) { + actor.printError(String.format( + "'%s' could not be evaluated (error: %s)", input, e.getMessage())); + } + } + @Command( aliases = { "/help" }, usage = "[]", diff --git a/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java b/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java index 49b345d71..9d2b56f19 100644 --- a/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java +++ b/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java @@ -42,6 +42,26 @@ public class ParserContext { private boolean restricted = true; private boolean preferringWildcard; + /** + * Create a new instance. + */ + public ParserContext() { + } + + /** + * Creates a copy of another instance. + * + * @param other the other instance + */ + public ParserContext(ParserContext other) { + setExtent(other.getExtent()); + setSession(other.getSession()); + setWorld(other.getWorld()); + setActor(other.getActor()); + setRestricted(other.isRestricted()); + setPreferringWildcard(other.isPreferringWildcard()); + } + /** * Get the {@link Extent} set on this context. * diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java b/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java index 61ee2bfb8..a753b0fd2 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Capability.java @@ -65,6 +65,11 @@ public enum Capability { */ PERMISSIONS, + /** + * The capability of a platform to dispatch WorldEditCUI events. + */ + WORLDEDIT_CUI, + /** * The capability of a platform to perform modifications to a world. */ 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 9c71cfe69..466903332 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -33,6 +33,7 @@ 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.UserCommandCompleter; import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; import com.sk89q.worldedit.session.request.Request; @@ -93,6 +94,7 @@ public final class CommandManager { // Set up the commands manager ParametricBuilder builder = new ParametricBuilder(); builder.setAuthorizer(new ActorAuthorizer()); + builder.setDefaultCompleter(new UserCommandCompleter(platformManager)); builder.addBinding(new WorldEditBinding(worldEdit)); builder.addExceptionConverter(new WorldEditExceptionConverter(worldEdit)); builder.addInvokeListener(new LegacyCommandsHandler()); diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/MultiUserPlatform.java b/src/main/java/com/sk89q/worldedit/extension/platform/MultiUserPlatform.java new file mode 100644 index 000000000..6fa94e017 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/MultiUserPlatform.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.extension.platform; + +import java.util.Collection; + +/** + * Implements a platform with multiple connected users. + */ +public interface MultiUserPlatform extends Platform { + + /** + * Get a list of connected users. + * + * @return a list of connected users + */ + Collection getConnectedUsers(); + +} 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 3e70e2483..a62bc247a 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,12 @@ public class PlatformManager { permActor = player; } - return (T) new PlayerProxy(player, permActor, getWorldForEditing(player.getWorld())); + Player cuiActor = queryCapability(Capability.WORLDEDIT_CUI).matchPlayer(player); + if (cuiActor == null) { + cuiActor = player; + } + + return (T) new PlayerProxy(player, permActor, cuiActor, getWorldForEditing(player.getWorld())); } else { return base; } diff --git a/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index 7da9d7853..4aeb23d7a 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.WorldVector; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; @@ -33,14 +34,17 @@ class PlayerProxy extends AbstractPlayerActor { private final Player basePlayer; private final Actor permActor; + private final Actor cuiActor; private final World world; - PlayerProxy(Player basePlayer, Actor permActor, World world) { + PlayerProxy(Player basePlayer, Actor permActor, Actor cuiActor, World world) { checkNotNull(basePlayer); checkNotNull(permActor); + checkNotNull(cuiActor); checkNotNull(world); this.basePlayer = basePlayer; this.permActor = permActor; + this.cuiActor = cuiActor; this.world = world; } @@ -128,4 +132,9 @@ class PlayerProxy extends AbstractPlayerActor { public boolean hasPermission(String perm) { return permActor.hasPermission(perm); } + + @Override + public void dispatchCUIEvent(CUIEvent event) { + cuiActor.dispatchCUIEvent(event); + } } diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java b/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java index b85b9ff1f..304a49b60 100644 --- a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java +++ b/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java @@ -132,7 +132,10 @@ class DefaultMaskParser extends InputParser { } default: - return new BlockMask(extent, worldEdit.getBlockRegistry().parseFromListInput(component, context)); + ParserContext tempContext = new ParserContext(context); + tempContext.setRestricted(false); + tempContext.setPreferringWildcard(true); + return new BlockMask(extent, worldEdit.getBlockRegistry().parseFromListInput(component, tempContext)); } } diff --git a/src/main/java/com/sk89q/worldedit/internal/command/UserCommandCompleter.java b/src/main/java/com/sk89q/worldedit/internal/command/UserCommandCompleter.java new file mode 100644 index 000000000..63957cbca --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/command/UserCommandCompleter.java @@ -0,0 +1,72 @@ +/* + * 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.internal.command; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.MultiUserPlatform; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extension.platform.PlatformManager; +import com.sk89q.worldedit.util.command.CommandCompleter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Provides the names of connected users as suggestions. + */ +public class UserCommandCompleter implements CommandCompleter { + + private final PlatformManager platformManager; + + /** + * Create a new instance. + * + * @param platformManager the platform manager + */ + public UserCommandCompleter(PlatformManager platformManager) { + checkNotNull(platformManager); + this.platformManager = platformManager; + } + + @Override + public List getSuggestions(String arguments, CommandLocals locals) throws CommandException { + Platform platform = platformManager.queryCapability(Capability.USER_COMMANDS); + if (platform instanceof MultiUserPlatform) { + List suggestions = new ArrayList(); + Collection users = ((MultiUserPlatform) platform).getConnectedUsers(); + for (Actor user : users) { + if (user.getName().toLowerCase().startsWith(arguments.toLowerCase().trim())) { + suggestions.add(user.getName()); + } + } + return suggestions; + } else { + return Collections.emptyList(); + } + } + +} 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 3c75884c5..85b4133a0 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.java @@ -22,12 +22,10 @@ package com.sk89q.worldedit.util.command; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; -import java.util.List; - /** * A command that can be executed. */ -public interface CommandCallable { +public interface CommandCallable extends CommandCompleter { /** * Execute the correct command based on the input. @@ -41,16 +39,6 @@ public interface CommandCallable { * @throws CommandException thrown on a command error */ boolean call(String arguments, CommandLocals locals, String[] parentCommands) throws CommandException; - - /** - * 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, CommandLocals locals) throws CommandException; /** * Get an object describing this command. diff --git a/src/main/java/com/sk89q/worldedit/util/command/CommandCompleter.java b/src/main/java/com/sk89q/worldedit/util/command/CommandCompleter.java new file mode 100644 index 000000000..19bf47d60 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCompleter.java @@ -0,0 +1,42 @@ +/* + * 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.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; + +import java.util.List; + +/** + * Provides a method that can provide tab completion for commands. + */ +public interface CommandCompleter { + + /** + * 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, CommandLocals locals) throws CommandException; + +} diff --git a/src/main/java/com/sk89q/worldedit/util/command/NullCompleter.java b/src/main/java/com/sk89q/worldedit/util/command/NullCompleter.java new file mode 100644 index 000000000..10850c696 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/NullCompleter.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.command; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; + +import java.util.Collections; +import java.util.List; + +/** + * Always returns an empty list of suggestions. + */ +public class NullCompleter implements CommandCompleter { + + @Override + public List getSuggestions(String arguments, CommandLocals locals) throws CommandException { + return Collections.emptyList(); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java b/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java index a4ed7ab0c..2a5ee0ccf 100644 --- a/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java @@ -19,8 +19,16 @@ package com.sk89q.worldedit.util.command.binding; -import com.sk89q.worldedit.util.command.parametric.*; +import com.sk89q.worldedit.internal.expression.Expression; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.internal.expression.runtime.EvaluationException; +import com.sk89q.worldedit.util.command.parametric.ArgumentStack; +import com.sk89q.worldedit.util.command.parametric.BindingBehavior; +import com.sk89q.worldedit.util.command.parametric.BindingHelper; +import com.sk89q.worldedit.util.command.parametric.BindingMatch; +import com.sk89q.worldedit.util.command.parametric.ParameterException; +import javax.annotation.Nullable; import java.lang.annotation.Annotation; /** @@ -84,6 +92,35 @@ public final class PrimitiveBindings extends BindingHelper { return context.nextBoolean(); } + /** + * Try to parse numeric input as either a number or a mathematical expression. + * + * @param input input + * @return a number + * @throws ParameterException thrown on parse error + */ + private @Nullable Double parseNumericInput(@Nullable String input) throws ParameterException { + if (input == null) { + return null; + } + + try { + return Double.parseDouble(input); + } catch (NumberFormatException e1) { + try { + Expression expression = Expression.compile(input); + return expression.evaluate(); + } catch (EvaluationException e) { + throw new ParameterException(String.format( + "Expected '%s' to be a valid number (or a valid mathematical expression)", input)); + } catch (ExpressionException e) { + throw new ParameterException(String.format( + "Expected '%s' to be a number or valid math expression (error: %s)", input, e.getMessage())); + } + + } + } + /** * Gets a type from a {@link ArgumentStack}. * @@ -96,13 +133,15 @@ public final class PrimitiveBindings extends BindingHelper { behavior = BindingBehavior.CONSUMES, consumedCount = 1, provideModifiers = true) - public Integer getInteger(ArgumentStack context, Annotation[] modifiers) - throws ParameterException { - Integer v = context.nextInt(); + public Integer getInteger(ArgumentStack context, Annotation[] modifiers) throws ParameterException { + Double v = parseNumericInput(context.next()); if (v != null) { - validate(v, modifiers); + int intValue = v.intValue(); + validate(intValue, modifiers); + return intValue; + } else { + return null; } - return v; } /** @@ -117,8 +156,7 @@ public final class PrimitiveBindings extends BindingHelper { behavior = BindingBehavior.CONSUMES, consumedCount = 1, provideModifiers = true) - public Short getShort(ArgumentStack context, Annotation[] modifiers) - throws ParameterException { + public Short getShort(ArgumentStack context, Annotation[] modifiers) throws ParameterException { Integer v = getInteger(context, modifiers); if (v != null) { return v.shortValue(); @@ -138,13 +176,14 @@ public final class PrimitiveBindings extends BindingHelper { behavior = BindingBehavior.CONSUMES, consumedCount = 1, provideModifiers = true) - public Double getDouble(ArgumentStack context, Annotation[] modifiers) - throws ParameterException { - Double v = context.nextDouble(); + public Double getDouble(ArgumentStack context, Annotation[] modifiers) throws ParameterException { + Double v = parseNumericInput(context.next()); if (v != null) { validate(v, modifiers); + return v; + } else { + return null; } - return v; } /** @@ -159,8 +198,7 @@ public final class PrimitiveBindings extends BindingHelper { behavior = BindingBehavior.CONSUMES, consumedCount = 1, provideModifiers = true) - public Float getFloat(ArgumentStack context, Annotation[] modifiers) - throws ParameterException { + public Float getFloat(ArgumentStack context, Annotation[] modifiers) throws ParameterException { Double v = getDouble(context, modifiers); if (v != null) { return v.floatValue(); 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 dd96c4a2a..c7b94ebde 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 @@ -27,7 +27,9 @@ 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.CommandCompleter; import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.NullCompleter; import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; import com.sk89q.worldedit.util.command.binding.StandardBindings; import com.sk89q.worldedit.util.command.binding.Switch; @@ -57,6 +59,7 @@ public class ParametricBuilder { private final List invokeListeners = new ArrayList(); private final List exceptionConverters = new ArrayList(); private Authorizer authorizer = new NullAuthorizer(); + private CommandCompleter defaultCompleter = new NullCompleter(); /** * Create a new builder. @@ -225,4 +228,26 @@ public class ParametricBuilder { checkNotNull(authorizer); this.authorizer = authorizer; } + + /** + * Get the default command suggestions provider that will be used if + * no suggestions are available. + * + * @return the default command completer + */ + public CommandCompleter getDefaultCompleter() { + return defaultCompleter; + } + + /** + * Set the default command suggestions provider that will be used if + * no suggestions are available. + * + * @param defaultCompleter the default command completer + */ + public void setDefaultCompleter(CommandCompleter defaultCompleter) { + checkNotNull(defaultCompleter); + this.defaultCompleter = defaultCompleter; + } + } 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 a96434ac0..5a7836264 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 @@ -41,7 +41,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -273,8 +272,8 @@ class ParametricCallable implements CommandCallable { } @Override - public List getSuggestions(String stringArguments, CommandLocals locals) throws CommandException { - return Collections.emptyList(); + public List getSuggestions(String arguments, CommandLocals locals) throws CommandException { + return builder.getDefaultCompleter().getSuggestions(arguments, locals); } /**