diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 093d84f0e..6c5bcbfc2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -19,20 +19,11 @@ package com.sk89q.worldedit.command; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; -import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION; -import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; -import static com.sk89q.worldedit.regions.Regions.asFlatRegion; -import static com.sk89q.worldedit.regions.Regions.maximumBlockY; -import static com.sk89q.worldedit.regions.Regions.minimumBlockY; - -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.util.CommandPermissions; +import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.function.GroundFunction; @@ -57,56 +48,54 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionOperationException; 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 org.enginehub.piston.annotation.Command; +import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; +import org.enginehub.piston.annotation.param.Switch; import java.util.ArrayList; import java.util.List; +import static com.google.common.base.Preconditions.checkArgument; +import static com.sk89q.worldedit.command.util.Logging.LogMode.ALL; +import static com.sk89q.worldedit.command.util.Logging.LogMode.ORIENTATION_REGION; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import static com.sk89q.worldedit.regions.Regions.asFlatRegion; +import static com.sk89q.worldedit.regions.Regions.maximumBlockY; +import static com.sk89q.worldedit.regions.Regions.minimumBlockY; + /** * Commands that operate on regions. */ +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class RegionCommands { - private final WorldEdit worldEdit; - /** * Create a new instance. - * - * @param worldEdit reference to WorldEdit */ - public RegionCommands(WorldEdit worldEdit) { - checkNotNull(worldEdit); - this.worldEdit = worldEdit; + public RegionCommands() { } @Command( - aliases = { "/line" }, - usage = " [thickness]", - desc = "Draws a line segment between cuboid selection corners", - help = - "Draws a line segment between cuboid selection corners.\n" + - "Can only be used with cuboid selections.\n" + - "Flags:\n" + - " -h generates only a shell", - flags = "h", - min = 1, - max = 2 + name = "/line", + desc = "Draws a line segment between cuboid selection corners", + descFooter = "Can only be used with a cuboid selection" ) @CommandPermissions("worldedit.region.line") @Logging(REGION) - 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 { - + public int line(Player player, EditSession editSession, + @Selection Region region, + @Arg(desc = "The pattern of blocks to place") + Pattern pattern, + @Arg(desc = "The thickness of the line", def = "0") + int thickness, + @Switch(name = 'h', desc = "Generate only a shell") + boolean shell) throws WorldEditException { if (!(region instanceof CuboidRegion)) { player.printError("//line only works with cuboid selections"); - return; + return 0; } + checkArgument(thickness >= 0, "Thickness must be >= 0"); CuboidRegion cuboidregion = (CuboidRegion) region; BlockVector3 pos1 = cuboidregion.getPos1(); @@ -114,32 +103,29 @@ public class RegionCommands { int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell); player.print(blocksChanged + " block(s) have been changed."); + return blocksChanged; } @Command( - aliases = { "/curve" }, - usage = " [thickness]", - desc = "Draws a spline through selected points", - help = - "Draws a spline through selected points.\n" + - "Can only be used with convex polyhedral selections.\n" + - "Flags:\n" + - " -h generates only a shell", - flags = "h", - min = 1, - max = 2 + name = "/curve", + desc = "Draws a spline through selected points", + descFooter = "Can only be used with a convex polyhedral selection" ) @CommandPermissions("worldedit.region.curve") @Logging(REGION) - 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 { + public int curve(Player player, EditSession editSession, + @Selection Region region, + @Arg(desc = "The pattern of blocks to place") + Pattern pattern, + @Arg(desc = "The thickness of the curve", def = "0") + int thickness, + @Switch(name = 'h', desc = "Generate only a shell") + boolean shell) throws WorldEditException { if (!(region instanceof ConvexPolyhedralRegion)) { player.printError("//curve only works with convex polyhedral selections"); - return; + return 0; } + checkArgument(thickness >= 0, "Thickness must be >= 0"); ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region; List vectors = new ArrayList<>(cpregion.getVertices()); @@ -147,139 +133,140 @@ public class RegionCommands { int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, thickness, !shell); player.print(blocksChanged + " block(s) have been changed."); + return blocksChanged; } @Command( - aliases = { "/replace", "/re", "/rep" }, - usage = "[from-block] ", - desc = "Replace all blocks in the selection with another", - flags = "f", - min = 1, - max = 2 + name = "/replace", + aliases = { "/re", "/rep" }, + desc = "Replace all blocks in the selection with another" ) @CommandPermissions("worldedit.region.replace") @Logging(REGION) - public void replace(Player player, EditSession editSession, @Selection Region region, @Optional Mask from, Pattern to) throws WorldEditException { + public int replace(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The mask representing blocks to replace", def = "") + Mask from, + @Arg(desc = "The pattern of blocks to replace with") + Pattern to) throws WorldEditException { if (from == null) { from = new ExistingBlockMask(editSession); } int affected = editSession.replaceBlocks(region, from, to); player.print(affected + " block(s) have been replaced."); + return affected; } @Command( - aliases = { "/overlay" }, - usage = "", - desc = "Set a block on top of blocks in the region", - min = 1, - max = 1 + name = "/overlay", + desc = "Set a block on top of blocks in the region" ) @CommandPermissions("worldedit.region.overlay") @Logging(REGION) - public void overlay(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + public int overlay(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The pattern of blocks to overlay") + Pattern pattern) throws WorldEditException { int affected = editSession.overlayCuboidBlocks(region, pattern); player.print(affected + " block(s) have been overlaid."); + return affected; } @Command( - aliases = { "/center", "/middle" }, - usage = "", - desc = "Set the center block(s)", - min = 1, - max = 1 + name = "/center", + aliases = { "/middle" }, + desc = "Set the center block(s)" ) @Logging(REGION) @CommandPermissions("worldedit.region.center") - public void center(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + public int center(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern) throws WorldEditException { int affected = editSession.center(region, pattern); - player.print("Center set ("+ affected + " blocks changed)"); + player.print("Center set (" + affected + " block(s) changed)"); + return affected; } @Command( - aliases = { "/naturalize" }, - usage = "", - desc = "3 layers of dirt on top then rock below", - min = 0, - max = 0 + name = "/naturalize", + desc = "3 layers of dirt on top then rock below" ) @CommandPermissions("worldedit.region.naturalize") @Logging(REGION) - public void naturalize(Player player, EditSession editSession, @Selection Region region) throws WorldEditException { + public int naturalize(Player player, EditSession editSession, @Selection Region region) throws WorldEditException { int affected = editSession.naturalizeCuboidBlocks(region); player.print(affected + " block(s) have been made to look more natural."); + return affected; } @Command( - aliases = { "/walls" }, - usage = "", - desc = "Build the four sides of the selection", - min = 1, - max = 1 + name = "/walls", + desc = "Build the four sides of the selection" ) @CommandPermissions("worldedit.region.walls") @Logging(REGION) - public void walls(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + public int walls(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern) throws WorldEditException { int affected = editSession.makeWalls(region, pattern); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/faces", "/outline" }, - usage = "", - desc = "Build the walls, ceiling, and floor of a selection", - min = 1, - max = 1 + name = "/faces", + aliases = { "/outline" }, + desc = "Build the walls, ceiling, and floor of a selection" ) @CommandPermissions("worldedit.region.faces") @Logging(REGION) - public void faces(Player player, EditSession editSession, @Selection Region region, Pattern pattern) throws WorldEditException { + public int faces(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The pattern of blocks to set") + Pattern pattern) throws WorldEditException { int affected = editSession.makeCuboidFaces(region, pattern); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/smooth" }, - usage = "[iterations] [filter]", + name = "/smooth", desc = "Smooth the elevation in the selection", - help = - "Smooths the elevation in the selection.\n" + - "Optionally, restricts the height map to a set of blocks specified with mask syntax.\n" + - "For example, '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain.", - min = 0, - max = 2 + descFooter = "Example: '//smooth 1 grass_block,dirt,stone' would only smooth natural surface terrain." ) @CommandPermissions("worldedit.region.smooth") @Logging(REGION) - public void smooth(Player player, EditSession editSession, @Selection Region region, @Optional("1") int iterations, @Optional Mask mask) throws WorldEditException { + public int smooth(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "# of iterations to perform", def = "1") + int iterations, + @Arg(desc = "The mask of blocks to use as the height map", def = "") + Mask mask) throws WorldEditException { HeightMap heightMap = new HeightMap(editSession, region, mask); 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."); - + return affected; } @Command( - aliases = { "/move" }, - usage = "[count] [direction] [leave-id]", - flags = "sa", - desc = "Move the contents of the selection", - help = - "Moves the contents of the selection.\n" + - "The -s flag shifts the selection to the target location.\n" + - "The -a flag skips air blocks.\n" + - "Optionally fills the old location with .", - min = 0, - max = 3 + name = "/move", + desc = "Move the contents of the selection" ) @CommandPermissions("worldedit.region.move") @Logging(ORIENTATION_REGION) - public void move(Player player, EditSession editSession, LocalSession session, - @Selection Region region, - @Optional("1") @Range(min = 1) int count, - @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, - @Optional("air") Pattern replace, - @Switch('s') boolean moveSelection, - @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { + public int move(Player player, EditSession editSession, LocalSession session, + @Selection Region region, + @Arg(desc = "# of blocks to move", def = "1") + int count, + @Arg(desc = "The direction to move", def = Direction.AIM) + @Direction(includeDiagonals = true) + BlockVector3 direction, + @Arg(desc = "The pattern of blocks to leave", def = "air") + Pattern replace, + @Switch(name = 's', desc = "Shift the selection to the target location") + boolean moveSelection, + @Switch(name = 'a', desc = "Ignore air blocks") + boolean ignoreAirBlocks) throws WorldEditException { + if (count < 1) { + throw new IllegalArgumentException("Count must be >= 1"); + } int affected = editSession.moveRegion(region, direction, count, !ignoreAirBlocks, replace); @@ -294,30 +281,27 @@ public class RegionCommands { } } - player.print(affected + " blocks moved."); + player.print(affected + " block(s) moved."); + return affected; } @Command( - aliases = { "/stack" }, - usage = "[count] [direction]", - flags = "sa", - desc = "Repeat the contents of the selection", - help = - "Repeats the contents of the selection.\n" + - "Flags:\n" + - " -s shifts the selection to the last stacked copy\n" + - " -a skips air blocks", - min = 0, - max = 2 + name = "/stack", + desc = "Repeat the contents of the selection" ) @CommandPermissions("worldedit.region.stack") @Logging(ORIENTATION_REGION) - public void stack(Player player, EditSession editSession, LocalSession session, - @Selection Region region, - @Optional("1") @Range(min = 1) int count, - @Optional(Direction.AIM) @Direction(includeDiagonals = true) BlockVector3 direction, - @Switch('s') boolean moveSelection, - @Switch('a') boolean ignoreAirBlocks) throws WorldEditException { + public int stack(Player player, EditSession editSession, LocalSession session, + @Selection Region region, + @Arg(desc = "# of copies to stack", def = "1") + int count, + @Arg(desc = "The direction to stack", def = Direction.AIM) + @Direction(includeDiagonals = true) + BlockVector3 direction, + @Switch(name = 's', desc = "Shift the selection to the last stacked copy") + boolean moveSelection, + @Switch(name = 'a', desc = "Ignore air blocks") + boolean ignoreAirBlocks) throws WorldEditException { int affected = editSession.stackCuboidRegion(region, direction, count, !ignoreAirBlocks); if (moveSelection) { @@ -334,26 +318,22 @@ public class RegionCommands { } } - player.print(affected + " blocks changed. Undo with //undo"); + player.print(affected + " block(s) changed. Undo with //undo"); + return affected; } @Command( - aliases = { "/regen" }, - usage = "", + name = "/regen", desc = "Regenerates the contents of the selection", - help = - "Regenerates the contents of the current selection.\n" + - "This command might affect things outside the selection,\n" + - "if they are within the same chunk.", - min = 0, - max = 0 + descFooter = "This command might affect things outside the selection,\n" + + "if they are within the same chunk." ) @CommandPermissions("worldedit.regen") @Logging(REGION) public void regenerateChunk(Player player, LocalSession session, EditSession editSession, @Selection Region region) throws WorldEditException { Mask mask = session.getMask(); try { - session.setMask((Mask) null); + session.setMask(null); player.getWorld().regenerate(region, editSession); } finally { session.setMask(mask); @@ -362,25 +342,22 @@ public class RegionCommands { } @Command( - aliases = { "/deform" }, - usage = "", - desc = "Deforms a selected region with an expression", - help = - "Deforms a selected region with an expression\n" + - "The expression is executed for each block and is expected\n" + - "to modify the variables x, y and z to point to a new block\n" + - "to fetch. See also tinyurl.com/wesyntax.", - flags = "ro", - min = 1, - max = -1 + name = "/deform", + desc = "Deforms a selected region with an expression", + descFooter = "The expression is executed for each block and is expected\n" + + "to modify the variables x, y and z to point to a new block\n" + + "to fetch. See also tinyurl.com/wesyntax." ) @CommandPermissions("worldedit.region.deform") @Logging(ALL) - 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 { + public int deform(Player player, LocalSession session, EditSession editSession, + @Selection Region region, + @Arg(desc = "The expression to use") + String expression, + @Switch(name = 'r', desc = "Use the game's coordinate origin") + boolean useRawCoords, + @Switch(name = 'o', desc = "Use the selection's center as origin") + boolean offset) throws WorldEditException { final Vector3 zero; Vector3 unit; @@ -406,58 +383,60 @@ public class RegionCommands { final int affected = editSession.deformRegion(region, zero, unit, expression, session.getTimeout()); player.findFreePosition(); player.print(affected + " block(s) have been deformed."); + return affected; } catch (ExpressionException e) { player.printError(e.getMessage()); + return 0; } } @Command( - aliases = { "/hollow" }, - usage = "[[ ]]", + name = "/hollow", desc = "Hollows out the object contained in this selection", - help = - "Hollows out the object contained in this selection.\n" + - "Optionally fills the hollowed out part with the given block.\n" + - "Thickness is measured in manhattan distance.", - min = 0, - max = 2 + descFooter = "Thickness is measured in manhattan distance." ) @CommandPermissions("worldedit.region.hollow") @Logging(REGION) - public void hollow(Player player, EditSession editSession, - @Selection Region region, - @Optional("0") @Range(min = 0) int thickness, - @Optional("air") Pattern pattern) throws WorldEditException { + public int hollow(Player player, EditSession editSession, + @Selection Region region, + @Arg(desc = "Thickness of the shell to leave", def = "0") + int thickness, + @Arg(desc = "The pattern of blocks to replace the hollowed area with", def = "air") + Pattern pattern) throws WorldEditException { + checkArgument(thickness >= 0, "Thickness must be >= 0"); int affected = editSession.hollowOutRegion(region, thickness, pattern); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/forest" }, - usage = "[type] [density]", - desc = "Make a forest within the region", - min = 0, - max = 2 + name = "/forest", + desc = "Make a forest within the region" ) @CommandPermissions("worldedit.region.forest") @Logging(REGION) - public void forest(Player player, EditSession editSession, @Selection Region region, @Optional("tree") TreeType type, - @Optional("5") @Range(min = 0, max = 100) double density) throws WorldEditException { + public int forest(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The type of tree to place", def = "tree") + TreeType type, + @Arg(desc = "The density of the forest", def = "5") + double density) throws WorldEditException { + checkArgument(0 <= density && density <= 100, "Density must be in [0, 100]"); int affected = editSession.makeForest(region, density / 100, type); player.print(affected + " trees created."); + return affected; } @Command( - aliases = { "/flora" }, - usage = "[density]", - desc = "Make flora within the region", - min = 0, - max = 1 + name = "/flora", + desc = "Make flora within the region" ) @CommandPermissions("worldedit.region.flora") @Logging(REGION) - public void flora(Player player, EditSession editSession, @Selection Region region, @Optional("10") @Range(min = 0, max = 100) double density) throws WorldEditException { + public int flora(Player player, EditSession editSession, @Selection Region region, + @Arg(desc = "The density of the forest", def = "5") + double density) throws WorldEditException { + checkArgument(0 <= density && density <= 100, "Density must be in [0, 100]"); density = density / 100; FloraGenerator generator = new FloraGenerator(editSession); GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator); @@ -465,7 +444,9 @@ public class RegionCommands { visitor.setMask(new NoiseFilter2D(new RandomNoise(), density)); Operations.completeLegacy(visitor); - player.print(ground.getAffected() + " flora created."); + int affected = ground.getAffected(); + player.print(affected + " flora created."); + return affected; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java index 59013d552..7dd59b9f2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java @@ -42,6 +42,8 @@ import com.sk89q.worldedit.command.HistoryCommands; import com.sk89q.worldedit.command.HistoryCommandsRegistration; import com.sk89q.worldedit.command.NavigationCommands; import com.sk89q.worldedit.command.NavigationCommandsRegistration; +import com.sk89q.worldedit.command.RegionCommands; +import com.sk89q.worldedit.command.RegionCommandsRegistration; import com.sk89q.worldedit.command.SchematicCommands; import com.sk89q.worldedit.command.SchematicCommandsRegistration; import com.sk89q.worldedit.command.argument.Arguments; @@ -291,13 +293,17 @@ public final class PlatformCommandMananger { NavigationCommandsRegistration.builder(), new NavigationCommands(worldEdit) ); + register( + commandManager, + RegionCommandsRegistration.builder(), + new RegionCommands() + ); // Unported commands are below. Delete once they're added to the main manager above. /* dispatcher = new CommandGraph() .builder(builder) .commands() - .registerMethods(new RegionCommands(worldEdit)) .registerMethods(new ScriptingCommands(worldEdit)) .registerMethods(new SelectionCommands(worldEdit)) .registerMethods(new SnapshotUtilCommands(worldEdit)) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java index 2e9c6fa62..b75d1c5b3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditExceptionConverter.java @@ -163,10 +163,8 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper { } @ExceptionMatch - public void convert(ConditionFailedException e) throws CommandException { - if (e.getCondition() instanceof PermissionCondition) { - throw new CommandException("You are not permitted to do that. Are you in the right mode?", e, null); - } + public void convert(IllegalArgumentException e) throws CommandException { + throw new CommandException(e.getMessage(), e, null); } }