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();
+ }
+
+}