From 9b0fda9f83d7310696d9926408e7d06b0c98e3ca Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Thu, 25 Apr 2019 19:36:22 -0700 Subject: [PATCH] Port utility commands --- .../java/com/sk89q/worldedit/EditSession.java | 6 +- .../worldedit/command/UtilityCommands.java | 706 +++++++----------- .../worldedit/command/WorldEditCommands.java | 1 - .../argument/EntityRemoverConverter.java | 53 ++ .../worldedit/command/util/EntityRemover.java | 20 +- .../command/util/PrintCommandHelp.java | 160 ++++ .../platform/PlatformCommandMananger.java | 21 +- .../worldedit/util/command/CommandUtil.java | 53 ++ .../formatting/component/CommandUsageBox.java | 67 +- 9 files changed, 574 insertions(+), 513 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/command/CommandUtil.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index a831c10b8..43843e8a3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -122,7 +122,6 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -905,16 +904,15 @@ public class EditSession implements Extent, AutoCloseable { * Remove blocks of a certain type nearby a given position. * * @param position center position of cuboid - * @param blockType the block type to match + * @param mask the mask to match * @param apothem an apothem of the cuboid, where the minimum is 1 * @return number of blocks affected * @throws MaxChangedBlocksException thrown if too many blocks are changed */ - public int removeNear(BlockVector3 position, BlockType blockType, int apothem) throws MaxChangedBlocksException { + public int removeNear(BlockVector3 position, Mask mask, int apothem) throws MaxChangedBlocksException { checkNotNull(position); checkArgument(apothem >= 1, "apothem >= 1"); - Mask mask = new BlockTypeMask(this, blockType); BlockVector3 adjustment = BlockVector3.ONE.multiply(apothem - 1); Region region = new CuboidRegion( getWorld(), // Causes clamping of Y range diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 4d69452b2..d6f916bd9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -19,28 +19,29 @@ package com.sk89q.worldedit.command; -import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; - -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.EditSession; +import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.MaxChangedBlocksException; 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.CreatureButcher; import com.sk89q.worldedit.command.util.EntityRemover; import com.sk89q.worldedit.command.util.Logging; +import com.sk89q.worldedit.command.util.PrintCommandHelp; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.function.EntityFunction; +import com.sk89q.worldedit.function.mask.BlockTypeMask; +import com.sk89q.worldedit.function.mask.ExistingBlockMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operations; -import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.EntityVisitor; import com.sk89q.worldedit.internal.expression.Expression; @@ -50,22 +51,23 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.session.SessionOwner; -import com.sk89q.worldedit.util.command.CommandMapping; -import com.sk89q.worldedit.util.command.Dispatcher; -import com.sk89q.worldedit.util.command.binding.Text; import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockTypes; -import org.enginehub.piston.CommandManager; +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 java.util.Set; +import java.util.function.Supplier; + +import static com.sk89q.worldedit.command.util.Logging.LogMode.PLACEMENT; /** * Utility commands. */ +@CommandContainer(superTypes = CommandPermissionsConditionGenerator.Registration.class) public class UtilityCommands { private final WorldEdit we; @@ -75,357 +77,370 @@ public class UtilityCommands { } @Command( - aliases = { "/fill" }, - usage = " [depth]", - desc = "Fill a hole", - min = 2, - max = 3 + name = "/fill", + desc = "Fill a hole" ) @CommandPermissions("worldedit.fill") @Logging(PLACEMENT) - public void fill(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - ParserContext context = new ParserContext(); - context.setActor(player); - context.setWorld(player.getWorld()); - context.setSession(session); - Pattern pattern = we.getPatternFactory().parseFromInput(args.getString(0), context); - - double radius = Math.max(1, args.getDouble(1)); + public int fill(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The blocks to fill with") + Pattern pattern, + @Arg(desc = "The radius to fill in") + double radius, + @Arg(desc = "The depth to fill", def = "1") + int depth) throws WorldEditException { + radius = Math.max(1, radius); we.checkMaxRadius(radius); - int depth = args.argsLength() > 2 ? Math.max(1, args.getInteger(2)) : 1; + depth = Math.max(1, depth); BlockVector3 pos = session.getPlacementPosition(player); int affected = editSession.fillXZ(pos, pattern, radius, depth, false); player.print(affected + " block(s) have been created."); + return affected; } @Command( - aliases = { "/fillr" }, - usage = " [depth]", - desc = "Fill a hole recursively", - min = 2, - max = 3 + name = "/fillr", + desc = "Fill a hole recursively" ) @CommandPermissions("worldedit.fill.recursive") @Logging(PLACEMENT) - public void fillr(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - ParserContext context = new ParserContext(); - context.setActor(player); - context.setWorld(player.getWorld()); - context.setSession(session); - Pattern pattern = we.getPatternFactory().parseFromInput(args.getString(0), context); - - double radius = Math.max(1, args.getDouble(1)); + public int fillr(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The blocks to fill with") + Pattern pattern, + @Arg(desc = "The radius to fill in") + double radius, + @Arg(desc = "The depth to fill", def = "") + Integer depth) throws WorldEditException { + radius = Math.max(1, radius); + we.checkMaxRadius(radius); + depth = depth == null ? Integer.MAX_VALUE : Math.max(1, depth); we.checkMaxRadius(radius); - int depth = args.argsLength() > 2 ? Math.max(1, args.getInteger(2)) : Integer.MAX_VALUE; BlockVector3 pos = session.getPlacementPosition(player); - int affected = 0; - if (pattern instanceof BlockPattern) { - affected = editSession.fillXZ(pos, ((BlockPattern) pattern).getBlock(), radius, depth, true); - } else { - affected = editSession.fillXZ(pos, pattern, radius, depth, true); - } + int affected = editSession.fillXZ(pos, pattern, radius, depth, true); player.print(affected + " block(s) have been created."); + return affected; } @Command( - aliases = { "/drain" }, - usage = "", - flags = "w", - desc = "Drain a pool", - help = "Removes all connected water sources.\n" + - " If -w is specified, also makes waterlogged blocks non-waterlogged.", - min = 1, - max = 1 + name = "/drain", + desc = "Drain a pool" ) @CommandPermissions("worldedit.drain") @Logging(PLACEMENT) - public void drain(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - double radius = Math.max(0, args.getDouble(0)); - boolean waterlogged = args.hasFlag('w'); + public int drain(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to drain") + double radius, + @Switch(name = 'w', desc = "Also un-waterlog blocks") + boolean waterlogged) throws WorldEditException { + radius = Math.max(0, radius); we.checkMaxRadius(radius); int affected = editSession.drainArea( - session.getPlacementPosition(player), radius, waterlogged); + session.getPlacementPosition(player), radius, waterlogged); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/fixlava", "fixlava" }, - usage = "", - desc = "Fix lava to be stationary", - min = 1, - max = 1 + name = "fixlava", + aliases = { "/fixlava" }, + desc = "Fix lava to be stationary" ) @CommandPermissions("worldedit.fixlava") @Logging(PLACEMENT) - public void fixLava(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - double radius = Math.max(0, args.getDouble(0)); + public int fixLava(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to fix in") + double radius) throws WorldEditException { + radius = Math.max(0, radius); we.checkMaxRadius(radius); int affected = editSession.fixLiquid(session.getPlacementPosition(player), radius, BlockTypes.LAVA); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/fixwater", "fixwater" }, - usage = "", - desc = "Fix water to be stationary", - min = 1, - max = 1 + name = "fixwater", + aliases = { "/fixwater" }, + desc = "Fix water to be stationary" ) @CommandPermissions("worldedit.fixwater") @Logging(PLACEMENT) - public void fixWater(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - double radius = Math.max(0, args.getDouble(0)); + public int fixWater(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius to fix in") + double radius) throws WorldEditException { + radius = Math.max(0, radius); we.checkMaxRadius(radius); int affected = editSession.fixLiquid(session.getPlacementPosition(player), radius, BlockTypes.WATER); player.print(affected + " block(s) have been changed."); + return affected; } @Command( - aliases = { "/removeabove", "removeabove" }, - usage = "[size] [height]", - desc = "Remove blocks above your head.", - min = 0, - max = 2 + name = "removeabove", + aliases = { "/removeabove" }, + desc = "Remove blocks above your head." ) @CommandPermissions("worldedit.removeabove") @Logging(PLACEMENT) - 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; + public int removeAbove(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The apothem of the square to remove from", def = "1") + int size, + @Arg(desc = "The maximum height above you to remove from", def = "") + Integer height) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); World world = player.getWorld(); - int height = args.argsLength() > 1 ? Math.min((world.getMaxY() + 1), args.getInteger(1) + 2) : (world.getMaxY() + 1); + height = height != null ? Math.min((world.getMaxY() + 1), height + 2) : (world.getMaxY() + 1); int affected = editSession.removeAbove( - session.getPlacementPosition(player), size, height); + session.getPlacementPosition(player), size, height); player.print(affected + " block(s) have been removed."); + return affected; } @Command( - aliases = { "/removebelow", "removebelow" }, - usage = "[size] [height]", - desc = "Remove blocks below you.", - min = 0, - max = 2 + name = "removebelow", + aliases = { "/removebelow" }, + desc = "Remove blocks below you." ) @CommandPermissions("worldedit.removebelow") @Logging(PLACEMENT) - 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; + public int removeBelow(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The apothem of the square to remove from", def = "1") + int size, + @Arg(desc = "The maximum height below you to remove from", def = "") + Integer height) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); World world = player.getWorld(); - int height = args.argsLength() > 1 ? Math.min((world.getMaxY() + 1), args.getInteger(1) + 2) : (world.getMaxY() + 1); + height = height != null ? Math.min((-world.getMaxY() + 1), height + 2) : (world.getMaxY() + 1); int affected = editSession.removeBelow(session.getPlacementPosition(player), size, height); player.print(affected + " block(s) have been removed."); + return affected; } @Command( - aliases = { "/removenear", "removenear" }, - usage = " [size]", - desc = "Remove blocks near you.", - min = 1, - max = 2 + name = "removenear", + aliases = { "/removenear" }, + desc = "Remove blocks near you." ) @CommandPermissions("worldedit.removenear") @Logging(PLACEMENT) - public void removeNear(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public int removeNear(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The mask of blocks to remove") + Mask mask, + @Arg(desc = "The radius of the square to remove from", def = "50") + int radius) throws WorldEditException { + radius = Math.max(1, radius); + we.checkMaxRadius(radius); - ParserContext context = new ParserContext(); - context.setActor(player); - context.setWorld(player.getWorld()); - context.setSession(session); - context.setRestricted(false); - context.setPreferringWildcard(false); - - BaseBlock block = we.getBlockFactory().parseFromInput(args.getString(0), context); - int size = Math.max(1, args.getInteger(1, 50)); - we.checkMaxRadius(size); - - int affected = editSession.removeNear(session.getPlacementPosition(player), block.getBlockType(), size); + int affected = editSession.removeNear(session.getPlacementPosition(player), mask, radius); player.print(affected + " block(s) have been removed."); + return affected; } @Command( - aliases = { "/replacenear", "replacenear" }, - usage = " ", - desc = "Replace nearby blocks", - flags = "f", - min = 3, - max = 3 + name = "replacenear", + aliases = { "/replacenear" }, + desc = "Replace nearby blocks" ) @CommandPermissions("worldedit.replacenear") @Logging(PLACEMENT) - public void replaceNear(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { - - int size = Math.max(1, args.getInteger(0)); - we.checkMaxRadius(size); - int affected; - Set from; - Pattern to; - - ParserContext context = new ParserContext(); - context.setActor(player); - context.setWorld(player.getWorld()); - context.setSession(session); - context.setRestricted(false); - context.setPreferringWildcard(!args.hasFlag('f')); - - if (args.argsLength() == 2) { - from = null; - context.setRestricted(true); - to = we.getPatternFactory().parseFromInput(args.getString(1), context); - } else { - from = we.getBlockFactory().parseFromListInput(args.getString(1), context); - context.setRestricted(true); - to = we.getPatternFactory().parseFromInput(args.getString(2), context); - } + public int replaceNear(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the square to remove in") + int radius, + @Arg(desc = "The mask matching blocks to remove", def = "") + Mask from, + @Arg(desc = "The pattern of blocks to replace with") + Pattern to) throws WorldEditException { + radius = Math.max(1, radius); + we.checkMaxRadius(radius); BlockVector3 base = session.getPlacementPosition(player); - BlockVector3 min = base.subtract(size, size, size); - BlockVector3 max = base.add(size, size, size); + BlockVector3 min = base.subtract(radius, radius, radius); + BlockVector3 max = base.add(radius, radius, radius); Region region = new CuboidRegion(player.getWorld(), min, max); - if (to instanceof BlockPattern) { - affected = editSession.replaceBlocks(region, from, ((BlockPattern) to).getBlock()); - } else { - affected = editSession.replaceBlocks(region, from, to); + 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 = { "/snow", "snow" }, - usage = "[radius]", - desc = "Simulates snow", - min = 0, - max = 1 + name = "snow", + aliases = { "/snow" }, + desc = "Simulates snow" ) @CommandPermissions("worldedit.snow") @Logging(PLACEMENT) - 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; + public int snow(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the circle to snow in", def = "10") + double size) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); int affected = editSession.simulateSnow(session.getPlacementPosition(player), size); - player.print(affected + " surfaces covered. Let it snow~"); + player.print(affected + " surface(s) covered. Let it snow~"); + return affected; } @Command( - aliases = {"/thaw", "thaw"}, - usage = "[radius]", - desc = "Thaws the area", - min = 0, - max = 1 + name = "thaw", + aliases = { "/thaw" }, + desc = "Thaws the area" ) @CommandPermissions("worldedit.thaw") @Logging(PLACEMENT) - 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; + public int thaw(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the circle to thaw in", def = "10") + double size) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); int affected = editSession.thaw(session.getPlacementPosition(player), size); - player.print(affected + " surfaces thawed."); + player.print(affected + " surface(s) thawed."); + return affected; } @Command( - aliases = { "/green", "green" }, - usage = "[radius]", - desc = "Greens the area", - help = "Converts dirt to grass blocks. -f also converts coarse dirt.", - flags = "f", - min = 0, - max = 1 + name = "green", + aliases = { "/green" }, + desc = "Converts dirt to grass blocks in the area" ) @CommandPermissions("worldedit.green") @Logging(PLACEMENT) - 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; + public int green(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the circle to convert in", def = "10") + double size, + @Switch(name = 'f', desc = "Also convert coarse dirt") + boolean convertCoarse) throws WorldEditException { + size = Math.max(1, size); we.checkMaxRadius(size); - final boolean onlyNormalDirt = !args.hasFlag('f'); + final boolean onlyNormalDirt = !convertCoarse; final int affected = editSession.green(session.getPlacementPosition(player), size, onlyNormalDirt); - player.print(affected + " surfaces greened."); + player.print(affected + " surface(s) greened."); + return affected; } @Command( - aliases = { "/ex", "/ext", "/extinguish", "ex", "ext", "extinguish" }, - usage = "[radius]", - desc = "Extinguish nearby fire", - min = 0, - max = 1 - ) + name = "extinguish", + aliases = { "/ex", "/ext", "/extinguish", "ex", "ext" }, + desc = "Extinguish nearby fire" + ) @CommandPermissions("worldedit.extinguish") @Logging(PLACEMENT) - public void extinguish(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException { + public void extinguish(Player player, LocalSession session, EditSession editSession, + @Arg(desc = "The radius of the square to remove in", def = "") + Integer radius) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); int defaultRadius = config.maxRadius != -1 ? Math.min(40, config.maxRadius) : 40; - int size = args.argsLength() > 0 ? Math.max(1, args.getInteger(0)) - : defaultRadius; + int size = radius != null ? Math.max(1, radius) : defaultRadius; we.checkMaxRadius(size); - int affected = editSession.removeNear(session.getPlacementPosition(player), BlockTypes.FIRE, size); + Mask mask = new BlockTypeMask(editSession, BlockTypes.FIRE); + int affected = editSession.removeNear(session.getPlacementPosition(player), mask, size); player.print(affected + " block(s) have been removed."); } @Command( - aliases = { "butcher" }, - usage = "[radius]", - flags = "plangbtfr", - desc = "Kill all or nearby mobs", - help = - "Kills nearby mobs, based on radius, if none is given uses default in configuration.\n" + - "Flags:\n" + - " -p also kills pets.\n" + - " -n also kills NPCs.\n" + - " -g also kills Golems.\n" + - " -a also kills animals.\n" + - " -b also kills ambient mobs.\n" + - " -t also kills mobs with name tags.\n" + - " -f compounds all previous flags.\n" + - " -r also destroys armor stands.\n" + - " -l currently does nothing.", - min = 0, - max = 1 + name = "butcher", + desc = "Kill all or nearby mobs" ) @CommandPermissions("worldedit.butcher") @Logging(PLACEMENT) - public void butcher(Actor actor, CommandContext args) throws WorldEditException { + public int butcher(Actor actor, + @Arg(desc = "Radius to kill mobs in", def = "") + Integer radius, + @Switch(name = 'p', desc = "Also kill pets") + boolean killPets, + @Switch(name = 'n', desc = "Also kill NPCs") + boolean killNpcs, + @Switch(name = 'g', desc = "Also kill golems") + boolean killGolems, + @Switch(name = 'a', desc = "Also kill animals") + boolean killAnimals, + @Switch(name = 'b', desc = "Also kill ambient mobs") + boolean killAmbient, + @Switch(name = 't', desc = "Also kill mobs with name tags") + boolean killWithName, + @Switch(name = 'f', desc = "Also kill all friendly mobs (Applies the flags `-abgnpt`)") + boolean killFriendly, + @Switch(name = 'r', desc = "Also destroy armor stands") + boolean killArmorStands, + @Switch(name = 'l', desc = "Kill via lightning. Currently non-functioning.") + boolean killWithLightning) throws WorldEditException { LocalConfiguration config = we.getConfiguration(); Player player = actor instanceof Player ? (Player) actor : null; - // technically the default can be larger than the max, but that's not my problem - int radius = config.butcherDefaultRadius; - - // there might be a better way to do this but my brain is fried right now - if (args.argsLength() > 0) { // user inputted radius, override the default - radius = args.getInteger(0); - if (radius < -1) { - actor.printError("Use -1 to remove all mobs in loaded chunks"); - return; - } - if (config.butcherMaxRadius != -1) { // clamp if there is a max - if (radius == -1) { - radius = config.butcherMaxRadius; - } else { // Math.min does not work if radius is -1 (actually highest possible value) - radius = Math.min(radius, config.butcherMaxRadius); - } + int defaultRadius = config.butcherDefaultRadius; + if (radius == null) { + radius = config.butcherDefaultRadius; + } else if (radius < -1) { + actor.printError("Use -1 to remove all mobs in loaded chunks"); + return 0; + } else if (radius == -1) { + if (config.butcherMaxRadius != -1) { + radius = config.butcherMaxRadius; } } + if (config.butcherMaxRadius != -1) { + radius = Math.min(radius, config.butcherMaxRadius); + } CreatureButcher flags = new CreatureButcher(actor); - flags.fromCommand(args); + flags.or(CreatureButcher.Flags.FRIENDLY, killFriendly); // No permission check here. Flags will instead be filtered by the subsequent calls. + flags.or(CreatureButcher.Flags.PETS, killPets, "worldedit.butcher.pets"); + flags.or(CreatureButcher.Flags.NPCS, killNpcs, "worldedit.butcher.npcs"); + flags.or(CreatureButcher.Flags.GOLEMS, killGolems, "worldedit.butcher.golems"); + flags.or(CreatureButcher.Flags.ANIMALS, killAnimals, "worldedit.butcher.animals"); + flags.or(CreatureButcher.Flags.AMBIENT, killAmbient, "worldedit.butcher.ambient"); + flags.or(CreatureButcher.Flags.TAGGED, killWithName, "worldedit.butcher.tagged"); + flags.or(CreatureButcher.Flags.ARMOR_STAND, killArmorStands, "worldedit.butcher.armorstands"); + flags.or(CreatureButcher.Flags.WITH_LIGHTNING, killWithLightning, "worldedit.butcher.lightning"); + + int killed = killMatchingEntities(radius, player, flags::createFunction); + + actor.print("Killed " + killed + (killed != 1 ? " mobs" : " mob") + (radius < 0 ? "" : " in a radius of " + radius) + "."); + + return killed; + } + + @Command( + name = "remove", + aliases = { "rem", "rement" }, + desc = "Remove all entities of a type" + ) + @CommandPermissions("worldedit.remove") + @Logging(PLACEMENT) + public int remove(Actor actor, + @Arg(desc = "The type of entity to remove") + EntityRemover remover, + @Arg(desc = "The radius of the cuboid to remove from") + int radius) throws WorldEditException { + Player player = actor instanceof Player ? (Player) actor : null; + + if (radius < -1) { + actor.printError("Use -1 to remove all entities in loaded chunks"); + return 0; + } + + int removed = killMatchingEntities(radius, player, remover::createFunction); + + actor.print("Marked " + removed + (removed != 1 ? " entities" : " entity") + " for removal."); + return removed; + } + + private int killMatchingEntities(Integer radius, Player player, Supplier func) throws IncompleteRegionException, MaxChangedBlocksException { List visitors = new ArrayList<>(); LocalSession session = null; EditSession editSession = null; @@ -441,12 +456,12 @@ public class UtilityCommands { } else { entities = editSession.getEntities(); } - visitors.add(new EntityVisitor(entities.iterator(), flags.createFunction())); + visitors.add(new EntityVisitor(entities.iterator(), func.get())); } else { Platform platform = we.getPlatformManager().queryCapability(Capability.WORLD_EDITING); for (World world : platform.getWorlds()) { List entities = world.getEntities(); - visitors.add(new EntityVisitor(entities.iterator(), flags.createFunction())); + visitors.add(new EntityVisitor(entities.iterator(), func.get())); } } @@ -456,246 +471,47 @@ public class UtilityCommands { killed += visitor.getAffected(); } - actor.print("Killed " + killed + (killed != 1 ? " mobs" : " mob") + (radius < 0 ? "" : " in a radius of " + radius) + "."); - if (editSession != null) { session.remember(editSession); editSession.flushSession(); } + return killed; } @Command( - aliases = { "remove", "rem", "rement" }, - usage = " ", - desc = "Remove all entities of a type", - min = 2, - max = 2 - ) - @CommandPermissions("worldedit.remove") - @Logging(PLACEMENT) - public void remove(Actor actor, CommandContext args) throws WorldEditException, CommandException { - String typeStr = args.getString(0); - int radius = args.getInteger(1); - Player player = actor instanceof Player ? (Player) actor : null; - - if (radius < -1) { - actor.printError("Use -1 to remove all entities in loaded chunks"); - return; - } - - EntityRemover remover = new EntityRemover(); - remover.fromString(typeStr); - - List visitors = new ArrayList<>(); - LocalSession session = null; - EditSession editSession = null; - - if (player != null) { - session = we.getSessionManager().get(player); - BlockVector3 center = session.getPlacementPosition(player); - editSession = session.createEditSession(player); - List entities; - if (radius >= 0) { - CylinderRegion region = CylinderRegion.createRadius(editSession, center, radius); - entities = editSession.getEntities(region); - } else { - entities = editSession.getEntities(); - } - visitors.add(new EntityVisitor(entities.iterator(), remover.createFunction())); - } else { - Platform platform = we.getPlatformManager().queryCapability(Capability.WORLD_EDITING); - for (World world : platform.getWorlds()) { - List entities = world.getEntities(); - visitors.add(new EntityVisitor(entities.iterator(), remover.createFunction())); - } - } - - int removed = 0; - for (EntityVisitor visitor : visitors) { - Operations.completeLegacy(visitor); - removed += visitor.getAffected(); - } - - actor.print("Marked " + removed + (removed != 1 ? " entities" : " entity") + " for removal."); - - if (editSession != null) { - session.remember(editSession); - editSession.flushSession(); - } - } - - @Command( - aliases = { "/calc", "/calculate", "/eval", "/evaluate", "/solve" }, - usage = "", + name = "/calculate", + aliases = { "/calc", "/eval", "/evaluate", "/solve" }, desc = "Evaluate a mathematical expression" ) @CommandPermissions("worldedit.calc") - public void calc(Actor actor, @Text String input) throws CommandException { + public void calc(Actor actor, + @Arg(desc = "Expression to evaluate") + String input) { try { Expression expression = Expression.compile(input); - if (actor instanceof SessionOwner) { - actor.print("= " + expression.evaluate( - new double[]{}, WorldEdit.getInstance().getSessionManager().get((SessionOwner) actor).getTimeout())); - } else { - actor.print("= " + expression.evaluate()); - } + actor.print("= " + expression.evaluate( + new double[] {}, WorldEdit.getInstance().getSessionManager().get(actor).getTimeout())); } catch (EvaluationException e) { actor.printError(String.format( - "'%s' could not be evaluated (error: %s)", input, e.getMessage())); + "'%s' could not be evaluated (error: %s)", input, e.getMessage())); } catch (ExpressionException e) { actor.printError(String.format( - "'%s' could not be parsed as a valid expression", input)); + "'%s' could not be parsed as a valid expression", input)); } } @Command( - aliases = { "/help" }, - usage = "[]", - desc = "Displays help for WorldEdit commands", - min = 0, - max = -1 + name = "/help", + desc = "Displays help for WorldEdit commands" ) @CommandPermissions("worldedit.help") - public void help(Actor actor, CommandContext args) throws WorldEditException { - help(args, we, actor); + public void help(Actor actor, + @Arg(desc = "The page to retrieve", def = "1") + int page, + @Arg(desc = "The command to retrieve help for", def = "", variable = true) + List commandPath) throws WorldEditException { + PrintCommandHelp.help(commandPath, page, we, actor); } - private static CommandMapping detectCommand(Dispatcher dispatcher, String command, boolean isRootLevel) { - CommandMapping mapping; - - // First try the command as entered - mapping = dispatcher.get(command); - if (mapping != null) { - return mapping; - } - - // Then if we're looking at root commands and the user didn't use - // any slashes, let's try double slashes and then single slashes. - // However, be aware that there exists different single slash - // and double slash commands in WorldEdit - if (isRootLevel && !command.contains("/")) { - mapping = dispatcher.get("//" + command); - if (mapping != null) { - return mapping; - } - - mapping = dispatcher.get("/" + command); - if (mapping != null) { - return mapping; - } - } - - return null; - } - - public static void help(CommandContext args, WorldEdit we, Actor actor) { - CommandManager manager = we.getPlatformManager().getPlatformCommandMananger().getCommandManager(); - - // TODO this will be implemented as a special utility in the manager - /* - - int page = 0; - final int perPage = actor instanceof Player ? 8 : 20; // More pages for console - int effectiveLength = args.argsLength(); - - // Detect page from args - try { - if (args.argsLength() > 0) { - page = args.getInteger(args.argsLength() - 1); - if (page <= 0) { - page = 1; - } else { - page--; - } - - effectiveLength--; - } - } catch (NumberFormatException ignored) { - } - - boolean isRootLevel = true; - List visited = new ArrayList<>(); - - // Drill down to the command - for (int i = 0; i < effectiveLength; i++) { - String command = args.getString(i); - - if (manager instanceof Dispatcher) { - // Chop off the beginning / if we're are the root level - if (isRootLevel && command.length() > 1 && command.charAt(0) == '/') { - command = command.substring(1); - } - - CommandMapping mapping = detectCommand((Dispatcher) manager, command, isRootLevel); - if (mapping != null) { - manager = mapping.getCallable(); - } else { - if (isRootLevel) { - actor.printError(String.format("The command '%s' could not be found.", args.getString(i))); - return; - } else { - actor.printError(String.format("The sub-command '%s' under '%s' could not be found.", - command, Joiner.on(" ").join(visited))); - return; - } - } - - visited.add(args.getString(i)); - isRootLevel = false; - } else { - actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)", - Joiner.on(" ").join(visited), command)); - return; - } - } - - // Create the message - if (manager instanceof Dispatcher) { - Dispatcher dispatcher = (Dispatcher) manager; - - // Get a list of aliases - List aliases = new ArrayList<>(dispatcher.getCommands()); - aliases.sort(new PrimaryAliasComparator(PlatformCommandMananger.COMMAND_CLEAN_PATTERN)); - - // Calculate pagination - int offset = perPage * page; - int pageTotal = (int) Math.ceil(aliases.size() / (double) perPage); - - // Box - CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page + 1, pageTotal)); - StyledFragment contents = box.getContents(); - StyledFragment tip = contents.createFragment(Style.GRAY); - - if (offset >= aliases.size()) { - tip.createFragment(Style.RED).append(String.format("There is no page %d (total number of pages is %d).", page + 1, pageTotal)).newLine(); - } else { - List list = aliases.subList(offset, Math.min(offset + perPage, aliases.size())); - - tip.append("Type "); - tip.append(new Code().append("//help ").append(" []")); - tip.append(" for more information.").newLine(); - - // Add each command - for (CommandMapping mapping : list) { - StringBuilder builder = new StringBuilder(); - if (isRootLevel) { - builder.append("/"); - } - if (!visited.isEmpty()) { - builder.append(Joiner.on(" ").join(visited)); - builder.append(" "); - } - builder.append(mapping.getPrimaryAlias()); - box.appendCommand(builder.toString(), mapping.getDescription().getDescription()); - } - } - - actor.printRaw(ColorCodeBuilder.asColorCodes(box)); - } else { - CommandUsageBox box = new CommandUsageBox(manager, Joiner.on(" ").join(visited)); - actor.printRaw(ColorCodeBuilder.asColorCodes(box)); - } - */ - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index 5378d5819..aee7ba0f7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -157,6 +157,5 @@ public class WorldEditCommands { ) @CommandPermissions("worldedit.help") public void help(Actor actor, CommandContext args) throws WorldEditException { - UtilityCommands.help(args, we, actor); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java new file mode 100644 index 000000000..03db41b28 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java @@ -0,0 +1,53 @@ +/* + * 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.command.argument; + +import com.sk89q.worldedit.command.util.EntityRemover; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +public class EntityRemoverConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(EntityRemover.class), new EntityRemoverConverter()); + } + + private EntityRemoverConverter() { + } + + @Override + public String describeAcceptableArguments() { + return "projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"; + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + try { + return SuccessfulConversion.fromSingle(EntityRemover.fromString(argument)); + } catch (Exception e) { + return FailedConversion.from(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java index cac57b0dc..516f33945 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java @@ -19,15 +19,13 @@ package com.sk89q.worldedit.command.util; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.function.EntityFunction; +import javax.annotation.Nullable; import java.util.regex.Pattern; -import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; /** * The implementation of /remove. @@ -125,17 +123,21 @@ public class EntityRemover { } } - private Type type; - - public void fromString(String str) throws CommandException { + public static EntityRemover fromString(String str) { Type type = Type.findByPattern(str); if (type != null) { - this.type = type; + return new EntityRemover(type); } else { - throw new CommandException("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); + throw new IllegalArgumentException("Acceptable types: projectiles, items, paintings, itemframes, boats, minecarts, tnt, xp, or all"); } } + private final Type type; + + private EntityRemover(Type type) { + this.type = type; + } + public EntityFunction createFunction() { final Type type = this.type; checkNotNull(type, "type can't be null"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java new file mode 100644 index 000000000..c22880f04 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/PrintCommandHelp.java @@ -0,0 +1,160 @@ +/* + * 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.command.util; + +import com.google.common.base.Joiner; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; +import com.sk89q.worldedit.util.formatting.Style; +import com.sk89q.worldedit.util.formatting.StyledFragment; +import com.sk89q.worldedit.util.formatting.component.Code; +import com.sk89q.worldedit.util.formatting.component.CommandListBox; +import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import static com.sk89q.worldedit.util.command.CommandUtil.byCleanName; +import static com.sk89q.worldedit.util.command.CommandUtil.getSubCommands; +import static java.util.stream.Collectors.toList; + +/** + * Implementation of the //help command. + */ +// Stored in a separate class to prevent import conflicts. +public class PrintCommandHelp { + + private static Command detectCommand(CommandManager manager, String command) { + Optional mapping; + + // First try the command as entered + mapping = manager.getCommand(command); + if (mapping.isPresent()) { + return mapping.get(); + } + + // If tried with slashes, try dropping a slash + if (command.startsWith("/")) { + mapping = manager.getCommand(command.substring(1)); + return mapping.orElse(null); + } + + // Otherwise, check /command, since that's common + mapping = manager.getCommand("/" + command); + return mapping.orElse(null); + } + + public static void help(List commandPath, int page, WorldEdit we, Actor actor) { + if (page < 1) { + actor.printError("Page must be >= 1."); + return; + } + CommandManager manager = we.getPlatformManager().getPlatformCommandMananger().getCommandManager(); + + final int perPage = actor instanceof Player ? 8 : 20; // More pages for console + + if (commandPath.isEmpty()) { + printAllCommands(page, perPage, manager.getAllCommands(), actor, true); + return; + } + + List visited = new ArrayList<>(); + Command currentCommand = detectCommand(manager, commandPath.get(0)); + if (currentCommand == null) { + actor.printError(String.format("The command '%s' could not be found.", commandPath.get(0))); + return; + } + visited.add(commandPath.get(0)); + + // Drill down to the command + for (int i = 1; i < commandPath.size(); i++) { + String subCommand = commandPath.get(i); + Map subCommands = getSubCommands(currentCommand); + + if (subCommands.isEmpty()) { + actor.printError(String.format("'%s' has no sub-commands. (Maybe '%s' is for a parameter?)", + Joiner.on(" ").join(visited), subCommand)); + return; + } + + if (subCommands.containsKey(subCommand)) { + visited.add(subCommand); + currentCommand = subCommands.get(subCommand); + } else { + actor.printError(String.format("The sub-command '%s' under '%s' could not be found.", + subCommand, Joiner.on(" ").join(visited))); + return; + } + } + + Map subCommands = getSubCommands(currentCommand); + + if (subCommands.isEmpty()) { + // Create the message + CommandUsageBox box = new CommandUsageBox(currentCommand, String.join(" ", visited)); + actor.printRaw(ColorCodeBuilder.asColorCodes(box)); + } else { + printAllCommands(page, perPage, subCommands.values().stream(), actor, false); + } + } + + private static void printAllCommands(int page, int perPage, Stream commandStream, Actor actor, boolean isRootLevel) { + // Get a list of aliases + List commands = commandStream + .sorted(byCleanName()) + .collect(toList()); + + // Calculate pagination + int offset = perPage * (page - 1); + int pageTotal = (int) Math.ceil(commands.size() / (double) perPage); + + // Box + CommandListBox box = new CommandListBox(String.format("Help: page %d/%d ", page, pageTotal)); + StyledFragment contents = box.getContents(); + StyledFragment tip = contents.createFragment(Style.GRAY); + + if (offset >= commands.size()) { + tip.createFragment(Style.RED).append(String.format("There is no page %d (total number of pages is %d).", page, pageTotal)).newLine(); + } else { + List list = commands.subList(offset, Math.min(offset + perPage, commands.size())); + + tip.append("Type "); + tip.append(new Code().append("//help ").append("[] ")); + tip.append(" for more information.").newLine(); + + // Add each command + for (Command mapping : list) { + box.appendCommand((isRootLevel ? "/" : "") + mapping.getName(), mapping.getDescription()); + } + } + + actor.printRaw(ColorCodeBuilder.asColorCodes(box)); + } + + private PrintCommandHelp() { + } +} 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 016500666..25266047f 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 @@ -60,10 +60,13 @@ import com.sk89q.worldedit.command.ToolCommands; import com.sk89q.worldedit.command.ToolCommandsRegistration; import com.sk89q.worldedit.command.ToolUtilCommands; import com.sk89q.worldedit.command.ToolUtilCommandsRegistration; +import com.sk89q.worldedit.command.UtilityCommands; +import com.sk89q.worldedit.command.UtilityCommandsRegistration; import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.argument.BooleanConverter; import com.sk89q.worldedit.command.argument.CommaSeparatedValuesConverter; import com.sk89q.worldedit.command.argument.DirectionConverter; +import com.sk89q.worldedit.command.argument.EntityRemoverConverter; import com.sk89q.worldedit.command.argument.EnumConverter; import com.sk89q.worldedit.command.argument.ExpandAmountConverter; import com.sk89q.worldedit.command.argument.MaskConverter; @@ -78,16 +81,11 @@ import com.sk89q.worldedit.event.platform.CommandEvent; import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.annotation.Selection; -import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.command.CommandLoggingHandler; -import com.sk89q.worldedit.internal.command.UserCommandCompleter; -import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.command.parametric.ExceptionConverter; -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; @@ -188,12 +186,6 @@ public final class PlatformCommandMananger { commandLog.addHandler(dynamicHandler); // Set up the commands manager - ParametricBuilder builder = new ParametricBuilder(); - builder.setAuthorizer(new ActorAuthorizer()); - builder.setDefaultCompleter(new UserCommandCompleter(platformManager)); - builder.addBinding(new WorldEditBinding(worldEdit)); - builder.addInvokeListener(new LegacyCommandsHandler()); - registerAlwaysInjectedValues(); registerArgumentConverters(); registerAllCommands(); @@ -215,6 +207,7 @@ public final class PlatformCommandMananger { ExpandAmountConverter.register(commandManager); ZonedDateTimeConverter.register(commandManager); BooleanConverter.register(commandManager); + EntityRemoverConverter.register(commandManager); } private void registerAlwaysInjectedValues() { @@ -383,13 +376,17 @@ public final class PlatformCommandMananger { ToolUtilCommandsRegistration.builder(), new ToolUtilCommands(worldEdit) ); + register( + commandManager, + UtilityCommandsRegistration.builder(), + new UtilityCommands(worldEdit) + ); // Unported commands are below. Delete once they're added to the main manager above. /* dispatcher = new CommandGraph() .builder(builder) .commands() - .registerMethods(new UtilityCommands(worldEdit)) .register(adapt(new SelectionCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within selection"), "worldedit.region.set")), "/set") .group("worldedit", "we") .describeAs("WorldEdit commands") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/CommandUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/CommandUtil.java new file mode 100644 index 000000000..c583add43 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/CommandUtil.java @@ -0,0 +1,53 @@ +/* + * 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.extension.platform.PlatformCommandMananger; +import org.enginehub.piston.Command; +import org.enginehub.piston.part.SubCommandPart; + +import java.util.Comparator; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class CommandUtil { + + public static Map getSubCommands(Command currentCommand) { + return currentCommand.getParts().stream() + .filter(p -> p instanceof SubCommandPart) + .flatMap(p -> ((SubCommandPart) p).getCommands().stream()) + .collect(Collectors.toMap(Command::getName, Function.identity())); + } + + private static String clean(String input) { + return PlatformCommandMananger.COMMAND_CLEAN_PATTERN.matcher(input).replaceAll(""); + } + + private static final Comparator BY_CLEAN_NAME = + Comparator.comparing(c -> clean(c.getName())); + + public static Comparator byCleanName() { + return BY_CLEAN_NAME; + } + + private CommandUtil() { + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java index c7c5cf40d..03c01bf2a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/CommandUsageBox.java @@ -19,21 +19,18 @@ package com.sk89q.worldedit.util.formatting.component; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.sk89q.minecraft.util.commands.CommandLocals; -import com.sk89q.worldedit.extension.platform.PlatformCommandMananger; -import com.sk89q.worldedit.util.command.CommandCallable; -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.PrimaryAliasComparator; import com.sk89q.worldedit.util.formatting.StyledFragment; - -import java.util.ArrayList; -import java.util.List; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandParameters; import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.util.command.CommandUtil.byCleanName; +import static com.sk89q.worldedit.util.command.CommandUtil.getSubCommands; /** * A box to describe usage of a command. @@ -46,7 +43,7 @@ public class CommandUsageBox extends StyledFragment { * @param command the command to describe * @param commandString the command that was used, such as "/we" or "/brush sphere" */ - public CommandUsageBox(CommandCallable command, String commandString) { + public CommandUsageBox(Command command, String commandString) { this(command, commandString, null); } @@ -55,54 +52,40 @@ public class CommandUsageBox extends StyledFragment { * * @param command the command to describe * @param commandString the command that was used, such as "/we" or "/brush sphere" - * @param locals list of locals to use + * @param parameters list of parameters to use */ - public CommandUsageBox(CommandCallable command, String commandString, @Nullable CommandLocals locals) { + public CommandUsageBox(Command command, String commandString, @Nullable CommandParameters parameters) { checkNotNull(command); checkNotNull(commandString); - if (command instanceof Dispatcher) { - attachDispatcherUsage((Dispatcher) command, commandString, locals); + Map subCommands = getSubCommands(command); + if (subCommands.isEmpty()) { + attachCommandUsage(command, commandString); } else { - attachCommandUsage(command.getDescription(), commandString); + attachSubcommandUsage(subCommands, commandString, parameters); } } - private void attachDispatcherUsage(Dispatcher dispatcher, String commandString, @Nullable CommandLocals locals) { + private void attachSubcommandUsage(Map dispatcher, String commandString, @Nullable CommandParameters parameters) { CommandListBox box = new CommandListBox("Subcommands"); String prefix = !commandString.isEmpty() ? commandString + " " : ""; - List list = new ArrayList<>(dispatcher.getCommands()); - list.sort(new PrimaryAliasComparator(PlatformCommandMananger.COMMAND_CLEAN_PATTERN)); + List list = dispatcher.values().stream() + .sorted(byCleanName()) + .collect(Collectors.toList()); - for (CommandMapping mapping : list) { - if (locals == null || mapping.getCallable().testPermission(locals)) { - box.appendCommand(prefix + mapping.getPrimaryAlias(), mapping.getDescription().getDescription()); + for (Command mapping : list) { + if (parameters == null || mapping.getCondition().satisfied(parameters)) { + box.appendCommand(prefix + mapping.getName(), mapping.getDescription()); } } append(box); } - private void attachCommandUsage(Description description, String commandString) { + private void attachCommandUsage(Command description, String commandString) { MessageBox box = new MessageBox("Help for " + commandString); - StyledFragment contents = box.getContents(); - if (description.getUsage() != null) { - contents.append(new Label().append("Usage: ")); - contents.append(description.getUsage()); - } else { - contents.append(new Subtle().append("Usage information is not available.")); - } - - contents.newLine(); - - if (description.getHelp() != null) { - contents.append(description.getHelp()); - } else if (description.getDescription() != null) { - contents.append(description.getDescription()); - } else { - contents.append(new Subtle().append("No further help is available.")); - } + box.getContents().append(description.getFullHelp()); append(box); }