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