From 9d08f266bf196b6648ef815b41606736458269a8 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 3 Apr 2014 17:52:53 -0700 Subject: [PATCH] Created pattern, mask, and block registries. Deprecated getBlock, getBlockPattern, and so-on in WorldEdit. --- .../worldedit/masks/DynamicRegionMask.java | 11 +- .../worldedit/masks/UnderOverlayMask.java | 7 +- .../java/com/sk89q/worldedit/EditSession.java | 14 +- .../com/sk89q/worldedit/LocalSession.java | 15 +- .../java/com/sk89q/worldedit/LocalWorld.java | 11 +- .../java/com/sk89q/worldedit/WorldEdit.java | 567 ++++-------------- .../sk89q/worldedit/WorldEditException.java | 43 +- .../worldedit/command/tool/BrushTool.java | 2 + .../input/DisallowedUsageException.java | 46 ++ .../extension/input/InputParseException.java | 48 ++ .../extension/input/NoMatchException.java | 46 ++ .../extension/input/ParserContext.java | 211 +++++++ .../extension/registry/BlockRegistry.java | 67 +++ .../registry/DefaultBlockParser.java | 307 ++++++++++ .../extension/registry/DefaultMaskParser.java | 139 +++++ .../registry/HashTagPatternParser.java | 60 ++ .../extension/registry/MaskRegistry.java | 46 ++ .../extension/registry/PatternRegistry.java | 48 ++ .../registry/RandomPatternParser.java | 67 +++ .../registry/SingleBlockPatternParser.java | 46 ++ .../com/sk89q/worldedit/extent/Extent.java | 23 + .../worldedit/extent/ExtentDelegate.java | 11 + .../sk89q/worldedit/function/mask/Masks.java | 56 +- .../worldedit/function/mask/OffsetMask.java | 90 +++ .../function/pattern/ClipboardPattern.java | 59 ++ .../worldedit/function/pattern/Patterns.java | 21 + .../pattern/RepeatingExtentPattern.java | 95 +++ .../internal/registry/AbstractRegistry.java | 67 +++ .../internal/registry/InputParser.java | 42 ++ .../sk89q/worldedit/regions/NullRegion.java | 139 +++++ .../worldedit/session/request/Request.java | 116 ++++ .../session/request/RequestSelection.java | 150 +++++ 32 files changed, 2192 insertions(+), 478 deletions(-) create mode 100644 src/main/java/com/sk89q/worldedit/extension/input/DisallowedUsageException.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/input/InputParseException.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/input/NoMatchException.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java create mode 100644 src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.java create mode 100644 src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java create mode 100644 src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java create mode 100644 src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java create mode 100644 src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java create mode 100644 src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java create mode 100644 src/main/java/com/sk89q/worldedit/regions/NullRegion.java create mode 100644 src/main/java/com/sk89q/worldedit/session/request/Request.java create mode 100644 src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java diff --git a/src/legacy/java/com/sk89q/worldedit/masks/DynamicRegionMask.java b/src/legacy/java/com/sk89q/worldedit/masks/DynamicRegionMask.java index d55d6cee2..7c2b9438f 100644 --- a/src/legacy/java/com/sk89q/worldedit/masks/DynamicRegionMask.java +++ b/src/legacy/java/com/sk89q/worldedit/masks/DynamicRegionMask.java @@ -1,12 +1,13 @@ package com.sk89q.worldedit.masks; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.IncompleteRegionException; -import com.sk89q.worldedit.LocalPlayer; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.session.request.RequestSelection; +/** + * @deprecated Use {@link RequestSelection} with {@link com.sk89q.worldedit.function.mask.RegionMask} + */ +@Deprecated public class DynamicRegionMask extends AbstractMask { private Region region; diff --git a/src/legacy/java/com/sk89q/worldedit/masks/UnderOverlayMask.java b/src/legacy/java/com/sk89q/worldedit/masks/UnderOverlayMask.java index e2e0216ae..154da7139 100644 --- a/src/legacy/java/com/sk89q/worldedit/masks/UnderOverlayMask.java +++ b/src/legacy/java/com/sk89q/worldedit/masks/UnderOverlayMask.java @@ -24,13 +24,16 @@ import com.sk89q.worldedit.LocalPlayer; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.function.mask.MaskIntersection; +import com.sk89q.worldedit.function.mask.Masks; +import com.sk89q.worldedit.function.mask.OffsetMask; import java.util.Set; /** - * - * @author 1337 + * @deprecated Use {@link OffsetMask} with {@link MaskIntersection} and {@link Masks#negate(com.sk89q.worldedit.function.mask.Mask)} */ +@Deprecated public class UnderOverlayMask extends AbstractMask { private final int yMod; private Mask mask; diff --git a/src/main/java/com/sk89q/worldedit/EditSession.java b/src/main/java/com/sk89q/worldedit/EditSession.java index 16a0ac93c..b226b5397 100644 --- a/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/src/main/java/com/sk89q/worldedit/EditSession.java @@ -250,7 +250,7 @@ public class EditSession implements Extent { if (mask == null) { maskingExtent.setMask(Masks.alwaysTrue()); } else { - maskingExtent.setMask(Masks.wrap(this, mask)); + maskingExtent.setMask(Masks.wrap(mask, this)); } } @@ -570,6 +570,16 @@ public class EditSession implements Extent { return getBlockChangeCount(); } + @Override + public Vector getMinimumPoint() { + return getWorld().getMinimumPoint(); + } + + @Override + public Vector getMaximumPoint() { + return getWorld().getMaximumPoint(); + } + /** * Finish off the queue. */ @@ -818,7 +828,7 @@ public class EditSession implements Extent { checkNotNull(pattern); BlockReplace replace = new BlockReplace(this, Patterns.wrap(pattern)); - RegionMaskingFilter filter = new RegionMaskingFilter(Masks.wrap(this, mask), replace); + RegionMaskingFilter filter = new RegionMaskingFilter(Masks.wrap(mask, this), replace); RegionVisitor visitor = new RegionVisitor(region, filter); Operations.completeLegacy(visitor); return visitor.getAffected(); diff --git a/src/main/java/com/sk89q/worldedit/LocalSession.java b/src/main/java/com/sk89q/worldedit/LocalSession.java index fc1801522..d0f8ffdfc 100644 --- a/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -19,28 +19,26 @@ package com.sk89q.worldedit; -import java.util.Calendar; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.TimeZone; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; import com.sk89q.jchronic.utils.Span; import com.sk89q.jchronic.utils.Time; -import com.sk89q.worldedit.world.snapshot.Snapshot; +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.BlockTool; import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.internal.cui.CUIRegion; 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.CuboidRegionSelector; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.world.snapshot.Snapshot; + +import java.util.*; /** * An instance of this represents the WorldEdit session of a user. A session @@ -704,6 +702,7 @@ public class LocalSession { .getEditSession(player.isPlayer() ? player.getWorld() : null, getBlockChangeLimit(), blockBag, player); editSession.setFastMode(fastMode); + Request.request().setEditSession(editSession); if (mask != null) { mask.prepare(this, player, null); } diff --git a/src/main/java/com/sk89q/worldedit/LocalWorld.java b/src/main/java/com/sk89q/worldedit/LocalWorld.java index 8b6d63aef..9ce353181 100644 --- a/src/main/java/com/sk89q/worldedit/LocalWorld.java +++ b/src/main/java/com/sk89q/worldedit/LocalWorld.java @@ -626,9 +626,18 @@ public abstract class LocalWorld implements World, Extent { new BaseBlock(BlockID.WATER, -1)); } + @Override + public Vector getMaximumPoint() { + return new Vector(30000000, 30000000, 30000000); + } + + @Override + public Vector getMinimumPoint() { + return new Vector(-30000000, -30000000, -30000000); + } + @Override public @Nullable Operation commit() { return null; } - } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index e3d50a9c1..0423172a4 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -23,17 +23,26 @@ import com.sk89q.minecraft.util.commands.*; import com.sk89q.minecraft.util.commands.Console; import com.sk89q.util.StringUtil; import com.sk89q.worldedit.CuboidClipboard.FlipDirection; -import com.sk89q.worldedit.extent.inventory.BlockBag; -import com.sk89q.worldedit.blocks.*; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldedit.command.*; +import com.sk89q.worldedit.command.tool.*; import com.sk89q.worldedit.event.extent.EditSessionEvent; -import com.sk89q.worldedit.masks.*; -import com.sk89q.worldedit.patterns.*; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.registry.BlockRegistry; +import com.sk89q.worldedit.extension.registry.MaskRegistry; +import com.sk89q.worldedit.extension.registry.PatternRegistry; +import com.sk89q.worldedit.extent.inventory.BlockBag; +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; -import com.sk89q.worldedit.command.tool.*; +import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.LogFormat; import com.sk89q.worldedit.util.eventbus.EventBus; @@ -66,6 +75,10 @@ public class WorldEdit { private final EditSessionFactory editSessionFactory = new EditSessionFactory(eventBus); private final HashMap sessions = new HashMap(); + private final BlockRegistry blockRegistry = new BlockRegistry(this); + private final MaskRegistry maskRegistry = new MaskRegistry(this); + private final PatternRegistry patternRegistry = new PatternRegistry(this); + static { getVersion(); } @@ -207,6 +220,36 @@ public class WorldEdit { return eventBus; } + /** + * Get the block registry from which new {@link BaseBlock}s can be + * constructed. + * + * @return the block registry + */ + public BlockRegistry getBlockRegistry() { + return blockRegistry; + } + + /** + * Get the mask registry from which new {@link com.sk89q.worldedit.function.mask.Mask}s + * can be constructed. + * + * @return the mask registry + */ + public MaskRegistry getMaskRegistry() { + return maskRegistry; + } + + /** + * Get the pattern registry from which new {@link com.sk89q.worldedit.function.pattern.Pattern}s + * can be constructed. + * + * @return the pattern registry + */ + public PatternRegistry getPatternRegistry() { + return patternRegistry; + } + /** * Gets the LocalSession for a player name if it exists * @@ -283,289 +326,42 @@ public class WorldEdit { } } - public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed) - throws WorldEditException { + /** + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + */ + @Deprecated + public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed) throws WorldEditException { return getBlock(player, arg, allAllowed, false); } /** - * Get an item ID from an item name or an item ID number. - * - * @param player - * @param arg - * @param allAllowed true to ignore blacklists - * @param allowNoData return -1 for data if no data was given. - * @return - * @throws UnknownItemException - * @throws DisallowedItemException + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} */ - public BaseBlock getBlock(LocalPlayer player, String arg, - boolean allAllowed, boolean allowNoData) - throws WorldEditException { - BlockType blockType; - arg = arg.replace("_", " "); - arg = arg.replace(";", "|"); - String[] blockAndExtraData = arg.split("\\|"); - String[] typeAndData = blockAndExtraData[0].split(":", 2); - String testID = typeAndData[0]; - - int blockId = -1; - - int data = -1; - - boolean parseDataValue = true; - if ("hand".equalsIgnoreCase(testID)) { - // Get the block type from the item in the user's hand. - final BaseBlock blockInHand = player.getBlockInHand(); - if (blockInHand.getClass() != BaseBlock.class) { - return blockInHand; - } - - blockId = blockInHand.getId(); - blockType = BlockType.fromID(blockId); - data = blockInHand.getData(); - } else if ("pos1".equalsIgnoreCase(testID)) { - // Get the block type from the "primary position" - final LocalWorld world = player.getWorld(); - final BlockVector primaryPosition = getSession(player).getRegionSelector(world).getPrimaryPosition(); - final BaseBlock blockInHand = world.getBlock(primaryPosition); - if (blockInHand.getClass() != BaseBlock.class) { - return blockInHand; - } - - blockId = blockInHand.getId(); - blockType = BlockType.fromID(blockId); - data = blockInHand.getData(); - } else { - // Attempt to parse the item ID or otherwise resolve an item/block - // name to its numeric ID - try { - blockId = Integer.parseInt(testID); - blockType = BlockType.fromID(blockId); - } catch (NumberFormatException e) { - blockType = BlockType.lookup(testID); - if (blockType == null) { - int t = server.resolveItem(testID); - if (t > 0) { - blockType = BlockType.fromID(t); // Could be null - blockId = t; - } - } - } - - if (blockId == -1 && blockType == null) { - // Maybe it's a cloth - ClothColor col = ClothColor.lookup(testID); - if (col == null) { - throw new UnknownItemException(arg); - } - - blockType = BlockType.CLOTH; - data = col.getID(); - - // Prevent overriding the data value - parseDataValue = false; - } - - // Read block ID - if (blockId == -1) { - blockId = blockType.getID(); - } - - if (!player.getWorld().isValidBlockType(blockId)) { - throw new UnknownItemException(arg); - } - } - - if (!allowNoData && data == -1) { - // No wildcards allowed => eliminate them. - data = 0; - } - - if (parseDataValue) { // Block data not yet detected - // Parse the block data (optional) - try { - if (typeAndData.length > 1 && typeAndData[1].length() > 0) { - data = Integer.parseInt(typeAndData[1]); - } - - if (data > 15) { - throw new InvalidItemException(arg, "Invalid data value '" + typeAndData[1] + "'"); - } - - if (data < 0 && !(allAllowed && data == -1)) { - data = 0; - } - } catch (NumberFormatException e) { - if (blockType == null) { - throw new InvalidItemException(arg, "Unknown data value '" + typeAndData[1] + "'"); - } - - switch (blockType) { - case CLOTH: - case STAINED_CLAY: - case CARPET: - ClothColor col = ClothColor.lookup(typeAndData[1]); - if (col == null) { - throw new InvalidItemException(arg, "Unknown cloth color '" + typeAndData[1] + "'"); - } - - data = col.getID(); - break; - - case STEP: - case DOUBLE_STEP: - BlockType dataType = BlockType.lookup(typeAndData[1]); - - if (dataType == null) { - throw new InvalidItemException(arg, "Unknown step type '" + typeAndData[1] + "'"); - } - - switch (dataType) { - case STONE: - data = 0; - break; - case SANDSTONE: - data = 1; - break; - case WOOD: - data = 2; - break; - case COBBLESTONE: - data = 3; - break; - case BRICK: - data = 4; - break; - case STONE_BRICK: - data = 5; - break; - case NETHER_BRICK: - data = 6; - break; - case QUARTZ_BLOCK: - data = 7; - break; - - default: - throw new InvalidItemException(arg, "Invalid step type '" + typeAndData[1] + "'"); - } - break; - - default: - throw new InvalidItemException(arg, "Unknown data value '" + typeAndData[1] + "'"); - } - } - } - - // Check if the item is allowed - if (!allAllowed && !player.hasPermission("worldedit.anyblock") && config.disallowedBlocks.contains(blockId)) { - throw new DisallowedItemException(arg); - } - - if (blockType == null) { - return new BaseBlock(blockId, data); - } - - switch (blockType) { - case SIGN_POST: - case WALL_SIGN: - // Allow special sign text syntax - String[] text = new String[4]; - text[0] = blockAndExtraData.length > 1 ? blockAndExtraData[1] : ""; - text[1] = blockAndExtraData.length > 2 ? blockAndExtraData[2] : ""; - text[2] = blockAndExtraData.length > 3 ? blockAndExtraData[3] : ""; - text[3] = blockAndExtraData.length > 4 ? blockAndExtraData[4] : ""; - return new SignBlock(blockType.getID(), data, text); - - case MOB_SPAWNER: - // Allow setting mob spawn type - if (blockAndExtraData.length > 1) { - String mobName = blockAndExtraData[1]; - for (MobType mobType : MobType.values()) { - if (mobType.getName().toLowerCase().equals(mobName.toLowerCase())) { - mobName = mobType.getName(); - break; - } - } - if (!server.isValidMobType(mobName)) { - throw new InvalidItemException(arg, "Unknown mob type '" + mobName + "'"); - } - return new MobSpawnerBlock(data, mobName); - } else { - return new MobSpawnerBlock(data, MobType.PIG.getName()); - } - - case NOTE_BLOCK: - // Allow setting note - if (blockAndExtraData.length <= 1) { - return new NoteBlock(data, (byte) 0); - } - - byte note = Byte.parseByte(blockAndExtraData[1]); - if (note < 0 || note > 24) { - throw new InvalidItemException(arg, "Out of range note value: '" + blockAndExtraData[1] + "'"); - } - - return new NoteBlock(data, note); - - case HEAD: - // allow setting type/player/rotation - if (blockAndExtraData.length <= 1) { - return new SkullBlock(data); - } - - byte rot = 0; - String type = ""; - try { - rot = Byte.parseByte(blockAndExtraData[1]); - } catch (NumberFormatException e) { - type = blockAndExtraData[1]; - if (blockAndExtraData.length > 2) { - try { - rot = Byte.parseByte(blockAndExtraData[2]); - } catch (NumberFormatException e2) { - throw new InvalidItemException(arg, "Second part of skull metadata should be a number."); - } - } - } - byte skullType = 0; - // type is either the mob type or the player name - // sorry for the four minecraft accounts named "skeleton", "wither", "zombie", or "creeper" - if (!type.isEmpty()) { - if (type.equalsIgnoreCase("skeleton")) skullType = 0; - else if (type.equalsIgnoreCase("wither")) skullType = 1; - else if (type.equalsIgnoreCase("zombie")) skullType = 2; - else if (type.equalsIgnoreCase("creeper")) skullType = 4; - else skullType = 3; - } - if (skullType == 3) { - return new SkullBlock(data, rot, type.replace(" ", "_")); // valid MC usernames - } else { - return new SkullBlock(data, skullType, rot); - } - - default: - return new BaseBlock(blockId, data); - } + @Deprecated + public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed, boolean allowNoData) throws WorldEditException { + ParserContext context = new ParserContext(); + context.setPlayer(player); + context.setWorld(player.getWorld()); + context.setSession(getSession(player)); + context.setRestricted(!allAllowed); + context.setPreferringWildcard(allowNoData); + return getBlockRegistry().parseFromInput(arg, context); } /** - * Get a block. - * - * @param player - * @param id - * @return - * @throws UnknownItemException - * @throws DisallowedItemException + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} */ - public BaseBlock getBlock(LocalPlayer player, String id) - throws WorldEditException { + @Deprecated + public BaseBlock getBlock(LocalPlayer player, String id) throws WorldEditException { return getBlock(player, id, false); } - public Set getBlocks(LocalPlayer player, String list, boolean allAllowed, boolean allowNoData) - throws WorldEditException { + /** + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromListInput(String, ParserContext)} + */ + @Deprecated + @SuppressWarnings("deprecation") + public Set getBlocks(LocalPlayer player, String list, boolean allAllowed, boolean allowNoData) throws WorldEditException { String[] items = list.split(","); Set blocks = new HashSet(); for (String id : items) { @@ -574,182 +370,30 @@ public class WorldEdit { return blocks; } - public Set getBlocks(LocalPlayer player, String list, boolean allAllowed) - throws WorldEditException { + /** + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + */ + @Deprecated + @SuppressWarnings("deprecation") + public Set getBlocks(LocalPlayer player, String list, boolean allAllowed) throws WorldEditException { return getBlocks(player, list, allAllowed, false); } - public Set getBlocks(LocalPlayer player, String list) - throws WorldEditException { + /** + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromListInput(String, ParserContext)} + */ + @Deprecated + @SuppressWarnings("deprecation") + public Set getBlocks(LocalPlayer player, String list) throws WorldEditException { return getBlocks(player, list, false); } /** - * Returns a Pattern corresponding to the specified pattern string, - * as given by the player on the command line. - * - * @param player - * @param patternString - * @return pattern - * @throws UnknownItemException - * @throws DisallowedItemException + * @deprecated Use {@link #getBlockRegistry()} and {@link BlockRegistry#parseFromListInput(String, ParserContext)} */ - public Pattern getBlockPattern(LocalPlayer player, String patternString) - throws WorldEditException { - - String[] items = patternString.split(","); - - // Handle special block pattern types - if (patternString.charAt(0) == '#') { - if (!patternString.equals("#clipboard") && !patternString.equals("#copy")) { - throw new UnknownItemException(patternString); - } - - LocalSession session = getSession(player); - - try { - return new ClipboardPattern(session.getClipboard()); - } catch (EmptyClipboardException e) { - player.printError("Copy a selection first with //copy."); - throw new UnknownItemException("#clipboard"); - } - } - - // If it's only one block, then just return that single one - if (items.length == 1) { - return new SingleBlockPattern(getBlock(player, items[0])); - } - - List blockChances = new ArrayList(); - - for (String s : items) { - BaseBlock block; - - double chance; - - // Parse special percentage syntax - if (s.matches("[0-9]+(\\.[0-9]*)?%.*")) { - String[] p = s.split("%"); - if (p.length < 2) { - throw new UnknownItemException(s); - } else { - chance = Double.parseDouble(p[0]); - block = getBlock(player, p[1]); - } - } else { - chance = 1; - block = getBlock(player, s); - } - - blockChances.add(new BlockChance(block, chance)); - } - - return new RandomFillPattern(blockChances); - } - - /** - * Get a block mask. Block masks are used to determine which - * blocks to include when replacing. - * - * @param player - * @param session - * @param maskString - * @return - * @throws WorldEditException - */ - public Mask getBlockMask(LocalPlayer player, LocalSession session, - String maskString) throws WorldEditException { - List masks = new ArrayList(); - - for (String component : maskString.split(" ")) { - if (component.length() == 0) { - continue; - } - - Mask current = getBlockMaskComponent(player, session, masks, component); - - masks.add(current); - } - - switch (masks.size()) { - case 0: - return null; - - case 1: - return masks.get(0); - - default: - return new CombinedMask(masks); - } - } - - private Mask getBlockMaskComponent(LocalPlayer player, LocalSession session, List masks, String component) throws WorldEditException { - final char firstChar = component.charAt(0); - switch (firstChar) { - case '#': - if (component.equalsIgnoreCase("#existing")) { - return new ExistingBlockMask(); - } else if (component.equalsIgnoreCase("#solid")) { - return new SolidBlockMask(); - } else if (component.equalsIgnoreCase("#dregion") - || component.equalsIgnoreCase("#dselection") - || component.equalsIgnoreCase("#dsel")) { - return new DynamicRegionMask(); - } else if (component.equalsIgnoreCase("#selection") - || component.equalsIgnoreCase("#region") - || component.equalsIgnoreCase("#sel")) { - return new RegionMask(session.getSelection(player.getWorld())); - } else { - throw new UnknownItemException(component); - } - - case '>': - case '<': - Mask submask; - if (component.length() > 1) { - submask = getBlockMaskComponent(player, session, masks, component.substring(1)); - } else { - submask = new ExistingBlockMask(); - } - return new UnderOverlayMask(submask, firstChar == '>'); - - case '$': - Set biomes = new HashSet(); - String[] biomesList = component.substring(1).split(","); - for (String biomeName : biomesList) { - BiomeType biome = server.getBiomes().get(biomeName); - biomes.add(biome); - } - return new BiomeTypeMask(biomes); - - case '%': - int i = Integer.parseInt(component.substring(1)); - return new RandomMask(((double) i) / 100); - - case '!': - if (component.length() > 1) { - return new InvertedMask(getBlockMaskComponent(player, session, masks, component.substring(1))); - } - - default: - return new BlockMask(getBlocks(player, component, true, true)); - } - } - - /** - * Get a list of blocks as a set. - * - * @param player - * @param list - * @param allBlocksAllowed - * @return set - * @throws UnknownItemException - * @throws DisallowedItemException - */ - public Set getBlockIDs(LocalPlayer player, - String list, boolean allBlocksAllowed) - throws WorldEditException { - + @Deprecated + @SuppressWarnings("deprecation") + public Set getBlockIDs(LocalPlayer player, String list, boolean allBlocksAllowed) throws WorldEditException { String[] items = list.split(","); Set blocks = new HashSet(); for (String s : items) { @@ -758,6 +402,32 @@ public class WorldEdit { return blocks; } + /** + * @deprecated Use {@link #getPatternRegistry()} and {@link BlockRegistry#parseFromInput(String, ParserContext)} + */ + @Deprecated + @SuppressWarnings("deprecation") + public Pattern getBlockPattern(LocalPlayer player, String input) throws WorldEditException { + ParserContext context = new ParserContext(); + context.setPlayer(player); + context.setWorld(player.getWorld()); + context.setSession(getSession(player)); + return Patterns.wrap(getPatternRegistry().parseFromInput(input, context)); + } + + /** + * @deprecated Use {@link #getMaskRegistry()} ()} and {@link MaskRegistry#parseFromInput(String, ParserContext)} + */ + @Deprecated + @SuppressWarnings("deprecation") + public Mask getBlockMask(LocalPlayer player, LocalSession session, String input) throws WorldEditException { + ParserContext context = new ParserContext(); + context.setPlayer(player); + context.setWorld(player.getWorld()); + context.setSession(session); + return Masks.wrap(getMaskRegistry().parseFromInput(input, context)); + } + /** * Gets the path to a file. This method will check to see if the filename * has valid characters and has an extension. It also prevents directory @@ -1361,6 +1031,8 @@ public class WorldEdit { * @return whether the command was processed */ public boolean handleCommand(LocalPlayer player, String[] split) { + Request.reset(); + try { split = commandDetection(split); @@ -1464,6 +1136,8 @@ public class WorldEdit { } public String[] commandDetection(String[] split) { + Request.reset(); + split[0] = split[0].substring(1); // Quick script shortcut @@ -1496,8 +1170,9 @@ public class WorldEdit { * @param args * @throws WorldEditException */ - public void runScript(LocalPlayer player, File f, String[] args) - throws WorldEditException { + public void runScript(LocalPlayer player, File f, String[] args) throws WorldEditException { + Request.reset(); + String filename = f.getPath(); int index = filename.lastIndexOf("."); String ext = filename.substring(index + 1, filename.length()); diff --git a/src/main/java/com/sk89q/worldedit/WorldEditException.java b/src/main/java/com/sk89q/worldedit/WorldEditException.java index 82ddcae6d..6520123c1 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEditException.java +++ b/src/main/java/com/sk89q/worldedit/WorldEditException.java @@ -1,7 +1,7 @@ -// $Id$ /* - * WorldEdit - * Copyright (C) 2010 sk89q and contributors + * 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 General Public License as published by @@ -15,21 +15,46 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . -*/ + */ package com.sk89q.worldedit; /** - * - * @author sk89q + * Parent for all WorldEdit exceptions. */ public abstract class WorldEditException extends Exception { - private static final long serialVersionUID = 3201997990797993987L; + /** + * Create a new exception. + */ protected WorldEditException() { } - protected WorldEditException(String msg) { - super(msg); + /** + * Create a new exception with a message. + * + * @param message the message + */ + protected WorldEditException(String message) { + super(message); + } + + /** + * Create a new exception with a message and a cause. + * + * @param message the message + * @param cause the cause + */ + protected WorldEditException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Create a new exception with a cause. + * + * @param cause the cause + */ + protected WorldEditException(Throwable cause) { + super(cause); } } 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 9004d6c0a..595198cae 100644 --- a/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -29,6 +29,7 @@ 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.session.request.Request; /** * Builds a shape at the place being looked at. @@ -177,6 +178,7 @@ public class BrushTool implements TraceTool { BlockBag bag = session.getBlockBag(player); EditSession editSession = session.createEditSession(player); + Request.request().setEditSession(editSession); if (mask != null) { mask.prepare(session, player, target); Mask existingMask = editSession.getMask(); diff --git a/src/main/java/com/sk89q/worldedit/extension/input/DisallowedUsageException.java b/src/main/java/com/sk89q/worldedit/extension/input/DisallowedUsageException.java new file mode 100644 index 000000000..13e48ff73 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/input/DisallowedUsageException.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.input; + +/** + * Thrown when usage is disallowed. + */ +public class DisallowedUsageException extends InputParseException { + + /** + * Create with a message. + * + * @param message the message + */ + public DisallowedUsageException(String message) { + super(message); + } + + /** + * Create with a message and a cause. + * + * @param message the message + * @param cause the cause + */ + public DisallowedUsageException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/input/InputParseException.java b/src/main/java/com/sk89q/worldedit/extension/input/InputParseException.java new file mode 100644 index 000000000..8f0980531 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/input/InputParseException.java @@ -0,0 +1,48 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.input; + +import com.sk89q.worldedit.WorldEditException; + +/** + * Thrown when parsed input results in an error. + */ +public class InputParseException extends WorldEditException { + + /** + * Throw with a message. + * + * @param message the message + */ + public InputParseException(String message) { + super(message); + } + + /** + * Throw with a message and a cause. + * + * @param message the message + * @param cause the cause + */ + public InputParseException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/input/NoMatchException.java b/src/main/java/com/sk89q/worldedit/extension/input/NoMatchException.java new file mode 100644 index 000000000..2fcc67f82 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/input/NoMatchException.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.input; + +/** + * Thrown when a match fails when input is parsed. + */ +public class NoMatchException extends InputParseException { + + /** + * Create with a message. + * + * @param message the message + */ + public NoMatchException(String message) { + super(message); + } + + /** + * Create with a message and a cause. + * + * @param message the message + * @param cause the cause + */ + public NoMatchException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java b/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java new file mode 100644 index 000000000..cf3f7e56d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java @@ -0,0 +1,211 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.input; + +import com.sk89q.worldedit.LocalPlayer; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.LocalWorld; +import com.sk89q.worldedit.extension.registry.MaskRegistry; +import com.sk89q.worldedit.extent.Extent; + +import javax.annotation.Nullable; + +/** + * Contains contextual information that may be useful when constructing + * objects from a registry (such as {@link MaskRegistry}). + *

+ * By default, {@link #isRestricted()} will return true. + */ +public class ParserContext { + + private @Nullable Extent extent; + private @Nullable LocalSession session; + private @Nullable LocalWorld world; + private @Nullable LocalPlayer player; + private boolean restricted = true; + private boolean preferringWildcard; + + /** + * Get the {@link Extent} set on this context. + * + * @return an extent + */ + public @Nullable Extent getExtent() { + return extent; + } + + /** + * Set the extent. + * + * @param extent an extent, or null if none is available + */ + public void setExtent(@Nullable Extent extent) { + this.extent = extent; + } + + /** + * Get the {@link LocalSession}. + * + * @return a session + */ + public @Nullable LocalSession getSession() { + return session; + } + + /** + * Set the session. + * + * @param session a session, or null if none is available + */ + public void setSession(@Nullable LocalSession session) { + this.session = session; + } + + /** + * Get the {@link LocalWorld} set on this context. + * + * @return a world + */ + public @Nullable LocalWorld getWorld() { + return world; + } + + /** + * Set the world. + * + * @param world a world, or null if none is available + */ + public void setWorld(@Nullable LocalWorld world) { + this.world = world; + } + + /** + * Get the {@link LocalPlayer} set on this context. + * + * @return a player + */ + public @Nullable LocalPlayer getPlayer() { + return player; + } + + /** + * Set the player. + * + * @param player a player, or null if none is available + */ + public void setPlayer(@Nullable LocalPlayer player) { + this.player = player; + } + + /** + * Get the {@link Extent} set on this context. + * + * @return an extent + * @throws InputParseException thrown if no {@link Extent} is set + */ + public Extent requireExtent() throws InputParseException { + Extent extent = getExtent(); + if (extent == null) { + throw new InputParseException("No Extent is known"); + } + return extent; + } + + /** + * Get the {@link LocalSession}. + * + * @return a session + * @throws InputParseException thrown if no {@link LocalSession} is set + */ + public LocalSession requireSession() throws InputParseException { + LocalSession session = getSession(); + if (session == null) { + throw new InputParseException("No LocalSession is known"); + } + return session; + } + + /** + * Get the {@link LocalWorld} set on this context. + * + * @return a world + * @throws InputParseException thrown if no {@link LocalWorld} is set + */ + public LocalWorld requireWorld() throws InputParseException { + LocalWorld world = getWorld(); + if (world == null) { + throw new InputParseException("No world is known"); + } + return world; + } + + /** + * Get the {@link LocalPlayer} set on this context. + * + * @return a player + * @throws InputParseException thrown if no {@link LocalPlayer} is set + */ + public LocalPlayer requirePlayer() throws InputParseException { + LocalPlayer player = getPlayer(); + if (player == null) { + throw new InputParseException("No player is known"); + } + return player; + } + + /** + * Returns whether there should be restrictions (as a result of + * limits or permissions) considered when parsing the input. + * + * @return true if restricted + */ + public boolean isRestricted() { + return restricted; + } + + /** + * Set whether there should be restrictions (as a result of + * limits or permissions) considered when parsing the input. + * + * @param restricted true if restricted + */ + public void setRestricted(boolean restricted) { + this.restricted = restricted; + } + + /** + * Get whether wildcards are preferred. + * + * @return true if wildcards are preferred + */ + public boolean isPreferringWildcard() { + return preferringWildcard; + } + + /** + * Set whether wildcards are preferred. + * + * @param preferringWildcard true if wildcards are preferred + */ + public void setPreferringWildcard(boolean preferringWildcard) { + this.preferringWildcard = preferringWildcard; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java b/src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java new file mode 100644 index 000000000..c1b6c611b --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/BlockRegistry.java @@ -0,0 +1,67 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.internal.registry.AbstractRegistry; + +import java.util.HashSet; +import java.util.Set; + +/** + * A registry of known {@link BaseBlock}s. Provides methods to instantiate + * new blocks from input. + *

+ * Instances of this class can be taken from + * {@link WorldEdit#getBlockRegistry()}. + */ +public class BlockRegistry extends AbstractRegistry { + + /** + * Create a new instance. + * + * @param worldEdit the WorldEdit instance. + */ + public BlockRegistry(WorldEdit worldEdit) { + super(worldEdit); + + parsers.add(new DefaultBlockParser(worldEdit)); + } + + /** + * Return a set of blocks from a comma-delimited list of blocks. + * + * @param input the input + * @param context the context + * @return a set of blocks + * @throws InputParseException thrown in error with the input + */ + public Set parseFromListInput(String input, ParserContext context) throws InputParseException { + Set blocks = new HashSet(); + for (String token : input.split(",")) { + blocks.add(parseFromInput(token, context)); + } + return blocks; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java b/src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java new file mode 100644 index 000000000..497fa3d5c --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/DefaultBlockParser.java @@ -0,0 +1,307 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.*; +import com.sk89q.worldedit.extension.input.DisallowedUsageException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.NoMatchException; +import com.sk89q.worldedit.internal.registry.InputParser; + +/** + * Parses block input strings. + */ +class DefaultBlockParser extends InputParser { + + protected DefaultBlockParser(WorldEdit worldEdit) { + super(worldEdit); + } + + private static BaseBlock getBlockInHand(LocalPlayer player) throws InputParseException { + try { + return player.getBlockInHand(); + } catch (NotABlockException e) { + throw new InputParseException("You're not holding a block!"); + } catch (WorldEditException e) { + throw new InputParseException("Unknown error occurred: " + e.getMessage(), e); + } + } + + @Override + public BaseBlock parseFromInput(String input, ParserContext context) throws InputParseException { + BlockType blockType; + input = input.replace("_", " "); + input = input.replace(";", "|"); + String[] blockAndExtraData = input.split("\\|"); + String[] typeAndData = blockAndExtraData[0].split(":", 2); + String testID = typeAndData[0]; + + int blockId = -1; + + int data = -1; + + boolean parseDataValue = true; + + if ("hand".equalsIgnoreCase(testID)) { + // Get the block type from the item in the user's hand. + final BaseBlock blockInHand = getBlockInHand(context.requirePlayer()); + if (blockInHand.getClass() != BaseBlock.class) { + return blockInHand; + } + + blockId = blockInHand.getId(); + blockType = BlockType.fromID(blockId); + data = blockInHand.getData(); + } else if ("pos1".equalsIgnoreCase(testID)) { + // Get the block type from the "primary position" + final LocalWorld world = context.requireWorld(); + final BlockVector primaryPosition; + try { + primaryPosition = context.requireSession().getRegionSelector(world).getPrimaryPosition(); + } catch (IncompleteRegionException e) { + throw new InputParseException("Your selection is not complete."); + } + final BaseBlock blockInHand = world.getBlock(primaryPosition); + if (blockInHand.getClass() != BaseBlock.class) { + return blockInHand; + } + + blockId = blockInHand.getId(); + blockType = BlockType.fromID(blockId); + data = blockInHand.getData(); + } else { + // Attempt to parse the item ID or otherwise resolve an item/block + // name to its numeric ID + try { + blockId = Integer.parseInt(testID); + blockType = BlockType.fromID(blockId); + } catch (NumberFormatException e) { + blockType = BlockType.lookup(testID); + if (blockType == null) { + int t = worldEdit.getServer().resolveItem(testID); + if (t > 0) { + blockType = BlockType.fromID(t); // Could be null + blockId = t; + } + } + } + + if (blockId == -1 && blockType == null) { + // Maybe it's a cloth + ClothColor col = ClothColor.lookup(testID); + if (col == null) { + throw new NoMatchException("Unknown wool color '" + input + "'"); + } + + blockType = BlockType.CLOTH; + data = col.getID(); + + // Prevent overriding the data value + parseDataValue = false; + } + + // Read block ID + if (blockId == -1) { + blockId = blockType.getID(); + } + + if (!context.requireWorld().isValidBlockType(blockId)) { + throw new NoMatchException("Does not match a valid block type: '" + input + "'"); + } + } + + if (!context.isPreferringWildcard() && data == -1) { + // No wildcards allowed => eliminate them. + data = 0; + } + + if (parseDataValue) { // Block data not yet detected + // Parse the block data (optional) + try { + if (typeAndData.length > 1 && typeAndData[1].length() > 0) { + data = Integer.parseInt(typeAndData[1]); + } + + if (data > 15) { + throw new NoMatchException("Invalid data value '" + typeAndData[1] + "'"); + } + + if (data < 0 && (context.isRestricted() || data != -1)) { + data = 0; + } + } catch (NumberFormatException e) { + if (blockType == null) { + throw new NoMatchException("Unknown data value '" + typeAndData[1] + "'"); + } + + switch (blockType) { + case CLOTH: + case STAINED_CLAY: + case CARPET: + ClothColor col = ClothColor.lookup(typeAndData[1]); + if (col == null) { + throw new NoMatchException("Unknown wool color '" + typeAndData[1] + "'"); + } + + data = col.getID(); + break; + + case STEP: + case DOUBLE_STEP: + BlockType dataType = BlockType.lookup(typeAndData[1]); + + if (dataType == null) { + throw new NoMatchException("Unknown step type '" + typeAndData[1] + "'"); + } + + switch (dataType) { + case STONE: + data = 0; + break; + case SANDSTONE: + data = 1; + break; + case WOOD: + data = 2; + break; + case COBBLESTONE: + data = 3; + break; + case BRICK: + data = 4; + break; + case STONE_BRICK: + data = 5; + break; + case NETHER_BRICK: + data = 6; + break; + case QUARTZ_BLOCK: + data = 7; + break; + + default: + throw new NoMatchException("Invalid step type '" + typeAndData[1] + "'"); + } + break; + + default: + throw new NoMatchException("Unknown data value '" + typeAndData[1] + "'"); + } + } + } + + // Check if the item is allowed + LocalPlayer player = context.requirePlayer(); + if (context.isRestricted() && player != null && !player.hasPermission("worldedit.anyblock") + && worldEdit.getConfiguration().disallowedBlocks.contains(blockId)) { + throw new DisallowedUsageException("You are not allowed to use '" + input + "'"); + } + + if (blockType == null) { + return new BaseBlock(blockId, data); + } + + switch (blockType) { + case SIGN_POST: + case WALL_SIGN: + // Allow special sign text syntax + String[] text = new String[4]; + text[0] = blockAndExtraData.length > 1 ? blockAndExtraData[1] : ""; + text[1] = blockAndExtraData.length > 2 ? blockAndExtraData[2] : ""; + text[2] = blockAndExtraData.length > 3 ? blockAndExtraData[3] : ""; + text[3] = blockAndExtraData.length > 4 ? blockAndExtraData[4] : ""; + return new SignBlock(blockType.getID(), data, text); + + case MOB_SPAWNER: + // Allow setting mob spawn type + if (blockAndExtraData.length > 1) { + String mobName = blockAndExtraData[1]; + for (MobType mobType : MobType.values()) { + if (mobType.getName().toLowerCase().equals(mobName.toLowerCase())) { + mobName = mobType.getName(); + break; + } + } + if (!worldEdit.getServer().isValidMobType(mobName)) { + throw new NoMatchException("Unknown mob type '" + mobName + "'"); + } + return new MobSpawnerBlock(data, mobName); + } else { + return new MobSpawnerBlock(data, MobType.PIG.getName()); + } + + case NOTE_BLOCK: + // Allow setting note + if (blockAndExtraData.length <= 1) { + return new NoteBlock(data, (byte) 0); + } + + byte note = Byte.parseByte(blockAndExtraData[1]); + if (note < 0 || note > 24) { + throw new InputParseException("Out of range note value: '" + blockAndExtraData[1] + "'"); + } + + return new NoteBlock(data, note); + + case HEAD: + // allow setting type/player/rotation + if (blockAndExtraData.length <= 1) { + return new SkullBlock(data); + } + + byte rot = 0; + String type = ""; + try { + rot = Byte.parseByte(blockAndExtraData[1]); + } catch (NumberFormatException e) { + type = blockAndExtraData[1]; + if (blockAndExtraData.length > 2) { + try { + rot = Byte.parseByte(blockAndExtraData[2]); + } catch (NumberFormatException e2) { + throw new InputParseException("Second part of skull metadata should be a number."); + } + } + } + byte skullType = 0; + // type is either the mob type or the player name + // sorry for the four minecraft accounts named "skeleton", "wither", "zombie", or "creeper" + if (!type.isEmpty()) { + if (type.equalsIgnoreCase("skeleton")) skullType = 0; + else if (type.equalsIgnoreCase("wither")) skullType = 1; + else if (type.equalsIgnoreCase("zombie")) skullType = 2; + else if (type.equalsIgnoreCase("creeper")) skullType = 4; + else skullType = 3; + } + if (skullType == 3) { + return new SkullBlock(data, rot, type.replace(" ", "_")); // valid MC usernames + } else { + return new SkullBlock(data, skullType, rot); + } + + default: + return new BaseBlock(blockId, data); + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java b/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java new file mode 100644 index 000000000..6e0a3fd26 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/DefaultMaskParser.java @@ -0,0 +1,139 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.NoMatchException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.*; +import com.sk89q.worldedit.internal.registry.InputParser; +import com.sk89q.worldedit.masks.BiomeTypeMask; +import com.sk89q.worldedit.math.noise.RandomNoise; +import com.sk89q.worldedit.session.request.Request; +import com.sk89q.worldedit.session.request.RequestSelection; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Parses mask input strings. + */ +class DefaultMaskParser extends InputParser { + + protected DefaultMaskParser(WorldEdit worldEdit) { + super(worldEdit); + } + + @Override + public Mask parseFromInput(String input, ParserContext context) throws InputParseException { + List masks = new ArrayList(); + + for (String component : input.split(" ")) { + if (component.length() == 0) { + continue; + } + + Mask current = getBlockMaskComponent(masks, component, context); + + masks.add(current); + } + + switch (masks.size()) { + case 0: + return null; + + case 1: + return masks.get(0); + + default: + return new MaskIntersection(masks); + } + } + + private Mask getBlockMaskComponent(List masks, String component, ParserContext context) throws InputParseException { + Extent extent = Request.request().getEditSession(); + + final char firstChar = component.charAt(0); + switch (firstChar) { + case '#': + if (component.equalsIgnoreCase("#existing")) { + return new ExistingBlockMask(extent); + } else if (component.equalsIgnoreCase("#solid")) { + return new SolidBlockMask(extent); + } else if (component.equalsIgnoreCase("#dregion") + || component.equalsIgnoreCase("#dselection") + || component.equalsIgnoreCase("#dsel")) { + return new RegionMask(new RequestSelection()); + } else if (component.equalsIgnoreCase("#selection") + || component.equalsIgnoreCase("#region") + || component.equalsIgnoreCase("#sel")) { + try { + return new RegionMask(context.requireSession().getSelection(context.requireWorld()).clone()); + } catch (IncompleteRegionException e) { + throw new InputParseException("Please make a selection first."); + } + } else { + throw new NoMatchException("Unrecognized mask '" + component + "'"); + } + + case '>': + case '<': + Mask submask; + if (component.length() > 1) { + submask = getBlockMaskComponent(masks, component.substring(1), context); + } else { + submask = new ExistingBlockMask(extent); + } + OffsetMask offsetMask = new OffsetMask(submask, new Vector(0, firstChar == '>' ? -1 : 1, 0)); + return new MaskIntersection(offsetMask, Masks.negate(submask)); + + case '$': + Set biomes = new HashSet(); + String[] biomesList = component.substring(1).split(","); + for (String biomeName : biomesList) { + try { + BiomeType biome = worldEdit.getServer().getBiomes().get(biomeName); + biomes.add(biome); + } catch (UnknownBiomeTypeException e) { + throw new InputParseException("Unknown biome '" + biomeName + "'"); + } + } + + return Masks.wrap(new BiomeTypeMask(biomes)); + + case '%': + int i = Integer.parseInt(component.substring(1)); + return new NoiseFilter(new RandomNoise(), ((double) i) / 100); + + case '!': + if (component.length() > 1) { + return Masks.negate(getBlockMaskComponent(masks, component.substring(1), context)); + } + + default: + return new BlockMask(extent, worldEdit.getBlockRegistry().parseFromInput(component, context)); + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java b/src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java new file mode 100644 index 000000000..49342fe7a --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/HashTagPatternParser.java @@ -0,0 +1,60 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.EmptyClipboardException; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.ClipboardPattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.registry.InputParser; +import com.sk89q.worldedit.extension.input.InputParseException; + +class HashTagPatternParser extends InputParser { + + HashTagPatternParser(WorldEdit worldEdit) { + super(worldEdit); + } + + @Override + public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { + if (input.charAt(0) == '#') { + if (!input.equals("#clipboard") && !input.equals("#copy")) { + throw new InputParseException("#clipboard or #copy is acceptable for patterns starting with #"); + } + + LocalSession session = context.requireSession(); + + if (session != null) { + try { + return new ClipboardPattern(session.getClipboard()); + } catch (EmptyClipboardException e) { + throw new InputParseException("To use #clipboard, please first copy something to your clipboard"); + } + } else { + throw new InputParseException("No session is available, so no clipboard is available"); + } + } else { + return null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.java b/src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.java new file mode 100644 index 000000000..1a18f35a9 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/MaskRegistry.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.internal.registry.AbstractRegistry; + +/** + * A registry of known {@link Mask}s. Provides methods to instantiate + * new masks from input. + *

+ * Instances of this class can be taken from + * {@link WorldEdit#getMaskRegistry()}. + */ +public final class MaskRegistry extends AbstractRegistry { + + /** + * Create a new mask registry. + * + * @param worldEdit the WorldEdit instance + */ + public MaskRegistry(WorldEdit worldEdit) { + super(worldEdit); + + parsers.add(new DefaultMaskParser(worldEdit)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java b/src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java new file mode 100644 index 000000000..d1dc569c8 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/PatternRegistry.java @@ -0,0 +1,48 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.registry.AbstractRegistry; + +/** + * A registry of known {@link Pattern}s. Provides methods to instantiate + * new patterns from input. + *

+ * Instances of this class can be taken from + * {@link WorldEdit#getPatternRegistry()}. + */ +public final class PatternRegistry extends AbstractRegistry { + + /** + * Create a new instance. + * + * @param worldEdit the WorldEdit instance + */ + public PatternRegistry(WorldEdit worldEdit) { + super(worldEdit); + + parsers.add(new HashTagPatternParser(worldEdit)); + parsers.add(new SingleBlockPatternParser(worldEdit)); + parsers.add(new RandomPatternParser(worldEdit)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java b/src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java new file mode 100644 index 000000000..051687f3e --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/RandomPatternParser.java @@ -0,0 +1,67 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.function.pattern.BlockPattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.function.pattern.RandomPattern; +import com.sk89q.worldedit.internal.registry.InputParser; + +class RandomPatternParser extends InputParser { + + RandomPatternParser(WorldEdit worldEdit) { + super(worldEdit); + } + + @Override + public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { + BlockRegistry blockRegistry = worldEdit.getBlockRegistry(); + RandomPattern randomPattern = new RandomPattern(); + + for (String token : input.split(",")) { + BaseBlock block; + + double chance; + + // Parse special percentage syntax + if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) { + String[] p = token.split("%"); + + if (p.length < 2) { + throw new InputParseException("Missing the type after the % symbol for '" + input + "'"); + } else { + chance = Double.parseDouble(p[0]); + block = blockRegistry.parseFromInput(p[1], context); + } + } else { + chance = 1; + block = blockRegistry.parseFromInput(token, context); + } + + randomPattern.add(new BlockPattern(block), chance); + } + + return randomPattern; + } +} diff --git a/src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.java b/src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.java new file mode 100644 index 000000000..8d527a08d --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/extension/registry/SingleBlockPatternParser.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extension.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.BlockPattern; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.internal.registry.InputParser; +import com.sk89q.worldedit.extension.input.InputParseException; + +class SingleBlockPatternParser extends InputParser { + + SingleBlockPatternParser(WorldEdit worldEdit) { + super(worldEdit); + } + + @Override + public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { + String[] items = input.split(","); + + if (items.length == 1) { + return new BlockPattern(worldEdit.getBlockRegistry().parseFromInput(items[0], context)); + } else { + return null; + } + } + +} diff --git a/src/main/java/com/sk89q/worldedit/extent/Extent.java b/src/main/java/com/sk89q/worldedit/extent/Extent.java index 244fd5af3..3fd01129a 100644 --- a/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.extent; +import com.sk89q.worldedit.Vector; + /** * A world, portion of a world, clipboard, or other object that can have blocks * set or entities placed. @@ -27,4 +29,25 @@ package com.sk89q.worldedit.extent; * @see OutputExtent the set____() portion */ public interface Extent extends InputExtent, OutputExtent { + + /** + * Get the minimum point in the extent. + *

+ * If the extent is unbounded, then a large (negative) value may + * be returned. + * + * @return the minimum point + */ + Vector getMinimumPoint(); + + /** + * Get the maximum point in the extent. + *

+ * If the extent is unbounded, then a large (positive) value may + * be returned. + * + * @return the maximum point + */ + Vector getMaximumPoint(); + } diff --git a/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java b/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java index 19d1688e5..cfc88b6c2 100644 --- a/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java +++ b/src/main/java/com/sk89q/worldedit/extent/ExtentDelegate.java @@ -70,6 +70,16 @@ public class ExtentDelegate implements Extent { return extent.setBlock(location, block); } + @Override + public Vector getMinimumPoint() { + return extent.getMinimumPoint(); + } + + @Override + public Vector getMaximumPoint() { + return extent.getMaximumPoint(); + } + protected Operation commitBefore() { return null; } @@ -88,4 +98,5 @@ public class ExtentDelegate implements Extent { return null; } } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/Masks.java b/src/main/java/com/sk89q/worldedit/function/mask/Masks.java index 7f8275aeb..97869b241 100644 --- a/src/main/java/com/sk89q/worldedit/function/mask/Masks.java +++ b/src/main/java/com/sk89q/worldedit/function/mask/Masks.java @@ -19,9 +19,8 @@ package com.sk89q.worldedit.function.mask; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.session.request.Request; import static com.google.common.base.Preconditions.checkNotNull; @@ -95,12 +94,19 @@ public final class Masks { /** * Wrap an old-style mask and convert it to a new mask. + *

+ * Note, however, that this is strongly not recommended because + * {@link com.sk89q.worldedit.masks.Mask#prepare(LocalSession, LocalPlayer, Vector)} + * is not called. * - * @param editSession the edit session to bind to * @param mask the old-style mask + * @param editSession the edit session to bind to * @return a new-style mask + * @deprecated Please avoid if possible */ - public static Mask wrap(final EditSession editSession, final com.sk89q.worldedit.masks.Mask mask) { + @Deprecated + @SuppressWarnings("deprecation") + public static Mask wrap(final com.sk89q.worldedit.masks.Mask mask, final EditSession editSession) { checkNotNull(mask); return new AbstractMask() { @Override @@ -110,4 +116,44 @@ public final class Masks { }; } + /** + * Wrap an old-style mask and convert it to a new mask. + *

+ * As an {@link EditSession} is not provided in this case, one will be + * taken from the {@link Request}, if possible. If not possible, then the + * mask will return false. + * + * @param mask the old-style mask + * @return a new-style mask + */ + @SuppressWarnings("deprecation") + public static Mask wrap(final com.sk89q.worldedit.masks.Mask mask) { + checkNotNull(mask); + return new AbstractMask() { + @Override + public boolean test(Vector vector) { + EditSession editSession = Request.request().getEditSession(); + return editSession != null && mask.matches(editSession, vector); + } + }; + } + + /** + * Convert a new-style mask to an old-style mask. + * + * @param mask the new-style mask + * @return an old-style mask + */ + @SuppressWarnings("deprecation") + public static com.sk89q.worldedit.masks.Mask wrap(final Mask mask) { + checkNotNull(mask); + return new com.sk89q.worldedit.masks.AbstractMask() { + @Override + public boolean matches(EditSession editSession, Vector pos) { + Request.request().setEditSession(editSession); + return mask.test(pos); + } + }; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java b/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java new file mode 100644 index 000000000..5767137a1 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.mask; + +import com.sk89q.worldedit.Vector; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Checks whether the provided mask tests true for an offset position. + */ +public class OffsetMask extends AbstractMask { + + private Mask mask; + private Vector offset; + + /** + * Create a new instance. + * + * @param mask the mask + * @param offset the offset + */ + public OffsetMask(Mask mask, Vector offset) { + checkNotNull(mask); + checkNotNull(offset); + this.mask = mask; + this.offset = offset; + } + + /** + * Get the mask. + * + * @return the mask + */ + public Mask getMask() { + return mask; + } + + /** + * Set the mask. + * + * @param mask the mask + */ + public void setMask(Mask mask) { + checkNotNull(mask); + this.mask = mask; + } + + /** + * Get the offset. + * + * @return the offset + */ + public Vector getOffset() { + return offset; + } + + /** + * Set the offset. + * + * @param offset the offset + */ + public void setOffset(Vector offset) { + checkNotNull(offset); + this.offset = offset; + } + + @Override + public boolean test(Vector vector) { + return getMask().test(vector.add(offset)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java b/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.java new file mode 100644 index 000000000..0fbed5165 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/pattern/ClipboardPattern.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.pattern; + +import com.sk89q.worldedit.CuboidClipboard; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A pattern that reads from {@link CuboidClipboard}. + * + * @deprecated May be removed without notice, but there is no direct replacement yet + */ +@Deprecated +public class ClipboardPattern extends AbstractPattern { + + private final CuboidClipboard clipboard; + private final Vector size; + + /** + * Create a new clipboard pattern. + * + * @param clipboard the clipboard + */ + public ClipboardPattern(CuboidClipboard clipboard) { + checkNotNull(clipboard); + this.clipboard = clipboard; + this.size = clipboard.getSize(); + } + + @Override + public BaseBlock apply(Vector position) { + int xp = Math.abs(position.getBlockX()) % size.getBlockX(); + int yp = Math.abs(position.getBlockY()) % size.getBlockY(); + int zp = Math.abs(position.getBlockZ()) % size.getBlockZ(); + + return clipboard.getPoint(new Vector(xp, yp, zp)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/function/pattern/Patterns.java b/src/main/java/com/sk89q/worldedit/function/pattern/Patterns.java index 9e42dbe7e..1375b2209 100644 --- a/src/main/java/com/sk89q/worldedit/function/pattern/Patterns.java +++ b/src/main/java/com/sk89q/worldedit/function/pattern/Patterns.java @@ -48,4 +48,25 @@ public final class Patterns { }; } + /** + * Wrap a new-style pattern and return an old-style pattern. + * + * @param pattern the pattern + * @return an old-style pattern + */ + public static com.sk89q.worldedit.patterns.Pattern wrap(final Pattern pattern) { + checkNotNull(pattern); + return new com.sk89q.worldedit.patterns.Pattern() { + @Override + public BaseBlock next(Vector position) { + return pattern.apply(position); + } + + @Override + public BaseBlock next(int x, int y, int z) { + return next(new Vector(x, y, z)); + } + }; + } + } diff --git a/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java b/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java new file mode 100644 index 000000000..5585ec999 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/function/pattern/RepeatingExtentPattern.java @@ -0,0 +1,95 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.pattern; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.Extent; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Returns the blocks from {@link Extent}, repeating when out of bounds. + */ +public class RepeatingExtentPattern extends AbstractPattern { + + private Extent extent; + private Vector offset; + + /** + * Create a new instance. + * + * @param extent the extent + * @param offset the offset + */ + public RepeatingExtentPattern(Extent extent, Vector offset) { + setExtent(extent); + setOffset(offset); + } + + /** + * Get the extent. + * + * @return the extent + */ + public Extent getExtent() { + return extent; + } + + /** + * Set the extent. + * + * @param extent the extent + */ + public void setExtent(Extent extent) { + checkNotNull(extent); + this.extent = extent; + } + + /** + * Get the offset. + * + * @return the offset + */ + public Vector getOffset() { + return offset; + } + + /** + * Set the offset. + * + * @param offset the offset + */ + public void setOffset(Vector offset) { + checkNotNull(offset); + this.offset = offset; + } + + @Override + public BaseBlock apply(Vector position) { + Vector base = position.add(offset); + Vector size = extent.getMaximumPoint().subtract(extent.getMinimumPoint()); + int x = base.getBlockX() % size.getBlockX(); + int y = base.getBlockY() % size.getBlockY(); + int z = base.getBlockZ() % size.getBlockZ(); + return extent.getBlock(new Vector(x, y, z)); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java b/src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java new file mode 100644 index 000000000..6e79efe31 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/registry/AbstractRegistry.java @@ -0,0 +1,67 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.NoMatchException; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An abstract implementation of a registry for internal usage. + * + * @param the element that the registry returns + */ +@SuppressWarnings("ProtectedField") +public abstract class AbstractRegistry { + + protected final WorldEdit worldEdit; + protected final List> parsers = new ArrayList>(); + + /** + * Create a new registry. + * + * @param worldEdit the WorldEdit instance + */ + protected AbstractRegistry(WorldEdit worldEdit) { + checkNotNull(worldEdit); + this.worldEdit = worldEdit; + } + + public E parseFromInput(String input, ParserContext context) throws InputParseException { + E match; + + for (InputParser parser : parsers) { + match = parser.parseFromInput(input, context); + + if (match != null) { + return match; + } + } + + throw new NoMatchException("No match for '" + input + "'"); + } + +} diff --git a/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java b/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java new file mode 100644 index 000000000..5dd6a13e2 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.internal.registry; + +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.extension.input.InputParseException; + +/** + * Input parser interface for {@link AbstractRegistry}. + * + * @param the element + */ +@SuppressWarnings("ProtectedField") +public abstract class InputParser { + + protected final WorldEdit worldEdit; + + protected InputParser(WorldEdit worldEdit) { + this.worldEdit = worldEdit; + } + + public abstract E parseFromInput(String input, ParserContext context) throws InputParseException; + +} diff --git a/src/main/java/com/sk89q/worldedit/regions/NullRegion.java b/src/main/java/com/sk89q/worldedit/regions/NullRegion.java new file mode 100644 index 000000000..5a8ad3bde --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/regions/NullRegion.java @@ -0,0 +1,139 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.regions; + +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.Vector; + +import java.util.*; + +/** + * A region that contains no points. + */ +public class NullRegion implements Region { + + private LocalWorld world; + + @Override + public Vector getMinimumPoint() { + return new Vector(0, 0, 0); + } + + @Override + public Vector getMaximumPoint() { + return new Vector(0, 0, 0); + } + + @Override + public Vector getCenter() { + return new Vector(0, 0, 0); + } + + @Override + public int getArea() { + return 0; + } + + @Override + public int getWidth() { + return 0; + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public int getLength() { + return 0; + } + + @Override + public void expand(Vector... changes) throws RegionOperationException { + throw new RegionOperationException("Cannot change NullRegion"); + } + + @Override + public void contract(Vector... changes) throws RegionOperationException { + throw new RegionOperationException("Cannot change NullRegion"); + } + + @Override + public void shift(Vector change) throws RegionOperationException { + throw new RegionOperationException("Cannot change NullRegion"); + } + + @Override + public boolean contains(Vector pt) { + return false; + } + + @Override + public Set getChunks() { + return Collections.emptySet(); + } + + @Override + public Set getChunkCubes() { + return Collections.emptySet(); + } + + @Override + public LocalWorld getWorld() { + return world; + } + + @Override + public void setWorld(LocalWorld world) { + this.world = world; + } + + @Override + public NullRegion clone() { + return new NullRegion(); + } + + @Override + public List polygonize(int maxPoints) { + return Collections.emptyList(); + } + + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public BlockVector next() { + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Cannot remove"); + } + }; + } + +} diff --git a/src/main/java/com/sk89q/worldedit/session/request/Request.java b/src/main/java/com/sk89q/worldedit/session/request/Request.java new file mode 100644 index 000000000..58dfc9960 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/session/request/Request.java @@ -0,0 +1,116 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.session.request; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.LocalWorld; + +import javax.annotation.Nullable; + +/** + * Describes the current request using a {@link ThreadLocal}. + */ +public final class Request { + + private static final ThreadLocal threadLocal = + new ThreadLocal() { + @Override protected Request initialValue() { + return new Request(); + } + }; + + private @Nullable LocalWorld world; + private @Nullable LocalSession session; + private @Nullable EditSession editSession; + + private Request() { + } + + /** + * Get the request world. + * + * @return the world, which may be null + */ + public @Nullable LocalWorld getWorld() { + return world; + } + + /** + * Set the request world. + * + * @param world the world, which may be null + */ + public void setWorld(@Nullable LocalWorld world) { + this.world = world; + } + + /** + * Get the request session. + * + * @return the session, which may be null + */ + public @Nullable LocalSession getSession() { + return session; + } + + /** + * Get the request session. + * + * @param session the session, which may be null + */ + public void setSession(@Nullable LocalSession session) { + this.session = session; + } + + /** + * Get the {@link EditSession}. + * + * @return the edit session, which may be null + */ + public @Nullable EditSession getEditSession() { + return editSession; + } + + /** + * Set the {@link EditSession}. + * + * @param editSession the edit session, which may be null + */ + public void setEditSession(@Nullable EditSession editSession) { + this.editSession = editSession; + } + + /** + * Get the current request, which is specific to the current thread. + * + * @return the current request + */ + public static Request request() { + return threadLocal.get(); + } + + /** + * Reset the current request and clear all fields. + */ + public static void reset() { + threadLocal.remove(); + } +} diff --git a/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java b/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.java new file mode 100644 index 000000000..1e4e60101 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/session/request/RequestSelection.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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.session.request; + +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.regions.NullRegion; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * A region that mirrors the current selection according to the current + * {@link LocalSession} and {@link LocalWorld} set on the current + * {@link Request}. + *

+ * If a selection cannot be taken, then the selection will be assumed to be + * that of a {@link NullRegion}. + */ +public class RequestSelection implements Region { + + /** + * Get the delegate region. + * + * @return the delegate region + */ + protected Region getRegion() { + LocalSession session = Request.request().getSession(); + LocalWorld world = Request.request().getWorld(); + + if (session != null && world != null) { + try { + return session.getSelection(world); + } catch (IncompleteRegionException ignored) { + } + } + + return new NullRegion(); + } + + @Override + public Vector getMinimumPoint() { + return getRegion().getMinimumPoint(); + } + + @Override + public Vector getMaximumPoint() { + return getRegion().getMaximumPoint(); + } + + @Override + public Vector getCenter() { + return getRegion().getCenter(); + } + + @Override + public int getArea() { + return getRegion().getArea(); + } + + @Override + public int getWidth() { + return getRegion().getWidth(); + } + + @Override + public int getHeight() { + return getRegion().getHeight(); + } + + @Override + public int getLength() { + return getRegion().getLength(); + } + + @Override + public void expand(Vector... changes) throws RegionOperationException { + getRegion().expand(changes); + } + + @Override + public void contract(Vector... changes) throws RegionOperationException { + getRegion().contract(changes); + } + + @Override + public void shift(Vector change) throws RegionOperationException { + getRegion().shift(change); + } + + @Override + public boolean contains(Vector pt) { + return getRegion().contains(pt); + } + + @Override + public Set getChunks() { + return getRegion().getChunks(); + } + + @Override + public Set getChunkCubes() { + return getRegion().getChunkCubes(); + } + + @Override + public LocalWorld getWorld() { + return getRegion().getWorld(); + } + + @Override + public void setWorld(LocalWorld world) { + getRegion().setWorld(world); + } + + @Override + public Region clone() { + return this; + } + + @Override + public List polygonize(int maxPoints) { + return getRegion().polygonize(maxPoints); + } + + @Override + public Iterator iterator() { + return getRegion().iterator(); + } + +}