From 855389c7857e072c1cb2494b5a36aab70a2406a5 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Fri, 25 Sep 2020 15:00:42 +0100 Subject: [PATCH] Allow parallelisation of masks (#636) * Allow parallelisation of masks Increasing performance improvement with increased masking complexity and edit size. * Address comments - Rename Mask#clone to Mask#copy - Rename Mask2D#copy to Mask2D#copy2D - Correct formatting * cx -> centerX * Make various operations relying on a single SingleThreadQueueExtent instance (mainly brushes) thread safe --- .../implementation/blocks/CharBlocks.java | 2 +- .../implementation/filter/MaskFilter.java | 21 +++- .../processors/MultiBatchProcessor.java | 22 +++- .../queue/SingleThreadQueueExtent.java | 88 +++++++------- .../boydti/fawe/object/brush/ImageBrush.java | 75 ++---------- .../boydti/fawe/object/brush/LayerBrush.java | 47 +------- .../fawe/object/brush/SplatterBrush.java | 23 +--- .../fawe/object/brush/StencilBrush.java | 51 +-------- .../object/brush/mask/ImageBrushMask.java | 107 ++++++++++++++++++ .../object/brush/mask/LayerBrushMask.java | 66 +++++++++++ .../object/brush/mask/SplatterBrushMask.java | 49 ++++++++ .../object/brush/mask/StencilBrushMask.java | 95 ++++++++++++++++ .../function/mask/AbstractDelegateMask.java | 5 + .../fawe/object/mask/AdjacentAnyMask.java | 6 +- .../boydti/fawe/object/mask/AdjacentMask.java | 7 +- .../{function => object}/mask/AirMask.java | 8 +- .../boydti/fawe/object/mask/AngleMask.java | 7 +- .../boydti/fawe/object/mask/CachedMask.java | 5 + .../com/boydti/fawe/object/mask/DataMask.java | 6 + .../boydti/fawe/object/mask/ExtremaMask.java | 6 + .../boydti/fawe/object/mask/IdDataMask.java | 6 + .../com/boydti/fawe/object/mask/IdMask.java | 6 + .../{function => object}/mask/LiquidMask.java | 8 +- .../boydti/fawe/object/mask/PlaneMask.java | 6 + .../boydti/fawe/object/mask/ROCAngleMask.java | 6 + .../boydti/fawe/object/mask/RadiusMask.java | 11 ++ .../boydti/fawe/object/mask/RandomMask.java | 6 + .../boydti/fawe/object/mask/SimplexMask.java | 7 ++ .../boydti/fawe/object/mask/SurfaceMask.java | 8 ++ .../com/boydti/fawe/object/mask/WallMask.java | 5 + .../{function => object}/mask/XAxisMask.java | 9 +- .../{function => object}/mask/YAxisMask.java | 9 +- .../{function => object}/mask/ZAxisMask.java | 9 +- .../java/com/sk89q/worldedit/EditSession.java | 11 +- .../factory/parser/mask/LiquidMaskParser.java | 2 +- .../factory/parser/mask/XAxisMaskParser.java | 2 +- .../factory/parser/mask/YAxisMaskParser.java | 2 +- .../factory/parser/mask/ZAxisMaskParser.java | 2 +- .../sk89q/worldedit/extent/MaskingExtent.java | 40 ++++--- .../worldedit/function/mask/AbstractMask.java | 11 ++ .../worldedit/function/mask/BiomeMask.java | 5 + .../worldedit/function/mask/BiomeMask2D.java | 5 + .../function/mask/BlockCategoryMask.java | 5 + .../worldedit/function/mask/BlockMask.java | 5 + .../function/mask/BlockStateMask.java | 8 ++ .../function/mask/BlockTypeMask.java | 11 ++ .../function/mask/BoundedHeightMask.java | 6 + .../function/mask/ExistingBlockMask.java | 6 + .../function/mask/ExpressionMask.java | 5 + .../function/mask/ExpressionMask2D.java | 5 + .../worldedit/function/mask/InverseMask.java | 5 + .../mask/InverseSingleBlockStateMask.java | 11 ++ .../mask/InverseSingleBlockTypeMask.java | 6 + .../sk89q/worldedit/function/mask/Mask.java | 6 + .../sk89q/worldedit/function/mask/Mask2D.java | 2 + .../function/mask/MaskIntersection.java | 20 +++- .../function/mask/MaskIntersection2D.java | 9 +- .../worldedit/function/mask/MaskUnion.java | 13 +++ .../worldedit/function/mask/MaskUnion2D.java | 8 ++ .../sk89q/worldedit/function/mask/Masks.java | 34 ++++++ .../worldedit/function/mask/NoiseFilter.java | 5 + .../function/mask/NoiseFilter2D.java | 5 + .../worldedit/function/mask/OffsetMask.java | 5 + .../worldedit/function/mask/OffsetMask2D.java | 5 + .../worldedit/function/mask/RegionMask.java | 5 + .../function/mask/SingleBlockStateMask.java | 11 ++ .../function/mask/SingleBlockTypeMask.java | 11 ++ .../function/mask/SolidBlockMask.java | 5 + .../worldedit/function/mask/WallMakeMask.java | 26 +++++ .../internal/expression/Expression.java | 44 ++++++- 70 files changed, 893 insertions(+), 266 deletions(-) create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/ImageBrushMask.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/LayerBrushMask.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/SplatterBrushMask.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/StencilBrushMask.java rename worldedit-core/src/main/java/com/boydti/fawe/{function => object}/mask/AirMask.java (60%) rename worldedit-core/src/main/java/com/boydti/fawe/{function => object}/mask/LiquidMask.java (60%) rename worldedit-core/src/main/java/com/boydti/fawe/{function => object}/mask/XAxisMask.java (77%) rename worldedit-core/src/main/java/com/boydti/fawe/{function => object}/mask/YAxisMask.java (77%) rename worldedit-core/src/main/java/com/boydti/fawe/{function => object}/mask/ZAxisMask.java (75%) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/WallMakeMask.java diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index 81a5c32cc..15ce82298 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -17,7 +17,7 @@ public abstract class CharBlocks implements IBlocks { }; public static final Section EMPTY = new Section() { @Override - public final char[] get(CharBlocks blocks, int layer) { + public synchronized final char[] get(CharBlocks blocks, int layer) { char[] arr = blocks.blocks[layer]; if (arr == null) { arr = blocks.blocks[layer] = blocks.update(layer, null); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/MaskFilter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/MaskFilter.java index 7d821c281..eaca8a67a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/MaskFilter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/MaskFilter.java @@ -6,6 +6,7 @@ import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; /** @@ -16,7 +17,7 @@ import java.util.function.Supplier; public class MaskFilter extends DelegateFilter { private final Supplier supplier; private final Mask mask; - private int changes; + private final AtomicInteger changes; public MaskFilter(T other, Mask mask) { this(other, () -> mask); @@ -27,9 +28,14 @@ public class MaskFilter extends DelegateFilter { } public MaskFilter(T other, Supplier supplier, Mask root) { + this(other, supplier, root, new AtomicInteger()); + } + + public MaskFilter(T other, Supplier supplier, Mask root, AtomicInteger changes) { super(other); this.supplier = supplier; this.mask = root; + this.changes = changes; } @Override @@ -37,11 +43,11 @@ public class MaskFilter extends DelegateFilter { if (mask instanceof AbstractExtentMask) { if (((AbstractExtentMask) mask).test(block, block)) { getParent().applyBlock(block); - this.changes++; + this.changes.incrementAndGet(); } } else if (mask.test(block)) { getParent().applyBlock(block); - this.changes++; + this.changes.incrementAndGet(); } } @@ -51,11 +57,16 @@ public class MaskFilter extends DelegateFilter { * @return number of blocks which passed the Mask test and were applied to */ public int getBlocksApplied(){ - return this.changes; + return this.changes.get(); } @Override - public MaskFilter newInstance(Filter other) { + public MaskFilter newInstance(Filter other) { return new MaskFilter<>(other, supplier); } + + @Override + public Filter fork() { + return new MaskFilter<>(getParent().fork(), mask::copy, mask.copy(), changes); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java index f3a6a3df1..8a5ef7fa7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java @@ -1,18 +1,26 @@ package com.boydti.fawe.beta.implementation.processors; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.util.StringMan; +import com.google.common.cache.LoadingCache; import com.sk89q.worldedit.extent.Extent; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Supplier; public class MultiBatchProcessor implements IBatchProcessor { private IBatchProcessor[] processors; + private final LoadingCache, Map> classToThreadIdToFilter = + FaweCache.IMP.createCache((Supplier>) HashMap::new); public MultiBatchProcessor(IBatchProcessor... processors) { this.processors = processors; @@ -56,14 +64,20 @@ public class MultiBatchProcessor implements IBatchProcessor { @Override public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { try { - for (int i = processors.length - 1 ; i >= 0; i--) { + IChunkSet chunkSet = set; + for (int i = processors.length - 1; i >= 0; i--) { IBatchProcessor processor = processors[i]; - set = processor.processSet(chunk, get, set); - if (set == null) { + if (processor instanceof Filter) { + chunkSet = ((IBatchProcessor) classToThreadIdToFilter.getUnchecked(processor.getClass()) + .computeIfAbsent(Thread.currentThread().getId(), k -> ((Filter) processor).fork())).processSet(chunk, get, chunkSet); + } else { + chunkSet = processor.processSet(chunk, get, chunkSet); + } + if (chunkSet == null) { return null; } } - return set; + return chunkSet; } catch (Throwable e) { e.printStackTrace(); throw e; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java index 571191418..8195fd826 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java @@ -24,6 +24,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.locks.ReentrantLock; /** * Single threaded implementation for IQueueExtent (still abstract) - Does not implement creation of @@ -52,6 +53,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen private boolean fastmode = false; + private final ReentrantLock getChunkLock = new ReentrantLock(); + /** * Safety check to ensure that the thread being used matches the one being initialized on. - Can * be removed later @@ -217,50 +220,55 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen @Override public final IQueueChunk getOrCreateChunk(int x, int z) { - final long pair = (long) x << 32 | z & 0xffffffffL; - if (pair == lastPair) { - return lastChunk; - } - if (!processGet(x, z)) { - lastPair = pair; - lastChunk = NullChunk.getInstance(); - return NullChunk.getInstance(); - } - IQueueChunk chunk = chunks.get(pair); - if (chunk != null) { + getChunkLock.lock(); + try { + final long pair = (long) x << 32 | z & 0xffffffffL; + if (pair == lastPair) { + return lastChunk; + } + if (!processGet(x, z)) { + lastPair = pair; + lastChunk = NullChunk.getInstance(); + return NullChunk.getInstance(); + } + IQueueChunk chunk = chunks.get(pair); + if (chunk != null) { + lastPair = pair; + lastChunk = chunk; + } + if (chunk != null) { + return chunk; + } + final int size = chunks.size(); + final boolean lowMem = MemUtil.isMemoryLimited(); + // If queueing is enabled AND either of the following + // - memory is low & queue size > num threads + 8 + // - queue size > target size and primary queue has less than num threads submissions + if (enabledQueue && ((lowMem && size > Settings.IMP.QUEUE.PARALLEL_THREADS + 8) || (size > Settings.IMP.QUEUE.TARGET_SIZE && Fawe.get().getQueueHandler().isUnderutilized()))) { + chunk = chunks.removeFirst(); + final Future future = submitUnchecked(chunk); + if (future != null && !future.isDone()) { + final int targetSize; + if (lowMem) { + targetSize = Settings.IMP.QUEUE.PARALLEL_THREADS + 8; + } else { + targetSize = Settings.IMP.QUEUE.TARGET_SIZE; + } + pollSubmissions(targetSize, lowMem); + submissions.add(future); + } + } + chunk = poolOrCreate(x, z); + chunk = wrap(chunk); + + chunks.put(pair, chunk); lastPair = pair; lastChunk = chunk; - } - if (chunk != null) { + return chunk; + } finally { + getChunkLock.unlock(); } - final int size = chunks.size(); - final boolean lowMem = MemUtil.isMemoryLimited(); - // If queueing is enabled AND either of the following - // - memory is low & queue size > num threads + 8 - // - queue size > target size and primary queue has less than num threads submissions - if (enabledQueue && ((lowMem && size > Settings.IMP.QUEUE.PARALLEL_THREADS + 8) || (size > Settings.IMP.QUEUE.TARGET_SIZE && Fawe.get().getQueueHandler().isUnderutilized()))) { - chunk = chunks.removeFirst(); - final Future future = submitUnchecked(chunk); - if (future != null && !future.isDone()) { - final int targetSize; - if (lowMem) { - targetSize = Settings.IMP.QUEUE.PARALLEL_THREADS + 8; - } else { - targetSize = Settings.IMP.QUEUE.TARGET_SIZE; - } - pollSubmissions(targetSize, lowMem); - submissions.add(future); - } - } - chunk = poolOrCreate(x, z); - chunk = wrap(chunk); - - chunks.put(pair, chunk); - lastPair = pair; - lastChunk = chunk; - - return chunk; } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ImageBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ImageBrush.java index 298ced9d5..c710e20d4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ImageBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/ImageBrush.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.brush; +import com.boydti.fawe.object.brush.mask.ImageBrushMask; import com.boydti.fawe.object.collection.SummedColorTable; import com.boydti.fawe.object.mask.SurfaceMask; import com.boydti.fawe.util.TextureUtil; @@ -8,18 +9,14 @@ import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.MutableVector3; -import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockType; import java.awt.image.BufferedImage; import java.io.IOException; @@ -30,8 +27,8 @@ public class ImageBrush implements Brush { private final SummedColorTable table; private final int width; private final int height; - private final double centerX; - private final double centerZ; + private final double centerImageX; + private final double centerImageZ; private final ColorFunction colorFunction; @@ -40,8 +37,8 @@ public class ImageBrush implements Brush { this.table = new SummedColorTable(image, alpha); this.width = image.getWidth(); this.height = image.getHeight(); - this.centerX = width / 2d; - this.centerZ = height / 2d; + this.centerImageX = width / 2d; + this.centerImageZ = height / 2d; if (alpha) { colorFunction = (x1, z1, x2, z2, extent, pos) -> { @@ -65,17 +62,12 @@ public class ImageBrush implements Brush { } } - private interface ColorFunction { + public interface ColorFunction { int call(int x1, int z1, int x2, int z2, Extent extent, BlockVector3 pos); } @Override - public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { - TextureUtil texture = session.getTextureUtil(); - - final int cx = position.getBlockX(); - final int cy = position.getBlockY(); - final int cz = position.getBlockZ(); + public void build(EditSession editSession, BlockVector3 center, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { final Mask solid = new SurfaceMask(editSession); double scale = Math.max(width, height) / sizeDouble; @@ -85,56 +77,11 @@ public class ImageBrush implements Brush { float pitch = loc.getPitch(); AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX((pitch - 90) % 360).inverse(); - RecursiveVisitor visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) { - private final MutableVector3 mutable = new MutableVector3(); - - @Override - public boolean test(Extent extent, BlockVector3 vector) { - return test(vector); - } - - @Override - public boolean test(BlockVector3 vector) { - if (solid.test(vector)) { - int dx = vector.getBlockX() - cx; - int dy = vector.getBlockY() - cy; - int dz = vector.getBlockZ() - cz; - - Vector3 pos1 = transform.apply(mutable.setComponents(dx - 0.5, dy - 0.5, dz - 0.5)); - int x1 = (int) (pos1.getX() * scale + centerX); - int z1 = (int) (pos1.getZ() * scale + centerZ); - - Vector3 pos2 = transform.apply(mutable.setComponents(dx + 0.5, dy + 0.5, dz + 0.5)); - int x2 = (int) (pos2.getX() * scale + centerX); - int z2 = (int) (pos2.getZ() * scale + centerZ); - if (x2 < x1) { - int tmp = x1; - x1 = x2; - x2 = tmp; - } - if (z2 < z1) { - int tmp = z1; - z1 = z2; - z2 = tmp; - } - - if (x1 >= width || x2 < 0 || z1 >= height || z2 < 0) return false; - - - int color = colorFunction.call(x1, z1, x2, z2, editSession, vector); - if (color != 0) { - BlockType block = texture.getNearestBlock(color); - if (block != null) { - editSession.setBlock(vector, block.getDefaultState()); - } - } - return true; - } - return false; - } - }, vector -> true, Integer.MAX_VALUE); + RecursiveVisitor visitor = new RecursiveVisitor( + new ImageBrushMask(solid, center, transform, scale, centerImageX, centerImageZ, width, height, colorFunction, editSession, + session.getTextureUtil()), vector -> true, Integer.MAX_VALUE); visitor.setDirections(Arrays.asList(visitor.DIAGONAL_DIRECTIONS)); - visitor.visit(position); + visitor.visit(center); Operations.completeBlindly(visitor); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/LayerBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/LayerBrush.java index 9c2cc2faf..6a9859653 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/LayerBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/LayerBrush.java @@ -1,22 +1,20 @@ package com.boydti.fawe.object.brush; +import com.boydti.fawe.object.brush.mask.LayerBrushMask; import com.boydti.fawe.object.collection.BlockVectorSet; import com.boydti.fawe.object.mask.AdjacentAnyMask; import com.boydti.fawe.object.mask.RadiusMask; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.command.tool.brush.Brush; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.BlockMask; -import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.MaskIntersection; import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.MutableBlockVector3; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; @@ -26,7 +24,6 @@ public class LayerBrush implements Brush { private final BlockState[] layers; private RecursiveVisitor visitor; - private MutableBlockVector3 mutable = new MutableBlockVector3(); public LayerBrush(BlockState[] layers) { this.layers = layers; @@ -37,48 +34,12 @@ public class LayerBrush implements Brush { final AdjacentAnyMask adjacent = new AdjacentAnyMask(new BlockMask(editSession).add(BlockTypes.AIR, BlockTypes.CAVE_AIR, BlockTypes.VOID_AIR)); final SolidBlockMask solid = new SolidBlockMask(editSession); final RadiusMask radius = new RadiusMask(0, (int) size); - visitor = new RecursiveVisitor(new Mask() { - @Override - public boolean test(BlockVector3 vector) { - return solid.test(vector) && radius.test(vector) && adjacent.test(vector); - } - }, function -> true); + visitor = new RecursiveVisitor(new MaskIntersection(adjacent, solid, radius), function -> true); visitor.visit(position); visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS)); Operations.completeBlindly(visitor); BlockVectorSet visited = visitor.getVisited(); - visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) { - @Override - public boolean test(Extent extent, BlockVector3 vector) { - return test(vector); - } - - @Override - public boolean test(BlockVector3 pos) { - int depth = visitor.getDepth() + 1; - if (depth > 1) { - boolean found = false; - BlockState previous = layers[depth - 1]; - BlockState previous2 = layers[depth - 2]; - for (BlockVector3 dir : BreadthFirstSearch.DEFAULT_DIRECTIONS) { - mutable.setComponents(pos.getBlockX() + dir.getBlockX(), pos.getBlockY() + dir.getBlockY(), pos.getBlockZ() + dir.getBlockZ()); - if (visitor.isVisited(mutable) && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous) { - mutable.setComponents(pos.getBlockX() + dir.getBlockX() * 2, pos.getBlockY() + dir.getBlockY() * 2, pos.getBlockZ() + dir.getBlockZ() * 2); - if (visitor.isVisited(mutable) && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous2) { - found = true; - break; - } else { - return false; - } - } - } - if (!found) { - return false; - } - } - return !adjacent.test(pos); - } - }, pos -> { + visitor = new RecursiveVisitor(new LayerBrushMask(editSession, visitor, layers, adjacent), pos -> { int depth = visitor.getDepth(); BlockState currentPattern = layers[depth]; return currentPattern.apply(editSession, pos, pos); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java index 061235dda..3e525da6c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/SplatterBrush.java @@ -1,11 +1,10 @@ package com.boydti.fawe.object.brush; +import com.boydti.fawe.object.brush.mask.SplatterBrushMask; import com.boydti.fawe.object.collection.LocalBlockVectorSet; import com.boydti.fawe.object.mask.SurfaceMask; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; @@ -13,7 +12,6 @@ import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.math.BlockVector3; import java.util.Arrays; -import java.util.concurrent.ThreadLocalRandom; public class SplatterBrush extends ScatterBrush { private final boolean solid; @@ -36,23 +34,8 @@ public class SplatterBrush extends ScatterBrush { final int size2 = (int) (size * size); SurfaceMask surface = new SurfaceMask(editSession); - RecursiveVisitor visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) { - @Override - public boolean test(Extent extent, BlockVector3 vector) { - return test(vector); - } - - @Override - public boolean test(BlockVector3 vector) { - double dist = vector.distanceSq(position); - if (dist < size2 && !placed.contains(vector) && ThreadLocalRandom.current().nextInt(5) < 2 - && surface.test(vector)) { - placed.add(vector); - return true; - } - return false; - } - }, vector -> editSession.setBlock(vector, finalPattern), recursion); + RecursiveVisitor visitor = new RecursiveVisitor(new SplatterBrushMask(editSession, position, size2, surface, placed), + vector -> editSession.setBlock(vector, finalPattern), recursion); visitor.setMaxBranch(2); visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS)); visitor.visit(position); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java index 01e59173a..8d87076db 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/StencilBrush.java @@ -1,26 +1,21 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.object.brush.heightmap.HeightMap; -import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.object.brush.mask.StencilBrushMask; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.MutableVector3; -import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.util.Location; import java.io.InputStream; import java.util.Arrays; -import java.util.concurrent.ThreadLocalRandom; public class StencilBrush extends HeightBrush { private final boolean onlyWhite; @@ -31,10 +26,7 @@ public class StencilBrush extends HeightBrush { } @Override - public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { - final int cx = position.getBlockX(); - final int cy = position.getBlockY(); - final int cz = position.getBlockZ(); + public void build(EditSession editSession, BlockVector3 center, Pattern pattern, double sizeDouble) throws MaxChangedBlocksException { int size = (int) sizeDouble; int size2 = (int) (sizeDouble * sizeDouble); int maxY = editSession.getMaxY(); @@ -55,42 +47,11 @@ public class StencilBrush extends HeightBrush { AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX(pitch - 90).inverse(); double scale = (yscale / sizeDouble) * (maxY + 1); - RecursiveVisitor visitor = new RecursiveVisitor(new AbstractExtentMask(editSession) { - @Override - public boolean test(Extent extent, BlockVector3 vector) { - return test(vector); - } - - private final MutableVector3 mutable = new MutableVector3(); - @Override - public boolean test(BlockVector3 vector) { - if (solid.test(vector)) { - int dx = vector.getBlockX() - cx; - int dy = vector.getBlockY() - cy; - int dz = vector.getBlockZ() - cz; - - Vector3 srcPos = transform.apply(mutable.setComponents(dx, dy, dz)); - dx = MathMan.roundInt(srcPos.getX()); - dz = MathMan.roundInt(srcPos.getZ()); - - int distance = dx * dx + dz * dz; - if (distance > size2 || Math.abs(dx) > 256 || Math.abs(dz) > 256) return false; - - double raise = map.getHeight(dx, dz); - int val = (int) Math.ceil(raise * scale) + add; - if (val < cutoff) { - return true; - } - if (val >= 255 || ThreadLocalRandom.current().nextInt(maxY) < val) { - editSession.setBlock(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), pattern); - } - return true; - } - return false; - } - }, vector -> true, Integer.MAX_VALUE); + RecursiveVisitor visitor = + new RecursiveVisitor(new StencilBrushMask(editSession, solid, center, transform, size2, map, scale, add, cutoff, maxY, pattern), + vector -> true, Integer.MAX_VALUE); visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS)); - visitor.visit(position); + visitor.visit(center); Operations.completeBlindly(visitor); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/ImageBrushMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/ImageBrushMask.java new file mode 100644 index 000000000..0de5d8f18 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/ImageBrushMask.java @@ -0,0 +1,107 @@ +package com.boydti.fawe.object.brush.mask; + +import com.boydti.fawe.object.brush.ImageBrush; +import com.boydti.fawe.util.TextureUtil; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.MutableVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.world.block.BlockType; + +public class ImageBrushMask extends AbstractExtentMask { + + private final MutableVector3 mutable = new MutableVector3(); + private final Mask solid; + private final BlockVector3 center; + private final Transform transform; + private final double scale; + private final double centerImageX; + private final double centerImageZ; + private final int width; + private final int height; + private final ImageBrush.ColorFunction colorFunction; + private final EditSession session; + private final TextureUtil texture; + + public ImageBrushMask(Mask solid, + BlockVector3 center, + Transform transform, + double scale, + double centerImageX, + double centerImageZ, + int width, + int height, + ImageBrush.ColorFunction colorFunction, + EditSession session, + TextureUtil texture) { + super(session); + this.solid = solid; + this.center = center; + this.transform = transform; + this.scale = scale; + this.centerImageX = centerImageX; + this.centerImageZ = centerImageZ; + this.width = width; + this.height = height; + this.colorFunction = colorFunction; + this.session = session; + this.texture = texture; + } + + @Override + public boolean test(Extent extent, BlockVector3 vector) { + return test(vector); + } + + @Override + public boolean test(BlockVector3 vector) { + if (solid.test(vector)) { + int dx = vector.getBlockX() - center.getBlockX(); + int dy = vector.getBlockY() - center.getBlockY(); + int dz = vector.getBlockZ() - center.getBlockZ(); + + Vector3 pos1 = transform.apply(mutable.setComponents(dx - 0.5, dy - 0.5, dz - 0.5)); + int x1 = (int) (pos1.getX() * scale + centerImageX); + int z1 = (int) (pos1.getZ() * scale + centerImageZ); + + Vector3 pos2 = transform.apply(mutable.setComponents(dx + 0.5, dy + 0.5, dz + 0.5)); + int x2 = (int) (pos2.getX() * scale + centerImageX); + int z2 = (int) (pos2.getZ() * scale + centerImageZ); + if (x2 < x1) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + if (z2 < z1) { + int tmp = z1; + z1 = z2; + z2 = tmp; + } + + if (x1 >= width || x2 < 0 || z1 >= height || z2 < 0) { + return false; + } + + + int color = colorFunction.call(x1, z1, x2, z2, session, vector); + if (color != 0) { + BlockType block = texture.getNearestBlock(color); + if (block != null) { + session.setBlock(vector, block.getDefaultState()); + } + } + return true; + } + return false; + } + + @Override + public Mask copy() { + return new ImageBrushMask(solid.copy(), center.toImmutable(), transform, scale, centerImageX, centerImageZ, width, height, colorFunction, + session, texture); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/LayerBrushMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/LayerBrushMask.java new file mode 100644 index 000000000..529549af2 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/LayerBrushMask.java @@ -0,0 +1,66 @@ +package com.boydti.fawe.object.brush.mask; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.visitor.BreadthFirstSearch; +import com.sk89q.worldedit.function.visitor.RecursiveVisitor; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.MutableBlockVector3; +import com.sk89q.worldedit.world.block.BlockState; + +public class LayerBrushMask extends AbstractExtentMask { + + private final EditSession editSession; + private final RecursiveVisitor visitor; + private final BlockState[] layers; + private final MutableBlockVector3 mutable = new MutableBlockVector3(); + private final Mask adjacent; + + public LayerBrushMask(EditSession editSession, RecursiveVisitor visitor, BlockState[] layers, Mask adjacent) { + super(editSession); + this.editSession = editSession; + this.visitor = visitor; + this.layers = layers; + this.adjacent = adjacent; + } + + @Override + public boolean test(Extent extent, BlockVector3 vector) { + return test(vector); + } + + @Override + public boolean test(BlockVector3 pos) { + int depth = visitor.getDepth() + 1; + if (depth > 1) { + boolean found = false; + BlockState previous = layers[depth - 1]; + BlockState previous2 = layers[depth - 2]; + for (BlockVector3 dir : BreadthFirstSearch.DEFAULT_DIRECTIONS) { + mutable.setComponents(pos.getBlockX() + dir.getBlockX(), pos.getBlockY() + dir.getBlockY(), pos.getBlockZ() + dir.getBlockZ()); + if (visitor.isVisited(mutable) && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous) { + mutable.setComponents(pos.getBlockX() + dir.getBlockX() * 2, pos.getBlockY() + dir.getBlockY() * 2, + pos.getBlockZ() + dir.getBlockZ() * 2); + if (visitor.isVisited(mutable) + && editSession.getBlock(mutable.getBlockX(), mutable.getBlockY(), mutable.getBlockZ()) == previous2) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return !adjacent.test(pos); + } + + @Override + public Mask copy() { + return new LayerBrushMask(editSession, visitor, layers.clone(), adjacent.copy()); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/SplatterBrushMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/SplatterBrushMask.java new file mode 100644 index 000000000..94fcc0475 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/SplatterBrushMask.java @@ -0,0 +1,49 @@ +package com.boydti.fawe.object.brush.mask; + +import com.boydti.fawe.object.collection.LocalBlockVectorSet; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.math.BlockVector3; + +import java.util.concurrent.ThreadLocalRandom; + +public class SplatterBrushMask extends AbstractExtentMask { + + private final BlockVector3 position; + private final int size2; + private final Mask surface; + private final LocalBlockVectorSet placed; + + public SplatterBrushMask(EditSession editSession, BlockVector3 position, int size2, Mask surface, LocalBlockVectorSet placed) { + super(editSession); + this.position = position; + this.size2 = size2; + this.surface = surface; + this.placed = placed; + } + + @Override + public boolean test(Extent extent, BlockVector3 vector) { + return test(vector); + } + + @Override + public boolean test(BlockVector3 vector) { + double dist = vector.distanceSq(position); + synchronized (placed) { + if (dist < size2 && !placed.contains(vector) && ThreadLocalRandom.current().nextInt(5) < 2 && surface.test(vector)) { + placed.add(vector); + return true; + } + } + return false; + } + + @Override + public Mask copy() { + // There should not be multiple instances to be thread safe + return this; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/StencilBrushMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/StencilBrushMask.java new file mode 100644 index 000000000..607c4fe5c --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/mask/StencilBrushMask.java @@ -0,0 +1,95 @@ +package com.boydti.fawe.object.brush.mask; + +import com.boydti.fawe.object.brush.heightmap.HeightMap; +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.MutableVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.math.transform.Transform; + +import java.util.concurrent.ThreadLocalRandom; + +public class StencilBrushMask extends AbstractExtentMask { + + private final MutableVector3 mutable = new MutableVector3(); + private final EditSession editSession; + private final Mask solid; + private final BlockVector3 center; + private final Transform transform; + private final int size2; + private final HeightMap map; + private final double scale; + private final int add; + private final int cutoff; + private final int maxY; + private final Pattern pattern; + + public StencilBrushMask(EditSession editSession, + Mask solid, + BlockVector3 center, + Transform transform, + int size2, + HeightMap map, + double scale, + int add, + int cutoff, + int maxY, + Pattern pattern) { + super(editSession); + this.editSession = editSession; + this.solid = solid; + this.center = center; + this.transform = transform; + this.size2 = size2; + this.map = map; + this.scale = scale; + this.add = add; + this.cutoff = cutoff; + this.maxY = maxY; + this.pattern = pattern; + } + + @Override + public boolean test(Extent extent, BlockVector3 vector) { + return test(vector); + } + + @Override + public boolean test(BlockVector3 vector) { + if (solid.test(vector)) { + int dx = vector.getBlockX() - center.getBlockX(); + int dy = vector.getBlockY() - center.getBlockY(); + int dz = vector.getBlockZ() - center.getBlockZ(); + + Vector3 srcPos = transform.apply(mutable.setComponents(dx, dy, dz)); + dx = MathMan.roundInt(srcPos.getX()); + dz = MathMan.roundInt(srcPos.getZ()); + + int distance = dx * dx + dz * dz; + if (distance > size2 || Math.abs(dx) > 256 || Math.abs(dz) > 256) { + return false; + } + + double raise = map.getHeight(dx, dz); + int val = (int) Math.ceil(raise * scale) + add; + if (val < cutoff) { + return true; + } + if (val >= 255 || ThreadLocalRandom.current().nextInt(maxY) < val) { + editSession.setBlock(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), pattern); + } + return true; + } + return false; + } + + @Override + public Mask copy() { + return new StencilBrushMask(editSession, solid.copy(), center.toImmutable(), transform, size2, map, scale, add, cutoff, maxY, pattern); + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java index d18db85ba..4ece175d0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/function/mask/AbstractDelegateMask.java @@ -30,4 +30,9 @@ public class AbstractDelegateMask extends AbstractMask { return mask.toMask2D(); } + @Override + public Mask copy() { + return new AbstractDelegateMask(mask.copy()); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java index 5ee452445..db7f79297 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentAnyMask.java @@ -1,6 +1,5 @@ package com.boydti.fawe.object.mask; -import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractMask; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; @@ -53,4 +52,9 @@ public class AdjacentAnyMask extends AbstractMask implements ResettableMask { return null; } } + + @Override + public Mask copy() { + return new AdjacentAnyMask(mask.copy()); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentMask.java index 499cb7893..d1184ba98 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AdjacentMask.java @@ -9,7 +9,7 @@ public class AdjacentMask extends AbstractMask { private final int min; private final int max; private final Mask mask; - private MutableBlockVector3 vector; + private final MutableBlockVector3 vector; public AdjacentMask(Mask mask, int requiredMin, int requiredMax) { this.mask = mask; @@ -60,4 +60,9 @@ public class AdjacentMask extends AbstractMask { vector.mutZ(z); return count >= min && count <= max; } + + @Override + public Mask copy() { + return new AdjacentMask(mask.copy(), min, max); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/AirMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AirMask.java similarity index 60% rename from worldedit-core/src/main/java/com/boydti/fawe/function/mask/AirMask.java rename to worldedit-core/src/main/java/com/boydti/fawe/object/mask/AirMask.java index 48d7f3e17..950f1d9df 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/AirMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AirMask.java @@ -1,7 +1,8 @@ -package com.boydti.fawe.function.mask; +package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.Mask; public class AirMask extends BlockMask { @@ -10,4 +11,9 @@ public class AirMask extends BlockMask { add(state -> state.getMaterial().isAir()); } + @Override + public Mask copy() { + return new AirMask(getExtent()); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java index 123afcd2d..96cb95286 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/AngleMask.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.SolidBlockMask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.MutableBlockVector3; @@ -44,7 +45,6 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { } } - protected transient int cacheCenX; protected transient int cacheCenZ; protected transient int cacheBotX = Integer.MIN_VALUE; protected transient int cacheBotZ = Integer.MIN_VALUE; @@ -173,4 +173,9 @@ public class AngleMask extends SolidBlockMask implements ResettableMask { return testSlope(getExtent(), x, y, z); } + @Override + public Mask copy() { + return new AngleMask(getExtent(), min, max, overlay, distance); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java index ec0b0d4ef..1734fb53b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/CachedMask.java @@ -65,4 +65,9 @@ public class CachedMask extends AbstractDelegateMask implements ResettableMask { return result; } } + + @Override + public Mask copy() { + return new CachedMask(getMask().copy()); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/DataMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/DataMask.java index 41d5d5aef..f295da9ff 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/DataMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/DataMask.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class DataMask extends AbstractExtentMask implements ResettableMask { @@ -37,4 +38,9 @@ public class DataMask extends AbstractExtentMask implements ResettableMask { this.data = -1; } + @Override + public Mask copy() { + return new DataMask(getExtent()); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ExtremaMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ExtremaMask.java index bd544326a..674d5eaa5 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ExtremaMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ExtremaMask.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; public class ExtremaMask extends AngleMask { public ExtremaMask(Extent extent, double min, double max, boolean overlay, int distance) { @@ -62,4 +63,9 @@ public class ExtremaMask extends AngleMask { } return (lastHeight1 - base) + (lastHeight2 - base); } + + @Override + public Mask copy() { + return new ExtremaMask(getExtent(), min, max, overlay, distance); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java index dcdd4987d..0329f0b5c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdDataMask.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class IdDataMask extends AbstractExtentMask implements ResettableMask { @@ -31,4 +32,9 @@ public class IdDataMask extends AbstractExtentMask implements ResettableMask { this.combined = -1; } + @Override + public Mask copy() { + return new IdDataMask(getExtent()); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdMask.java index b73fbedee..6528d5407 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/IdMask.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class IdMask extends AbstractExtentMask implements ResettableMask { @@ -32,4 +33,9 @@ public class IdMask extends AbstractExtentMask implements ResettableMask { this.id = -1; } + @Override + public Mask copy() { + return new IdMask(getExtent()); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/LiquidMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/LiquidMask.java similarity index 60% rename from worldedit-core/src/main/java/com/boydti/fawe/function/mask/LiquidMask.java rename to worldedit-core/src/main/java/com/boydti/fawe/object/mask/LiquidMask.java index cde52cc5e..cdbee5401 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/LiquidMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/LiquidMask.java @@ -1,7 +1,8 @@ -package com.boydti.fawe.function.mask; +package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.BlockMask; +import com.sk89q.worldedit.function.mask.Mask; public class LiquidMask extends BlockMask { @@ -10,4 +11,9 @@ public class LiquidMask extends BlockMask { add(state -> state.getMaterial().isLiquid()); } + @Override + public Mask copy() { + return new LiquidMask(getExtent()); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/PlaneMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/PlaneMask.java index 9628e20ce..385c16e30 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/PlaneMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/PlaneMask.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class PlaneMask extends AbstractMask implements ResettableMask { @@ -55,4 +56,9 @@ public class PlaneMask extends AbstractMask implements ResettableMask { mode = -1; } + @Override + public Mask copy() { + return new PlaneMask(); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ROCAngleMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ROCAngleMask.java index a7da74224..bd9771fab 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ROCAngleMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ROCAngleMask.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; public class ROCAngleMask extends AngleMask { @@ -31,4 +32,9 @@ public class ROCAngleMask extends AngleMask { return lastValue = slope >= min && slope <= max; } + + @Override + public Mask copy() { + return new ROCAngleMask(getExtent(), min, max, overlay, distance); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java index fa3f94675..5ba49fe35 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RadiusMask.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class RadiusMask extends AbstractMask implements ResettableMask { @@ -13,6 +14,11 @@ public class RadiusMask extends AbstractMask implements ResettableMask { this.maxSqr = max * max; } + private RadiusMask(Integer minSqr, Integer maxSqr) { + this.minSqr = minSqr; + this.maxSqr = maxSqr; + } + @Override public void reset() { pos = null; @@ -41,4 +47,9 @@ public class RadiusMask extends AbstractMask implements ResettableMask { return true; } + @Override + public Mask copy() { + return new RadiusMask(minSqr, maxSqr); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RandomMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RandomMask.java index cff3dd6f7..c42415e17 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RandomMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/RandomMask.java @@ -1,6 +1,7 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import java.util.SplittableRandom; @@ -23,4 +24,9 @@ public class RandomMask extends AbstractMask implements ResettableMask { public void reset() { random = new SplittableRandom(); } + + @Override + public Mask copy() { + return new RandomMask(threshold); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SimplexMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SimplexMask.java index 9358552ca..554a7dbd7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SimplexMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SimplexMask.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.mask; import com.boydti.fawe.object.random.SimplexNoise; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class SimplexMask extends AbstractMask { @@ -18,4 +19,10 @@ public class SimplexMask extends AbstractMask { double value = SimplexNoise.noise(vector.getBlockX() * scale, vector.getBlockY() * scale, vector.getBlockZ() * scale); return value >= min && value <= max; } + + @Override + public Mask copy() { + // The mask is not mutable. There is no need to clone it. + return this; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java index 3cc468858..ea0497dff 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/SurfaceMask.java @@ -3,10 +3,12 @@ package com.boydti.fawe.object.mask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractExtentMask; import com.sk89q.worldedit.function.mask.BlockMaskBuilder; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockTypes; public class SurfaceMask extends AdjacentAnyMask { + public SurfaceMask(Extent extent) { super(getMask(extent)); } @@ -22,4 +24,10 @@ public class SurfaceMask extends AdjacentAnyMask { public boolean test(BlockVector3 v) { return !getParentMask().test(v.getBlockX(), v.getBlockY(), v.getBlockZ()) && super.test(v); } + + @Override + public Mask copy() { + // The mask is not mutable. There is no need to clone it. + return this; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/WallMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/WallMask.java index 83b2d110d..a431043a7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/mask/WallMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/WallMask.java @@ -48,4 +48,9 @@ public class WallMask extends AbstractMask { v.mutZ(z); return count >= min && count <= max; } + + @Override + public Mask copy() { + return new WallMask(mask.copy(), min, max); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/XAxisMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/XAxisMask.java similarity index 77% rename from worldedit-core/src/main/java/com/boydti/fawe/function/mask/XAxisMask.java rename to worldedit-core/src/main/java/com/boydti/fawe/object/mask/XAxisMask.java index 92bf88390..9446f8fe4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/XAxisMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/XAxisMask.java @@ -1,8 +1,8 @@ -package com.boydti.fawe.function.mask; +package com.boydti.fawe.object.mask; -import com.boydti.fawe.object.mask.ResettableMask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class XAxisMask extends AbstractMask implements ResettableMask { @@ -26,4 +26,9 @@ public class XAxisMask extends AbstractMask implements ResettableMask { this.layer = -1; } + @Override + public Mask copy() { + return new XAxisMask(null); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/YAxisMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/YAxisMask.java similarity index 77% rename from worldedit-core/src/main/java/com/boydti/fawe/function/mask/YAxisMask.java rename to worldedit-core/src/main/java/com/boydti/fawe/object/mask/YAxisMask.java index 2a55b8de0..caf8a68f3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/YAxisMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/YAxisMask.java @@ -1,8 +1,8 @@ -package com.boydti.fawe.function.mask; +package com.boydti.fawe.object.mask; -import com.boydti.fawe.object.mask.ResettableMask; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class YAxisMask extends AbstractMask implements ResettableMask { @@ -25,4 +25,9 @@ public class YAxisMask extends AbstractMask implements ResettableMask { this.layer = -1; } + @Override + public Mask copy() { + return new YAxisMask(null); + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/ZAxisMask.java b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ZAxisMask.java similarity index 75% rename from worldedit-core/src/main/java/com/boydti/fawe/function/mask/ZAxisMask.java rename to worldedit-core/src/main/java/com/boydti/fawe/object/mask/ZAxisMask.java index dd910546c..43bd2243e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/function/mask/ZAxisMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/mask/ZAxisMask.java @@ -1,7 +1,7 @@ -package com.boydti.fawe.function.mask; +package com.boydti.fawe.object.mask; -import com.boydti.fawe.object.mask.ResettableMask; import com.sk89q.worldedit.function.mask.AbstractMask; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; public class ZAxisMask extends AbstractMask implements ResettableMask { @@ -24,4 +24,9 @@ public class ZAxisMask extends AbstractMask implements ResettableMask { this.layer = -1; } + @Override + public Mask copy() { + return new ZAxisMask(); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 9f61672a0..66042e6d6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -71,6 +71,7 @@ import com.sk89q.worldedit.function.mask.NoiseFilter2D; import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.mask.SingleBlockTypeMask; import com.sk89q.worldedit.function.mask.SolidBlockMask; +import com.sk89q.worldedit.function.mask.WallMakeMask; import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operations; @@ -1406,15 +1407,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { if (region instanceof CuboidRegion) { return makeCuboidWalls(region, pattern); } else { - replaceBlocks(region, new Mask() { - @Override - public boolean test(BlockVector3 position) { - int x = position.getBlockX(); - int z = position.getBlockZ(); - return !region.contains(x, z + 1) || !region.contains(x, z - 1) || !region - .contains(x + 1, z) || !region.contains(x - 1, z); - } - }, pattern); + replaceBlocks(region, new WallMakeMask(region), pattern); } return changes; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LiquidMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LiquidMaskParser.java index 5ad703df2..ef61430f5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LiquidMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LiquidMaskParser.java @@ -1,6 +1,6 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.boydti.fawe.function.mask.LiquidMask; +import com.boydti.fawe.object.mask.LiquidMask; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/XAxisMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/XAxisMaskParser.java index 3fb69b1aa..91f873e36 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/XAxisMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/XAxisMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.boydti.fawe.function.mask.XAxisMask; +import com.boydti.fawe.object.mask.XAxisMask; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/YAxisMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/YAxisMaskParser.java index e7ff315ba..916e7fe4c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/YAxisMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/YAxisMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.boydti.fawe.function.mask.YAxisMask; +import com.boydti.fawe.object.mask.YAxisMask; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ZAxisMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ZAxisMaskParser.java index 8d37d0c47..072857e7e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ZAxisMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ZAxisMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.boydti.fawe.function.mask.ZAxisMask; +import com.boydti.fawe.object.mask.ZAxisMask; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 294bae88e..86f9bcd1c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -44,7 +44,7 @@ import static com.google.common.base.Preconditions.checkNotNull; public class MaskingExtent extends AbstractDelegateExtent implements IBatchProcessor, Filter { private Mask mask; - private LoadingCache threadIdToFilter = FaweCache.IMP.createCache(() -> new CharFilterBlock(getExtent())); + private final LoadingCache threadIdToFilter; /** * Create a new instance. @@ -56,6 +56,14 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce super(extent); checkNotNull(mask); this.mask = mask; + this.threadIdToFilter = FaweCache.IMP.createCache(() -> new CharFilterBlock(getExtent())); + } + + private MaskingExtent(Extent extent, Mask mask, LoadingCache threadIdToFilter) { + super(extent); + checkNotNull(mask); + this.mask = mask; + this.threadIdToFilter = threadIdToFilter; } /** @@ -64,7 +72,7 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce * @return the mask */ public Mask getMask() { - return mask; + return this.mask; } /** @@ -79,38 +87,40 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { - return mask.test(location) && super.setBlock(location, block); + return this.mask.test(location) && super.setBlock(location, block); } @Override public boolean setBiome(BlockVector2 position, BiomeType biome) { - return mask.test(position.toBlockVector3()) && super.setBiome(position, biome); + return this.mask.test(position.toBlockVector3()) && super.setBiome(position, biome); } @Override public boolean setBiome(int x, int y, int z, BiomeType biome) { - return mask.test(BlockVector3.at(x, y, z)) && super.setBiome(x, y, z, biome); + return this.mask.test(BlockVector3.at(x, y, z)) && super.setBiome(x, y, z, biome); } @Override - public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { - ChunkFilterBlock filter = threadIdToFilter.getUnchecked(Thread.currentThread().getId()); - return filter.filter(chunk, get, set, this); + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + final ChunkFilterBlock filter = threadIdToFilter.getUnchecked(Thread.currentThread().getId()); + return filter.filter(chunk, get, set, MaskingExtent.this); } @Override - public void applyBlock(FilterBlock block) { - //TODO: Find a way to make masking thread safe without having to synchonise the whole extent - synchronized (this) { - if (!mask.test(block)) { - block.setOrdinal(0); - } + public void applyBlock(final FilterBlock block) { + if (!this.mask.test(block)) { + block.setOrdinal(0); } } @Override public Extent construct(Extent child) { if (child == getExtent()) return this; - return new MaskingExtent(child, mask); + return new MaskingExtent(child, this.mask.copy(), this.threadIdToFilter); + } + + @Override + public Filter fork() { + return new MaskingExtent(getExtent(), this.mask.copy(), this.threadIdToFilter); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/AbstractMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/AbstractMask.java index c14ce3862..45478ff06 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/AbstractMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/AbstractMask.java @@ -19,9 +19,20 @@ package com.sk89q.worldedit.function.mask; +import org.jetbrains.annotations.Nullable; + /** * A base class of {@link Mask} that all masks should inherit from. */ public abstract class AbstractMask implements Mask { + @Override + @Nullable + public Mask copy() { + try { + return (Mask) super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java index 0418573b1..8189d583e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask.java @@ -102,4 +102,9 @@ public class BiomeMask extends AbstractMask { return null; } + @Override + public Mask copy() { + return new BiomeMask(extent, new HashSet<>(biomes)); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java index 5df46facb..5b26617a5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BiomeMask2D.java @@ -98,4 +98,9 @@ public class BiomeMask2D extends AbstractMask2D { return biomes.contains(biome); } + @Override + public Mask2D copy2D() { + return new BiomeMask2D(extent, new HashSet<>(biomes)); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java index 7b64f19d6..e600929b3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockCategoryMask.java @@ -56,4 +56,9 @@ public class BlockCategoryMask extends AbstractExtentMask { public Mask2D toMask2D() { return null; } + + @Override + public Mask copy() { + return new BlockCategoryMask(getExtent(), category); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index b46ed64f7..28b597259 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -328,4 +328,9 @@ public class BlockMask extends ABlockMask { } return new BlockMask(getExtent(), cloned); } + + @Override + public Mask copy() { + return new BlockMask(getExtent(), ordinals.clone()); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java index 1c52df34a..dd0b774fc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; @@ -82,4 +83,11 @@ public class BlockStateMask extends AbstractExtentMask { public Mask2D toMask2D() { return null; } + + @Override + public Mask copy() { + Map statesClone = new HashMap<>(); + states.forEach(statesClone::put); + return new BlockStateMask(getExtent(), statesClone, strict); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java index 8776037df..219591c79 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockTypeMask.java @@ -70,6 +70,12 @@ public class BlockTypeMask extends AbstractExtentMask { } } + private BlockTypeMask(Extent extent, boolean[] types, boolean hasAir) { + super(extent); + this.types = types; + this.hasAir = hasAir; + } + /** * Add the given blocks to the list of criteria. * @@ -134,4 +140,9 @@ public class BlockTypeMask extends AbstractExtentMask { return null; } + @Override + public Mask copy() { + return new BlockTypeMask(getExtent(), types.clone(), hasAir); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java index 10e969c79..ccbf44fc7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java @@ -57,4 +57,10 @@ public class BoundedHeightMask extends AbstractMask { return null; } + @Override + public Mask copy() { + // The mask is not mutable. There is no need to clone it. + return this; + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java index 1504d46d5..b37527e53 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExistingBlockMask.java @@ -55,4 +55,10 @@ public class ExistingBlockMask extends AbstractExtentMask { return null; } + @Override + public Mask copy() { + // The mask is not mutable. There is no need to clone it. + return this; + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java index cfffe2807..0df7ca6c9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask.java @@ -89,4 +89,9 @@ public class ExpressionMask extends AbstractMask { return new ExpressionMask2D(expression, timeout); } + @Override + public Mask copy() { + return new ExpressionMask(expression.clone(), timeout); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java index 9c09a7430..98a5aa2d3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/ExpressionMask2D.java @@ -72,4 +72,9 @@ public class ExpressionMask2D extends AbstractMask2D { } } + @Override + public Mask2D copy2D() { + return new ExpressionMask2D(expression.clone(), timeout); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java index ec8c037d5..ed86f6bc0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseMask.java @@ -31,4 +31,9 @@ public class InverseMask extends AbstractMask { public Mask inverse() { return mask; } + + @Override + public Mask copy() { + return new InverseMask(mask.copy()); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java index f27f9816e..e94f1dd11 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockStateMask.java @@ -18,6 +18,12 @@ public class InverseSingleBlockStateMask extends ABlockMask { this.ordinal = state.getOrdinalChar(); } + private InverseSingleBlockStateMask(Extent extent, char ordinal, boolean isAir) { + super(extent); + this.ordinal = ordinal; + this.isAir = isAir; + } + @Override public boolean test(BlockVector3 vector) { int test = getExtent().getBlock(vector).getOrdinal(); @@ -40,4 +46,9 @@ public class InverseSingleBlockStateMask extends ABlockMask { public Mask inverse() { return new SingleBlockStateMask(getExtent(), BlockState.getFromOrdinal(ordinal)); } + + @Override + public Mask copy() { + return new InverseSingleBlockStateMask(getExtent(), ordinal, isAir); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java index 2fc3c8d52..fa6da2278 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/InverseSingleBlockTypeMask.java @@ -27,4 +27,10 @@ public class InverseSingleBlockTypeMask extends ABlockMask { public BlockType getBlockType() { return BlockTypes.get(internalId); } + + @Override + public Mask copy() { + // The mask is not mutable. There is no need to clone it. + return this; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java index 3de7466c8..5c8f66b81 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask.java @@ -100,4 +100,10 @@ public interface Mask { default boolean replacesAir() { return false; } + + /** + * Returns a copy of the mask. Usually for multi-threaded operation + * @return a clone of the mask + */ + Mask copy(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask2D.java index efc69018a..4a493557d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Mask2D.java @@ -34,4 +34,6 @@ public interface Mask2D { */ boolean test(BlockVector2 vector); + Mask2D copy2D(); + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java index 2c87cdf25..ccad2158b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; @@ -45,9 +46,9 @@ import static org.slf4j.LoggerFactory.getLogger; */ public class MaskIntersection extends AbstractMask { - private final Set masks; - private Mask[] masksArray; - private boolean defaultReturn; + protected final Set masks; + protected Mask[] masksArray; + protected boolean defaultReturn; /** * Create a new intersection. @@ -60,6 +61,12 @@ public class MaskIntersection extends AbstractMask { formArray(); } + protected MaskIntersection(Set masks, Mask[] masksArray, boolean defaultReturn) { + this.masks = masks; + this.masksArray = masksArray; + this.defaultReturn = defaultReturn; + } + public static Mask of(Mask... masks) { Set set = new LinkedHashSet<>(); for (Mask mask : masks) { @@ -250,4 +257,11 @@ public class MaskIntersection extends AbstractMask { return new MaskIntersection2D(mask2dList); } + @Override + public Mask copy(){ + Set masks = this.masks.stream().map(Mask::copy).collect(Collectors.toSet()); + Mask[] maskArray = (Mask[]) Arrays.stream(this.masksArray).map(Mask::copy).toArray(); + return new MaskIntersection(masks, maskArray, this.defaultReturn); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java index 12ab2b0b8..64dc3e26f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection2D.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -33,7 +34,7 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class MaskIntersection2D implements Mask2D { - private final Set masks = new HashSet<>(); + protected final Set masks = new HashSet<>(); /** * Create a new intersection. @@ -97,4 +98,10 @@ public class MaskIntersection2D implements Mask2D { return true; } + @Override + public Mask2D copy2D() { + Set masksCopy = this.masks.stream().map(Mask2D::copy2D).collect(Collectors.toSet()); + return new MaskIntersection2D(masksCopy); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java index a58ace750..356d3e748 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion.java @@ -22,12 +22,14 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.math.BlockVector3; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nullable; /** @@ -55,6 +57,10 @@ public class MaskUnion extends MaskIntersection { super(mask); } + private MaskUnion(Set masks, Mask[] maskArray, boolean defaultReturn) { + super(masks, maskArray, defaultReturn); + } + public static Mask of(Mask... masks) { Set set = new LinkedHashSet<>(); for (Mask mask : masks) { @@ -111,4 +117,11 @@ public class MaskUnion extends MaskIntersection { } return new MaskUnion2D(mask2dList); } + + @Override + public Mask copy() { + Set masksCopy = masks.stream().map(Mask::copy).collect(Collectors.toSet()); + Mask[] maskArray = (Mask[]) Arrays.stream(masksArray).map(Mask::copy).toArray(); + return new MaskUnion(masksCopy, maskArray, defaultReturn); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java index daf1a6e85..28073e2d4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskUnion2D.java @@ -22,6 +22,8 @@ package com.sk89q.worldedit.function.mask; import com.sk89q.worldedit.math.BlockVector2; import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; /** * Tests true if any contained mask is true, even if it just one. @@ -59,4 +61,10 @@ public class MaskUnion2D extends MaskIntersection2D { return false; } + @Override + public Mask2D copy2D() { + Set masksCopy = masks.stream().map(Mask2D::copy2D).collect(Collectors.toSet()); + return new MaskUnion2D(masksCopy); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java index b3333d268..9a0923983 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/Masks.java @@ -92,6 +92,11 @@ public final class Masks { public boolean test(BlockVector2 vector) { return !mask.test(vector); } + + @Override + public Mask2D copy2D() { + return Masks.negate(mask.copy2D()); + } }; } @@ -113,6 +118,11 @@ public final class Masks { public Mask2D toMask2D() { return mask; } + + @Override + public Mask copy() { + return Masks.asMask(mask.copy2D()); + } }; } @@ -143,6 +153,18 @@ public final class Masks { return this; } + // No need to properly clone an always true mask + @Override + public Mask copy() { + return new AlwaysTrue(); + } + + // No need to properly clone an always true mask + @Override + public Mask2D copy2D() { + return new AlwaysTrue(); + } + } protected static class AlwaysFalse implements Mask, Mask2D { @@ -172,6 +194,18 @@ public final class Masks { return other; } + // No need to properly clone an always false mask + @Override + public Mask copy() { + return new AlwaysFalse(); + } + + // No need to properly clone an always true mask + @Override + public Mask2D copy2D() { + return new AlwaysFalse(); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java index 589b8bdf9..39c29107d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter.java @@ -96,4 +96,9 @@ public class NoiseFilter extends AbstractMask { return new NoiseFilter2D(getNoiseGenerator(), getDensity()); } + @Override + public Mask copy() { + return new NoiseFilter(noiseGenerator, density); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter2D.java index ac59d3ef1..f192e1eb0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/NoiseFilter2D.java @@ -87,4 +87,9 @@ public class NoiseFilter2D extends AbstractMask2D { return noiseGenerator.noise(pos.toVector2()) <= density; } + @Override + public Mask2D copy2D() { + return new NoiseFilter2D(noiseGenerator, density); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java index e1cc492f1..99865fc2f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java @@ -105,4 +105,9 @@ public class OffsetMask extends AbstractMask { } } + @Override + public Mask copy() { + return new OffsetMask(mask.copy(), offset.toImmutable()); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java index fa1bd7a08..e3d5786cd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask2D.java @@ -92,4 +92,9 @@ public class OffsetMask2D extends AbstractMask2D { return getMask().test(mutable); } + @Override + public Mask2D copy2D() { + return new OffsetMask2D(mask.copy2D(), BlockVector2.at(offset.getX(), offset.getZ())); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java index a3f0032c1..d20e7cd64 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/RegionMask.java @@ -72,4 +72,9 @@ public class RegionMask extends AbstractMask { return null; } + @Override + public Mask copy() { + return new RegionMask(region.clone()); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java index 6e3ab7785..5810a28ef 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockStateMask.java @@ -18,6 +18,12 @@ public class SingleBlockStateMask extends ABlockMask { this.ordinal = state.getOrdinalChar(); } + private SingleBlockStateMask(Extent extent, char ordinal, boolean isAir) { + super(extent); + this.ordinal = ordinal; + this.isAir = isAir; + } + @Override public boolean test(BlockVector3 vector) { int test = getExtent().getBlock(vector).getOrdinal(); @@ -50,4 +56,9 @@ public class SingleBlockStateMask extends ABlockMask { } return null; } + + @Override + public Mask copy() { + return new SingleBlockStateMask(getExtent(), ordinal, isAir); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java index 6ceea320c..a23ef18ac 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SingleBlockTypeMask.java @@ -17,6 +17,12 @@ public class SingleBlockTypeMask extends ABlockMask { this.internalId = type.getInternalId(); } + private SingleBlockTypeMask(Extent extent, int internalId, boolean isAir) { + super(extent); + this.internalId = internalId; + this.isAir = isAir; + } + @Override public final boolean test(BlockState state) { return state.getBlockType().getInternalId() == internalId; @@ -35,4 +41,9 @@ public class SingleBlockTypeMask extends ABlockMask { public boolean replacesAir() { return isAir; } + + @Override + public Mask copy() { + return new SingleBlockTypeMask(getExtent(), internalId, isAir); + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java index e13b86611..441d95e18 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/SolidBlockMask.java @@ -35,4 +35,9 @@ public class SolidBlockMask extends BlockMask { return null; } + @Override + public Mask copy() { + return new SolidBlockMask(getExtent()); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/WallMakeMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/WallMakeMask.java new file mode 100644 index 000000000..5834a1fb8 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/WallMakeMask.java @@ -0,0 +1,26 @@ +package com.sk89q.worldedit.function.mask; + +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; + +public class WallMakeMask implements Mask { + + private final Region region; + + public WallMakeMask(Region region) { + this.region = region.clone(); + } + + @Override + public boolean test(BlockVector3 position) { + int x = position.getBlockX(); + int z = position.getBlockZ(); + return !region.contains(x, z + 1) || !region.contains(x, z - 1) || !region.contains(x + 1, z) || !region.contains(x - 1, z); + } + + @Override + public Mask copy() { + return new WallMakeMask(region); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java index 25bb93a9e..d1009dbbe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java @@ -31,8 +31,10 @@ import org.antlr.v4.runtime.misc.ParseCancellationException; import org.antlr.v4.runtime.tree.ParseTreeWalker; import java.time.Instant; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Compiles and evaluates expressions. @@ -60,19 +62,55 @@ import java.util.Objects; * pass values for all slots specified while compiling. * To query slots after evaluation, you can use the {@linkplain #getSlots() slot table}. */ -public class Expression { +public class Expression implements Cloneable { private final SlotTable slots = new SlotTable(); private final List providedSlots; private final ExpressionParser.AllStatementsContext root; private final Functions functions = Functions.create(); private final CompiledExpression compiledExpression; + private final String initialExpression; public static Expression compile(String expression, String... variableNames) throws ExpressionException { return new Expression(expression, variableNames); } private Expression(String expression, String... variableNames) throws ExpressionException { + this.initialExpression = expression; + + slots.putSlot("e", new LocalSlot.Constant(Math.E)); + slots.putSlot("pi", new LocalSlot.Constant(Math.PI)); + slots.putSlot("true", new LocalSlot.Constant(1)); + slots.putSlot("false", new LocalSlot.Constant(0)); + + for (String variableName : variableNames) { + slots.initVariable(variableName) + .orElseThrow(() -> new ExpressionException(-1, + "Tried to overwrite identifier '" + variableName + "'")); + } + this.providedSlots = ImmutableList.copyOf(variableNames); + + CharStream cs = CharStreams.fromString(expression, ""); + ExpressionLexer lexer = new ExpressionLexer(cs); + lexer.removeErrorListeners(); + lexer.addErrorListener(new LexerErrorListener()); + CommonTokenStream tokens = new CommonTokenStream(lexer); + ExpressionParser parser = new ExpressionParser(tokens); + parser.removeErrorListeners(); + parser.addErrorListener(new ParserErrorListener()); + try { + root = parser.allStatements(); + Objects.requireNonNull(root, "Unable to parse root, but no exceptions?"); + } catch (ParseCancellationException e) { + throw new ParserException(parser.getState(), e); + } + ParseTreeWalker.DEFAULT.walk(new ExpressionValidator(slots.keySet(), functions), root); + this.compiledExpression = new ExpressionCompiler().compileExpression(root, functions); + } + + private Expression(String expression, Set variableNames) throws ExpressionException { + this.initialExpression = expression; + slots.putSlot("e", new LocalSlot.Constant(Math.E)); slots.putSlot("pi", new LocalSlot.Constant(Math.PI)); slots.putSlot("true", new LocalSlot.Constant(1)); @@ -147,4 +185,8 @@ public class Expression { functions.setEnvironment(environment); } + public Expression clone() { + return new Expression(initialExpression, new HashSet<>(providedSlots)); + } + }