From 30700559dbba2d5edcb57d14f15c8eb4dff1079c Mon Sep 17 00:00:00 2001
From: Jesse Boyd
Date: Thu, 15 Dec 2016 18:36:04 +1100
Subject: [PATCH] Masking changes
Add light related masks
- #opacity
- #brightness
- #blocklight
- #skylight
- #light
- #nolight
- #haslight
Add mask tab completion
Add source masks
- `/gsmask` and `/smask`
- Masking the source instead of the destination (e.g. with //paste)
- if there is no source, the current block/extent will be used
---
.../com/boydti/fawe/command/MaskBinding.java | 44 ++++
.../main/java/com/boydti/fawe/config/BBC.java | 5 +
.../object/brush/DoubleActionBrushTool.java | 32 +++
.../object/extent/FastWorldEditExtent.java | 25 +++
.../fawe/object/extent/LightingExtent.java | 11 +
.../fawe/object/extent/SourceMaskExtent.java | 55 +++++
.../fawe/object/extent/TemporalExtent.java | 86 ++++++++
.../fawe/object/mask/BlockLightMask.java | 35 ++++
.../fawe/object/mask/BrightnessMask.java | 35 ++++
.../boydti/fawe/object/mask/LightMask.java | 35 ++++
.../boydti/fawe/object/mask/OpacityMask.java | 35 ++++
.../boydti/fawe/object/mask/SkyLightMask.java | 35 ++++
.../fawe/object/schematic/Schematic.java | 8 +
.../java/com/sk89q/worldedit/EditSession.java | 74 ++++++-
.../com/sk89q/worldedit/LocalSession.java | 32 +++
.../worldedit/command/ClipboardCommands.java | 15 +-
.../worldedit/command/GeneralCommands.java | 26 ++-
.../worldedit/command/RegionCommands.java | 4 +
.../worldedit/command/ToolUtilCommands.java | 40 +++-
.../worldedit/command/tool/BrushTool.java | 32 +++
.../extension/factory/DefaultMaskParser.java | 189 +++++++++++++-----
.../factory/HashTagPatternParser.java | 14 +-
.../extension/platform/CommandManager.java | 2 +
.../extent/AbstractDelegateExtent.java | 49 ++++-
.../extent/clipboard/BlockArrayClipboard.java | 45 ++++-
.../sk89q/worldedit/session/PasteBuilder.java | 11 +
26 files changed, 910 insertions(+), 64 deletions(-)
create mode 100644 core/src/main/java/com/boydti/fawe/command/MaskBinding.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/LightingExtent.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/SourceMaskExtent.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/extent/TemporalExtent.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/BlockLightMask.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/BrightnessMask.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/LightMask.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/OpacityMask.java
create mode 100644 core/src/main/java/com/boydti/fawe/object/mask/SkyLightMask.java
diff --git a/core/src/main/java/com/boydti/fawe/command/MaskBinding.java b/core/src/main/java/com/boydti/fawe/command/MaskBinding.java
new file mode 100644
index 00000000..a913f922
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/command/MaskBinding.java
@@ -0,0 +1,44 @@
+package com.boydti.fawe.command;
+
+import com.boydti.fawe.util.MainUtil;
+import com.boydti.fawe.util.StringMan;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.extension.factory.DefaultMaskParser;
+import com.sk89q.worldedit.util.command.parametric.ParameterData;
+import com.sk89q.worldedit.world.registry.BundledBlockData;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MaskBinding extends FaweBinding {
+ private final WorldEdit worldEdit;
+
+ public MaskBinding(WorldEdit worldEdit) {
+ super(worldEdit);
+ this.worldEdit = worldEdit;
+ }
+
+ @Override
+ public List getSuggestions(ParameterData parameter, String prefix) {
+ int index = prefix.lastIndexOf(",");
+ String start = index != -1 ? prefix.substring(0, index) : "";
+ String current = index != -1 ? prefix.substring(index) : prefix;
+ if (current.isEmpty()) {
+ return MainUtil.prepend(start, Arrays.asList(DefaultMaskParser.ALL_MASKS));
+ }
+ if (current.startsWith("#") || current.startsWith("=")) {
+ return new ArrayList<>();
+ }
+ if (StringMan.isAlphanumeric(current.charAt(0) + "")) {
+ String[] split2 = current.split(":");
+ if (split2.length == 2 || current.endsWith(":")) {
+ start = (start.isEmpty() ? split2[0] : start + " " + split2[0]) + ":";
+ current = split2.length == 2 ? split2[1] : "";
+ return MainUtil.prepend(start, MainUtil.filter(current, BundledBlockData.getInstance().getBlockStates(split2[0])));
+ }
+ List blocks = BundledBlockData.getInstance().getBlockNames(split2[0]);
+ return MainUtil.prepend(start, blocks);
+ }
+ return new ArrayList<>();
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java
index 051de287..ac691c5e 100644
--- a/core/src/main/java/com/boydti/fawe/config/BBC.java
+++ b/core/src/main/java/com/boydti/fawe/config/BBC.java
@@ -50,6 +50,8 @@ public enum BBC {
MASK_DISABLED("Global mask disabled", "WorldEdit.General"),
MASK("Global mask set", "WorldEdit.General"),
+ SOURCE_MASK_DISABLED("Global source mask disabled", "WorldEdit.General"),
+ SOURCE_MASK("Global source mask set", "WorldEdit.General"),
TRANSFORM_DISABLED("Global transform disabled", "WorldEdit.General"),
TRANSFORM("Global transform set", "WorldEdit.General"),
@@ -85,6 +87,7 @@ public enum BBC {
SELECTION_CLEARED("Selection cleared", "WorldEdit.Selection"),
SELECTION_NONE("Make a region selection first", "WorldEdit.Selection"),
+ BRUSH_NONE("You aren't holding a brush!", "WorldEdit.Brush"),
BRUSH_BUTCHER("Butcher brush equiped (%s0)", "WorldEdit.Brush"),
BRUSH_CLIPBOARD("Clipboard brush shape equipped", "WorldEdit.Brush"),
BRUSH_CYLINDER("Cylinder brush shape equipped (%s0 by %s1).", "WorldEdit.Brush"),
@@ -110,6 +113,8 @@ public enum BBC {
BRUSH_RANGE("Brush size set", "WorldEdit.Brush"),
BRUSH_MASK_DISABLED("Brush mask disabled", "WorldEdit.Brush"),
BRUSH_MASK("Brush mask set", "WorldEdit.Brush"),
+ BRUSH_SOURCE_MASK_DISABLED("Brush source mask disabled", "WorldEdit.Brush"),
+ BRUSH_SOURCE_MASK("Brush source mask set", "WorldEdit.Brush"),
BRUSH_TRANSFORM_DISABLED("Brush transform disabled", "WorldEdit.Brush"),
BRUSH_TRANSFORM("Brush transform set", "WorldEdit.Brush"),
BRUSH_MATERIAL("Brush material set", "WorldEdit.Brush"),
diff --git a/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java b/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java
index 0971e36e..41ca5941 100644
--- a/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java
+++ b/core/src/main/java/com/boydti/fawe/object/brush/DoubleActionBrushTool.java
@@ -30,6 +30,7 @@ public class DoubleActionBrushTool implements DoubleActionTraceTool {
protected static int MAX_RANGE = 500;
protected int range = -1;
private Mask mask = null;
+ private Mask sourceMask = null;
private ResettableExtent transform = null;
private DoubleActionBrush brush = null;
@Nullable
@@ -69,6 +70,15 @@ public class DoubleActionBrushTool implements DoubleActionTraceTool {
return mask;
}
+ /**
+ * Get the filter.
+ *
+ * @return the filter
+ */
+ public Mask getSourceMask() {
+ return sourceMask;
+ }
+
/**
* Set the block filter used for identifying blocks to replace.
*
@@ -78,6 +88,15 @@ public class DoubleActionBrushTool implements DoubleActionTraceTool {
this.mask = filter;
}
+ /**
+ * Set the block filter used for identifying blocks to replace.
+ *
+ * @param filter the filter to set
+ */
+ public void setSourceMask(Mask filter) {
+ this.sourceMask = filter;
+ }
+
/**
* Set the brush.
*
@@ -178,6 +197,19 @@ public class DoubleActionBrushTool implements DoubleActionTraceTool {
editSession.setMask(newMask);
}
}
+ if (sourceMask != null) {
+ Mask existingMask = editSession.getSourceMask();
+
+ if (existingMask == null) {
+ editSession.setSourceMask(sourceMask);
+ } else if (existingMask instanceof MaskIntersection) {
+ ((MaskIntersection) existingMask).add(sourceMask);
+ } else {
+ MaskIntersection newMask = new MaskIntersection(existingMask);
+ newMask.add(sourceMask);
+ editSession.setSourceMask(newMask);
+ }
+ }
if (transform != null) {
editSession.addTransform(transform);
}
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java
index c8c4c949..15f5274b 100644
--- a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java
+++ b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java
@@ -40,6 +40,31 @@ public class FastWorldEditExtent extends AbstractDelegateExtent implements HasFa
return queue;
}
+ @Override
+ public int getLight(int x, int y, int z) {
+ return queue.getLight(x, y, z);
+ }
+
+ @Override
+ public int getBlockLight(int x, int y, int z) {
+ return queue.getEmmittedLight(x, y, z);
+ }
+
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ return queue.getSkyLight(x, y, z);
+ }
+
+ @Override
+ public int getBrightness(int x, int y, int z) {
+ return queue.getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(int x, int y, int z) {
+ return queue.getOpacity(x, y, z);
+ }
+
@Override
public Entity createEntity(final Location loc, final BaseEntity entity) {
if (entity != null) {
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/LightingExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/LightingExtent.java
new file mode 100644
index 00000000..f02aa8ef
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/extent/LightingExtent.java
@@ -0,0 +1,11 @@
+package com.boydti.fawe.object.extent;
+
+import com.sk89q.worldedit.extent.Extent;
+
+public interface LightingExtent extends Extent {
+ int getLight(int x, int y, int z);
+ int getSkyLight(int x, int y, int z);
+ int getBlockLight(int x, int y, int z);
+ int getOpacity(int x, int y, int z);
+ int getBrightness(int x, int y, int z);
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/SourceMaskExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/SourceMaskExtent.java
new file mode 100644
index 00000000..ce18e99e
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/extent/SourceMaskExtent.java
@@ -0,0 +1,55 @@
+package com.boydti.fawe.object.extent;
+
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class SourceMaskExtent extends TemporalExtent {
+ private Mask mask;
+ private Vector mutable = new Vector();
+
+
+ /**
+ * Get the mask.
+ *
+ * @return the mask
+ */
+ public Mask getMask() {
+ return mask;
+ }
+
+ /**
+ * Set a mask.
+ *
+ * @param mask a mask
+ */
+ public void setMask(Mask mask) {
+ checkNotNull(mask);
+ this.mask = mask;
+ }
+
+ public SourceMaskExtent(Extent extent, Mask mask) {
+ super(extent);
+ checkNotNull(mask);
+ this.mask = mask;
+ }
+ @Override
+ public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
+ set((int) location.x, (int) location.y, (int) location.z, block);
+ return mask.test(location) && super.setBlock(location, block);
+ }
+
+ @Override
+ public boolean setBlock(int x, int y, int z, BaseBlock block) throws WorldEditException {
+ set(x, y, z, block);
+ mutable.x = x;
+ mutable.y = y;
+ mutable.z = z;
+ return mask.test(mutable) && super.setBlock(x, y, z, block);
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/extent/TemporalExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/TemporalExtent.java
new file mode 100644
index 00000000..90893130
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/extent/TemporalExtent.java
@@ -0,0 +1,86 @@
+package com.boydti.fawe.object.extent;
+
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.Vector2D;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.BlockMaterial;
+import com.sk89q.worldedit.extent.AbstractDelegateExtent;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.world.biome.BaseBiome;
+import com.sk89q.worldedit.world.registry.BundledBlockData;
+
+public class TemporalExtent extends AbstractDelegateExtent {
+ private int x,y,z = Integer.MAX_VALUE;
+ private BaseBlock block = EditSession.nullBlock;
+
+ private int bx,bz = Integer.MAX_VALUE;
+ private BaseBiome biome = EditSession.nullBiome;
+
+ /**
+ * Create a new instance.
+ *
+ * @param extent the extent
+ */
+ public TemporalExtent(Extent extent) {
+ super(extent);
+ }
+
+
+ public void set(int x, int y, int z, BaseBlock block) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.block = block;
+ }
+
+ public void set(int x, int z, BaseBiome biome) {
+ this.bx = x;
+ this.bz = z;
+ this.biome = biome;
+ }
+
+ @Override
+ public int getBrightness(int x, int y, int z) {
+ if (this.x == x && this.y == y && this.z == z) {
+ BlockMaterial block = BundledBlockData.getInstance().getMaterialById(this.block.getId());
+ if (block == null) {
+ return 15;
+ }
+ return Math.min(15, block.getLightValue());
+ }
+ return super.getBrightness(x, y, z);
+ }
+
+ @Override
+ public BaseBlock getBlock(Vector position) {
+ if (position.x == x && position.y == y && position.z == z) {
+ return block;
+ }
+ return super.getBlock(position);
+ }
+
+ @Override
+ public BaseBlock getLazyBlock(Vector position) {
+ if (position.x == x && position.y == y && position.z == z) {
+ return block;
+ }
+ return super.getLazyBlock(position);
+ }
+
+ @Override
+ public BaseBlock getLazyBlock(int x, int y, int z) {
+ if (this.x == x && this.y == y && this.z == z) {
+ return block;
+ }
+ return super.getLazyBlock(x, y, z);
+ }
+
+ @Override
+ public BaseBiome getBiome(Vector2D position) {
+ if (position.getX() == bx && position.getZ() == bz) {
+ return biome;
+ }
+ return super.getBiome(position);
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/BlockLightMask.java b/core/src/main/java/com/boydti/fawe/object/mask/BlockLightMask.java
new file mode 100644
index 00000000..33a5f272
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/BlockLightMask.java
@@ -0,0 +1,35 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.object.extent.LightingExtent;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class BlockLightMask implements Mask {
+
+ private final Extent extent;
+ private final int min,max;
+
+ public BlockLightMask(Extent extent, int min, int max) {
+ this.extent = extent;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public boolean test(Vector vector) {
+ if (extent instanceof LightingExtent) {
+ int light = ((LightingExtent) extent).getBlockLight((int) vector.x, (int) vector.y, (int) vector.z);
+ return light >= min && light <= max;
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/BrightnessMask.java b/core/src/main/java/com/boydti/fawe/object/mask/BrightnessMask.java
new file mode 100644
index 00000000..a397ae8b
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/BrightnessMask.java
@@ -0,0 +1,35 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.object.extent.LightingExtent;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class BrightnessMask implements Mask {
+
+ private final Extent extent;
+ private final int min,max;
+
+ public BrightnessMask(Extent extent, int min, int max) {
+ this.extent = extent;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public boolean test(Vector vector) {
+ if (extent instanceof LightingExtent) {
+ int light = ((LightingExtent) extent).getBrightness((int) vector.x, (int) vector.y, (int) vector.z);
+ return light >= min && light <= max;
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/LightMask.java b/core/src/main/java/com/boydti/fawe/object/mask/LightMask.java
new file mode 100644
index 00000000..fb3294ef
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/LightMask.java
@@ -0,0 +1,35 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.object.extent.LightingExtent;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class LightMask implements Mask {
+
+ private final Extent extent;
+ private final int min,max;
+
+ public LightMask(Extent extent, int min, int max) {
+ this.extent = extent;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public boolean test(Vector vector) {
+ if (extent instanceof LightingExtent) {
+ int light = ((LightingExtent) extent).getLight((int) vector.x, (int) vector.y, (int) vector.z);
+ return light >= min && light <= max;
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/OpacityMask.java b/core/src/main/java/com/boydti/fawe/object/mask/OpacityMask.java
new file mode 100644
index 00000000..9f1d645c
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/OpacityMask.java
@@ -0,0 +1,35 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.object.extent.LightingExtent;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class OpacityMask implements Mask {
+
+ private final Extent extent;
+ private final int min,max;
+
+ public OpacityMask(Extent extent, int min, int max) {
+ this.extent = extent;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public boolean test(Vector vector) {
+ if (extent instanceof LightingExtent) {
+ int light = ((LightingExtent) extent).getOpacity((int) vector.x, (int) vector.y, (int) vector.z);
+ return light >= min && light <= max;
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/mask/SkyLightMask.java b/core/src/main/java/com/boydti/fawe/object/mask/SkyLightMask.java
new file mode 100644
index 00000000..57f17b6e
--- /dev/null
+++ b/core/src/main/java/com/boydti/fawe/object/mask/SkyLightMask.java
@@ -0,0 +1,35 @@
+package com.boydti.fawe.object.mask;
+
+import com.boydti.fawe.object.extent.LightingExtent;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.function.mask.Mask2D;
+import javax.annotation.Nullable;
+
+public class SkyLightMask implements Mask {
+
+ private final Extent extent;
+ private final int min,max;
+
+ public SkyLightMask(Extent extent, int min, int max) {
+ this.extent = extent;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public boolean test(Vector vector) {
+ if (extent instanceof LightingExtent) {
+ int light = ((LightingExtent) extent).getSkyLight((int) vector.x, (int) vector.y, (int) vector.z);
+ return light >= min && light <= max;
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public Mask2D toMask2D() {
+ return null;
+ }
+}
diff --git a/core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java b/core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java
index 3d4414de..1d055379 100644
--- a/core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java
+++ b/core/src/main/java/com/boydti/fawe/object/schematic/Schematic.java
@@ -2,6 +2,7 @@ package com.boydti.fawe.object.schematic;
import com.boydti.fawe.object.clipboard.ReadOnlyClipboard;
import com.boydti.fawe.util.EditSessionBuilder;
+import com.boydti.fawe.util.MaskTraverser;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
@@ -12,6 +13,7 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
+import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.transform.Transform;
@@ -114,6 +116,12 @@ public class Schematic {
if (transform != null) {
copy.setTransform(transform);
}
+ Mask sourceMask = editSession.getSourceMask();
+ if (sourceMask != null) {
+ new MaskTraverser(sourceMask).reset(extent);
+ copy.setSourceMask(sourceMask);
+ editSession.setSourceMask(null);
+ }
if (!pasteAir) {
copy.setSourceMask(new ExistingBlockMask(clipboard));
}
diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java
index 64557da8..27f355df 100644
--- a/core/src/main/java/com/sk89q/worldedit/EditSession.java
+++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java
@@ -44,10 +44,12 @@ import com.boydti.fawe.object.changeset.MemoryOptimizedHistory;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.boydti.fawe.object.extent.FaweRegionExtent;
+import com.boydti.fawe.object.extent.LightingExtent;
import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.object.extent.ProcessedWEExtent;
import com.boydti.fawe.object.extent.ResettableExtent;
import com.boydti.fawe.object.extent.SlowExtent;
+import com.boydti.fawe.object.extent.SourceMaskExtent;
import com.boydti.fawe.object.mask.ResettableMask;
import com.boydti.fawe.object.progress.DefaultProgressTracker;
import com.boydti.fawe.util.ExtentTraverser;
@@ -155,7 +157,7 @@ import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
* {@link Extent}s that are chained together. For example, history is logged
* using the {@link ChangeSetExtent}.
*/
-public class EditSession extends AbstractWorld implements HasFaweQueue {
+public class EditSession extends AbstractWorld implements HasFaweQueue, LightingExtent {
/**
* Used by {@link #setBlock(Vector, BaseBlock, Stage)} to
* determine which {@link Extent}s should be bypassed.
@@ -592,6 +594,16 @@ public class EditSession extends AbstractWorld implements HasFaweQueue {
return maskingExtent != null ? maskingExtent.get().getMask() : null;
}
+ /**
+ * Get the mask.
+ *
+ * @return mask, may be null
+ */
+ public Mask getSourceMask() {
+ ExtentTraverser maskingExtent = new ExtentTraverser(this.extent).find(SourceMaskExtent.class);
+ return maskingExtent != null ? maskingExtent.get().getMask() : null;
+ }
+
public void addTransform(ResettableExtent transform) {
if (transform == null) {
ExtentTraverser traverser = new ExtentTraverser(this.extent).find(ResettableExtent.class);
@@ -607,6 +619,29 @@ public class EditSession extends AbstractWorld implements HasFaweQueue {
}
}
+ /**
+ * Set a mask.
+ *
+ * @param mask mask or null
+ */
+ public void setSourceMask(Mask mask) {
+ if (mask == null) {
+ mask = Masks.alwaysTrue();
+ } else {
+ new MaskTraverser(mask).reset(this);
+ }
+ ExtentTraverser maskingExtent = new ExtentTraverser(this.extent).find(SourceMaskExtent.class);
+ if (maskingExtent != null && maskingExtent.get() != null) {
+ Mask oldMask = maskingExtent.get().getMask();
+ if (oldMask instanceof ResettableMask) {
+ ((ResettableMask) oldMask).reset();
+ }
+ maskingExtent.get().setMask(mask);
+ } else if (mask != Masks.alwaysTrue()) {
+ this.extent = new SourceMaskExtent(this.extent, mask);
+ }
+ }
+
/**
* Set a mask.
*
@@ -792,6 +827,31 @@ public class EditSession extends AbstractWorld implements HasFaweQueue {
return this.extent.setBiome(position, biome);
}
+ @Override
+ public int getLight(int x, int y, int z) {
+ return queue.getLight(x, y, z);
+ }
+
+ @Override
+ public int getBlockLight(int x, int y, int z) {
+ return queue.getEmmittedLight(x, y, z);
+ }
+
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ return queue.getSkyLight(x, y, z);
+ }
+
+ @Override
+ public int getBrightness(int x, int y, int z) {
+ return queue.getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(int x, int y, int z) {
+ return queue.getOpacity(x, y, z);
+ }
+
@Override
public BaseBlock getLazyBlock(final Vector position) {
if (position.y > maxY || position.y < 0) {
@@ -1794,6 +1854,12 @@ public class EditSession extends AbstractWorld implements HasFaweQueue {
final ForwardExtentCopy copy = new ForwardExtentCopy(EditSession.this, region, EditSession.this, to);
copy.setRepetitions(count);
copy.setTransform(new AffineTransform().translate(dir.multiply(size)));
+ Mask sourceMask = getSourceMask();
+ if (sourceMask != null) {
+ new MaskTraverser(sourceMask).reset(EditSession.this);
+ copy.setSourceMask(sourceMask);
+ setSourceMask(null);
+ }
if (!copyAir) {
copy.setSourceMask(new ExistingBlockMask(EditSession.this));
}
@@ -1844,6 +1910,12 @@ public class EditSession extends AbstractWorld implements HasFaweQueue {
copy.setTransform(new AffineTransform().translate(dir.multiply(distance)));
copy.setSourceFunction(remove); // Remove
copy.setRemovingEntities(true);
+ Mask sourceMask = getSourceMask();
+ if (sourceMask != null) {
+ new MaskTraverser(sourceMask).reset(EditSession.this);
+ copy.setSourceMask(sourceMask);
+ setSourceMask(null);
+ }
if (!copyAir) {
copy.setSourceMask(new ExistingBlockMask(EditSession.this));
}
diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java
index 87707962..a821326d 100644
--- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java
+++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java
@@ -139,6 +139,7 @@ public class LocalSession {
private transient int cuiVersion = -1;
private transient boolean fastMode = false;
private transient Mask mask;
+ private transient Mask sourceMask;
private ResettableExtent transform = null;
private transient TimeZone timezone = TimeZone.getDefault();
@@ -1238,6 +1239,9 @@ public class LocalSession {
if (mask != null) {
editSession.setMask(mask);
}
+ if (sourceMask != null) {
+ editSession.setSourceMask(sourceMask);
+ }
if (transform != null) {
editSession.addTransform(transform);
}
@@ -1272,6 +1276,15 @@ public class LocalSession {
return mask;
}
+ /**
+ * Get the mask.
+ *
+ * @return mask, may be null
+ */
+ public Mask getSourceMask() {
+ return sourceMask;
+ }
+
/**
* Set a mask.
*
@@ -1281,6 +1294,15 @@ public class LocalSession {
this.mask = mask;
}
+ /**
+ * Set a mask.
+ *
+ * @param mask mask or null
+ */
+ public void setSourceMask(Mask mask) {
+ this.sourceMask = mask;
+ }
+
/**
* Set a mask.
*
@@ -1291,6 +1313,16 @@ public class LocalSession {
setMask(mask != null ? Masks.wrap(mask) : null);
}
+ /**
+ * Set a mask.
+ *
+ * @param mask mask or null
+ */
+ @SuppressWarnings("deprecation")
+ public void setSourceMask(com.sk89q.worldedit.masks.Mask mask) {
+ setSourceMask(mask != null ? Masks.wrap(mask) : null);
+ }
+
public ResettableExtent getTransform() {
return transform;
}
diff --git a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java
index ad28bac9..443c7a3f 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java
@@ -25,6 +25,7 @@ import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.clipboard.ReadOnlyClipboard;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.boydti.fawe.util.ImgurUtility;
+import com.boydti.fawe.util.MaskTraverser;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissions;
@@ -141,6 +142,12 @@ public class ClipboardCommands {
clipboard.setOrigin(session.getPlacementPosition(player));
ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint());
+ Mask sourceMask = editSession.getSourceMask();
+ if (sourceMask != null) {
+ new MaskTraverser(sourceMask).reset(editSession);
+ copy.setSourceMask(sourceMask);
+ editSession.setSourceMask(null);
+ }
if (mask != null && mask != Masks.alwaysTrue()) {
copy.setSourceMask(mask);
}
@@ -173,6 +180,12 @@ public class ClipboardCommands {
clipboard.setOrigin(session.getPlacementPosition(player));
ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint());
copy.setSourceFunction(new BlockReplace(editSession, leavePattern));
+ Mask sourceMask = editSession.getSourceMask();
+ if (sourceMask != null) {
+ new MaskTraverser(sourceMask).reset(editSession);
+ copy.setSourceMask(sourceMask);
+ editSession.setSourceMask(null);
+ }
if (mask != null) {
copy.setSourceMask(mask);
}
@@ -253,7 +266,7 @@ public class ClipboardCommands {
@Switch('a') boolean ignoreAirBlocks, @Switch('o') boolean atOrigin,
@Switch('s') boolean selectPasted) throws WorldEditException {
ClipboardHolder holder = session.getClipboard();
- if (holder.getTransform().isIdentity()) {
+ if (holder.getTransform().isIdentity() && editSession.getSourceMask() == null) {
place(player, session, editSession, ignoreAirBlocks, atOrigin, selectPasted);
return;
}
diff --git a/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java
index f740713c..7cc1e290 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java
@@ -101,7 +101,7 @@ public class GeneralCommands {
}
@Command(
- aliases = { "/gmask", "gmask" },
+ aliases = { "/gmask", "gmask", "globalmask", "/globalmask" },
usage = "[mask]",
desc = "Set the global mask",
min = 0,
@@ -124,6 +124,30 @@ public class GeneralCommands {
}
}
+ @Command(
+ aliases = { "/gsmask", "gsmask", "globalsourcemask", "/globalsourcemask" },
+ usage = "[mask]",
+ desc = "Set the global source mask",
+ min = 0,
+ max = -1
+ )
+ @CommandPermissions("worldedit.global-mask")
+ public void gsmask(Player player, LocalSession session, EditSession editSession, @Optional CommandContext context) throws WorldEditException {
+ if (context == null || context.argsLength() == 0) {
+ session.setSourceMask((Mask) null);
+ BBC.SOURCE_MASK_DISABLED.send(player);
+ } else {
+ ParserContext parserContext = new ParserContext();
+ parserContext.setActor(player);
+ parserContext.setWorld(player.getWorld());
+ parserContext.setSession(session);
+ parserContext.setExtent(editSession);
+ Mask mask = worldEdit.getMaskFactory().parseFromInput(context.getJoinedStrings(0), parserContext);
+ session.setSourceMask(mask);
+ BBC.SOURCE_MASK.send(player);
+ }
+ }
+
@Command(
aliases = { "/gtransform", "gtransform" },
usage = "[transform]",
diff --git a/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
index 78f688ef..a3e3cdce 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java
@@ -577,11 +577,15 @@ public class RegionCommands {
@Logging(REGION)
public void regenerateChunk(Player player, LocalSession session, EditSession editSession, @Selection Region region) throws WorldEditException {
Mask mask = session.getMask();
+ Mask sourceMask = session.getSourceMask();
try {
session.setMask((Mask) null);
+ session.setSourceMask((Mask) null);
player.getWorld().regenerate(region, editSession);
} finally {
session.setMask(mask);
+ session.setSourceMask(mask);
+
}
BBC.COMMAND_REGEN.send(player);
}
diff --git a/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java
index 48a8fbc4..dc35e47b 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java
@@ -61,7 +61,7 @@ public class ToolUtilCommands {
}
@Command(
- aliases = { "mask" },
+ aliases = { "mask", "/mask" },
usage = "[mask]",
desc = "Set the brush mask",
min = 0,
@@ -71,6 +71,7 @@ public class ToolUtilCommands {
public void mask(Player player, LocalSession session, EditSession editSession, @Optional CommandContext context) throws WorldEditException {
Tool tool = session.getTool(player.getItemInHand());
if (tool == null) {
+ player.print(BBC.BRUSH_NONE.f());
return;
}
if (context == null || context.argsLength() == 0) {
@@ -96,6 +97,43 @@ public class ToolUtilCommands {
}
}
+ @Command(
+ aliases = { "smask", "/smask", "/sourcemask", "sourcemask" },
+ usage = "[mask]",
+ desc = "Set the brush mask",
+ min = 0,
+ max = -1
+ )
+ @CommandPermissions("worldedit.brush.options.mask")
+ public void smask(Player player, LocalSession session, EditSession editSession, @Optional CommandContext context) throws WorldEditException {
+ Tool tool = session.getTool(player.getItemInHand());
+ if (tool == null) {
+ player.print(BBC.BRUSH_NONE.f());
+ return;
+ }
+ if (context == null || context.argsLength() == 0) {
+ if (tool instanceof BrushTool) {
+ ((BrushTool) tool).setSourceMask(null);
+ } else if (tool instanceof DoubleActionBrushTool) {
+ ((DoubleActionBrushTool) tool).setMask(null);
+ }
+ BBC.BRUSH_SOURCE_MASK_DISABLED.send(player);
+ } else {
+ ParserContext parserContext = new ParserContext();
+ parserContext.setActor(player);
+ parserContext.setWorld(player.getWorld());
+ parserContext.setSession(session);
+ parserContext.setExtent(editSession);
+ Mask mask = we.getMaskFactory().parseFromInput(context.getJoinedStrings(0), parserContext);
+ if (tool instanceof BrushTool) {
+ ((BrushTool) tool).setSourceMask(mask);
+ } else if (tool instanceof DoubleActionBrushTool) {
+ ((DoubleActionBrushTool) tool).setSourceMask(mask);
+ }
+ BBC.BRUSH_SOURCE_MASK.send(player);
+ }
+ }
+
@Command(
aliases = { "transform" },
usage = "[transform]",
diff --git a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java
index bee42015..e60e1d4c 100644
--- a/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java
+++ b/core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java
@@ -29,6 +29,7 @@ public class BrushTool implements TraceTool {
protected static int MAX_RANGE = 500;
protected int range = -1;
private Mask mask = null;
+ private Mask sourceMask = null;
private ResettableExtent transform = null;
private Brush brush = new SphereBrush();
@Nullable
@@ -77,6 +78,24 @@ public class BrushTool implements TraceTool {
this.mask = filter;
}
+ /**
+ * Get the filter.
+ *
+ * @return the filter
+ */
+ public Mask getSourceMask() {
+ return sourceMask;
+ }
+
+ /**
+ * Set the block filter used for identifying blocks to replace.
+ *
+ * @param filter the filter to set
+ */
+ public void setSourceMask(Mask filter) {
+ this.sourceMask = filter;
+ }
+
/**
* Set the brush.
*
@@ -178,6 +197,19 @@ public class BrushTool implements TraceTool {
editSession.setMask(newMask);
}
}
+ if (sourceMask != null) {
+ Mask existingMask = editSession.getMask();
+
+ if (existingMask == null) {
+ editSession.setSourceMask(sourceMask);
+ } else if (existingMask instanceof MaskIntersection) {
+ ((MaskIntersection) existingMask).add(sourceMask);
+ } else {
+ MaskIntersection newMask = new MaskIntersection(existingMask);
+ newMask.add(sourceMask);
+ editSession.setSourceMask(newMask);
+ }
+ }
if (transform != null) {
editSession.addTransform(transform);
}
diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java
index d6909d67..98d85b99 100644
--- a/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java
+++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java
@@ -1,24 +1,16 @@
package com.sk89q.worldedit.extension.factory;
import com.boydti.fawe.command.FaweParser;
-import com.boydti.fawe.object.mask.AdjacentMask;
-import com.boydti.fawe.object.mask.AngleMask;
-import com.boydti.fawe.object.mask.CustomMask;
-import com.boydti.fawe.object.mask.DataMask;
-import com.boydti.fawe.object.mask.IdDataMask;
-import com.boydti.fawe.object.mask.IdMask;
-import com.boydti.fawe.object.mask.RadiusMask;
-import com.boydti.fawe.object.mask.WallMask;
-import com.boydti.fawe.object.mask.XAxisMask;
-import com.boydti.fawe.object.mask.YAxisMask;
-import com.boydti.fawe.object.mask.ZAxisMask;
+import com.boydti.fawe.command.SuggestInputParseException;
+import com.boydti.fawe.object.mask.*;
+import com.boydti.fawe.util.MainUtil;
+import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseBlock;
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.BiomeMask2D;
@@ -27,6 +19,7 @@ import com.sk89q.worldedit.function.mask.ExistingBlockMask;
import com.sk89q.worldedit.function.mask.ExpressionMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.MaskIntersection;
+import com.sk89q.worldedit.function.mask.MaskUnion;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.mask.NoiseFilter;
import com.sk89q.worldedit.function.mask.OffsetMask;
@@ -58,16 +51,24 @@ public class DefaultMaskParser extends FaweParser {
public static final String[] EXPRESSION_MASK = new String[] { "=" };
- public static final String[] BLOCK_MASK = new String[] { "" };
+ public static final String[] BLOCK_MASK = new String[] { "" };
public static final String[] SIMPLE_MASK = new String[] {
- "#existing", "#solid", "#dregion", "#dselection", "#dsel", "#selection", "#region", "#sel", "#xaxis", "#yaxis", "#zaxis", "#id", "#data", "#wall", "#surface",
+ "#nolight", "#haslight", "#existing", "#solid", "#dregion", "#dselection", "#dsel", "#selection", "#region", "#sel", "#xaxis", "#yaxis", "#zaxis", "#id", "#data", "#wall", "#surface",
};
- public static final String[] MISC_PATTERNS = new String[] {
- "hand", "pos1",
+ public static final String[] DELEGATE_MASKS = new String[] {
+ "#offset:", "#light:", "#blocklight:", "#skylight:", "#brightness:", "#opacity:"
};
+ public static final String[] CHARACTER_MASKS= new String[] {
+ "/", "{", "|", "~", ">", "<", "$", "%", "=", "!",
+ };
+
+ public static final String[] HASHTAG_MASKS = MainUtil.joinArrayGeneric(SIMPLE_MASK, DELEGATE_MASKS);
+
+ public static final String[] ALL_MASKS = MainUtil.joinArrayGeneric(EXPRESSION_MASK, BLOCK_MASK, SIMPLE_MASK, DELEGATE_MASKS, CHARACTER_MASKS);
+
public DefaultMaskParser(WorldEdit worldEdit) {
super(worldEdit);
}
@@ -111,19 +112,83 @@ public class DefaultMaskParser extends FaweParser {
}
}
- private Mask getBlockMaskComponent(List masks, String component, ParserContext context) throws InputParseException {
+ public Mask catchSuggestion(String currentInput, List masks, String nextInput, ParserContext context) throws InputParseException {
+ try {
+ return getBlockMaskComponent(masks, nextInput, context);
+ } catch (SuggestInputParseException e) {
+ e.prepend(currentInput.substring(0, currentInput.length() - nextInput.length()));
+ throw e;
+ }
+ }
+
+ private Mask getBlockMaskComponent(List masks, String input, ParserContext context) throws InputParseException {
Extent extent = Request.request().getExtent();
- final char firstChar = component.charAt(0);
+ final char firstChar = input.charAt(0);
switch (firstChar) {
case '#':
- int colon = component.indexOf(':');
+ int colon = input.indexOf(':');
+ String component = input;
if (colon != -1) {
- String rest = component.substring(colon + 1);
component = component.substring(0, colon);
- masks.add(getBlockMaskComponent(masks, rest, context));
+ String rest = input.substring(colon + 1);
+ switch (component.toLowerCase()) {
+ case "#light":
+ case "#skylight":
+ case "#blocklight":
+ case "#emittedlight":
+ case "#opacity":
+ case "#brightness":
+ String[] split = rest.split(":");
+ if (split.length < 2) {
+ throw new SuggestInputParseException(input, component + "::");
+ } else if (split.length > 2) {
+ masks.add(catchSuggestion(input, masks, StringMan.join(Arrays.copyOfRange(split, 2, split.length), ":"), context));
+ }
+ try {
+ int y1 = (int) Math.abs(Expression.compile(split[0]).evaluate());
+ int y2 = (int) Math.abs(Expression.compile(split[1]).evaluate());
+ switch (component.toLowerCase()) {
+ case "#light":
+ return new LightMask(extent, y1, y2);
+ case "#skylight":
+ return new SkyLightMask(extent, y1, y2);
+ case "#blocklight":
+ case "#emittedlight":
+ return new BlockLightMask(extent, y1, y2);
+ case "#opacity":
+ return new OpacityMask(extent, y1, y2);
+ case "#brightness":
+ return new BrightnessMask(extent, y1, y2);
+ }
+ } catch (NumberFormatException | ExpressionException e) {
+ e.printStackTrace();
+ throw new SuggestInputParseException(input, component + "::");
+ }
+ case "#~":
+ case "#rel":
+ case "#relative":
+ case "#offset":
+ try {
+ List split3 = suggestRemaining(rest, "#offset", "", "", "", "");
+ int x = (int) Expression.compile(split3.get(0)).evaluate();
+ int y = (int) Expression.compile(split3.get(1)).evaluate();
+ int z = (int) Expression.compile(split3.get(2)).evaluate();
+ rest = StringMan.join(split3.subList(3, split3.size()), ":");
+ Mask mask = catchSuggestion(input, masks, rest, context);
+ return new OffsetMask(mask, new Vector(x, y, z));
+ } catch (NumberFormatException | ExpressionException | IndexOutOfBoundsException e) {
+ throw new SuggestInputParseException(null, "#offset::::");
+ }
+ }
+ Mask mask = catchSuggestion(input, masks, rest, context);
+ masks.add(mask);
}
switch (component.toLowerCase()) {
+ case "#haslight":
+ return new LightMask(extent, 1, Integer.MAX_VALUE);
+ case "#nolight":
+ return new LightMask(extent, 0, 0);
case "#existing":
return new ExistingBlockMask(extent);
case "#solid":
@@ -160,38 +225,38 @@ public class DefaultMaskParser extends FaweParser {
masks.add(new ExistingBlockMask(extent));
return new AdjacentMask(extent, Arrays.asList(new BaseBlock(0)), 1, 8);
default:
- throw new NoMatchException("Unrecognized mask '" + component + "'");
+ throw new SuggestInputParseException(input, HASHTAG_MASKS);
}
case '\\':
case '/': {
- String[] split = component.substring(1).split(",");
+ String[] split = input.substring(1).split(":");
if (split.length != 2) {
- throw new InputParseException("Unknown angle '" + component + "' (not in form `/#,#`)");
+ throw new SuggestInputParseException(input, "/:");
}
try {
int y1 = (int) Math.abs(Expression.compile(split[0]).evaluate());
int y2 = (int) Math.abs(Expression.compile(split[1]).evaluate());
return new AngleMask(extent, y1, y2);
} catch (NumberFormatException | ExpressionException e) {
- throw new InputParseException("Unknown angle '" + component + "' (not in form `/#,#`)");
+ throw new SuggestInputParseException(input, "/:");
}
}
case '{': {
- String[] split = component.substring(1).split(",");
+ String[] split = input.substring(1).split(":");
if (split.length != 2) {
- throw new InputParseException("Unknown range '" + component + "' (not in form `{#,#`)");
+ throw new SuggestInputParseException(input, "{:");
}
try {
int y1 = (int) Math.abs(Expression.compile(split[0]).evaluate());
int y2 = (int) Math.abs(Expression.compile(split[1]).evaluate());
return new RadiusMask(y1, y2);
} catch (NumberFormatException | ExpressionException e) {
- throw new InputParseException("Unknown range '" + component + "' (not in form `{#,#`)");
+ throw new SuggestInputParseException(input, "{:");
}
}
case '|':
case '~': {
- String[] split = component.substring(1).split("=");
+ String[] split = input.substring(1).split("=");
ParserContext tempContext = new ParserContext(context);
tempContext.setRestricted(false);
tempContext.setPreferringWildcard(true);
@@ -206,19 +271,19 @@ public class DefaultMaskParser extends FaweParser {
}
}
if (firstChar == '~') {
- return new AdjacentMask(extent, worldEdit.getBlockFactory().parseFromListInput(component.substring(1), tempContext), requiredMin, requiredMax);
+ return new AdjacentMask(extent, worldEdit.getBlockFactory().parseFromListInput(input.substring(1), tempContext), requiredMin, requiredMax);
} else {
- return new WallMask(extent, worldEdit.getBlockFactory().parseFromListInput(component.substring(1), tempContext), requiredMin, requiredMax);
+ return new WallMask(extent, worldEdit.getBlockFactory().parseFromListInput(input.substring(1), tempContext), requiredMin, requiredMax);
}
} catch (NumberFormatException | ExpressionException e) {
- throw new InputParseException("Unknown adjacent mask '" + component + "' (not in form `~[=count]`)");
+ throw new SuggestInputParseException(input, "~=");
}
}
case '>':
case '<':
Mask submask;
- if (component.length() > 1) {
- submask = getBlockMaskComponent(masks, component.substring(1), context);
+ if (input.length() > 1) {
+ submask = getBlockMaskComponent(masks, input.substring(1), context);
} else {
submask = new ExistingBlockMask(extent);
}
@@ -227,58 +292,80 @@ public class DefaultMaskParser extends FaweParser {
case '$':
Set biomes = new HashSet();
- String[] biomesList = component.substring(1).split(",");
+ String[] biomesList = input.substring(1).split(",");
BiomeRegistry biomeRegistry = context.requireWorld().getWorldData().getBiomeRegistry();
List knownBiomes = biomeRegistry.getBiomes();
for (String biomeName : biomesList) {
BaseBiome biome = Biomes.findBiomeByName(knownBiomes, biomeName, biomeRegistry);
if (biome == null) {
- throw new InputParseException("Unknown biome '" + biomeName + "'");
+ throw new SuggestInputParseException(input, "$");
}
biomes.add(biome);
}
-
return Masks.asMask(new BiomeMask2D(context.requireExtent(), biomes));
case '%':
try {
- double i = Math.abs(Expression.compile(component.substring(1)).evaluate());
+ double i = Math.abs(Expression.compile(input.substring(1)).evaluate());
return new NoiseFilter(new RandomNoise(), (i) / 100);
} catch (NumberFormatException | ExpressionException e) {
- throw new InputParseException("Unknown percentage '" + component.substring(1) + "'");
+ throw new SuggestInputParseException(input, "%");
}
case '=':
try {
- Expression exp = Expression.compile(component.substring(1), "x", "y", "z");
+ Expression exp = Expression.compile(input.substring(1), "x", "y", "z");
WorldEditExpressionEnvironment env = new WorldEditExpressionEnvironment(
Request.request().getEditSession(), Vector.ONE, Vector.ZERO);
exp.setEnvironment(env);
return new ExpressionMask(exp);
} catch (ExpressionException e) {
- throw new InputParseException("Invalid expression: " + e.getMessage());
+ throw new SuggestInputParseException(input, "=");
}
case '!':
- if (component.length() > 1) {
- return Masks.negate(getBlockMaskComponent(masks, component.substring(1), context));
+ if (input.length() > 1) {
+ return Masks.negate(getBlockMaskComponent(masks, input.substring(1), context));
}
-
+ throw new SuggestInputParseException(input, "!");
default:
for (CustomMask mask : customMasks) {
- if (mask.accepts(component)) {
+ if (mask.accepts(input)) {
try {
Constructor extends CustomMask> constructor = mask.getClass().getDeclaredConstructor(List.class, String.class, ParserContext.class);
- return constructor.newInstance(masks, component, context);
+ return constructor.newInstance(masks, input, context);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
-
- ParserContext tempContext = new ParserContext(context);
- tempContext.setRestricted(false);
- tempContext.setPreferringWildcard(true);
- return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(component, tempContext));
+ List split = split(input, ',');
+ if (split.size() == 1) {
+ ParserContext tempContext = new ParserContext(context);
+ tempContext.setRestricted(false);
+ tempContext.setPreferringWildcard(true);
+ return new BlockMask(extent, worldEdit.getBlockFactory().parseFromListInput(input, tempContext));
+ }
+ HashSet blocks = new HashSet();
+ ArrayList maskUnion = new ArrayList();
+ for (String elem : split) {
+ ArrayList list = new ArrayList();
+ list.add(catchSuggestion(input, list, elem, context));
+ if (list.size() == 1) {
+ Mask mask = list.get(0);
+ if (mask instanceof BlockMask) {
+ blocks.addAll(((BlockMask) mask).getBlocks());
+ } else {
+ maskUnion.add(mask);
+ }
+ }
+ }
+ if (!blocks.isEmpty()) {
+ maskUnion.add(new BlockMask(extent, blocks));
+ }
+ if (maskUnion.size() == 1) {
+ return maskUnion.get(0);
+ }
+ return new MaskUnion(maskUnion);
}
}
diff --git a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java
index f5b2727e..66239195 100644
--- a/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java
+++ b/core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java
@@ -145,10 +145,10 @@ public class HashTagPatternParser extends FaweParser {
case "#offset":
try {
List split3 = suggestRemaining(rest, "#offset", "", "", "", "");
- int x = (int) Math.abs(Expression.compile(split3.get(0)).evaluate());
- int y = (int) Math.abs(Expression.compile(split3.get(1)).evaluate());
- int z = (int) Math.abs(Expression.compile(split3.get(2)).evaluate());
- rest = StringMan.join(split3.subList(3, split3.size() - 1), ":");
+ int x = (int) Expression.compile(split3.get(0)).evaluate();
+ int y = (int) Expression.compile(split3.get(1)).evaluate();
+ int z = (int) Expression.compile(split3.get(2)).evaluate();
+ rest = StringMan.join(split3.subList(3, split3.size()), ":");
Pattern pattern = catchSuggestion(input, rest, context);
return new OffsetPattern(pattern, x, y, z);
} catch (NumberFormatException | ExpressionException | IndexOutOfBoundsException e) {
@@ -160,7 +160,7 @@ public class HashTagPatternParser extends FaweParser {
int x = (int) Math.abs(Expression.compile(split3.get(0)).evaluate());
int y = (int) Math.abs(Expression.compile(split3.get(1)).evaluate());
int z = (int) Math.abs(Expression.compile(split3.get(2)).evaluate());
- rest = StringMan.join(split3.subList(3, split3.size() - 1), ":");
+ rest = StringMan.join(split3.subList(3, split3.size()), ":");
Pattern pattern = catchSuggestion(input, rest, context);
return new SurfaceRandomOffsetPattern(pattern, x, y, z);
} catch (NumberFormatException | ExpressionException | IndexOutOfBoundsException e) {
@@ -173,7 +173,7 @@ public class HashTagPatternParser extends FaweParser {
int x = (int) Math.abs(Expression.compile(split3.get(0)).evaluate());
int y = (int) Math.abs(Expression.compile(split3.get(1)).evaluate());
int z = (int) Math.abs(Expression.compile(split3.get(2)).evaluate());
- rest = StringMan.join(split3.subList(3, split3.size() - 1), ":");
+ rest = StringMan.join(split3.subList(3, split3.size()), ":");
Pattern pattern = catchSuggestion(input, rest, context);
return new SolidRandomOffsetPattern(pattern, x, y, z);
} catch (NumberFormatException | ExpressionException | IndexOutOfBoundsException e) {
@@ -187,7 +187,7 @@ public class HashTagPatternParser extends FaweParser {
int x = (int) Math.abs(Expression.compile(split3.get(0)).evaluate());
int y = (int) Math.abs(Expression.compile(split3.get(1)).evaluate());
int z = (int) Math.abs(Expression.compile(split3.get(2)).evaluate());
- rest = StringMan.join(split3.subList(3, split3.size() - 1), ":");
+ rest = StringMan.join(split3.subList(3, split3.size()), ":");
Pattern pattern = catchSuggestion(input, rest, context);
return new RandomOffsetPattern(pattern, x, y, z);
} catch (NumberFormatException | ExpressionException | IndexOutOfBoundsException e) {
diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java
index 9b508142..309b31a9 100644
--- a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java
+++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java
@@ -21,6 +21,7 @@ package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.command.AnvilCommands;
+import com.boydti.fawe.command.MaskBinding;
import com.boydti.fawe.command.PatternBinding;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
@@ -130,6 +131,7 @@ public final class CommandManager {
builder.addBinding(new WorldEditBinding(worldEdit));
builder.addBinding(new PatternBinding(worldEdit), com.sk89q.worldedit.function.pattern.Pattern.class);
+ builder.addBinding(new MaskBinding(worldEdit), com.sk89q.worldedit.function.mask.Mask.class);
builder.addInvokeListener(new LegacyCommandsHandler());
builder.addInvokeListener(new CommandLoggingHandler(worldEdit, commandLog));
diff --git a/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java
index e046a834..1af33258 100644
--- a/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java
+++ b/core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java
@@ -19,10 +19,12 @@
package com.sk89q.worldedit.extent;
+import com.boydti.fawe.object.extent.LightingExtent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.BlockMaterial;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.operation.Operation;
@@ -30,6 +32,7 @@ import com.sk89q.worldedit.function.operation.OperationQueue;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
+import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.util.List;
import javax.annotation.Nullable;
@@ -39,7 +42,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* A base class for {@link Extent}s that merely passes extents onto another.
*/
-public abstract class AbstractDelegateExtent implements Extent {
+public abstract class AbstractDelegateExtent implements LightingExtent {
private final Extent extent;
@@ -53,6 +56,50 @@ public abstract class AbstractDelegateExtent implements Extent {
this.extent = extent;
}
+ public int getSkyLight(int x, int y, int z) {
+ if (extent instanceof LightingExtent) {
+ return ((LightingExtent) extent).getSkyLight(x, y, z);
+ }
+ return 0;
+ }
+
+ public int getBlockLight(int x, int y, int z) {
+ if (extent instanceof LightingExtent) {
+ return ((LightingExtent) extent).getBlockLight(x, y, z);
+ }
+ return getBrightness(x, y, z);
+ }
+
+ public int getOpacity(int x, int y, int z) {
+ if (extent instanceof LightingExtent) {
+ return ((LightingExtent) extent).getOpacity(x, y, z);
+ }
+ BlockMaterial block = BundledBlockData.getInstance().getMaterialById(getLazyBlock(x, y, z).getId());
+ if (block == null) {
+ return 15;
+ }
+ return Math.min(15, block.getLightOpacity());
+ }
+
+ @Override
+ public int getLight(int x, int y, int z) {
+ if (extent instanceof LightingExtent) {
+ return ((LightingExtent) extent).getLight(x, y, z);
+ }
+ return 0;
+ }
+
+ public int getBrightness(int x, int y, int z) {
+ if (extent instanceof LightingExtent) {
+ return ((LightingExtent) extent).getBrightness(x, y, z);
+ }
+ BlockMaterial block = BundledBlockData.getInstance().getMaterialById(getLazyBlock(x, y, z).getId());
+ if (block == null) {
+ return 15;
+ }
+ return Math.min(15, block.getLightValue());
+ }
+
/**
* Get the extent.
*
diff --git a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java
index ea6870be..857cab03 100644
--- a/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java
+++ b/core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java
@@ -23,18 +23,21 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.object.clipboard.FaweClipboard;
import com.boydti.fawe.object.clipboard.MemoryOptimizedClipboard;
+import com.boydti.fawe.object.extent.LightingExtent;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.blocks.BlockMaterial;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
+import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -48,7 +51,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
* Stores block data as a multi-dimensional array of {@link BaseBlock}s and
* other data as lists or maps.
*/
-public class BlockArrayClipboard implements Clipboard {
+public class BlockArrayClipboard implements Clipboard, LightingExtent {
private Region region;
public FaweClipboard IMP;
@@ -57,6 +60,7 @@ public class BlockArrayClipboard implements Clipboard {
private int my;
private int mz;
private Vector origin;
+ private Vector mutable = new Vector();
public BlockArrayClipboard(Region region) {
checkNotNull(region);
@@ -227,4 +231,43 @@ public class BlockArrayClipboard implements Clipboard {
public static Class> inject() {
return BlockArrayClipboard.class;
}
+
+ @Override
+ public int getLight(int x, int y, int z) {
+ return getBlockLight(x, y, z);
+ }
+
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ return 0;
+ }
+
+ @Override
+ public int getBlockLight(int x, int y, int z) {
+ return getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(int x, int y, int z) {
+ mutable.x = x;
+ mutable.y = y;
+ mutable.z = z;
+ BlockMaterial block = BundledBlockData.getInstance().getMaterialById(getBlock(mutable).getId());
+ if (block == null) {
+ return 15;
+ }
+ return Math.min(15, block.getLightOpacity());
+ }
+
+ @Override
+ public int getBrightness(int x, int y, int z) {
+ mutable.x = x;
+ mutable.y = y;
+ mutable.z = z;
+ BlockMaterial block = BundledBlockData.getInstance().getMaterialById(getBlock(mutable).getId());
+ if (block == null) {
+ return 15;
+ }
+ return Math.min(15, block.getLightValue());
+ }
}
\ No newline at end of file
diff --git a/core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java b/core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java
index 8a1fff51..bfb41383 100644
--- a/core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java
+++ b/core/src/main/java/com/sk89q/worldedit/session/PasteBuilder.java
@@ -19,11 +19,14 @@
package com.sk89q.worldedit.session;
+import com.boydti.fawe.util.MaskTraverser;
+import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.transform.BlockTransformExtent;
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
+import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.math.transform.Transform;
@@ -97,6 +100,14 @@ public class PasteBuilder {
}
ForwardExtentCopy copy = new ForwardExtentCopy(extent, clipboard.getRegion(), clipboard.getOrigin(), targetExtent, to);
copy.setTransform(transform);
+ if (targetExtent instanceof EditSession) {
+ Mask sourceMask = ((EditSession) targetExtent).getSourceMask();
+ if (sourceMask != null) {
+ new MaskTraverser(sourceMask).reset(extent);
+ copy.setSourceMask(sourceMask);
+ ((EditSession) targetExtent).setSourceMask(null);
+ }
+ }
if (ignoreAirBlocks) {
copy.setSourceMask(new ExistingBlockMask(clipboard));
}