mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-05 12:36:40 +00:00
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
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<Long, ChunkFilterBlock> threadIdToFilter = FaweCache.IMP.createCache(() -> new CharFilterBlock(getExtent()));
|
||||
private final LoadingCache<Long, ChunkFilterBlock> 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<Long, ChunkFilterBlock> 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 <B extends BlockStateHolder<B>> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,4 +102,9 @@ public class BiomeMask extends AbstractMask {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new BiomeMask(extent, new HashSet<>(biomes));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -98,4 +98,9 @@ public class BiomeMask2D extends AbstractMask2D {
|
||||
return biomes.contains(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask2D copy2D() {
|
||||
return new BiomeMask2D(extent, new HashSet<>(biomes));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,4 +56,9 @@ public class BlockCategoryMask extends AbstractExtentMask {
|
||||
public Mask2D toMask2D() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new BlockCategoryMask(getExtent(), category);
|
||||
}
|
||||
}
|
||||
|
@ -328,4 +328,9 @@ public class BlockMask extends ABlockMask {
|
||||
}
|
||||
return new BlockMask(getExtent(), cloned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new BlockMask(getExtent(), ordinals.clone());
|
||||
}
|
||||
}
|
||||
|
@ -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<String, String> statesClone = new HashMap<>();
|
||||
states.forEach(statesClone::put);
|
||||
return new BlockStateMask(getExtent(), statesClone, strict);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,4 +89,9 @@ public class ExpressionMask extends AbstractMask {
|
||||
return new ExpressionMask2D(expression, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new ExpressionMask(expression.clone(), timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,4 +72,9 @@ public class ExpressionMask2D extends AbstractMask2D {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask2D copy2D() {
|
||||
return new ExpressionMask2D(expression.clone(), timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,4 +31,9 @@ public class InverseMask extends AbstractMask {
|
||||
public Mask inverse() {
|
||||
return mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new InverseMask(mask.copy());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -34,4 +34,6 @@ public interface Mask2D {
|
||||
*/
|
||||
boolean test(BlockVector2 vector);
|
||||
|
||||
Mask2D copy2D();
|
||||
|
||||
}
|
||||
|
@ -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<Mask> masks;
|
||||
private Mask[] masksArray;
|
||||
private boolean defaultReturn;
|
||||
protected final Set<Mask> masks;
|
||||
protected Mask[] masksArray;
|
||||
protected boolean defaultReturn;
|
||||
|
||||
/**
|
||||
* Create a new intersection.
|
||||
@ -60,6 +61,12 @@ public class MaskIntersection extends AbstractMask {
|
||||
formArray();
|
||||
}
|
||||
|
||||
protected MaskIntersection(Set<Mask> masks, Mask[] masksArray, boolean defaultReturn) {
|
||||
this.masks = masks;
|
||||
this.masksArray = masksArray;
|
||||
this.defaultReturn = defaultReturn;
|
||||
}
|
||||
|
||||
public static Mask of(Mask... masks) {
|
||||
Set<Mask> 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<Mask> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Mask2D> masks = new HashSet<>();
|
||||
protected final Set<Mask2D> masks = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Create a new intersection.
|
||||
@ -97,4 +98,10 @@ public class MaskIntersection2D implements Mask2D {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask2D copy2D() {
|
||||
Set<Mask2D> masksCopy = this.masks.stream().map(Mask2D::copy2D).collect(Collectors.toSet());
|
||||
return new MaskIntersection2D(masksCopy);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Mask> masks, Mask[] maskArray, boolean defaultReturn) {
|
||||
super(masks, maskArray, defaultReturn);
|
||||
}
|
||||
|
||||
public static Mask of(Mask... masks) {
|
||||
Set<Mask> 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<Mask> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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<Mask2D> masksCopy = masks.stream().map(Mask2D::copy2D).collect(Collectors.toSet());
|
||||
return new MaskUnion2D(masksCopy);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -96,4 +96,9 @@ public class NoiseFilter extends AbstractMask {
|
||||
return new NoiseFilter2D(getNoiseGenerator(), getDensity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new NoiseFilter(noiseGenerator, density);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -87,4 +87,9 @@ public class NoiseFilter2D extends AbstractMask2D {
|
||||
return noiseGenerator.noise(pos.toVector2()) <= density;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask2D copy2D() {
|
||||
return new NoiseFilter2D(noiseGenerator, density);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -105,4 +105,9 @@ public class OffsetMask extends AbstractMask {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new OffsetMask(mask.copy(), offset.toImmutable());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,4 +72,9 @@ public class RegionMask extends AbstractMask {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new RegionMask(region.clone());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -35,4 +35,9 @@ public class SolidBlockMask extends BlockMask {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask copy() {
|
||||
return new SolidBlockMask(getExtent());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<String> 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, "<input>");
|
||||
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<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));
|
||||
@ -147,4 +185,8 @@ public class Expression {
|
||||
functions.setEnvironment(environment);
|
||||
}
|
||||
|
||||
public Expression clone() {
|
||||
return new Expression(initialExpression, new HashSet<>(providedSlots));
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user