diff --git a/build.gradle b/build.gradle index cb0a83187..118600546 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' @@ -41,6 +44,7 @@ dependencies { compile group: 'com.google.guava', name: 'guava', version:'10.0.1' compile group: 'com.sk89q', name: 'jchronic', version:'0.2.4a' compile group: 'com.google.code.findbugs', name: 'jsr305', version: '1.3.9' + compile group: 'com.thoughtworks.paranamer', name: 'paranamer', version: '2.6' testCompile group: 'org.mockito', name: 'mockito-core', version:'1.9.0-rc1' } @@ -76,4 +80,36 @@ 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:' + include '*:paranamer: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) diff --git a/pom.xml b/pom.xml index b6b154888..b9d717b8b 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,13 @@ + + + sk89q-repo + http://maven.sk89q.com/repo/ + + + @@ -151,6 +158,15 @@ jar + + + com.thoughtworks.paranamer + paranamer + 2.6 + compile + jar + + org.mockito @@ -297,6 +313,26 @@ + + + com.thoughtworks.paranamer + paranamer-maven-plugin-largestack + 2.5.5-SNAPSHOT + + + run + compile + + ${project.build.sourceDirectory} + ${project.build.outputDirectory} + + + generate + + + + + org.apache.maven.plugins @@ -352,6 +388,7 @@ com.sk89q:jchronic + com.thoughtworks.paranamer:paranamer 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/BukkitCommandSender.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index 842eb4c55..a8dc5adc2 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -19,19 +19,29 @@ package com.sk89q.worldedit.bukkit; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.entity.BaseEntity; -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, ServerInterface server, CommandSender sender) { - super(server); + BukkitCommandSender(WorldEditPlugin plugin, CommandSender sender) { + checkNotNull(plugin); + checkNotNull(sender); + checkArgument(!(sender instanceof Player), "Cannot wrap a player"); + this.plugin = plugin; this.sender = sender; } @@ -69,6 +79,11 @@ public class BukkitCommandSender extends LocalPlayer { } } + @Override + public boolean canDestroyBedrock() { + return true; + } + @Override public String[] getGroups() { return new String[0]; @@ -76,65 +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 BaseEntity getState() { - throw new UnsupportedOperationException("Cannot create a state from this object"); + public File openFileSaveDialog(String[] extensions) { + return null; } @Override - public Location getLocation() { - throw new PlayerNeededException(); + public void dispatchCUIEvent(CUIEvent event) { } - @Override - public WorldVector getPosition() { - throw new PlayerNeededException(); - } - - @Override - public LocalWorld getWorld() { - 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/BukkitPlayer.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 7d3dbdeeb..1fcaf1c1d 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -42,7 +42,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; } @@ -53,6 +52,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 6a34748a9..bf08bf45a 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -21,19 +21,25 @@ package com.sk89q.worldedit.bukkit; 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.*; +import com.sk89q.worldedit.BiomeTypes; +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.Capability; +import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.Description; +import com.sk89q.worldedit.util.command.Dispatcher; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.EntityType; -import java.lang.reflect.Method; +import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; +import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -42,6 +48,7 @@ public class BukkitServerInterface extends ServerInterface { public WorldEditPlugin plugin; private CommandRegistration dynamicCommands; private BukkitBiomeTypes biomes; + private boolean hookingEvents; public BukkitServerInterface(WorldEditPlugin plugin, Server server) { this.plugin = plugin; @@ -50,6 +57,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); @@ -89,31 +100,50 @@ public class BukkitServerInterface extends ServerInterface { return ret; } + @Nullable @Override - public void onCommandRegistration(List commands, CommandsManager manager) { + 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 registerCommands(Dispatcher dispatcher) { List toRegister = new ArrayList(); - for (Command command : commands) { - List permissions = null; - Method cmdMethod = manager.getMethods().get(null).get(command.aliases()[0]); - Map childMethods = manager.getMethods().get(cmdMethod); + 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); - if (cmdMethod != null && cmdMethod.isAnnotationPresent(CommandPermissions.class)) { - permissions = Arrays.asList(cmdMethod.getAnnotation(CommandPermissions.class).value()); - } else if (cmdMethod != null && childMethods != null && childMethods.size() > 0) { - permissions = new ArrayList(); - for (Method m : childMethods.values()) { - if (m.isAnnotationPresent(CommandPermissions.class)) { - permissions.addAll(Arrays.asList(m.getAnnotation(CommandPermissions.class).value())); - } - } - } - - toRegister.add(new CommandInfo(command.usage(), command.desc(), command.aliases(), commands, permissions == null ? null : permissions.toArray(new String[permissions.size()]))); + toRegister.add(new CommandInfo(description.getUsage(), description.getShortDescription(), command.getAllAliases(), inspector, permissionsArray)); } dynamicCommands.register(toRegister); } + @Override + public void registerGameHooks() { + hookingEvents = true; + } + @Override public LocalConfiguration getConfiguration() { return plugin.getLocalConfiguration(); @@ -134,6 +164,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/BukkitWorld.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 18a28029f..dfafb852f 100644 --- a/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/src/bukkit/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -1136,13 +1136,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/WorldEditListener.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditListener.java index fbb335543..91a7adcda 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())); } @@ -88,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); } } @@ -114,6 +123,10 @@ public class WorldEditListener implements Listener { */ @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { + if (!plugin.getInternalPlatform().isHookingEvents()) { + return; + } + if (event.useItemInHand() == Result.DENY) { return; } @@ -125,7 +138,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 +172,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/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/src/bukkit/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 7efb2b240..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.*; @@ -26,20 +27,22 @@ 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.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.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.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; @@ -49,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 @@ -106,21 +109,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) { @@ -220,24 +221,33 @@ 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, 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(); - controller.handleCommand(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. * @@ -340,12 +350,12 @@ 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); } - return new BukkitCommandSender(this, this.server, sender); + return new BukkitCommandSender(this, sender); } /** @@ -357,6 +367,10 @@ public class WorldEditPlugin extends JavaPlugin { return server; } + BukkitServerInterface getInternalPlatform() { + return server; + } + /** * Get WorldEdit. * 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 diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java index f24396222..818a14573 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -19,36 +19,49 @@ package com.sk89q.worldedit.forge; -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.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.Description; +import com.sk89q.worldedit.util.command.Dispatcher; +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 java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import javax.annotation.Nullable; +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 +78,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()); @@ -89,20 +107,50 @@ class ForgePlatform extends ServerInterface { return ret; } + @Nullable @Override - public void onCommandRegistration(List commands) { + 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 registerCommands(Dispatcher dispatcher) { if (server == null) return; ServerCommandManager mcMan = (ServerCommandManager) server.getCommandManager(); - for (final Command cmd : commands) { + + for (final CommandMapping command : dispatcher.getCommands()) { + final Description description = command.getDescription(); + mcMan.registerCommand(new CommandBase() { @Override public String getCommandName() { - return cmd.aliases()[0]; + return command.getPrimaryAlias(); } @Override public List getCommandAliases() { - return Arrays.asList(cmd.aliases()); + return Arrays.asList(command.getAllAliases()); } @Override @@ -110,12 +158,14 @@ class ForgePlatform extends ServerInterface { @Override public String getCommandUsage(ICommandSender icommandsender) { - return "/" + cmd.aliases()[0] + " " + cmd.usage(); + return "/" + command.getPrimaryAlias() + " " + description.getUsage(); } @Override - public int compareTo(Object o) { - if (o instanceof ICommand) { + public int compareTo(@Nullable Object o) { + if (o == null) { + return -1; + } else if (o instanceof ICommand) { return super.compareTo((ICommand) o); } else { return -1; @@ -125,6 +175,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 +200,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/ForgePlayer.java b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java index 20ca43ad3..6a07c1ff8 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -39,7 +39,6 @@ public class ForgePlayer extends LocalPlayer { private EntityPlayerMP player; protected ForgePlayer(EntityPlayerMP player) { - super((ServerInterface) ForgeWorldEdit.inst.getPlatform()); this.player = player; } @@ -65,7 +64,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/ForgeWorld.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java index ebb90d33d..e9f712d09 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -113,7 +113,7 @@ public class ForgeWorld extends AbstractWorld { @Override public String getName() { - return getWorld().provider.getDimensionName(); + return getWorld().getWorldInfo().getWorldName(); } @Override @@ -465,11 +465,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/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 283e99a97..dbc53eb1d 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -19,13 +19,14 @@ 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; 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 +104,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,19 +113,28 @@ 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)) { 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); } } @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(); @@ -137,7 +144,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); @@ -146,9 +153,11 @@ public class ForgeWorldEdit { if (we.handleArmSwing(player)) { event.setCanceled(true); } + + break; } 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); @@ -157,11 +166,15 @@ public class ForgeWorldEdit { if (we.handleRightClick(player)) { event.setCanceled(true); } + + break; } case RIGHT_CLICK_AIR: { if (we.handleRightClick(player)) { event.setCanceled(true); } + + break; } } } @@ -236,7 +249,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(); diff --git a/src/forge/resources/worldedit.properties b/src/forge/resources/defaults/worldedit.properties similarity index 96% rename from src/forge/resources/worldedit.properties rename to src/forge/resources/defaults/worldedit.properties index f7e362f20..9c3f5d327 100644 --- a/src/forge/resources/worldedit.properties +++ b/src/forge/resources/defaults/worldedit.properties @@ -1,32 +1,32 @@ -#Don't put comments; they get removed -default-max-polygon-points=-1 -schematic-save-dir=schematics -allow-extra-data-values=false -super-pickaxe-many-drop-items=true -register-help=true -nav-wand-item=345 -profile=false -super-pickaxe-drop-items=true -disallowed-blocks=6,26,27,28,31,32,34,36,37,38,39,40,46,50,51,55,59,66,69,75,76,93,94,77,81,83,7,14,15,16,56 -max-super-pickaxe-size=5 -max-brush-radius=10 -craftscript-dir=craftscripts -no-double-slash=false -wand-item=271 -shell-save-type= -scripting-timeout=3000 -snapshots-dir= -use-inventory-creative-override=false -log-file=worldedit.log -max-changed-blocks=-1 -nav-wand-distance=50 -butcher-default-radius=-1 -default-max-changed-blocks=-1 -history-size=15 -use-inventory=false -allow-symbolic-links=false -use-inventory-override=false -log-commands=false -butcher-max-radius=-1 -max-polygon-points=20 -max-radius=-1 +#Don't put comments; they get removed +default-max-polygon-points=-1 +schematic-save-dir=schematics +allow-extra-data-values=false +super-pickaxe-many-drop-items=true +register-help=true +nav-wand-item=345 +profile=false +super-pickaxe-drop-items=true +disallowed-blocks=6,26,27,28,31,32,34,36,37,38,39,40,46,50,51,55,59,66,69,75,76,93,94,77,81,83,7,14,15,16,56 +max-super-pickaxe-size=5 +max-brush-radius=10 +craftscript-dir=craftscripts +no-double-slash=false +wand-item=271 +shell-save-type= +scripting-timeout=3000 +snapshots-dir= +use-inventory-creative-override=false +log-file=worldedit.log +max-changed-blocks=-1 +nav-wand-distance=50 +butcher-default-radius=-1 +default-max-changed-blocks=-1 +history-size=15 +use-inventory=false +allow-symbolic-links=false +use-inventory-override=false +log-commands=false +butcher-max-radius=-1 +max-polygon-points=20 +max-radius=-1 diff --git a/src/main/build/import-control.xml b/src/main/build/import-control.xml index 56ba42a5c..9ef48920d 100644 --- a/src/main/build/import-control.xml +++ b/src/main/build/import-control.xml @@ -9,6 +9,7 @@ + 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 3e26fdd0b..31900a9d2 100644 --- a/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java +++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandContext.java @@ -28,15 +28,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 { @@ -44,28 +51,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; } @@ -113,9 +142,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; @@ -140,16 +174,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() { @@ -176,6 +224,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)); @@ -271,4 +331,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 c87f0391c..eae94c75f 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..e0053f0b3 --- /dev/null +++ b/src/main/java/com/sk89q/minecraft/util/commands/CommandLocals.java @@ -0,0 +1,50 @@ +/* + * 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.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..7f435cf13 --- /dev/null +++ b/src/main/java/com/sk89q/minecraft/util/commands/SuggestionContext.java @@ -0,0 +1,68 @@ +/* + * 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.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/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index d246b3893..7ad6b867f 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -59,7 +59,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; @@ -276,7 +275,6 @@ public class EditSession implements Extent { * * @return mask, may be null */ - @SuppressWarnings("deprecation") public Mask getMask() { return oldMask; } @@ -286,13 +284,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)); } } @@ -798,7 +807,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 @@ -867,7 +876,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); } @@ -888,7 +897,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/EditSessionFactory.java b/src/main/java/com/sk89q/worldedit/EditSessionFactory.java index e16da791f..285ecc4ab 100644 --- a/src/main/java/com/sk89q/worldedit/EditSessionFactory.java +++ b/src/main/java/com/sk89q/worldedit/EditSessionFactory.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.extent.EditSessionEvent; import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.util.eventbus.EventBus; @@ -63,7 +64,7 @@ public class EditSessionFactory { * @param maxBlocks the maximum number of blocks that can be changed, or -1 to use no limit * @param player the player that the {@link EditSession} is for */ - public EditSession getEditSession(World world, int maxBlocks, LocalPlayer player) { + public EditSession getEditSession(World world, int maxBlocks, Player player) { throw new IllegalArgumentException("This class is being removed"); } @@ -96,7 +97,7 @@ public class EditSessionFactory { * @param blockBag an optional {@link BlockBag} to use, otherwise null * @param player the player that the {@link EditSession} is for */ - public EditSession getEditSession(World world, int maxBlocks, BlockBag blockBag, LocalPlayer player) { + public EditSession getEditSession(World world, int maxBlocks, BlockBag blockBag, Player player) { throw new IllegalArgumentException("This class is being removed"); } @@ -123,7 +124,7 @@ public class EditSessionFactory { } @Override - public EditSession getEditSession(World world, int maxBlocks, LocalPlayer player) { + public EditSession getEditSession(World world, int maxBlocks, Player player) { return new EditSession(eventBus, world, maxBlocks, null, new EditSessionEvent(world, player, maxBlocks, null)); } @@ -133,7 +134,7 @@ public class EditSessionFactory { } @Override - public EditSession getEditSession(World world, int maxBlocks, BlockBag blockBag, LocalPlayer player) { + public EditSession getEditSession(World world, int maxBlocks, BlockBag blockBag, Player player) { return new EditSession(eventBus, world, maxBlocks, blockBag, new EditSessionEvent(world, player, maxBlocks, null)); } 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/LocalSession.java b/src/main/java/com/sk89q/worldedit/LocalSession.java index 5b07345b5..df38b58a5 100644 --- a/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -27,15 +27,17 @@ import com.sk89q.worldedit.command.tool.BlockTool; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.SinglePickaxe; 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; @@ -140,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); @@ -163,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() @@ -394,8 +418,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(); } @@ -419,7 +442,7 @@ public class LocalSession { * @param player * @return */ - public BlockBag getBlockBag(LocalPlayer player) { + public BlockBag getBlockBag(Player player) { if (!useInventory) { return null; } @@ -550,7 +573,7 @@ public class LocalSession { * * @param player */ - public void tellVersion(LocalPlayer player) { + public void tellVersion(Actor player) { if (config.showFirstUseVersion) { if (!beenToldVersion) { player.printRaw("\u00A78WorldEdit ver. " + WorldEdit.getVersion() @@ -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; } @@ -713,6 +736,17 @@ public class LocalSession { * @return */ public EditSession createEditSession(LocalPlayer player) { + return createEditSession((Player) player); + } + + /** + * Construct a new edit session. + * + * @param player + * @return + */ + @SuppressWarnings("deprecation") + public EditSession createEditSession(Player player) { BlockBag blockBag = getBlockBag(player); // Create an edit session @@ -721,9 +755,6 @@ public class LocalSession { getBlockChangeLimit(), blockBag, player); editSession.setFastMode(fastMode); Request.request().setEditSession(editSession); - if (mask != null) { - mask.prepare(this, player, null); - } editSession.setMask(mask); return editSession; @@ -764,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 cf137bb4d..1a0d25a5e 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -19,14 +19,16 @@ package com.sk89q.worldedit; -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.entity.Player; import com.sk89q.worldedit.event.extent.EditSessionEvent; -import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.event.platform.BlockInteractEvent; +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; @@ -37,7 +39,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; @@ -55,6 +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.platform.Interaction.HIT; +import static com.sk89q.worldedit.event.platform.Interaction.OPEN; /** * The entry point and container for a working implementation of WorldEdit. @@ -179,7 +182,7 @@ public class WorldEdit { * @deprecated use {@link #getSessionManager()} */ @Deprecated - public LocalSession getSession(LocalPlayer player) { + public LocalSession getSession(Player player) { return sessions.get(player); } @@ -187,7 +190,7 @@ public class WorldEdit { * @deprecated use {@link #getSessionManager()} */ @Deprecated - public void removeSession(LocalPlayer player) { + public void removeSession(Player player) { sessions.remove(player); } @@ -203,7 +206,7 @@ public class WorldEdit { * @deprecated use {@link #getSessionManager()} */ @Deprecated - public boolean hasSession(LocalPlayer player) { + public boolean hasSession(Player player) { return sessions.contains(player); } @@ -212,7 +215,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); } @@ -221,7 +224,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()); @@ -236,7 +239,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); } @@ -245,7 +248,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) { @@ -259,7 +262,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); } @@ -268,7 +271,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); } @@ -277,7 +280,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) { @@ -291,7 +294,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()); @@ -304,7 +307,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()); @@ -326,8 +329,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); } @@ -345,8 +347,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); } @@ -362,9 +363,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; @@ -407,7 +406,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; } @@ -419,7 +418,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; } @@ -480,7 +479,7 @@ public class WorldEdit { * @return a direction vector * @throws UnknownDirectionException thrown if the direction is not known */ - public Vector getDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException { + public Vector getDirection(Player player, String dirStr) throws UnknownDirectionException { dirStr = dirStr.toLowerCase(); final PlayerDirection dir = getPlayerDirection(player, dirStr); @@ -508,7 +507,7 @@ public class WorldEdit { * @return a direction enum value * @throws UnknownDirectionException thrown if the direction is not known */ - private PlayerDirection getPlayerDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException { + private PlayerDirection getPlayerDirection(Player player, String dirStr) throws UnknownDirectionException { final PlayerDirection dir; switch (dirStr.charAt(0)) { @@ -582,9 +581,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(); } @@ -596,8 +593,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: @@ -620,10 +616,10 @@ 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(LocalPlayer player, EditSession editSession) { + public void flushBlockBag(Actor actor, EditSession editSession) { BlockBag blockBag = editSession.getBlockBag(); if (blockBag != null) { @@ -632,7 +628,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(); @@ -654,35 +650,17 @@ public class WorldEdit { } } - player.printError(str.toString()); + actor.printError(str.toString()); } } - /** - * Get the map of commands (internal usage only). - * - * @return the commands - */ - public Map getCommands() { - return getCommandsManager().getCommands(); - } - - /** - * Get the commands manager (internal usage only). - * - * @return the commands - */ - public CommandsManager getCommandsManager() { - return getPlatformManager().getCommandManager().getCommands(); - } - /** * Handle a disconnection. * * @param player the player */ @Deprecated - public void handleDisconnect(LocalPlayer player) { + public void handleDisconnect(Player player) { forgetPlayer(player); } @@ -691,7 +669,7 @@ public class WorldEdit { * * @param player the player */ - public void markExpire(LocalPlayer player) { + public void markExpire(Player player) { sessions.markforExpiration(player); } @@ -700,7 +678,7 @@ public class WorldEdit { * * @param player the player */ - public void forgetPlayer(LocalPlayer player) { + public void forgetPlayer(Player player) { sessions.remove(player); } @@ -717,36 +695,10 @@ public class WorldEdit { * @param player the player * @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; + public boolean handleArmSwing(Player player) { + PlayerInputEvent event = new PlayerInputEvent(player, InputType.PRIMARY); + getEventBus().post(event); + return event.isCancelled(); } /** @@ -755,34 +707,10 @@ public class WorldEdit { * @param player the player * @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; + public boolean handleRightClick(Player player) { + PlayerInputEvent event = new PlayerInputEvent(player, InputType.SECONDARY); + getEventBus().post(event); + return event.isCancelled(); } /** @@ -792,35 +720,10 @@ 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) { - 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; + public boolean handleBlockRightClick(Player player, WorldVector clicked) { + BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), OPEN); + getEventBus().post(event); + return event.isCancelled(); } /** @@ -830,60 +733,12 @@ 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) { - 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; - } - - /** - * - * @param player - * @param split - * @return whether the command was processed - */ - public boolean handleCommand(LocalPlayer player, String[] split) { - CommandEvent event = new CommandEvent(player, split); + public boolean handleBlockLeftClick(Player player, WorldVector clicked) { + BlockInteractEvent event = new BlockInteractEvent(player, clicked.toLocation(), HIT); getEventBus().post(event); return event.isCancelled(); } - public String[] commandDetection(String[] split) { - return getPlatformManager().getCommandManager().commandDetection(split); - } - /** * Executes a WorldEdit script. * @@ -892,7 +747,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(); @@ -930,9 +785,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/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/BiomeCommands.java b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 2f25cf454..a08f70ab3 100644 --- a/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -24,9 +24,11 @@ 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.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; @@ -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."); } } + } diff --git a/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 0b0a62ae3..f954e26d8 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,22 @@ 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 { + public void smoothBrush(Player player, LocalSession session, EditSession editSession, + @Optional("2") double radius, @Optional("4") int iterations, @Switch('n') + boolean naturalBlocksOnly) throws WorldEditException { - double radius = args.argsLength() > 0 ? args.getDouble(0) : 2; - we.checkMaxBrushRadius(radius); - - int iterations = args.argsLength() > 1 ? args.getInteger(1) : 4; + 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 +181,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 +207,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 +229,8 @@ public class BrushCommands { max = 2 ) @CommandPermissions("worldedit.brush.butcher") - public void butcherBrush(CommandContext args, LocalSession session, - LocalPlayer player, EditSession editSession) throws WorldEditException { - - LocalConfiguration config = we.getConfiguration(); + 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; double maxRadius = config.maxBrushRadius; 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 eec97d650..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,18 +253,10 @@ 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( - aliases = { "/schematic", "/schem"}, - desc = "Schematic-related commands" - ) - @NestedCommand(SchematicCommands.class) - public void schematic() {} - @Command( aliases = { "clearclipboard" }, usage = "", @@ -275,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 7f938fcee..19cef7ff9 100644 --- a/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -22,27 +22,30 @@ 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.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.*; import com.sk89q.worldedit.blocks.ItemType; -import com.sk89q.worldedit.masks.Mask; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +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( @@ -53,23 +56,26 @@ 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)); - 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( @@ -80,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()) { @@ -111,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."); } @@ -130,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."); @@ -153,9 +155,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'); @@ -167,9 +167,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; @@ -177,26 +177,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; } @@ -210,7 +210,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; } @@ -218,17 +218,8 @@ public class GeneralCommands { } if (found == 0) { - player.printError("No items found."); + actor.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/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..5534df372 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 (moveSelection) { 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 90afb104d..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,8 @@ 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; @@ -30,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( @@ -55,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; @@ -69,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!"); @@ -120,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) { @@ -142,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()) { @@ -180,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!"); @@ -207,11 +213,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 +229,7 @@ public class SchematicCommands { first = false; } first = true; - player.print(builder.toString()); + actor.print(builder.toString()); } } @@ -238,11 +242,9 @@ 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 { - File dir = we.getWorkingDirectoryFile(we.getConfiguration().saveDir); + public void list(Actor actor, CommandContext args) throws WorldEditException { + File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().saveDir); File[] files = dir.listFiles(new FileFilter(){ @Override public boolean accept(File file) { @@ -273,8 +275,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/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 661b370c9..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 { @@ -54,15 +49,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]", @@ -72,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 c01084961..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); @@ -151,15 +146,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 = "", @@ -168,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 " @@ -184,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()) + "."); @@ -199,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 17355a733..5ba04745f 100644 --- a/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -22,15 +22,14 @@ 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.NestedCommand; 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; @@ -47,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()) { @@ -70,24 +68,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]", @@ -96,13 +76,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."); } @@ -116,9 +94,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."); } @@ -131,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."); @@ -146,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 c8128d1eb..7487cd52e 100644 --- a/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -23,10 +23,16 @@ 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; 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; @@ -57,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)); @@ -86,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)); @@ -115,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); @@ -134,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); @@ -153,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); @@ -172,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); @@ -194,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); @@ -215,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)); @@ -236,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; @@ -273,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; @@ -291,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; @@ -310,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'); @@ -329,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(); @@ -364,9 +357,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(); @@ -385,7 +376,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"); @@ -397,7 +388,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; @@ -407,16 +398,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; } @@ -442,15 +433,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; } @@ -480,12 +469,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 { @@ -499,20 +488,17 @@ 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 ) - @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) { - final CommandsManager commandsManager = we.getCommandsManager(); + public static void help(CommandContext args, WorldEdit we, Actor actor) { + final Dispatcher dispatcher = we.getPlatformManager().getCommandManager().getDispatcher(); if (args.argsLength() == 0) { SortedSet commands = new TreeSet(new Comparator() { @@ -525,7 +511,7 @@ public class UtilityCommands { return ret; } }); - commands.addAll(commandsManager.getCommands().keySet()); + commands.addAll(dispatcher.getPrimaryAliases()); StringBuilder sb = new StringBuilder(); boolean first = true; @@ -539,19 +525,31 @@ public class UtilityCommands { first = false; } - player.print(sb.toString()); + actor.print(sb.toString()); return; } - String command = args.getJoinedStrings(0).toLowerCase().replaceAll("/", ""); + String command = args.getJoinedStrings(0).toLowerCase().replaceAll("^/", ""); + CommandMapping mapping = dispatcher.get(command); - String helpMessage = commandsManager.getHelpMessages().get(command); - if (helpMessage == null) { - player.printError("Unknown command '" + command + "'."); + if (mapping == null) { + actor.printError("Unknown command '" + command + "'."); return; } - player.print(helpMessage); + Description description = mapping.getDescription(); + + if (description.getUsage() != null) { + actor.printDebug("Usage: " + description.getUsage()); + } + + if (description.getHelp() != null) { + actor.print(description.getHelp()); + } 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/command/WorldEditCommands.java b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index a4ce0f806..33070a6b3 100644 --- a/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -22,8 +22,10 @@ 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.entity.Player; +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; @@ -48,55 +50,45 @@ 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(); - Platform primary = pm.getPrimaryPlatform(); - player.printDebug(""); - player.printDebug("Platforms:"); + actor.printDebug("----------- Platforms -----------"); for (Platform platform : pm.getPlatforms()) { - String prefix = ""; + actor.printDebug(String.format("* %s (%s)", platform.getPlatformName(), platform.getPlatformVersion())); + } - 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())); + actor.printDebug("----------- Capabilities -----------"); + for (Capability capability : Capability.values()) { + Platform platform = pm.queryCapability(capability); + actor.printDebug(String.format("%s: %s", capability.name(), platform != null ? platform.getPlatformName() : "NONE")); } } @Command( aliases = { "reload" }, usage = "", - desc = "Reload WorldEdit", + desc = "Reload configuration", min = 0, 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( aliases = { "cui" }, usage = "", - desc = "Complete CUI handshake", + desc = "Complete CUI handshake (internal usage)", 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); } @@ -104,13 +96,11 @@ public class WorldEditCommands { @Command( aliases = { "tz" }, usage = "[timezone]", - desc = "Set your timezone", + desc = "Set your timezone for snapshots", min = 1, max = 1 ) - @Console - 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()); @@ -121,15 +111,12 @@ 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 ) @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/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 fe1811c02..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; @@ -46,11 +47,12 @@ 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); 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/entity/Player.java b/src/main/java/com/sk89q/worldedit/entity/Player.java index 175c6617d..9f09a4b5b 100644 --- a/src/main/java/com/sk89q/worldedit/entity/Player.java +++ b/src/main/java/com/sk89q/worldedit/entity/Player.java @@ -21,12 +21,13 @@ package com.sk89q.worldedit.entity; import com.sk89q.worldedit.*; 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/platform/BlockInteractEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/BlockInteractEvent.java new file mode 100644 index 000000000..2fdb4e443 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/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.platform; + +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 Interaction 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, Interaction 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 Interaction 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/platform/CommandEvent.java b/src/main/java/com/sk89q/worldedit/event/platform/CommandEvent.java index 0cba50b7e..0790787f4 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 String[] args; + private final Actor actor; + private final String arguments; /** * Create a new instance. * - * @param player the player - * @param args the arguments + * @param actor the player + * @param arguments the arguments */ - public CommandEvent(LocalPlayer player, String[] args) { - checkNotNull(player); - checkNotNull(args); + public CommandEvent(Actor actor, String arguments) { + checkNotNull(actor); + checkNotNull(arguments); - this.player = player; - this.args = args; + this.actor = actor; + this.arguments = arguments; } /** - * 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; } /** @@ -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/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/platform/Interaction.java b/src/main/java/com/sk89q/worldedit/event/platform/Interaction.java new file mode 100644 index 000000000..91331fa56 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/event/platform/Interaction.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 interaction. + */ +public enum Interaction { + + /** + * Refers to primary input usage (left click). + */ + HIT, + + /** + * Refers to secondary input usage (right click). + */ + OPEN + +} 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/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/AbstractPlatform.java b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java index 4f84ff8bd..a6534f8a4 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlatform.java @@ -42,15 +42,4 @@ public abstract class AbstractPlatform implements Platform { return Collections.emptyList(); } - @Override - @Deprecated - public void onCommandRegistration(List commands) { - // Do nothing :) - } - - @Override - public void onCommandRegistration(List commands, CommandsManager manager) { - onCommandRegistration(commands); - } - } 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..e329baf22 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -31,8 +31,6 @@ import com.sk89q.worldedit.world.World; import java.io.File; -import static com.google.common.base.Preconditions.checkNotNull; - /** * An abstract implementation of both a {@link Actor} and a {@link Player} * that is intended for implementations of WorldEdit to use to wrap @@ -40,19 +38,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/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/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/CommandManager.java b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 6eb9f8994..cc9f26556 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -19,22 +19,37 @@ package com.sk89q.worldedit.extension.platform; -import com.sk89q.minecraft.util.commands.*; -import com.sk89q.util.StringUtil; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.blocks.ItemType; +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; +import com.sk89q.minecraft.util.commands.WrappedCommandException; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalConfiguration; +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.WorldEditBinding; +import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; import com.sk89q.worldedit.session.request.Request; -import com.sk89q.worldedit.util.logging.LogFormat; +import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.InvalidUsageException; +import com.sk89q.worldedit.util.command.fluent.CommandGraph; +import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler; +import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.util.eventbus.Subscribe; import com.sk89q.worldedit.util.logging.DynamicStreamHandler; +import com.sk89q.worldedit.util.logging.LogFormat; import java.io.File; import java.io.IOException; -import java.lang.reflect.Method; -import java.util.logging.*; -import java.util.regex.Matcher; +import java.util.logging.FileHandler; +import java.util.logging.Level; +import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -49,7 +64,8 @@ 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 CommandsManager commands; + private final PlatformManager platformManager; + private final Dispatcher dispatcher; private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler(); /** @@ -57,9 +73,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); @@ -69,8 +87,56 @@ public final class CommandManager { dynamicHandler.setFormatter(new LogFormat()); // Set up the commands manager - commands = new CommandsManagerImpl(); - commands.setInjector(new SimpleInjector(worldEdit)); + ParametricBuilder builder = new ParametricBuilder(); + builder.setAuthorizer(new ActorAuthorizer()); + builder.addBinding(new WorldEditBinding(worldEdit)); + builder.addExceptionConverter(new WorldEditExceptionConverter(worldEdit)); + builder.addInvokeListener(new LegacyCommandsHandler()); + builder.addInvokeListener(new CommandLoggingHandler(worldEdit, logger)); + + dispatcher = new CommandGraph() + .builder(builder) + .commands() + .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") + .describeAs("WorldEdit commands") + .registerMethods(new WorldEditCommands(worldEdit)) + .parent() + .group("schematic", "schem", "/schematic", "/schem") + .describeAs("Schematic commands for saving/loading areas") + .registerMethods(new SchematicCommands(worldEdit)) + .parent() + .group("snapshot", "snap") + .describeAs("Schematic commands for saving/loading areas") + .registerMethods(new SnapshotCommands(worldEdit)) + .parent() + .group("brush", "br") + .describeAs("Brushing commands") + .registerMethods(new BrushCommands(worldEdit)) + .parent() + .group("superpickaxe", "pickaxe", "sp") + .describeAs("Super-pickaxe commands") + .registerMethods(new SuperPickaxeCommands(worldEdit)) + .parent() + .group("tool") + .describeAs("Bind functions to held items") + .registerMethods(new ToolCommands(worldEdit)) + .parent() + .graph() + .getDispatcher(); } void register(Platform platform) { @@ -83,9 +149,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 { @@ -95,39 +164,14 @@ public final class CommandManager { } } - register(platform, BiomeCommands.class); - register(platform, ChunkCommands.class); - register(platform, ClipboardCommands.class); - register(platform, GeneralCommands.class); - register(platform, GenerationCommands.class); - register(platform, HistoryCommands.class); - register(platform, NavigationCommands.class); - register(platform, RegionCommands.class); - register(platform, ScriptingCommands.class); - register(platform, SelectionCommands.class); - register(platform, SnapshotUtilCommands.class); - register(platform, ToolUtilCommands.class); - register(platform, ToolCommands.class); - register(platform, UtilityCommands.class); + platform.registerCommands(dispatcher); } void unregister() { dynamicHandler.setHandler(null); } - private void register(Platform platform, Class clazz) { - platform.onCommandRegistration(commands.registerAndReturn(clazz), commands); - } - - public CommandsManager getCommands() { - return commands; - } - public String[] commandDetection(String[] split) { - Request.reset(); - - split[0] = split[0].substring(1); - // Quick script shortcut if (split[0].matches("^[^/].*\\.js$")) { String[] newSplit = new String[split.length + 1]; @@ -140,12 +184,12 @@ public final class CommandManager { String searchCmd = split[0].toLowerCase(); // Try to detect the command - if (commands.hasCommand(searchCmd)) { - } else if (worldEdit.getConfiguration().noDoubleSlash && commands.hasCommand("/" + searchCmd)) { - split[0] = "/" + split[0]; - } else if (split[0].length() >= 2 && split[0].charAt(0) == '/' - && commands.hasCommand(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; @@ -155,185 +199,80 @@ public final class CommandManager { public void handleCommand(CommandEvent event) { Request.reset(); - LocalPlayer player = event.getPlayer(); - String[] split = event.getArguments(); + Actor actor = platformManager.createProxyActor(event.getActor()); + String split[] = commandDetection(event.getArguments().split(" ")); + + // No command found! + if (!dispatcher.contains(split[0])) { + return; + } + + LocalSession session = worldEdit.getSessionManager().get(actor); + LocalConfiguration config = worldEdit.getConfiguration(); + + CommandLocals locals = new CommandLocals(); + locals.put(Actor.class, actor); + + long start = System.currentTimeMillis(); try { - split = commandDetection(split); + 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) { + actor.printError(e.getMessage() + "\nUsage: " + e.getUsage("/")); + } catch (WrappedCommandException e) { + Throwable t = e.getCause(); + actor.printError("Please report this error: [See console]"); + actor.printRaw(t.getClass().getName() + ": " + t.getMessage()); + t.printStackTrace(); + } catch (CommandException e) { + actor.printError(e.getMessage()); + } finally { + EditSession editSession = locals.get(EditSession.class); - // No command found! - if (!commands.hasCommand(split[0])) { - return; - } - - LocalSession session = worldEdit.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!"); - event.setCancelled(true); - return; - } finally { + if (editSession != null) { session.remember(editSession); editSession.flushQueue(); - if (worldEdit.getConfiguration().profile) { + if (config.profile) { long time = System.currentTimeMillis() - start; int changed = editSession.getBlockChangeCount(); if (time > 0) { double throughput = changed / (time / 1000.0); - player.printDebug((time / 1000.0) + "s elapsed (history: " + actor.printDebug((time / 1000.0) + "s elapsed (history: " + changed + " changed; " + Math.round(throughput) + " blocks/sec)."); } else { - player.printDebug((time / 1000.0) + "s elapsed."); + actor.printDebug((time / 1000.0) + "s elapsed."); } } - worldEdit.flushBlockBag(player, editSession); + worldEdit.flushBlockBag(actor, 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 (MaxBrushRadiusException e) { - player.printError("Maximum allowed brush size: " + worldEdit.getConfiguration().maxBrushRadius); - } catch (MaxRadiusException e) { - player.printError("Maximum allowed size: " + worldEdit.getConfiguration().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(); } event.setCancelled(true); } - private class CommandsManagerImpl extends 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); + @Subscribe + public void handleCommandSuggestion(CommandSuggestionEvent event) { + try { + 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()); } + } - @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 (worldEdit.getConfiguration().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 = worldEdit.getSessionManager().get(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; - } - } - - getLogger().info(msg); - } - super.invokeMethod(parent, args, player, method, instance, methodArgs, level); - } + /** + * Get the command dispatcher instance. + * + * @return the command dispatcher + */ + public Dispatcher getDispatcher() { + return dispatcher; } public static Logger getLogger() { 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..bc90eaa5d 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/Platform.java @@ -19,14 +19,15 @@ package com.sk89q.worldedit.extension.platform; -import com.sk89q.minecraft.util.commands.Command; -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.util.command.Dispatcher; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; import java.util.List; +import java.util.Map; /** * Represents a platform that WorldEdit has been implemented for. @@ -77,10 +78,37 @@ public interface Platform { List getWorlds(); - @Deprecated - void onCommandRegistration(List commands); + /** + * 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); - void onCommandRegistration(List commands, CommandsManager manager); + /** + * 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); + + /** + * Register the commands contained within the given command dispatcher. + * + * @param dispatcher the dispatcher + */ + void registerCommands(Dispatcher dispatcher); + + /** + * Register game hooks. + */ + void registerGameHooks(); /** * Get the configuration from this platform. @@ -116,4 +144,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 5f10187ee..3e70e2483 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -19,13 +19,23 @@ 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.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; +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.ArrayList; -import java.util.List; +import java.util.*; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; @@ -41,10 +51,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. @@ -53,63 +64,139 @@ public class PlatformManager { */ public PlatformManager(WorldEdit worldEdit) { checkNotNull(worldEdit); - this.commandManager = new CommandManager(worldEdit); + this.worldEdit = worldEdit; + this.commandManager = new CommandManager(worldEdit, this); + + // Register this instance for events + worldEdit.getEventBus().register(this); } /** * 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. *

@@ -122,12 +209,41 @@ public class PlatformManager { } /** - * Get the primary platform. + * Given a world, possibly return the same world but using a different + * platform preferred for world editing operations. * - * @return the primary platform (may be null) + * @param base the world to match + * @return the preferred world, if one was found, otherwise the given world */ - public @Nullable Platform getPrimaryPlatform() { - return primary; + 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(player.getWorld())); + } else { + return base; + } } /** @@ -148,26 +264,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(); } /** @@ -176,26 +273,174 @@ 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 new ServerInterfaceAdapter(platform); + return ServerInterfaceAdapter.adapt(queryCapability(Capability.USER_COMMANDS)); + } + + @Subscribe + public void handlePlatformReady(PlatformReadyEvent event) { + choosePreferred(); + } + + @SuppressWarnings("deprecation") + @Subscribe + public void handleBlockInteract(BlockInteractEvent event) { + // 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(); + + // 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() == Interaction.HIT) { + 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.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(player)) { + ((DoubleActionBlockTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), getConfiguration(), player, session, location); + event.setCancelled(true); + } + } + + } else if (event.getType() == Interaction.OPEN) { + 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; + } + + 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); + } + } } - } else { - throw new IllegalStateException("No platform has been registered"); } } - /** - * A default configuration for when none is set. - */ - private static class DefaultConfiguration extends LocalConfiguration { - @Override - public void load() { + @SuppressWarnings("deprecation") + @Subscribe + public void handlePlayerInput(PlayerInputEvent event) { + // 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: { + 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); + + 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; + } + } + + 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); + + 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; + } + } + + break; + } } } + } 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..7da9d7853 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -0,0 +1,131 @@ +/* + * 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.BaseEntity; +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 BaseEntity getState() { + throw new UnsupportedOperationException("Can't getState() on a player"); + } + + @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/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/LocalWorldAdapter.java b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java index 9ed250c7f..3b7025003 100644 --- a/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/LocalWorldAdapter.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; +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; @@ -254,7 +255,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); } @@ -351,7 +352,7 @@ public class LocalWorldAdapter extends LocalWorld { return world.getEntities(); } - 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 62% 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..16d86ee5e 100644 --- a/src/main/java/com/sk89q/worldedit/extension/platform/ServerInterfaceAdapter.java +++ b/src/main/java/com/sk89q/worldedit/internal/ServerInterfaceAdapter.java @@ -17,21 +17,28 @@ * 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.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.Platform; +import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.world.World; +import javax.annotation.Nullable; import java.util.List; +import java.util.Map; 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 +47,7 @@ class ServerInterfaceAdapter extends ServerInterface { * * @param platform the platform */ - ServerInterfaceAdapter(Platform platform) { + private ServerInterfaceAdapter(Platform platform) { checkNotNull(platform); this.platform = platform; } @@ -75,15 +82,25 @@ class ServerInterfaceAdapter extends ServerInterface { return platform.getWorlds(); } + @Nullable @Override - @Deprecated - public void onCommandRegistration(List commands) { - platform.onCommandRegistration(commands); + public Player matchPlayer(Player player) { + return platform.matchPlayer(player); + } + + @Nullable + @Override + public World matchWorld(World world) { + return platform.matchWorld(world); } @Override - public void onCommandRegistration(List commands, CommandsManager manager) { - platform.onCommandRegistration(commands, manager); + public void registerCommands(Dispatcher dispatcher) { + platform.registerCommands(dispatcher); + } + + @Override + public void registerGameHooks() { } @Override @@ -106,4 +123,19 @@ class ServerInterfaceAdapter extends ServerInterface { return platform.getPlatformVersion(); } + @Override + public Map getCapabilities() { + return platform.getCapabilities(); + } + + /** + * 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/internal/annotation/Direction.java b/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java new file mode 100644 index 000000000..45d495bbd --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java @@ -0,0 +1,39 @@ +/* + * 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.annotation; + +import com.sk89q.worldedit.Vector; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 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/internal/annotation/Selection.java b/src/main/java/com/sk89q/worldedit/internal/annotation/Selection.java new file mode 100644 index 000000000..9647c360f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/annotation/Selection.java @@ -0,0 +1,34 @@ +/* + * 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.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/internal/command/ActorAuthorizer.java b/src/main/java/com/sk89q/worldedit/internal/command/ActorAuthorizer.java new file mode 100644 index 000000000..f2f301ca2 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/command/ActorAuthorizer.java @@ -0,0 +1,40 @@ +/* + * 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.CommandLocals; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.util.auth.Authorizer; + +/** + * Implementation of an authorizer that uses {@link Actor#hasPermission(String)}. + */ +public class ActorAuthorizer implements Authorizer { + + @Override + 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/internal/command/CommandLoggingHandler.java b/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java new file mode 100644 index 000000000..b09a26bee --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/command/CommandLoggingHandler.java @@ -0,0 +1,156 @@ +/* + * 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.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; +import com.sk89q.worldedit.util.command.parametric.ParameterException; + +import java.io.Closeable; +import java.lang.reflect.Method; +import java.util.logging.Handler; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Logs called commands to a logger. + */ +public class CommandLoggingHandler extends AbstractInvokeListener implements InvokeHandler, Closeable { + + private final WorldEdit worldEdit; + private final Logger logger; + + /** + * Create a new instance. + * + * @param worldEdit an instance of WorldEdit + * @param logger the logger to send messages to + */ + public CommandLoggingHandler(WorldEdit worldEdit, Logger logger) { + checkNotNull(worldEdit); + checkNotNull(logger); + this.worldEdit = worldEdit; + this.logger = logger; + } + + @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 { + Logging loggingAnnotation = method.getAnnotation(Logging.class); + Logging.LogMode logMode; + StringBuilder builder = new StringBuilder(); + + if (loggingAnnotation == null) { + logMode = null; + } else { + logMode = loggingAnnotation.value(); + } + + 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 \"" + player.getWorld().getName() + "\")"); + } + + builder.append(": ").append(context.getCommand()); + + if (context.argsLength() > 0) { + builder.append(" ").append(context.getJoinedStrings(0)); + } + + if (logMode != null && sender.isPlayer()) { + Vector position = player.getPosition(); + LocalSession session = worldEdit.getSessionManager().get(player); + + switch (logMode) { + case PLACEMENT: + try { + position = session.getPlacementPosition(player); + } 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: " + player.getCardinalDirection().name()); + /* FALL-THROUGH */ + + case REGION: + try { + builder.append(" - Region: ") + .append(session.getSelection(player.getWorld())); + } catch (IncompleteRegionException e) { + break; + } + break; + } + } + + logger.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 : logger.getHandlers()) { + h.close(); + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java new file mode 100644 index 000000000..ed29e8fb5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -0,0 +1,296 @@ +/* + * 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.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; +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}. + */ +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 { + Player sender = getPlayer(context); + LocalSession session = worldEdit.getSessionManager().get(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 { + Player sender = getPlayer(context); + LocalSession session = worldEdit.getSessionManager().get(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 { + Player sender = getPlayer(context); + 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}. + * + * @param context the context + * @return a local player + * @throws ParameterException on error + */ + @BindingMatch(type = Player.class, + behavior = BindingBehavior.PROVIDES) + public Player getPlayer(ArgumentStack context) throws ParameterException { + Actor sender = context.getContext().getLocals().get(Actor.class); + if (sender == null) { + throw new ParameterException("No player to get a session for"); + } else if (sender instanceof Player) { + return (Player) sender; + } else { + throw new ParameterException("Caller is not a player"); + } + } + + /** + * Gets an {@link BaseBlock} from a {@link ArgumentStack}. + * + * @param context the context + * @return a pattern + * @throws ParameterException on error + * @throws WorldEditException on error + */ + @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)); + try { + return worldEdit.getBlockRegistry().parseFromInput(context.next(), parserContext); + } catch (NoMatchException e) { + throw new ParameterException(e.getMessage(), e); + } + } + + /** + * 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 { + 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)); + try { + return worldEdit.getPatternRegistry().parseFromInput(context.next(), parserContext); + } catch (NoMatchException e) { + throw new ParameterException(e.getMessage(), e); + } + } + + /** + * 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 { + 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)); + try { + return worldEdit.getMaskRegistry().parseFromInput(context.next(), parserContext); + } catch (NoMatchException e) { + throw new ParameterException(e.getMessage(), e); + } + } + + /** + * 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 { + Player sender = getPlayer(context); + 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/internal/command/WorldEditExceptionConverter.java b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java new file mode 100644 index 000000000..6b8831d13 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java @@ -0,0 +1,156 @@ +/* + * 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.worldedit.*; +import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldedit.command.InsufficientArgumentsException; +import com.sk89q.worldedit.internal.expression.ExpressionException; +import com.sk89q.worldedit.regions.RegionOperationException; +import com.sk89q.worldedit.util.command.parametric.ExceptionConverterHelper; +import com.sk89q.worldedit.util.command.parametric.ExceptionMatch; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * 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 WorldEdit worldEdit; + + public WorldEditExceptionConverter(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; + } + + @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(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 (in configuration): " + worldEdit.getConfiguration().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()); + } + +} 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; } + } diff --git a/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/src/main/java/com/sk89q/worldedit/util/TargetBlock.java index e5d474f72..a97a182a0 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,9 +73,8 @@ public class TargetBlock { * @param checkDistance how often to check for blocks, the smaller the more precise */ public TargetBlock(Player player, int maxDistance, double checkDistance) { - this.world = LocalWorldAdapter.wrap(player.getWorld()); - this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), - maxDistance, 1.65, checkDistance); + this.world = LocalWorldAdapter.adapt(player.getWorld()); + this.setValues(player.getPosition(), player.getYaw(), player.getPitch(), maxDistance, 1.65, checkDistance); } /** 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 new file mode 100644 index 000000000..9a16a467c --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandCallable.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.util.command; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandLocals; + +import java.util.List; +import java.util.Set; + +/** + * 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 correct command based on the input. + *

+ * The implementing class must perform the necessary permission checks. + * + * @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(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. + * + * @return the command description + */ + 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/CommandMapping.java b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java new file mode 100644 index 000000000..6e44bff17 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/CommandMapping.java @@ -0,0 +1,88 @@ +/* + * 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 java.util.Arrays; + +/** + * 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(); + } + + @Override + public String toString() { + return "CommandMapping{" + + "aliases=" + Arrays.toString(aliases) + + ", callable=" + callable + + '}'; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/command/Description.java b/src/main/java/com/sk89q/worldedit/util/command/Description.java new file mode 100644 index 000000000..72e9fd5f0 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/Description.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.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 getShortDescription(); + + /** + * 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/worldedit/util/command/Dispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java new file mode 100644 index 000000000..568db508e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/Dispatcher.java @@ -0,0 +1,85 @@ +/* + * 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 javax.annotation.Nullable; +import java.util.Collection; +import java.util.Set; + +/** + * Executes a command based on user input. + */ +public interface Dispatcher extends CommandCallable { + + /** + * 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 registerCommand(CommandCallable callable, String... alias); + + /** + * 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 + */ + Set 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, 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 getAliases(); + + /** + * 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 (null if not found) + */ + @Nullable 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); + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java b/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java new file mode 100644 index 000000000..a97ce4d2f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/InvalidUsageException.java @@ -0,0 +1,49 @@ +/* + * 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; + +/** + * 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/worldedit/util/command/MissingParameterException.java b/src/main/java/com/sk89q/worldedit/util/command/MissingParameterException.java new file mode 100644 index 000000000..8080929e3 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/MissingParameterException.java @@ -0,0 +1,31 @@ +/* + * 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.worldedit.util.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/worldedit/util/command/Parameter.java b/src/main/java/com/sk89q/worldedit/util/command/Parameter.java new file mode 100644 index 000000000..868c3840a --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/Parameter.java @@ -0,0 +1,66 @@ +/* + * 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; + +/** + * 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/worldedit/util/command/SimpleDescription.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java new file mode 100644 index 000000000..efd5aed95 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDescription.java @@ -0,0 +1,130 @@ +/* + * 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 java.util.ArrayList; +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 = new ArrayList(); + private List permissions = new ArrayList(); + 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 getShortDescription() { + return description; + } + + /** + * Set the description of the command. + * + * @param description the description + * @see #getShortDescription() + */ + 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/worldedit/util/command/SimpleDispatcher.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java new file mode 100644 index 000000000..a84c6b497 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -0,0 +1,200 @@ +/* + * 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.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.*; + +/** + * A simple implementation of {@link Dispatcher}. + */ +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); + + // 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 Set getCommands() { + return Collections.unmodifiableSet(new HashSet(commands.values())); + } + + @Override + public Set getAliases() { + 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 Set getValueFlags() { + return Collections.emptySet(); + } + + @Override + 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(); + } + + 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]; + 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(subArguments, locals, subParents); + } catch (CommandException e) { + e.prependStack(subCommand); + throw e; + } catch (Throwable t) { + throw new WrappedCommandException(t); + } + + return true; + } + + } + + throw new InvalidUsageException(ColorCodeBuilder.asColorCodes(getSubcommandList(locals, parentCommands)), getDescription()); + } + + @Override + public List getSuggestions(String arguments, CommandLocals locals) throws CommandException { + String[] split = CommandContext.split(arguments); + + if (split.length <= 1) { + String prefix = split.length > 0 ? split[0] : ""; + + 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 { + 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, locals); + } else { + return Collections.emptyList(); + } + } + } + + @Override + public SimpleDescription getDescription() { + return description; + } + + @Override + public boolean testPermission(CommandLocals locals) { + for (CommandMapping mapping : getCommands()) { + if (mapping.getCallable().testPermission(locals)) { + return true; + } + } + + return false; + } + + 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)) { + box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getShortDescription()); + } + } + + return box; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/command/SimpleParameter.java b/src/main/java/com/sk89q/worldedit/util/command/SimpleParameter.java new file mode 100644 index 000000000..59fc8dddd --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/SimpleParameter.java @@ -0,0 +1,131 @@ +/* + * 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; + +/** + * 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/worldedit/util/command/UnconsumedParameterException.java b/src/main/java/com/sk89q/worldedit/util/command/UnconsumedParameterException.java new file mode 100644 index 000000000..360a5408c --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/UnconsumedParameterException.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.worldedit.util.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/worldedit/util/command/binding/PrimitiveBindings.java b/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java new file mode 100644 index 000000000..a4ed7ab0c --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java @@ -0,0 +1,255 @@ +/* + * 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.binding; + +import com.sk89q.worldedit.util.command.parametric.*; + +import java.lang.annotation.Annotation; + +/** + * 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/worldedit/util/command/binding/Range.java b/src/main/java/com/sk89q/worldedit/util/command/binding/Range.java new file mode 100644 index 000000000..0ff21ca12 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/Range.java @@ -0,0 +1,50 @@ +/* + * 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.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/worldedit/util/command/binding/StandardBindings.java b/src/main/java/com/sk89q/worldedit/util/command/binding/StandardBindings.java new file mode 100644 index 000000000..a45b88a41 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/StandardBindings.java @@ -0,0 +1,46 @@ +/* + * 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.binding; + +import com.sk89q.minecraft.util.commands.CommandContext; +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.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/worldedit/util/command/binding/Switch.java b/src/main/java/com/sk89q/worldedit/util/command/binding/Switch.java new file mode 100644 index 000000000..34c25cad5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/Switch.java @@ -0,0 +1,44 @@ +/* + * 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.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/worldedit/util/command/binding/Text.java b/src/main/java/com/sk89q/worldedit/util/command/binding/Text.java new file mode 100644 index 000000000..030bc9ad4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/Text.java @@ -0,0 +1,41 @@ +/* + * 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.binding; + +import com.sk89q.worldedit.util.command.parametric.ArgumentStack; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 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/worldedit/util/command/binding/Validate.java b/src/main/java/com/sk89q/worldedit/util/command/binding/Validate.java new file mode 100644 index 000000000..3686aa359 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/binding/Validate.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.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/worldedit/util/command/fluent/CommandGraph.java b/src/main/java/com/sk89q/worldedit/util/command/fluent/CommandGraph.java new file mode 100644 index 000000000..1b9dacb32 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/fluent/CommandGraph.java @@ -0,0 +1,84 @@ +/* + * 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.fluent; + +import com.sk89q.worldedit.util.command.Dispatcher; +import com.sk89q.worldedit.util.command.SimpleDispatcher; +import com.sk89q.worldedit.util.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#registerMethods(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/worldedit/util/command/fluent/DispatcherNode.java b/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java new file mode 100644 index 000000000..ed0d6dae5 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/fluent/DispatcherNode.java @@ -0,0 +1,138 @@ +/* + * 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.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.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 describeAs(String description) { + 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.registerCommand(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#registerMethodsAsCommands(com.sk89q.worldedit.util.command.Dispatcher, Object) + */ + public DispatcherNode registerMethods(Object object) { + ParametricBuilder builder = graph.getBuilder(); + if (builder == null) { + throw new RuntimeException("No ParametricBuilder set"); + } + builder.registerMethodsAsCommands(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) { + SimpleDispatcher command = new SimpleDispatcher(); + getDispatcher().registerCommand(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/worldedit/util/command/parametric/AbstractInvokeListener.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/AbstractInvokeListener.java new file mode 100644 index 000000000..65fadc101 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/AbstractInvokeListener.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.util.command.parametric; + +import com.sk89q.worldedit.util.command.SimpleDescription; + +import java.lang.reflect.Method; + +/** + * 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/worldedit/util/command/parametric/ArgumentStack.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ArgumentStack.java new file mode 100644 index 000000000..0f0b9a52b --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ArgumentStack.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.util.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/worldedit/util/command/parametric/Binding.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/Binding.java new file mode 100644 index 000000000..dcf931c81 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/Binding.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.command.parametric; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; +import com.sk89q.worldedit.util.command.binding.StandardBindings; + +import java.lang.reflect.Type; +import java.util.List; + +/** + * 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/worldedit/util/command/parametric/BindingBehavior.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingBehavior.java new file mode 100644 index 000000000..e194bce56 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingBehavior.java @@ -0,0 +1,52 @@ +/* + * 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.CommandLocals; +import com.sk89q.worldedit.util.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/worldedit/util/command/parametric/BindingHelper.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java new file mode 100644 index 000000000..b442da6a2 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java @@ -0,0 +1,224 @@ +/* + * 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.CommandException; + +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; + +/** + * 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 RuntimeException(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/worldedit/util/command/parametric/BindingMatch.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMatch.java new file mode 100644 index 000000000..049d3dc4d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingMatch.java @@ -0,0 +1,71 @@ +/* + * 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 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/worldedit/util/command/parametric/ContextArgumentStack.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ContextArgumentStack.java new file mode 100644 index 000000000..c7222b9e1 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ContextArgumentStack.java @@ -0,0 +1,178 @@ +/* + * 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.worldedit.util.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/worldedit/util/command/parametric/ExceptionConverter.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverter.java new file mode 100644 index 000000000..1eb9491ca --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverter.java @@ -0,0 +1,52 @@ +/* + * 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.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/worldedit/util/command/parametric/ExceptionConverterHelper.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverterHelper.java new file mode 100644 index 000000000..8a72f1a33 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionConverterHelper.java @@ -0,0 +1,109 @@ +/* + * 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.CommandException; +import com.sk89q.minecraft.util.commands.WrappedCommandException; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 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/worldedit/util/command/parametric/ExceptionMatch.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionMatch.java new file mode 100644 index 000000000..619bc9d46 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ExceptionMatch.java @@ -0,0 +1,34 @@ +/* + * 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 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/worldedit/util/command/parametric/InvokeHandler.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/InvokeHandler.java new file mode 100644 index 000000000..465f89689 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/InvokeHandler.java @@ -0,0 +1,82 @@ +/* + * 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 java.lang.reflect.Method; + +/** + * 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/worldedit/util/command/parametric/InvokeListener.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/InvokeListener.java new file mode 100644 index 000000000..f30f86c9f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/InvokeListener.java @@ -0,0 +1,58 @@ +/* + * 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.CommandPermissions; +import com.sk89q.worldedit.util.command.CommandCallable; +import com.sk89q.worldedit.util.command.SimpleDescription; + +import java.lang.reflect.Method; + +/** + * 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/worldedit/util/command/parametric/LegacyCommandsHandler.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java new file mode 100644 index 000000000..fbc1794c2 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/LegacyCommandsHandler.java @@ -0,0 +1,97 @@ +/* + * 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.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldedit.util.command.MissingParameterException; +import com.sk89q.worldedit.util.command.SimpleDescription; +import com.sk89q.worldedit.util.command.UnconsumedParameterException; + +import java.lang.reflect.Method; + +/** + * 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; + break; + } + } + + if (!hasUserParameters) { + description.overrideUsage("(unknown usage information)"); + } + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/util/command/parametric/Optional.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/Optional.java new file mode 100644 index 000000000..bd6d5448f --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/Optional.java @@ -0,0 +1,41 @@ +/* + * 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 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/worldedit/util/command/parametric/ParameterData.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParameterData.java new file mode 100644 index 000000000..649c576b4 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParameterData.java @@ -0,0 +1,194 @@ +/* + * 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.worldedit.util.command.SimpleParameter; +import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; +import com.sk89q.worldedit.util.command.binding.Range; +import com.sk89q.worldedit.util.command.binding.Text; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +/** + * 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/worldedit/util/command/parametric/ParameterException.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParameterException.java new file mode 100644 index 000000000..8dc924f15 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParameterException.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.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/worldedit/util/command/parametric/ParametricBuilder.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java new file mode 100644 index 000000000..dd96c4a2a --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java @@ -0,0 +1,228 @@ +/* + * 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.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.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; +import com.sk89q.worldedit.util.command.binding.StandardBindings; +import com.sk89q.worldedit.util.command.binding.Switch; +import com.thoughtworks.paranamer.CachingParanamer; +import com.thoughtworks.paranamer.Paranamer; + +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 static com.google.common.base.Preconditions.checkNotNull; + +/** + * 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(); + private Authorizer authorizer = new NullAuthorizer(); + + /** + * 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 addInvokeListener(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 addExceptionConverter(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 registerMethodsAsCommands(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.registerCommand(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; + } + + /** + * 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 new file mode 100644 index 000000000..9cbecc03e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricCallable.java @@ -0,0 +1,448 @@ +/* + * 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.*; +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.*; + +/** + * 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(); + private final CommandPermissions commandPermissions; + + /** + * 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 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); + + // Get permissions annotation + commandPermissions = method.getAnnotation(CommandPermissions.class); + } + + @Override + public boolean call(String stringArguments, CommandLocals locals, String[] parentCommands) throws CommandException { + // Test permission + if (!testPermission(locals)) { + throw new CommandPermissionsException(); + } + + 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]; + 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) { + assert parameter != null; + 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); + } + + return true; + } + + @Override + public List getSuggestions(String stringArguments, CommandLocals locals) throws CommandException { + return Collections.emptyList(); + } + + @Override + public Set getValueFlags() { + return valueFlags; + } + + @Override + 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}. + * + * @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 + */ + private String getUnusedFlags(CommandContext context) { + Set unusedFlags = null; + for (char flag : context.getFlags()) { + boolean found = false; + + for (ParameterData parameter : parameters) { + Character paramFlag = parameter.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/worldedit/util/command/parametric/ParametricException.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricException.java new file mode 100644 index 000000000..f2c826d73 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricException.java @@ -0,0 +1,46 @@ +/* + * 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; + +/** + * 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/worldedit/util/command/parametric/StringArgumentStack.java b/src/main/java/com/sk89q/worldedit/util/command/parametric/StringArgumentStack.java new file mode 100644 index 000000000..7d93070fa --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/command/parametric/StringArgumentStack.java @@ -0,0 +1,128 @@ +/* + * 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.worldedit.util.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; + } + +} 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..3eafd3c8d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/util/formatting/Style.java @@ -0,0 +1,276 @@ +/* + * 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 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. + * + *

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) { + checkNotNull(code); + checkArgument(!code.isEmpty(), "Code must have at least one character"); + + 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; + } + +} 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 c711bcdf2..3bd5fd97a 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; @@ -343,7 +344,7 @@ public interface World extends Extent, Mapping { * @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);