Copy paste/merge FAWE classes to this WorldEdit fork

- so certain people can look at the diff and complain about my sloppy code :(

Signed-off-by: Jesse Boyd <jessepaleg@gmail.com>
This commit is contained in:
Jesse Boyd
2018-08-13 00:03:07 +10:00
parent a920c77cb8
commit a629d15c74
994 changed files with 117583 additions and 10745 deletions

View File

@ -19,22 +19,19 @@
package com.sk89q.worldedit.function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Executes several region functions in order.
*/
public class CombinedRegionFunction implements RegionFunction {
private final List<RegionFunction> functions = new ArrayList<>();
private RegionFunction[] functions;
/**
* Create a combined region function.
@ -49,7 +46,8 @@ public class CombinedRegionFunction implements RegionFunction {
*/
public CombinedRegionFunction(Collection<RegionFunction> functions) {
checkNotNull(functions);
this.functions.addAll(functions);
this.functions = functions.toArray(new RegionFunction[functions.size()]);
}
/**
@ -58,7 +56,21 @@ public class CombinedRegionFunction implements RegionFunction {
* @param function an array of functions to match
*/
public CombinedRegionFunction(RegionFunction... function) {
this(Arrays.asList(checkNotNull(function)));
this.functions = function;
}
public static CombinedRegionFunction combine(RegionFunction function, RegionFunction add) {
CombinedRegionFunction combined;
if (function instanceof CombinedRegionFunction) {
combined = ((CombinedRegionFunction) function);
combined.add(add);
} else if (add instanceof CombinedRegionFunction) {
combined = new CombinedRegionFunction(function);
combined.add(((CombinedRegionFunction) add).functions);
} else {
combined = new CombinedRegionFunction(function, add);
}
return combined;
}
/**
@ -68,7 +80,9 @@ public class CombinedRegionFunction implements RegionFunction {
*/
public void add(Collection<RegionFunction> functions) {
checkNotNull(functions);
this.functions.addAll(functions);
ArrayList<RegionFunction> functionsList = new ArrayList<>(Arrays.asList(this.functions));
functionsList.addAll(functions);
this.functions = functionsList.toArray(new RegionFunction[functionsList.size()]);
}
/**
@ -91,4 +105,8 @@ public class CombinedRegionFunction implements RegionFunction {
return ret;
}
public static Class<?> inject() {
return CombinedRegionFunction.class;
}
}

View File

@ -19,14 +19,14 @@
package com.sk89q.worldedit.function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.regions.Region;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class EditContext {
private Extent destination;

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.mask.Mask2D;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Passes calls to {@link #apply(com.sk89q.worldedit.Vector2D)} to the
* delegate {@link com.sk89q.worldedit.function.FlatRegionFunction} if they

View File

@ -19,12 +19,13 @@
package com.sk89q.worldedit.function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.mask.Mask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Applies a {@link RegionFunction} to the first ground block.
*/
@ -37,7 +38,7 @@ public class GroundFunction implements LayerFunction {
/**
* Create a new ground function.
*
* @param mask a mask
* @param mask a mask
* @param function the function to apply
*/
public GroundFunction(Mask mask, RegionFunction function) {
@ -87,7 +88,6 @@ public class GroundFunction implements LayerFunction {
affected++;
}
}
return false;
}

View File

@ -0,0 +1,64 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.regions.Region;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Passes calls to {@link #apply(com.sk89q.worldedit.Vector)} to the
* delegate {@link com.sk89q.worldedit.function.RegionFunction} if they
* match the given mask.
*/
public class RegionMaskTestFunction implements RegionFunction {
private final RegionFunction pass,fail;
private Mask mask;
/**
* Create a new masking filter.
*
* @param mask the mask
* @param function the function
*/
public RegionMaskTestFunction(Mask mask, RegionFunction success, RegionFunction failure) {
checkNotNull(success);
checkNotNull(failure);
checkNotNull(mask);
this.pass = success;
this.fail = failure;
this.mask = mask;
}
@Override
public boolean apply(Vector position) throws WorldEditException {
if (mask.test(position)) {
return pass.apply(position);
} else {
return fail.apply(position);
}
}
}

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.mask.Mask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Passes calls to {@link #apply(com.sk89q.worldedit.Vector)} to the
* delegate {@link com.sk89q.worldedit.function.RegionFunction} if they

View File

@ -19,14 +19,14 @@
package com.sk89q.worldedit.function.biome;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.FlatRegionFunction;
import com.sk89q.worldedit.world.biome.BaseBiome;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Replaces the biome at the locations that this function is applied to.
*/

View File

@ -19,14 +19,15 @@
package com.sk89q.worldedit.function.block;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.pattern.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Replaces blocks with a given pattern.
*/
@ -38,7 +39,7 @@ public class BlockReplace implements RegionFunction {
/**
* Create a new instance.
*
* @param extent an extent
* @param extent an extent
* @param pattern a pattern
*/
public BlockReplace(Extent extent, Pattern pattern) {
@ -50,7 +51,11 @@ public class BlockReplace implements RegionFunction {
@Override
public boolean apply(Vector position) throws WorldEditException {
return extent.setBlock(position, pattern.apply(position));
return pattern.apply(extent, position, position);
}
public static Class<?> inject() {
return BlockReplace.class;
}
}

View File

@ -19,19 +19,26 @@
package com.sk89q.worldedit.function.block;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.CompoundTagBuilder;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.internal.helper.MCDirections;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Direction.Flag;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Copies blocks from one extent to another.
@ -47,11 +54,11 @@ public class ExtentBlockCopy implements RegionFunction {
/**
* Make a new copy.
*
* @param source the source extent
* @param from the source offset
* @param source the source extent
* @param from the source offset
* @param destination the destination extent
* @param to the destination offset
* @param transform a transform to apply to positions (after source offset, before destination offset)
* @param to the destination offset
* @param transform a transform to apply to positions (after source offset, before destination offset)
*/
public ExtentBlockCopy(Extent source, Vector from, Extent destination, Vector to, Transform transform) {
checkNotNull(source);
@ -68,12 +75,11 @@ public class ExtentBlockCopy implements RegionFunction {
@Override
public boolean apply(Vector position) throws WorldEditException {
BaseBlock block = source.getFullBlock(position);
Vector orig = position.subtract(from);
Vector transformed = transform.apply(orig);
// Apply transformations to NBT data if necessary
block = transformNbtData(block);
BlockStateHolder block = transformNbtData(source.getBlock(position));
return destination.setBlock(transformed.add(to), block);
}
@ -85,9 +91,8 @@ public class ExtentBlockCopy implements RegionFunction {
* @param state the existing state
* @return a new state or the existing one
*/
private BaseBlock transformNbtData(BaseBlock state) {
private BlockState transformNbtData(BlockState state) {
CompoundTag tag = state.getNbtData();
if (tag != null) {
// Handle blocks which store their rotation in NBT
if (tag.containsKey("Rot")) {
@ -96,21 +101,26 @@ public class ExtentBlockCopy implements RegionFunction {
Direction direction = MCDirections.fromRotation(rot);
if (direction != null) {
Vector vector = transform.apply(direction.toVector()).subtract(transform.apply(Vector.ZERO)).normalize();
Direction newDirection = Direction.findClosest(vector, Flag.CARDINAL | Flag.ORDINAL | Flag.SECONDARY_ORDINAL);
Vector applyAbsolute = transform.apply(direction.toVector());
Vector applyOrigin = transform.apply(Vector.ZERO);
applyAbsolute.mutX(applyAbsolute.getX() - applyOrigin.getX());
applyAbsolute.mutY(applyAbsolute.getY() - applyOrigin.getY());
applyAbsolute.mutZ(applyAbsolute.getZ() - applyOrigin.getZ());
Direction newDirection = Direction.findClosest(applyAbsolute, Flag.CARDINAL | Flag.ORDINAL | Flag.SECONDARY_ORDINAL);
if (newDirection != null) {
CompoundTagBuilder builder = tag.createBuilder();
builder.putByte("Rot", (byte) MCDirections.toRotation(newDirection));
return state.toBaseBlock(builder.build());
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
values.put("Rot", new ByteTag((byte) MCDirections.toRotation(newDirection)));
}
}
}
}
return state;
}
public static Class<?> inject() {
return ExtentBlockCopy.class;
}
}

View File

@ -19,16 +19,19 @@
package com.sk89q.worldedit.function.block;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.mask.BlockTypeMask;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.function.LayerFunction;
import com.sk89q.worldedit.function.mask.BlockMask;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.world.block.BlockTypes;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Makes a layer of grass on top, three layers of dirt below, and smooth stone
@ -49,11 +52,7 @@ public class Naturalizer implements LayerFunction {
public Naturalizer(EditSession editSession) {
checkNotNull(editSession);
this.editSession = editSession;
this.mask = new BlockMask(editSession, Sets.newHashSet(
BlockTypes.GRASS_BLOCK.getDefaultState(),
BlockTypes.DIRT.getDefaultState(),
BlockTypes.STONE.getDefaultState()
));
this.mask = new BlockTypeMask(editSession, BlockTypes.GRASS, BlockTypes.DIRT, BlockTypes.STONE);
}
/**
@ -76,15 +75,15 @@ public class Naturalizer implements LayerFunction {
affected++;
switch (depth) {
case 0:
editSession.setBlock(position, BlockTypes.GRASS_BLOCK.getDefaultState());
editSession.setBlock(position, BlockTypes.GRASS);
break;
case 1:
case 2:
case 3:
editSession.setBlock(position, BlockTypes.DIRT.getDefaultState());
editSession.setBlock(position, BlockTypes.DIRT);
break;
default:
editSession.setBlock(position, BlockTypes.STONE.getDefaultState());
editSession.setBlock(position, BlockTypes.STONE);
}
}

View File

@ -19,10 +19,13 @@
package com.sk89q.worldedit.function.entity;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.CompoundTagBuilder;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.BaseEntity;
@ -34,6 +37,11 @@ import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Direction.Flag;
import com.sk89q.worldedit.util.Location;
import java.util.Arrays;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Copies entities provided to the function to the provided destination
@ -50,10 +58,10 @@ public class ExtentEntityCopy implements EntityFunction {
/**
* Create a new instance.
*
* @param from the from position
* @param from the from position
* @param destination the destination {@code Extent}
* @param to the destination position
* @param transform the transformation to apply to both position and orientation
* @param to the destination position
* @param transform the transformation to apply to both position and orientation
*/
public ExtentEntityCopy(Vector from, Extent destination, Vector to, Transform transform) {
checkNotNull(from);
@ -94,19 +102,19 @@ public class ExtentEntityCopy implements EntityFunction {
Vector pivot = from.round().add(0.5, 0.5, 0.5);
Vector newPosition = transform.apply(location.toVector().subtract(pivot));
Vector newDirection;
newDirection = transform.isIdentity() ?
entity.getLocation().getDirection()
: transform.apply(location.getDirection()).subtract(transform.apply(Vector.ZERO)).normalize();
newLocation = new Location(destination, newPosition.add(to.round().add(0.5, 0.5, 0.5)), newDirection);
// Some entities store their position data in NBT
state = transformNbtData(state);
if (transform.isIdentity()) {
newDirection = entity.getLocation().getDirection();
newLocation = new Location(destination, newPosition.add(to.round().add(0.5, 0.5, 0.5)), newDirection);
} else {
newDirection = new Vector(transform.apply(location.getDirection())).subtract(transform.apply(Vector.ZERO)).normalize();
newLocation = new Location(destination, newPosition.add(to.round().add(0.5, 0.5, 0.5)), newDirection);
state = transformNbtData(state);
}
boolean success = destination.createEntity(newLocation, state) != null;
// Remove
if (isRemoving() && success) {
if (isRemoving()) {
entity.remove();
}
@ -125,22 +133,27 @@ public class ExtentEntityCopy implements EntityFunction {
*/
private BaseEntity transformNbtData(BaseEntity state) {
CompoundTag tag = state.getNbtData();
if (tag != null) {
boolean changed = false;
// Handle hanging entities (paintings, item frames, etc.)
tag = tag.createBuilder().build();
Map<String, Tag> values = ReflectionUtils.getMap(tag.getValue());
boolean hasTilePosition = tag.containsKey("TileX") && tag.containsKey("TileY") && tag.containsKey("TileZ");
boolean hasDirection = tag.containsKey("Direction");
boolean hasLegacyDirection = tag.containsKey("Dir");
boolean hasFacing = tag.containsKey("Facing");
if (hasTilePosition) {
changed = true;
Vector tilePosition = new Vector(tag.asInt("TileX"), tag.asInt("TileY"), tag.asInt("TileZ"));
Vector newTilePosition = transform.apply(tilePosition.subtract(from)).add(to);
CompoundTagBuilder builder = tag.createBuilder()
.putInt("TileX", newTilePosition.getBlockX())
.putInt("TileY", newTilePosition.getBlockY())
.putInt("TileZ", newTilePosition.getBlockZ());
values.put("TileX", new IntTag(newTilePosition.getBlockX()));
values.put("TileY", new IntTag(newTilePosition.getBlockY()));
values.put("TileZ", new IntTag(newTilePosition.getBlockZ()));
if (hasDirection || hasLegacyDirection || hasFacing) {
int d;
@ -160,18 +173,38 @@ public class ExtentEntityCopy implements EntityFunction {
if (newDirection != null) {
byte hangingByte = (byte) MCDirections.toHanging(newDirection);
builder.putByte("Direction", hangingByte);
builder.putByte("Facing", hangingByte);
builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection)));
values.put("Direction", new ByteTag(hangingByte));
values.put("Facing", new ByteTag(hangingByte));
values.put("Dir", new ByteTag(MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection))));
}
}
}
}
return new BaseEntity(state.getType(), builder.build());
ListTag rotation = tag.getListTag("Rotation");
if (rotation != null && rotation.getValue().size() >= 2) {
changed = true;
double yaw = Math.toRadians(rotation.getFloat(0));
double pitch = Math.toRadians(rotation.getFloat(1));
double xz = Math.cos(pitch);
Vector direction = new Vector(-xz * Math.sin(yaw), -Math.sin(pitch), xz * Math.cos(yaw));
direction = transform.apply(direction);
FloatTag yawTag = new FloatTag(direction.toYaw());
FloatTag pitchTag = new FloatTag(direction.toPitch());
values.put("Rotation", new ListTag(FloatTag.class, Arrays.asList(yawTag, pitchTag)));
}
if (changed) {
return new BaseEntity(state.getType(), tag);
}
}
return state;
}
public static Class<?> inject() {
return ExtentEntityCopy.class;
}
}

View File

@ -19,9 +19,6 @@
package com.sk89q.worldedit.function.factory;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
import com.sk89q.worldedit.function.Contextual;
import com.sk89q.worldedit.function.EditContext;
import com.sk89q.worldedit.function.RegionFunction;
@ -30,6 +27,9 @@ import com.sk89q.worldedit.function.visitor.RegionVisitor;
import com.sk89q.worldedit.regions.NullRegion;
import com.sk89q.worldedit.regions.Region;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
public class Apply implements Contextual<Operation> {
private final Region region;

View File

@ -19,9 +19,6 @@
package com.sk89q.worldedit.function.factory;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
@ -37,6 +34,9 @@ import com.sk89q.worldedit.regions.Region;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
public class Deform implements Contextual<Operation> {
private Extent destination;

View File

@ -19,12 +19,6 @@
package com.sk89q.worldedit.function.factory;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.regions.Regions.asFlatRegion;
import static com.sk89q.worldedit.regions.Regions.maximumBlockY;
import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.NullExtent;
import com.sk89q.worldedit.function.Contextual;
@ -39,6 +33,10 @@ import com.sk89q.worldedit.math.noise.RandomNoise;
import com.sk89q.worldedit.regions.NullRegion;
import com.sk89q.worldedit.regions.Region;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.regions.Regions.*;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
public class Paint implements Contextual<Operation> {
private final Extent destination;

View File

@ -22,12 +22,12 @@ package com.sk89q.worldedit.function.generator;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
/**
* Generates flora (which may include tall grass, flowers, etc.).
@ -106,7 +106,7 @@ public class FloraGenerator implements RegionFunction {
public boolean apply(Vector position) throws WorldEditException {
BlockStateHolder block = editSession.getBlock(position);
if (block.getBlockType() == BlockTypes.GRASS_BLOCK) {
if (block.getBlockType() == BlockTypes.GRASS) {
editSession.setBlock(position.add(0, 1, 0), temperatePattern.apply(position));
return true;
} else if (block.getBlockType() == BlockTypes.SAND) {

View File

@ -22,11 +22,11 @@ package com.sk89q.worldedit.function.generator;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.util.TreeGenerator;
/**
* Generates forests by searching for the ground starting from the given upper Y
@ -53,10 +53,10 @@ public class ForestGenerator implements RegionFunction {
BlockStateHolder block = editSession.getBlock(position);
BlockType t = block.getBlockType();
if (t == BlockTypes.GRASS_BLOCK || t == BlockTypes.DIRT) {
if (t == BlockTypes.GRASS || t == BlockTypes.DIRT) {
treeType.generate(editSession, position.add(0, 1, 0));
return true;
} else if (t == BlockTypes.GRASS || t == BlockTypes.DEAD_BUSH || t == BlockTypes.POPPY || t == BlockTypes.DANDELION) { // TODO: This list needs to be moved
} else if (t == BlockTypes.TALL_GRASS || t == BlockTypes.DEAD_BUSH || t == BlockTypes.POPPY || t == BlockTypes.DANDELION) { // TODO: This list needs to be moved
editSession.setBlock(position, BlockTypes.AIR.getDefaultState());
treeType.generate(editSession, position);
return true;

View File

@ -26,8 +26,8 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.pattern.BlockPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.Random;
@ -104,7 +104,7 @@ public class GardenPatchGenerator implements RegionFunction {
int h = random.nextInt(3) - 1;
Vector p;
BlockState log = BlockTypes.OAK_LOG.getDefaultState();
BlockStateHolder log = BlockTypes.OAK_LOG.getDefaultState();
switch (t) {
case 0:
@ -163,11 +163,11 @@ public class GardenPatchGenerator implements RegionFunction {
position = position.add(0, 1, 0);
}
if (editSession.getBlock(position.add(0, -1, 0)).getBlockType() != BlockTypes.GRASS_BLOCK) {
if (editSession.getBlock(position.add(0, -1, 0)).getBlockType() != BlockTypes.GRASS) {
return false;
}
BlockState leavesBlock = BlockTypes.OAK_LEAVES.getDefaultState();
BlockStateHolder leavesBlock = BlockTypes.OAK_LEAVES.getDefaultState();
if (editSession.getBlock(position).getBlockType() == BlockTypes.AIR) {
editSession.setBlock(position, leavesBlock);
@ -187,7 +187,7 @@ public class GardenPatchGenerator implements RegionFunction {
* @return a pumpkin pattern
*/
public static Pattern getPumpkinPattern() {
return new BlockPattern(BlockTypes.PUMPKIN.getDefaultState());
return new BlockPattern(BlockTypes.CARVED_PUMPKIN.getDefaultState());
}
/**

View File

@ -1,61 +1,25 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Preconditions;
import com.sk89q.worldedit.extent.Extent;
/**
* An abstract implementation of {@link Mask} that takes uses an {@link Extent}.
*/
public abstract class AbstractExtentMask extends AbstractMask {
private transient Extent extent;
private Extent extent;
/**
* Construct a new mask.
*
* @param extent the extent
*/
protected AbstractExtentMask(Extent extent) {
setExtent(extent);
this.setExtent(extent);
}
/**
* Get the extent.
*
* @return the extent
*/
public Extent getExtent() {
return extent;
return this.extent;
}
/**
* Set the extent.
*
* @param extent the extent
*/
public void setExtent(Extent extent) {
checkNotNull(extent);
Preconditions.checkNotNull(extent);
this.extent = extent;
}
public static Class<?> inject() {
return AbstractExtentMask.class;
}
}

View File

@ -19,8 +19,13 @@
package com.sk89q.worldedit.function.mask;
import java.io.Serializable;
/**
* A base class of {@link Mask} that all masks should inherit from.
*/
public abstract class AbstractMask implements Mask {
public abstract class AbstractMask implements Mask, Serializable {
public static Class<?> inject() {
return AbstractMask.class;
}
}

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.biome.BaseBiome;
@ -30,6 +28,8 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Tests true if the biome at applied points is the same as the one given.
*/

View File

@ -1,108 +1,193 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.collection.FastBitSet;
import com.boydti.fawe.util.MainUtil;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.registry.state.AbstractProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A mask that checks whether blocks at the given positions are matched by
* a block in a list.
*
* <p>
* <p>This mask checks for both an exact block ID and data value match, as well
* for a block with the same ID but a data value of -1.</p>
*/
public class BlockMask extends AbstractExtentMask {
private final Set<BlockStateHolder> blocks = new HashSet<>();
private final long[][] bitSets;
protected final static long[] ALL = new long[0];
/**
* Create a new block mask.
*
* @param extent the extent
* @param blocks a list of blocks to match
*/
public BlockMask(Extent extent, Collection<BlockStateHolder> blocks) {
@Deprecated
public BlockMask(Extent extent, BaseBlock... blocks) {
super(extent);
checkNotNull(blocks);
this.blocks.addAll(blocks);
MainUtil.warnDeprecated(BlockMaskBuilder.class);
this.bitSets = new BlockMaskBuilder().addBlocks(blocks).optimize().getBits();
}
/**
* Create a new block mask.
*
* @param extent the extent
* @param block an array of blocks to match
*/
public BlockMask(Extent extent, BlockStateHolder... block) {
this(extent, Arrays.asList(checkNotNull(block)));
protected BlockMask(Extent extent, long[][] bitSets) {
super(extent);
this.bitSets = bitSets;
}
/**
* Add the given blocks to the list of criteria.
*
* @param blocks a list of blocks
*/
public void add(Collection<BlockStateHolder> blocks) {
checkNotNull(blocks);
this.blocks.addAll(blocks);
@Override
public Mask optimize() {
Map<Object, Integer> states = new HashMap<>();
int indexFound = -1;
{
int indexNull = -1;
int indexAll = -1;
for (int i = 0; i < bitSets.length; i++) {
long[] bs = bitSets[i];
if (bs == null) {
indexNull = i;
states.put(null, states.getOrDefault(null, 0) + 1);
} else if (bs.length == 0) {
indexAll = i;
states.put(ALL, states.getOrDefault(ALL, 0) + 1);
} else if (indexFound == -1) {
indexFound = i;
} else {
return this;
}
}
// Only types, no states
if (indexFound == -1) {
if (states.size() == 1) {
return states.keySet().iterator().next() == null ? Masks.alwaysFalse() : Masks.alwaysTrue();
}
if (states.get(ALL) == 1) return new SingleBlockTypeMask(getExtent(), BlockTypes.get(indexAll));
if (states.get(null) == 1)
return new SingleBlockTypeMask(getExtent(), BlockTypes.get(indexNull)).inverse();
boolean[] types = new boolean[BlockTypes.size()];
for (int i = 0; i < bitSets.length; i++) {
if (bitSets[i].length == 0) types[i] = true;
}
return new BlockTypeMask(getExtent(), types);
}
}
BlockType type = BlockTypes.get(indexFound);
{
Mask mask = getOptimizedMask(type, bitSets[indexFound]);
if (mask == null) { // Try with inverse
long[] newBitSet = bitSets[indexFound];
for (int i = 0; i < newBitSet.length; i++) newBitSet[i] = ~newBitSet[i];
mask = getOptimizedMask(type, bitSets[indexFound]);
if (mask != null) mask = mask.inverse();
}
return mask;
}
}
/**
* Add the given blocks to the list of criteria.
*
* @param block an array of blocks
*/
public void add(BlockStateHolder... block) {
add(Arrays.asList(checkNotNull(block)));
private Mask getOptimizedMask(BlockType type, long[] bitSet) {
boolean single = true;
int and = type.getInternalId();
List<? extends Property> properties = type.getProperties();
for (AbstractProperty prop : (List<AbstractProperty>) type.getProperties()) {
List values = prop.getValues();
int numSet = 0;
for (int i = 0; i < values.size(); i++) {
int localI = i << prop.getBitOffset();
if (FastBitSet.get(bitSet, localI)) {
numSet++;
and |= prop.modify(and, i);
}
}
// Cannot optimize multiple property values - use current mask (null)
if (numSet != values.size() && numSet != 1) {
return null;
}
single = single && numSet == 1;
}
if (single)
return new SingleBlockStateMask(getExtent(), BlockState.get(and));
return new SingleBlockStateBitMask(getExtent(), and);
}
/**
* Get the list of blocks that are tested with.
*
* @return a list of blocks
*/
public Collection<BlockStateHolder> getBlocks() {
return blocks;
@Override
public Mask and(Mask other) {
if (other instanceof BlockMask) {
long[][] otherBitSets = ((BlockMask) other).bitSets;
for (int i = 0; i < otherBitSets.length; i++) {
long[] otherBitSet = otherBitSets[i];
long[] bitSet = bitSets[i];
if (otherBitSet == null) bitSets[i] = null;
else if (otherBitSet.length == 0) continue;
else if (bitSet == null) continue;
else if (bitSet.length == 0) bitSets[i] = otherBitSet;
else for (int j = 0; j < otherBitSet.length; j++) bitSet[j] &= otherBitSet[j];
}
return this;
}
if (other instanceof SingleBlockStateMask) {
return new BlockMaskBuilder(bitSets).filter(((SingleBlockStateMask) other).getBlockState()).build(getExtent());
}
if (other instanceof SingleBlockTypeMask) {
return new BlockMaskBuilder(bitSets).filter(((SingleBlockTypeMask) other).getBlockType()).build(getExtent());
}
// TODO FIXME BlockTypeMask
return null;
}
@Override
public Mask or(Mask other) {
if (other instanceof BlockMask) {
long[][] otherBitSets = ((BlockMask) other).bitSets;
for (int i = 0; i < otherBitSets.length; i++) {
long[] otherBitSet = otherBitSets[i];
long[] bitSet = bitSets[i];
if (otherBitSet == null) continue;
else if (otherBitSet.length == 0) bitSets[i] = ALL;
else if (bitSet == null) bitSets[i] = otherBitSet;
else if (bitSet.length == 0) continue;
else for (int j = 0; j < otherBitSet.length; j++) bitSet[j] |= otherBitSet[j];
}
return this;
}
if (other instanceof SingleBlockStateMask) {
return new BlockMaskBuilder(bitSets).add(((SingleBlockStateMask) other).getBlockState()).build(getExtent());
}
if (other instanceof SingleBlockTypeMask) {
return new BlockMaskBuilder(bitSets).add(((SingleBlockTypeMask) other).getBlockType()).build(getExtent());
}
// TODO FIXME BlockTypeMask
return null;
}
@Override
public Mask inverse() {
for (int i = 0; i < bitSets.length; i++) {
if (bitSets[i] == null) bitSets[i] = ALL;
else if (bitSets[i] == ALL) bitSets[i] = null;
else {
for (int j = 0; j < bitSets[i].length; j++)
bitSets[i][j] = ~bitSets[i][j];
}
}
return this;
}
@Override
public boolean test(Vector vector) {
BlockStateHolder block = getExtent().getBlock(vector);
for (BlockStateHolder testBlock : blocks) {
if (testBlock.equalsFuzzy(block)) {
return true;
}
}
return false;
long[] bitSet = bitSets[block.getInternalBlockTypeId()];
if (bitSet == null) return false;
if (bitSet.length == 0) return true;
return FastBitSet.get(bitSet, block.getInternalPropertiesId());
}
@Nullable
@ -111,4 +196,7 @@ public class BlockMask extends AbstractExtentMask {
return null;
}
}
public static Class<?> inject() {
return BlockMask.class;
}
}

View File

@ -0,0 +1,490 @@
package com.sk89q.worldedit.function.mask;
import com.boydti.fawe.object.collection.FastBitSet;
import com.boydti.fawe.object.string.MutableCharSequence;
import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.registry.state.AbstractProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.registry.state.PropertyKey;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
public class BlockMaskBuilder {
private static final Operator GREATER = (a, b) -> a > b;
private static final Operator LESS = (a, b) -> a < b;
private static final Operator EQUAL = (a, b) -> a == b;
private static final Operator EQUAL_OR_NULL = (a, b) -> a == b;
private static final Operator GREATER_EQUAL = (a, b) -> a >= b;
private static final Operator LESS_EQUAL = (a, b) -> a <= b;
private static final Operator NOT = (a, b) -> a != b;
private interface Operator {
boolean test(int left, int right);
}
public BlockMaskBuilder filterRegex(BlockType blockType, PropertyKey key, String regex) {
Property property = blockType.getProperty(key);
if (property == null) return this;
List values = property.getValues();
for (int i = 0; i < values.size(); i++) {
Object value = values.get(i);
if (!value.toString().matches(regex)) {
filter(blockType, property, i);
}
}
return this;
}
private void filterOperator(BlockType blockType, PropertyKey key, Operator operator, CharSequence value) {
Property property = blockType.getProperty(key);
if (property == null) return;
int index = property.getIndexFor(value);
List values = property.getValues();
for (int i = 0; i < values.size(); i++) {
if (!operator.test(index, i)) {
filter(blockType, property, i);
}
}
}
private void filterRegexOrOperator(BlockType type, PropertyKey key, Operator operator, CharSequence value) {
if (!type.hasProperty(key)) {
if (operator == EQUAL) remove(type);
} else if (value.length() == 0) {
} else if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) {
filterRegex(type, key, value.toString());
} else {
filterOperator(type, key, operator, value);
}
}
public BlockMaskBuilder addRegex(String input) {
if (input.charAt(input.length() - 1) == ']') {
int propStart = StringMan.findMatchingBracket(input, input.length() - 1);
if (propStart == -1) return this;
MutableCharSequence charSequence = MutableCharSequence.getTemporal();
charSequence.setString(input);
charSequence.setSubstring(0, propStart);
BlockTypes type = null;
List<BlockTypes> blockTypeList = null;
if (StringMan.isAlphanumericUnd(charSequence)) {
type = BlockTypes.get(charSequence);
add(type);
} else {
String regex = charSequence.toString();
blockTypeList = new ArrayList<>();
for (BlockTypes myType : BlockTypes.values) {
if (myType.getId().matches(regex)) {
blockTypeList.add(myType);
add(myType);
}
}
if (blockTypeList.size() == 1) type = blockTypeList.get(0);
}
PropertyKey key = null;
int length = input.length();
int last = propStart + 1;
Operator operator = null;
for (int i = last; i < length; i++) {
char c = input.charAt(i);
switch (c) {
case '[':
case '{':
case '(':
int next = StringMan.findMatchingBracket(input, i);
if (next != -1) i = next;
break;
case ']':
case ',': {
charSequence.setSubstring(last, i);
char firstChar = input.charAt(last + 1);
if (type != null) filterRegexOrOperator(type, key, operator, charSequence);
else {
for (BlockTypes myType : blockTypeList) {
filterRegexOrOperator(myType, key, operator, charSequence);
}
}
last = i + 1;
break;
}
case '~':
case '!':
case '=':
case '<':
case '>': {
charSequence.setSubstring(last, i);
boolean extra = input.charAt(i + 1) == '=';
if (extra) i++;
switch (c) {
case '~':
operator = EQUAL_OR_NULL;
break;
case '!':
operator = NOT;
break;
case '=':
operator = EQUAL;
break;
case '<':
operator = extra ? LESS_EQUAL : LESS;
break;
case '>':
operator = extra ? GREATER_EQUAL : GREATER;
break;
}
if (charSequence.length() > 0) key = PropertyKey.get(charSequence);
last = i + 1;
break;
}
default:
break;
}
}
} else {
if (StringMan.isAlphanumericUnd(input)) {
add(BlockTypes.get(input));
} else {
for (BlockTypes myType : BlockTypes.values) {
if (myType.getId().matches(input)) {
add(myType);
}
}
}
}
return this;
}
///// end test /////
private long[][] bitSets;
private boolean optimizedStates = true;
public boolean isEmpty() {
for (int i = 0; i < bitSets.length; i++) {
if (bitSets[i] != null) return false;
}
return true;
}
public BlockMaskBuilder() {
this(new long[BlockTypes.size()][]);
}
protected BlockMaskBuilder(long[][] bitSets) {
this.bitSets = bitSets;
}
public BlockMaskBuilder parse(String input) {
return this;
}
public BlockMaskBuilder addAll() {
for (int i = 0; i < bitSets.length; i++) {
bitSets[i] = BlockMask.ALL;
}
optimizedStates = true;
return this;
}
public BlockMaskBuilder clear() {
for (int i = 0; i < bitSets.length; i++) {
bitSets[i] = null;
}
optimizedStates = true;
return this;
}
public BlockMaskBuilder remove(BlockType type) {
bitSets[type.getInternalId()] = null;
return this;
}
public BlockMaskBuilder remove(BlockStateHolder state) {
BlockType type = state.getBlockType();
int i = type.getInternalId();
long[] states = bitSets[i];
if (states != null) {
if (states == BlockMask.ALL) {
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
Arrays.fill(states, -1);
}
int stateId = state.getInternalPropertiesId();
FastBitSet.clear(states, stateId);
optimizedStates = false;
}
return this;
}
public BlockMaskBuilder filter(BlockType type) {
for (int i = 0; i < bitSets.length; i++) {
if (i != type.getInternalId()) bitSets[i] = null;
}
return this;
}
public BlockMaskBuilder filter(BlockStateHolder state) {
filter(state.getBlockType());
BlockType type = state.getBlockType();
int i = type.getInternalId();
long[] states = bitSets[i];
if (states != null) {
int stateId = state.getInternalPropertiesId();
boolean set = true;
if (states == BlockMask.ALL) {
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
} else {
set = FastBitSet.get(states, stateId);
Arrays.fill(states, 0);
}
if (set)
FastBitSet.set(states, stateId);
else
bitSets[i] = null;
optimizedStates = true;
}
return this;
}
public BlockMaskBuilder filter(Predicate<BlockType> allow) {
for (int i = 0; i < bitSets.length; i++) {
BlockType type = BlockTypes.get(i);
if (!allow.test(type)) {
bitSets[i] = null;
}
}
return this;
}
public <T> BlockMaskBuilder filter(Predicate<BlockType> typePredicate, BiPredicate<BlockType, Map.Entry<Property<T>, T>> allowed) {
for (int i = 0; i < bitSets.length; i++) {
long[] states = bitSets[i];
if (states == null) continue;
BlockType type = BlockTypes.get(i);
if (!typePredicate.test(type)) {
bitSets[i] = null;
continue;
}
List<AbstractProperty> properties = (List<AbstractProperty>) type.getProperties();
for (AbstractProperty prop : properties) {
List values = prop.getValues();
for (int j = 0; j < values.size(); j++) {
int localI = j << prop.getBitOffset() >> BlockTypes.BIT_OFFSET;
if (states == BlockMask.ALL || FastBitSet.get(states, localI)) {
if (!allowed.test(type, new AbstractMap.SimpleEntry(prop, values.get(j)))) {
if (states == BlockMask.ALL) {
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
FastBitSet.setAll(states);
}
FastBitSet.clear(states, localI);
optimizedStates = false;
}
}
}
}
}
return this;
}
public BlockMaskBuilder add(BlockType type) {
bitSets[type.getInternalId()] = BlockMask.ALL;
return this;
}
public BlockMaskBuilder add(BlockStateHolder state) {
BlockType type = state.getBlockType();
int i = type.getInternalId();
long[] states = bitSets[i];
if (states != BlockMask.ALL) {
if (states == null) {
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
}
int stateId = state.getInternalPropertiesId();
FastBitSet.set(states, stateId);
optimizedStates = false;
}
return this;
}
public BlockMaskBuilder addBlocks(Collection<BlockStateHolder> blocks) {
for (BlockStateHolder block : blocks) add(block);
return this;
}
public BlockMaskBuilder addTypes(Collection<BlockType> blocks) {
for (BlockType block : blocks) add(block);
return this;
}
public BlockMaskBuilder addBlocks(BlockStateHolder... blocks) {
for (BlockStateHolder block : blocks) add(block);
return this;
}
public BlockMaskBuilder addTypes(BlockType... blocks) {
for (BlockType block : blocks) add(block);
return this;
}
public BlockMaskBuilder addAll(Predicate<BlockType> allow) {
for (int i = 0; i < bitSets.length; i++) {
BlockType type = BlockTypes.get(i);
if (allow.test(type)) {
bitSets[i] = BlockMask.ALL;
}
}
return this;
}
public BlockMaskBuilder addAll(Predicate<BlockType> typePredicate, BiPredicate<BlockType, Map.Entry<Property<?>, ?>> propPredicate) {
for (int i = 0; i < bitSets.length; i++) {
long[] states = bitSets[i];
if (states == BlockMask.ALL) continue;
BlockType type = BlockTypes.get(i);
if (!typePredicate.test(type)) {
continue;
}
for (AbstractProperty prop : (List<AbstractProperty>) type.getProperties()) {
List values = prop.getValues();
for (int j = 0; j < values.size(); j++) {
int localI = j << prop.getBitOffset() >> BlockTypes.BIT_OFFSET;
if (states == null || !FastBitSet.get(states, localI)) {
if (propPredicate.test(type, new AbstractMap.SimpleEntry(prop, values.get(j)))) {
if (states == null) {
bitSets[i] = states = FastBitSet.create(type.getMaxStateId() + 1);
}
FastBitSet.set(states, localI);
optimizedStates = false;
}
}
}
}
}
return this;
}
public BlockMaskBuilder add(BlockType type, Property property, int index) {
AbstractProperty prop = (AbstractProperty) property;
long[] states = bitSets[type.getInternalId()];
if (states == BlockMask.ALL) return this;
List values = property.getValues();
int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET;
if (states == null || !FastBitSet.get(states, localI)) {
if (states == null) {
bitSets[type.getInternalId()] = states = FastBitSet.create(type.getMaxStateId() + 1);
}
set(type, states, property, index);
optimizedStates = false;
}
return this;
}
public BlockMaskBuilder filter(BlockType type, Property property, int index) {
AbstractProperty prop = (AbstractProperty) property;
long[] states = bitSets[type.getInternalId()];
if (states == null) return this;
List values = property.getValues();
int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET;
if (states == BlockMask.ALL || FastBitSet.get(states, localI)) {
if (states == BlockMask.ALL) {
bitSets[type.getInternalId()] = states = FastBitSet.create(type.getMaxStateId() + 1);
FastBitSet.setAll(states);
}
clear(type, states, property, index);
optimizedStates = false;
}
return this;
}
private void applyRecursive(List<Property> properties, int propertiesIndex, int state, long[] states, boolean set) {
AbstractProperty current = (AbstractProperty) properties.get(propertiesIndex);
List values = current.getValues();
if (propertiesIndex + 1 < properties.size()) {
for (int i = 0; i < values.size(); i++) {
int newState = current.modifyIndex(state, i);
applyRecursive(properties, propertiesIndex + 1, newState, states, set);
}
} else {
for (int i = 0; i < values.size(); i++) {
int index = current.modifyIndex(state, i) >> BlockTypes.BIT_OFFSET;
if (set) FastBitSet.set(states, index);
else FastBitSet.clear(states, index);
}
}
}
private void set(BlockType type, long[] bitSet, Property property, int index) {
FastBitSet.set(bitSet, index);
if (type.getProperties().size() > 1) {
ArrayList<Property> properties = new ArrayList<>(type.getProperties());
properties.remove(property);
int state = ((AbstractProperty) property).modifyIndex(type.getInternalId(), index);
applyRecursive(properties, 0, state, bitSet, true);
}
}
private void clear(BlockType type, long[] bitSet, Property property, int index) {
FastBitSet.clear(bitSet, index);
if (type.getProperties().size() > 1) {
ArrayList<Property> properties = new ArrayList<>(type.getProperties());
properties.remove(property);
int state = ((AbstractProperty) property).modifyIndex(type.getInternalId(), index);
applyRecursive(properties, 0, state, bitSet, false);
}
}
public BlockMaskBuilder optimize() {
if (!optimizedStates) {
for (int i = 0; i < bitSets.length; i++) {
long[] bitSet = bitSets[i];
if (bitSet == null || bitSet == BlockMask.ALL) continue;
BlockType type = BlockTypes.get(i);
int maxStateId = type.getMaxStateId();
if (maxStateId == 0) {
if (bitSet[0] == 0) {
bitSets[i] = null;
continue;
} else {
bitSets[i] = BlockMask.ALL;
continue;
}
}
int set = 0;
int clear = 0;
for (AbstractProperty prop : (List<AbstractProperty>) type.getProperties()) {
List values = prop.getValues();
for (int j = 0; j < values.size(); j++) {
int localI = j << prop.getBitOffset() >> BlockTypes.BIT_OFFSET;
if (FastBitSet.get(bitSet, localI)) set++;
else clear++;
}
}
if (set == 0) bitSets[i] = null;
else if (clear == 0) bitSets[i] = BlockMask.ALL;
}
optimizedStates = true;
}
return this;
}
protected long[][] getBits() {
return this.bitSets;
}
public Mask build(Extent extent) {
optimize();
return new BlockMask(extent, bitSets);
}
}

View File

@ -0,0 +1,26 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
public class BlockTypeMask extends AbstractExtentMask {
private final boolean[] types;
protected BlockTypeMask(Extent extent, boolean[] types) {
super(extent);
this.types = types;
}
public BlockTypeMask(Extent extent, BlockType... types) {
super(extent);
this.types = new boolean[BlockTypes.size()];
for (BlockType type : types) this.types[type.getInternalId()] = true;
}
@Override
public boolean test(Vector vector) {
return types[getExtent().getBlockType(vector).getInternalId()];
}
}

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkArgument;
import com.sk89q.worldedit.Vector;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Has the criteria where the Y value of passed positions must be within
* a certain range of Y values (inclusive).

View File

@ -19,9 +19,9 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.Vector;
import javax.annotation.Nullable;

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
@ -29,6 +27,8 @@ import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A mask that evaluates an expression.
*

View File

@ -19,13 +19,13 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
import static com.google.common.base.Preconditions.checkNotNull;
public class ExpressionMask2D extends AbstractMask2D {
private final Expression expression;

View File

@ -1,45 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.Blocks;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.Collection;
public class FuzzyBlockMask extends BlockMask {
public FuzzyBlockMask(Extent extent, Collection<BlockStateHolder> blocks) {
super(extent, blocks);
}
public FuzzyBlockMask(Extent extent, BlockStateHolder... block) {
super(extent, block);
}
@Override
public boolean test(Vector vector) {
Extent extent = getExtent();
Collection<BlockStateHolder> blocks = getBlocks();
return Blocks.containsFuzzy(blocks, extent.getFullBlock(vector));
}
}

View File

@ -0,0 +1,34 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import javax.annotation.Nullable;
public class InverseMask extends AbstractMask {
private final Mask mask;
public InverseMask(Mask other) {
this.mask = other;
}
@Override
public boolean test(Vector vector) {
return !mask.test(vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
Mask2D mask2d = mask.toMask2D();
if (mask2d != null) {
return Masks.negate(mask2d);
} else {
return null;
}
}
@Override
public Mask inverse() {
return mask;
}
}

View File

@ -19,13 +19,19 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.minecraft.util.commands.Link;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.command.UtilityCommands;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.function.mask.Masks.negate;
/**
* Tests whether a given vector meets a criteria.
*/
@Link(clazz = UtilityCommands.class, value = "masks")
public interface Mask {
/**
@ -42,6 +48,28 @@ public interface Mask {
* @return a 2D mask version or {@code null} if this mask can't be 2D
*/
@Nullable
Mask2D toMask2D();
default Mask2D toMask2D() {
return null;
}
default Mask optimize() {
return null;
}
default Mask and(Mask other) {
return null;
}
default Mask or(Mask other) {
return null;
}
default Mask inverse() {
if (this instanceof Masks.AlwaysTrue) {
return Masks.ALWAYS_FALSE;
} else if (this instanceof Masks.AlwaysFalse) {
return Masks.ALWAYS_TRUE;
}
return new InverseMask(this);
}
}

View File

@ -19,18 +19,13 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Function;
import com.sk89q.worldedit.Vector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Combines several masks and requires that all masks return true
@ -39,7 +34,8 @@ import javax.annotation.Nullable;
*/
public class MaskIntersection extends AbstractMask {
private final Set<Mask> masks = new HashSet<>();
private final Set<Mask> masks = new LinkedHashSet<>();
private Mask[] masksArray;
/**
* Create a new intersection.
@ -49,6 +45,82 @@ public class MaskIntersection extends AbstractMask {
public MaskIntersection(Collection<Mask> masks) {
checkNotNull(masks);
this.masks.addAll(masks);
formArray();
}
private void formArray() {
if (masks.isEmpty()) {
masksArray = new Mask[]{Masks.alwaysFalse()};
} else {
masksArray = masks.toArray(new Mask[masks.size()]);
}
}
public Function<Map.Entry<Mask, Mask>, Mask> pairingFunction() {
return input -> input.getKey().and(input.getValue());
}
private void optimizeMasks(Set<Mask> ignore) {
LinkedHashSet<Mask> newMasks = null;
for (Mask mask : masks) {
if (ignore.contains(mask)) continue;
Mask newMask = mask.optimize();
if (newMask != null) {
if (newMask != mask) {
if (newMasks == null) newMasks = new LinkedHashSet<>();
newMasks.add(newMask);
}
} else {
ignore.add(mask);
}
if (newMasks != null) {
masks.clear();
masks.addAll(newMasks);
}
}
}
@Override
public Mask optimize() {
Set<Mask> optimized = new HashSet<>();
Set<Map.Entry<Mask, Mask>> failedCombines = new HashSet<>();
// Combine the masks
while (combine(pairingFunction(), failedCombines));
// Optimize / combine
do optimizeMasks(optimized);
while (combine(pairingFunction(), failedCombines));
// Return result
formArray();
if (masks.size() == 0) return Masks.alwaysTrue();
if (masks.size() == 1) return masks.iterator().next();
return this;
}
private boolean combine(Function<Map.Entry<Mask, Mask>, Mask> pairing, Set<Map.Entry<Mask, Mask>> failedCombines) {
boolean hasOptimized = false;
while (true) {
Mask[] result = null;
outer:
for (Mask mask : masks) {
for (Mask other : masks) {
AbstractMap.SimpleEntry pair = new AbstractMap.SimpleEntry(mask, other);
if (failedCombines.contains(pair)) continue;
Mask combined = pairing.apply(pair);
if (combined != null) {
result = new Mask[]{combined, mask, other};
break outer;
} else {
failedCombines.add(pair);
}
}
}
if (result == null) break;
masks.remove(result[1]);
masks.remove(result[2]);
masks.add(result[0]);
hasOptimized = true;
}
return hasOptimized;
}
/**
@ -68,6 +140,7 @@ public class MaskIntersection extends AbstractMask {
public void add(Collection<Mask> masks) {
checkNotNull(masks);
this.masks.addAll(masks);
formArray();
}
/**
@ -88,25 +161,24 @@ public class MaskIntersection extends AbstractMask {
return masks;
}
public final Mask[] getMasksArray() {
return masksArray;
}
@Override
public boolean test(Vector vector) {
if (masks.isEmpty()) {
return false;
}
for (Mask mask : masks) {
for (Mask mask : masksArray) {
if (!mask.test(vector)) {
return false;
}
}
return true;
}
@Nullable
@Override
public Mask2D toMask2D() {
List<Mask2D> mask2dList = new ArrayList<>();
List<Mask2D> mask2dList = new ArrayList<Mask2D>();
for (Mask mask : masks) {
Mask2D mask2d = mask.toMask2D();
if (mask2d != null) {
@ -117,5 +189,4 @@ public class MaskIntersection extends AbstractMask {
}
return new MaskIntersection2D(mask2dList);
}
}

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import java.util.Arrays;
@ -28,6 +26,8 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Tests true if all contained masks test true.
*/

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function.mask;
import com.google.common.base.Function;
import com.sk89q.worldedit.Vector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
@ -53,10 +53,13 @@ public class MaskUnion extends MaskIntersection {
}
@Override
public boolean test(Vector vector) {
Collection<Mask> masks = getMasks();
public Function<Map.Entry<Mask, Mask>, Mask> pairingFunction() {
return input -> input.getKey().or(input.getValue());
}
for (Mask mask : masks) {
@Override
public boolean test(Vector vector) {
for (Mask mask : getMasksArray()) {
if (mask.test(vector)) {
return true;
}
@ -68,7 +71,7 @@ public class MaskUnion extends MaskIntersection {
@Nullable
@Override
public Mask2D toMask2D() {
List<Mask2D> mask2dList = new ArrayList<>();
List<Mask2D> mask2dList = new ArrayList<Mask2D>();
for (Mask mask : getMasks()) {
Mask2D mask2d = mask.toMask2D();
if (mask2d != null) {
@ -79,4 +82,8 @@ public class MaskUnion extends MaskIntersection {
}
return new MaskUnion2D(mask2dList);
}
public static Class<MaskUnion> inject() {
return MaskUnion.class;
}
}

View File

@ -1,42 +1,27 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Various utility functions related to {@link Mask} and {@link Mask2D}.
*/
public final class Masks {
private static final AlwaysTrue ALWAYS_TRUE = new AlwaysTrue();
private static final AlwaysFalse ALWAYS_FALSE = new AlwaysFalse();
protected static final AlwaysTrue ALWAYS_TRUE = new AlwaysTrue();
protected static final AlwaysFalse ALWAYS_FALSE = new AlwaysFalse();
private Masks() {
}
public static boolean isNull(Mask mask) {
return mask == null || mask == ALWAYS_TRUE;
}
/**
* Return a 3D mask that always returns true;
*
@ -46,6 +31,10 @@ public final class Masks {
return ALWAYS_TRUE;
}
public static Mask alwaysFalse() {
return ALWAYS_FALSE;
}
/**
* Return a 2D mask that always returns true;
*
@ -58,34 +47,11 @@ public final class Masks {
/**
* Negate the given mask.
*
* @param mask the mask
* @param finalMask the mask
* @return a new mask
*/
public static Mask negate(final Mask mask) {
if (mask instanceof AlwaysTrue) {
return ALWAYS_FALSE;
} else if (mask instanceof AlwaysFalse) {
return ALWAYS_TRUE;
}
checkNotNull(mask);
return new AbstractMask() {
@Override
public boolean test(Vector vector) {
return !mask.test(vector);
}
@Nullable
@Override
public Mask2D toMask2D() {
Mask2D mask2d = mask.toMask2D();
if (mask2d != null) {
return negate(mask2d);
} else {
return null;
}
}
};
public static Mask negate(final Mask finalMask) {
return finalMask.inverse();
}
/**
@ -131,7 +97,7 @@ public final class Masks {
};
}
private static class AlwaysTrue implements Mask, Mask2D {
protected static class AlwaysTrue implements Mask, Mask2D {
@Override
public boolean test(Vector vector) {
return true;
@ -147,9 +113,19 @@ public final class Masks {
public Mask2D toMask2D() {
return this;
}
@Override
public Mask and(Mask other) {
return other;
}
@Override
public Mask or(Mask other) {
return other;
}
}
private static class AlwaysFalse implements Mask, Mask2D {
protected static class AlwaysFalse implements Mask, Mask2D {
@Override
public boolean test(Vector vector) {
return false;
@ -165,6 +141,19 @@ public final class Masks {
public Mask2D toMask2D() {
return this;
}
@Override
public Mask and(Mask other) {
return this;
}
@Override
public Mask or(Mask other) {
return other;
}
}
}
public static Class<?> inject() {
return Masks.class;
}
}

View File

@ -19,14 +19,14 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.math.noise.NoiseGenerator;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A mask that uses a noise generator and returns true whenever the noise
* generator returns a value above the given density.

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.math.noise.NoiseGenerator;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A mask that uses a noise generator and returns true whenever the noise
* generator returns a value above the given density.

View File

@ -1,30 +1,12 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Checks whether another mask tests true for a position that is offset
* a given vector.
@ -33,11 +15,12 @@ public class OffsetMask extends AbstractMask {
private Mask mask;
private Vector offset;
private MutableBlockVector mutable = new MutableBlockVector();
/**
* Create a new instance.
*
* @param mask the mask
* @param mask the mask
* @param offset the offset
*/
public OffsetMask(Mask mask, Vector offset) {
@ -87,7 +70,10 @@ public class OffsetMask extends AbstractMask {
@Override
public boolean test(Vector vector) {
return getMask().test(vector.add(offset));
mutable.mutX((vector.getX() + offset.getX()));
mutable.mutY((vector.getY() + offset.getY()));
mutable.mutZ((vector.getZ() + offset.getZ()));
return getMask().test(mutable);
}
@Nullable
@ -101,4 +87,7 @@ public class OffsetMask extends AbstractMask {
}
}
public static Class<?> inject() {
return OffsetMask.class;
}
}

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Checks whether another mask tests true for a position that is offset
* a given vector.

View File

@ -19,13 +19,13 @@
package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.Region;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A mask that tests whether given positions are contained within a region.
*/

View File

@ -0,0 +1,19 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
public class SingleBlockStateBitMask extends AbstractExtentMask {
private final int bitMask;
protected SingleBlockStateBitMask(Extent extent, int bitMask) {
super(extent);
this.bitMask = bitMask;
}
@Override
public boolean test(Vector vector) {
int internalId = getExtent().getBlock(vector).getInternalId();
return (internalId & bitMask) == internalId;
}
}

View File

@ -0,0 +1,29 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
public class SingleBlockStateMask extends AbstractExtentMask {
private final BlockStateHolder state;
public BlockStateHolder getBlockState() {
return state;
}
public SingleBlockStateMask(Extent extent, BlockStateHolder state) {
super(extent);
this.state = state;
}
@Override
public boolean test(Vector vector) {
return state.equals(getExtent().getBlock(vector));
}
@Override
public Mask inverse() {
return new BlockMaskBuilder().add(state).build(getExtent()).inverse();
}
}

View File

@ -0,0 +1,30 @@
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
public class SingleBlockTypeMask extends AbstractExtentMask {
private final int internalId;
public SingleBlockTypeMask(Extent extent, BlockType type) {
super(extent);
this.internalId = type.getInternalId();
}
@Override
public boolean test(Vector vector) {
return getExtent().getBlockType(vector).getInternalId() == internalId;
}
@Override
public Mask inverse() {
return new BlockMaskBuilder().add(BlockTypes.get(internalId)).build(getExtent()).inverse();
}
public BlockType getBlockType() {
return BlockTypes.get(internalId);
}
}

View File

@ -1,41 +1,25 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.mask;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockTypes;
import javax.annotation.Nullable;
public class SolidBlockMask extends AbstractExtentMask {
public class SolidBlockMask extends BlockTypeMask {
public SolidBlockMask(Extent extent) {
super(extent);
public static boolean[] getTypes() {
boolean[] types = new boolean[BlockTypes.size()];
for (BlockTypes type : BlockTypes.values) {
types[type.ordinal()] = type.getMaterial().isSolid();
}
return types;
}
@Override
public boolean test(Vector vector) {
Extent extent = getExtent();
BlockState block = extent.getBlock(vector);
return block.getBlockType().getMaterial().isMovementBlocker();
public SolidBlockMask(Extent extent) {
super(extent, getTypes());
}
@Nullable
@ -44,4 +28,7 @@ public class SolidBlockMask extends AbstractExtentMask {
return null;
}
}
public static Class<?> inject() {
return SolidBlockMask.class;
}
}

View File

@ -0,0 +1,82 @@
package com.sk89q.worldedit.function.operation;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import java.util.List;
public class BackwardsExtentBlockCopy implements Operation {
private final Region region;
private final Transform transform;
private final Extent destination;
private final Extent source;
private final RegionFunction function;
private final Vector origin;
private Vector mutable = new MutableBlockVector();
public BackwardsExtentBlockCopy(Extent source, Region region, Extent destination, Vector origin, Transform transform, RegionFunction function) {
this.source = source;
this.region = region;
this.destination = destination;
this.transform = transform;
this.function = function;
this.origin = origin;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
CuboidRegion destRegion = transform(this.transform, this.region);
Transform inverse = this.transform.inverse();
for (Vector pt : destRegion) {
Vector copyFrom = transform(inverse, pt);
if (region.contains(copyFrom)) {
function.apply(pt);
}
}
return null;
}
private CuboidRegion transform(Transform transform, Region region) {
Vector min = new MutableBlockVector(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
Vector max = new MutableBlockVector(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
Vector pos1 = region.getMinimumPoint();
Vector pos2 = region.getMaximumPoint();
for (int x : new int[] { pos1.getBlockX(), pos2.getBlockX() }) {
for (int y : new int[] { pos1.getBlockY(), pos2.getBlockY() }) {
for (int z : new int[] { pos1.getBlockZ(), pos2.getBlockZ() }) {
Vector pt = transform(transform, new Vector(x, y, z)).toBlockVector();
min = Vector.getMinimum(min, pt);
max = Vector.getMaximum(max, pt);
}
}
}
return new CuboidRegion(min, max);
}
private Vector transform(Transform transform, Vector pt) {
mutable.mutX(((pt.getBlockX() - origin.getBlockX())));
mutable.mutY(((pt.getBlockY() - origin.getBlockY())));
mutable.mutZ(((pt.getBlockZ() - origin.getBlockZ())));
Vector tmp = transform.apply(mutable);
tmp.mutX((tmp.getBlockX() + origin.getBlockX()));
tmp.mutY((tmp.getBlockY() + origin.getBlockY()));
tmp.mutZ((tmp.getBlockZ() + origin.getBlockZ()));
return tmp;
}
@Override
public void cancel() {
}
@Override
public void addStatusMessages(List<String> messages) {
}
}

View File

@ -19,18 +19,20 @@
package com.sk89q.worldedit.function.operation;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.Extent;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Sets block from an iterator of {@link Map.Entry} containing a
* {@link BlockVector} as the key and a {@link BaseBlock} as the value.

View File

@ -19,16 +19,18 @@
package com.sk89q.worldedit.function.operation;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import java.util.Iterator;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Performs an undo or redo from a given {@link ChangeSet}.
*/
@ -44,18 +46,18 @@ public class ChangeSetExecutor implements Operation {
* Create a new instance.
*
* @param changeSet the change set
* @param type type of change
* @param context the undo context
* @param type type of change
* @param context the undo context
*/
private ChangeSetExecutor(ChangeSet changeSet, Type type, UndoContext context) {
private ChangeSetExecutor(ChangeSet changeSet, Type type, UndoContext context, BlockBag blockBag, int inventory) {
checkNotNull(changeSet);
checkNotNull(type);
checkNotNull(context);
this.type = type;
this.context = context;
if (type == Type.UNDO) {
if (changeSet instanceof FaweChangeSet) {
iterator = ((FaweChangeSet) changeSet).getIterator(blockBag, inventory, type == Type.REDO);
} else if (type == Type.UNDO) {
iterator = changeSet.backwardIterator();
} else {
iterator = changeSet.forwardIterator();
@ -64,15 +66,15 @@ public class ChangeSetExecutor implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
while (iterator.hasNext()) {
Change change = iterator.next();
if (type == Type.UNDO) {
change.undo(context);
} else {
change.redo(context);
if (type == Type.UNDO) {
while (iterator.hasNext()) {
iterator.next().undo(context);
}
} else {
while (iterator.hasNext()) {
iterator.next().redo(context);
}
}
return null;
}
@ -84,26 +86,35 @@ public class ChangeSetExecutor implements Operation {
public void addStatusMessages(List<String> messages) {
}
public static ChangeSetExecutor create(ChangeSet changeSet, UndoContext context, Type type, BlockBag blockBag, int inventory) {
return new ChangeSetExecutor(changeSet, type, context, blockBag, inventory);
}
/**
* Create a new undo operation.
*
* @param changeSet the change set
* @param context an undo context
* @param context an undo context
* @return an operation
*/
@Deprecated
public static ChangeSetExecutor createUndo(ChangeSet changeSet, UndoContext context) {
return new ChangeSetExecutor(changeSet, Type.UNDO, context);
return new ChangeSetExecutor(changeSet, Type.UNDO, context, null, 0);
}
/**
* Create a new redo operation.
*
* @param changeSet the change set
* @param context an undo context
* @param context an undo context
* @return an operation
*/
@Deprecated
public static ChangeSetExecutor createRedo(ChangeSet changeSet, UndoContext context) {
return new ChangeSetExecutor(changeSet, Type.REDO, context);
return new ChangeSetExecutor(changeSet, Type.REDO, context, null, 0);
}
public static Class<?> inject() {
return ChangeSetExecutor.class;
}
}

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function.operation;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.WorldEditException;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Executes a delegete operation, but returns to another operation upon
* completing the delegate.

View File

@ -19,33 +19,45 @@
package com.sk89q.worldedit.function.operation;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.extent.BlockTranslateExtent;
import com.boydti.fawe.object.extent.PositionTransformExtent;
import com.boydti.fawe.object.function.block.BiomeCopy;
import com.boydti.fawe.object.function.block.CombinedBlockCopy;
import com.boydti.fawe.object.function.block.SimpleBlockCopy;
import com.boydti.fawe.util.MaskTraverser;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.metadata.EntityProperties;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.function.CombinedRegionFunction;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.RegionMaskTestFunction;
import com.sk89q.worldedit.function.RegionMaskingFilter;
import com.sk89q.worldedit.function.block.ExtentBlockCopy;
import com.sk89q.worldedit.function.entity.ExtentEntityCopy;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.visitor.EntityVisitor;
import com.sk89q.worldedit.function.visitor.IntersectRegionFunction;
import com.sk89q.worldedit.function.visitor.RegionVisitor;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.Identity;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Makes a copy of a portion of one extent to another extent or another point.
*
* <p>
* <p>This is a forward extent copy, meaning that it iterates over the blocks
* in the source extent, and will copy as many blocks as there are in the
* source. Therefore, interpolation will not occur to fill in the gaps.</p>
@ -60,21 +72,22 @@ public class ForwardExtentCopy implements Operation {
private int repetitions = 1;
private Mask sourceMask = Masks.alwaysTrue();
private boolean removingEntities;
private boolean copyingEntities = true; // default to true for backwards compatibility, sort of
private RegionFunction sourceFunction = null;
private Transform transform = new Identity();
private Transform currentTransform = null;
private RegionVisitor lastVisitor;
private int affected;
private boolean copyEntities = true;
private boolean copyBiomes = false;
private RegionFunction filterFunction;
/**
* Create a new copy using the region's lowest minimum point as the
* "from" position.
*
* @param source the source extent
* @param region the region to copy
* @param source the source extent
* @param region the region to copy
* @param destination the destination extent
* @param to the destination position
* @param to the destination position
* @see #ForwardExtentCopy(Extent, Region, Vector, Extent, Vector) the main constructor
*/
public ForwardExtentCopy(Extent source, Region region, Extent destination, Vector to) {
@ -84,11 +97,11 @@ public class ForwardExtentCopy implements Operation {
/**
* Create a new copy.
*
* @param source the source extent
* @param region the region to copy
* @param from the source position
* @param source the source extent
* @param region the region to copy
* @param from the source position
* @param destination the destination extent
* @param to the destination position
* @param to the destination position
*/
public ForwardExtentCopy(Extent source, Region region, Vector from, Extent destination, Vector to) {
checkNotNull(source);
@ -105,7 +118,7 @@ public class ForwardExtentCopy implements Operation {
/**
* Get the transformation that will occur on every point.
*
* <p>
* <p>The transformation will stack with each repetition.</p>
*
* @return a transformation
@ -127,7 +140,7 @@ public class ForwardExtentCopy implements Operation {
/**
* Get the mask that gets applied to the source extent.
*
* <p>
* <p>This mask can be used to filter what will be copied from the source.</p>
*
* @return a source mask
@ -136,6 +149,22 @@ public class ForwardExtentCopy implements Operation {
return sourceMask;
}
public void setCopyEntities(boolean copyEntities) {
this.copyEntities = copyEntities;
}
public boolean isCopyEntities() {
return copyEntities;
}
public void setCopyBiomes(boolean copyBiomes) {
this.copyBiomes = copyBiomes;
}
public boolean isCopyBiomes() {
return copyBiomes;
}
/**
* Set a mask that gets applied to the source extent.
*
@ -147,6 +176,10 @@ public class ForwardExtentCopy implements Operation {
this.sourceMask = sourceMask;
}
public void setFilterFunction(RegionFunction filterFunction) {
this.filterFunction = filterFunction;
}
/**
* Get the function that gets applied to all source blocks <em>after</em>
* the copy has been made.
@ -163,6 +196,7 @@ public class ForwardExtentCopy implements Operation {
*
* @param function a source function, or null if none is to be applied
*/
@Deprecated
public void setSourceFunction(RegionFunction function) {
this.sourceFunction = function;
}
@ -186,24 +220,6 @@ public class ForwardExtentCopy implements Operation {
this.repetitions = repetitions;
}
/**
* Return whether entities should be copied along with blocks.
*
* @return true if copying
*/
public boolean isCopyingEntities() {
return copyingEntities;
}
/**
* Set whether entities should be copied along with blocks.
*
* @param copyingEntities true if copying
*/
public void setCopyingEntities(boolean copyingEntities) {
this.copyingEntities = copyingEntities;
}
/**
* Return whether entities that are copied should be removed.
*
@ -233,43 +249,124 @@ public class ForwardExtentCopy implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
if (lastVisitor != null) {
affected += lastVisitor.getAffected();
lastVisitor = null;
if (currentTransform == null) {
currentTransform = transform;
}
FaweQueue queue;
if (source instanceof EditSession) {
queue = ((EditSession) source).getQueue();
} else if (destination instanceof EditSession) {
queue = ((EditSession) destination).getQueue();
} else {
queue = null;
}
if (repetitions > 0) {
repetitions--;
Extent finalDest = destination;
Vector translation = to.subtract(from);
if (currentTransform == null) {
currentTransform = transform;
if (!translation.equals(Vector.ZERO)) {
finalDest = new BlockTranslateExtent(finalDest, translation.getBlockX(), translation.getBlockY(), translation.getBlockZ());
}
RegionFunction copy;
Operation blockCopy = null;
PositionTransformExtent transExt = null;
if (!currentTransform.isIdentity()) {
if (!(currentTransform instanceof AffineTransform) || ((AffineTransform) currentTransform).isOffAxis()) {
transExt = new PositionTransformExtent(source, currentTransform.inverse());
transExt.setOrigin(from);
copy = new SimpleBlockCopy(transExt, finalDest);
if (this.filterFunction != null) {
copy = new IntersectRegionFunction(filterFunction, copy);
}
if (sourceFunction != null) {
copy = CombinedRegionFunction.combine(copy, sourceFunction);
}
if (sourceMask != Masks.alwaysTrue()) {
new MaskTraverser(sourceMask).reset(transExt);
copy = new RegionMaskingFilter(sourceMask, copy);
}
if (copyBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) {
copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest));
}
blockCopy = new BackwardsExtentBlockCopy(transExt, region, finalDest, from, transform, copy);
} else {
currentTransform = currentTransform.combine(transform);
transExt = new PositionTransformExtent(finalDest, currentTransform);
transExt.setOrigin(from);
finalDest = transExt;
}
}
ExtentBlockCopy blockCopy = new ExtentBlockCopy(source, from, destination, to, currentTransform);
RegionMaskingFilter filter = new RegionMaskingFilter(sourceMask, blockCopy);
RegionFunction function = sourceFunction != null ? new CombinedRegionFunction(filter, sourceFunction) : filter;
RegionVisitor blockVisitor = new RegionVisitor(region, function);
if (blockCopy == null) {
RegionFunction maskFunc = null;
lastVisitor = blockVisitor;
if (sourceFunction != null) {
Vector disAbs = translation.positive();
Vector size = region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
boolean overlap = (disAbs.getBlockX() < size.getBlockX() && disAbs.getBlockY() < size.getBlockY() && disAbs.getBlockZ() < size.getBlockZ());
if (copyingEntities) {
RegionFunction copySrcFunc = sourceFunction;
if (overlap && translation.length() != 0) {
MutableBlockVector mutable = new MutableBlockVector();
int x = translation.getBlockX();
int y = translation.getBlockY();
int z = translation.getBlockZ();
maskFunc = position -> {
mutable.setComponents(position.getBlockX() + x, position.getBlockY() + y, position.getBlockZ() + z);
if (region.contains(mutable)) {
return sourceFunction.apply(mutable);
}
return false;
};
copySrcFunc = position -> {
mutable.setComponents(position.getBlockX() - x, position.getBlockY() - y, position.getBlockZ() - z);
if (!region.contains(mutable)) {
return sourceFunction.apply(position);
}
return false;
};
}
copy = new CombinedBlockCopy(source, finalDest, copySrcFunc);
}
else {
copy = new SimpleBlockCopy(source, finalDest);
}
if (this.filterFunction != null) {
copy = new IntersectRegionFunction(filterFunction, copy);
}
if (sourceMask != Masks.alwaysTrue()) {
if (maskFunc != null) copy = new RegionMaskTestFunction(sourceMask, copy, maskFunc);
else copy = new RegionMaskingFilter(sourceMask, copy);
}
if (copyBiomes && (!(source instanceof BlockArrayClipboard) || ((BlockArrayClipboard) source).IMP.hasBiomes())) {
copy = CombinedRegionFunction.combine(copy, new BiomeCopy(source, finalDest));
}
blockCopy = new RegionVisitor(region, copy, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null);
}
List<? extends Entity> entities = isCopyEntities() ? source.getEntities(region) : new ArrayList<>();
for (int i = 0; i < repetitions; i++) {
Operations.completeBlindly(blockCopy);
if (!entities.isEmpty()) {
ExtentEntityCopy entityCopy = new ExtentEntityCopy(from, destination, to, currentTransform);
entityCopy.setRemoving(removingEntities);
List<? extends Entity> entities = Lists.newArrayList(source.getEntities(region));
entities.removeIf(entity -> {
EntityProperties properties = entity.getFacet(EntityProperties.class);
return properties != null && !properties.isPasteable();
});
EntityVisitor entityVisitor = new EntityVisitor(entities.iterator(), entityCopy);
return new DelegateOperation(this, new OperationQueue(blockVisitor, entityVisitor));
} else {
return new DelegateOperation(this, blockVisitor);
Operations.completeBlindly(entityVisitor);
}
} else {
return null;
if (transExt != null) {
currentTransform = currentTransform.combine(transform);
transExt.setTransform(currentTransform);
}
}
affected = region.getArea();
return null;
}
@Override
@ -280,4 +377,7 @@ public class ForwardExtentCopy implements Operation {
public void addStatusMessages(List<String> messages) {
}
public static Class<?> inject() {
return ForwardExtentCopy.class;
}
}

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.function.operation;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
import com.sk89q.worldedit.WorldEditException;
@ -29,6 +27,8 @@ import java.util.Collection;
import java.util.Deque;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Executes multiple queues in order.
*/

View File

@ -30,15 +30,17 @@ public final class Operations {
private Operations() {
}
private static RunContext context = new RunContext();
/**
* Complete a given operation synchronously until it completes.
*
* @param op operation to execute
* @param operation operation to execute
* @throws WorldEditException WorldEdit exception
*/
public static void complete(Operation op) throws WorldEditException {
while (op != null) {
op = op.resume(new RunContext());
public static void complete(Operation operation) throws WorldEditException {
while (operation != null) {
operation = operation.resume(context);
}
}
@ -46,19 +48,11 @@ public final class Operations {
* Complete a given operation synchronously until it completes. Catch all
* errors that is not {@link MaxChangedBlocksException} for legacy reasons.
*
* @param op operation to execute
* @param operation operation to execute
* @throws MaxChangedBlocksException thrown when too many blocks have been changed
*/
public static void completeLegacy(Operation op) throws MaxChangedBlocksException {
while (op != null) {
try {
op = op.resume(new RunContext());
} catch (MaxChangedBlocksException e) {
throw e;
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
public static void completeLegacy(Operation operation) throws MaxChangedBlocksException {
completeBlindly(operation);
}
/**
@ -66,16 +60,27 @@ public final class Operations {
* {@link com.sk89q.worldedit.WorldEditException} exceptions as
* {@link java.lang.RuntimeException}s.
*
* @param op operation to execute
* @param operation operation to execute
*/
public static void completeBlindly(Operation op) {
while (op != null) {
try {
op = op.resume(new RunContext());
} catch (WorldEditException e) {
throw new RuntimeException(e);
public static void completeBlindly(Operation operation) {
try {
while (operation != null) {
operation = operation.resume(context);
}
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
public static void completeSmart(final Operation op, final Runnable whenDone, final boolean threadsafe) {
completeBlindly(op);
if (whenDone != null) {
whenDone.run();
}
return;
}
public static Class<?> inject() {
return Operations.class;
}
}

View File

@ -1,26 +1,12 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.pattern;
/**
* An abstract implementation for {@link Pattern}s.
*/
public abstract class AbstractPattern implements Pattern {
import java.io.Serializable;
public abstract class AbstractPattern implements Pattern, Serializable {
public AbstractPattern() {
}
public static Class<?> inject() {
return AbstractPattern.class;
}
}

View File

@ -1,44 +1,30 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.pattern;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
/**
* A pattern that returns the same {@link BaseBlock} each time.
* @deprecated Just use BaseBlock directly
*/
public class BlockPattern extends AbstractPattern {
@Deprecated
public class BlockPattern implements Pattern {
private BlockStateHolder block;
/**
* Create a new pattern with the given block.
*
* @param block the block
*/
public BlockPattern(BlockStateHolder block) {
setBlock(block);
this.block = block;
}
@Override
public BlockStateHolder apply(Vector position) {
return block;
}
/**
@ -60,9 +46,7 @@ public class BlockPattern extends AbstractPattern {
this.block = block;
}
@Override
public BlockStateHolder apply(Vector position) {
return block;
public static Class<BlockPattern> inject() {
return BlockPattern.class;
}
}

View File

@ -1,37 +1,24 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.pattern;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A pattern that reads from {@link Clipboard}.
*/
public class ClipboardPattern extends AbstractPattern {
private final Clipboard clipboard;
private final Vector size;
private final int sx, sy, sz;
private final Vector min;
private MutableBlockVector mutable = new MutableBlockVector();
/**
* Create a new clipboard pattern.
@ -41,16 +28,28 @@ public class ClipboardPattern extends AbstractPattern {
public ClipboardPattern(Clipboard clipboard) {
checkNotNull(clipboard);
this.clipboard = clipboard;
this.size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint()).add(1, 1, 1);
Vector size = clipboard.getMaximumPoint().subtract(clipboard.getMinimumPoint()).add(1, 1, 1);
this.sx = size.getBlockX();
this.sy = size.getBlockY();
this.sz = size.getBlockZ();
this.min = clipboard.getMinimumPoint();
}
@Override
public BlockStateHolder apply(Vector position) {
int xp = Math.abs(position.getBlockX()) % size.getBlockX();
int yp = Math.abs(position.getBlockY()) % size.getBlockY();
int zp = Math.abs(position.getBlockZ()) % size.getBlockZ();
return clipboard.getFullBlock(clipboard.getMinimumPoint().add(new Vector(xp, yp, zp)));
int xp = position.getBlockX() % sx;
int yp = position.getBlockY() % sy;
int zp = position.getBlockZ() % sz;
if (xp < 0) xp += sx;
if (yp < 0) yp += sy;
if (zp < 0) zp += sz;
mutable.mutX((min.getX() + xp));
mutable.mutY((min.getY() + yp));
mutable.mutZ((min.getZ() + zp));
return clipboard.getBlock(mutable);
}
}
public static Class<?> inject() {
return ClipboardPattern.class;
}
}

View File

@ -0,0 +1,32 @@
package com.sk89q.worldedit.function.pattern;
import com.sk89q.minecraft.util.commands.Link;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.command.UtilityCommands;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.NullExtent;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockState;
/**
* Returns a {@link BlockStateHolder} for a given position.
* - Adapts the vector apply to integer
*/
@Link(clazz = UtilityCommands.class, value = "patterns")
public interface FawePattern extends Pattern {
@Deprecated
default BlockStateHolder apply(Vector position) {
throw new UnsupportedOperationException("Please use apply(extent, get, set)");
}
/**
* Return a {@link BlockStateHolder} for the given position.
*
* @return a block
*/
@Override
boolean apply(Extent extent, Vector get, Vector set) throws WorldEditException;
}

View File

@ -19,13 +19,36 @@
package com.sk89q.worldedit.function.pattern;
import com.sk89q.minecraft.util.commands.Link;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.command.UtilityCommands;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.NullExtent;
import com.sk89q.worldedit.internal.expression.runtime.Return;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockState;
/**
* Returns a {@link BlockStateHolder} for a given position.
* @deprecated Use FawePattern
*/
public interface Pattern {
@Link(clazz = UtilityCommands.class, value = "patterns")
@Deprecated
public interface Pattern extends com.sk89q.worldedit.patterns.Pattern{
@Override
default BaseBlock next(Vector position) {
return new BaseBlock(apply(position));
}
@Override
default BaseBlock next(int x, int y, int z) {
return new BaseBlock(apply(new Vector(x, y, z)));
}
/**
* Return a {@link BlockStateHolder} for the given position.
@ -33,6 +56,10 @@ public interface Pattern {
* @param position the position
* @return a block
*/
@Deprecated
BlockStateHolder apply(Vector position);
}
default boolean apply(Extent extent, Vector get, Vector set) throws WorldEditException {
return extent.setBlock(set, apply(get));
}
}

View File

@ -1,88 +1,79 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.pattern;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.object.collection.RandomCollection;
import com.boydti.fawe.object.random.SimpleRandom;
import com.boydti.fawe.object.random.TrueRandom;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Uses a random pattern of a weighted list of patterns.
*/
public class RandomPattern extends AbstractPattern {
private final Random random = new Random();
private List<Chance> patterns = new ArrayList<>();
private double max = 0;
private final SimpleRandom random;
private Map<Pattern, Double> weights = new HashMap<>();
private RandomCollection<Pattern> collection;
private LinkedHashSet<Pattern> patterns = new LinkedHashSet<>();
public RandomPattern() {
this(new TrueRandom());
}
public RandomPattern(SimpleRandom random) {
this.random = random;
}
/**
* Add a pattern to the weight list of patterns.
*
* <p>
* <p>The probability for the pattern added is chance / max where max is
* the sum of the probabilities of all added patterns.</p>
*
* @param pattern the pattern
* @param chance the chance, which can be any positive number
* @param chance the chance, which can be any positive number
*/
public void add(Pattern pattern, double chance) {
checkNotNull(pattern);
patterns.add(new Chance(pattern, chance));
max += chance;
Double existingWeight = weights.get(pattern);
if (existingWeight != null) chance += existingWeight;
weights.put(pattern, chance);
collection = RandomCollection.of(weights, random);
this.patterns.add(pattern);
}
public Set<Pattern> getPatterns() {
return patterns;
}
public RandomCollection<Pattern> getCollection() {
return collection;
}
@Override
public BlockStateHolder apply(Vector position) {
double r = random.nextDouble();
double offset = 0;
for (Chance chance : patterns) {
if (r <= (offset + chance.getChance()) / max) {
return chance.getPattern().apply(position);
}
offset += chance.getChance();
}
throw new RuntimeException("ProportionalFillPattern");
public BlockStateHolder apply(Vector get) {
return collection.next(get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(get);
}
private static class Chance {
private Pattern pattern;
private double chance;
private Chance(Pattern pattern, double chance) {
this.pattern = pattern;
this.chance = chance;
}
public Pattern getPattern() {
return pattern;
}
public double getChance() {
return chance;
}
@Override
public boolean apply(Extent extent, Vector set, Vector get) throws WorldEditException {
return collection.next(get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, set, get);
}
}
public static Class<?> inject() {
return RandomPattern.class;
}
}

View File

@ -19,11 +19,12 @@
package com.sk89q.worldedit.function.pattern;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.extent.Extent;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Returns the blocks from {@link Extent}, repeating when out of bounds.
@ -89,7 +90,6 @@ public class RepeatingExtentPattern extends AbstractPattern {
int x = base.getBlockX() % size.getBlockX();
int y = base.getBlockY() % size.getBlockY();
int z = base.getBlockZ() % size.getBlockZ();
return extent.getFullBlock(new Vector(x, y, z));
return extent.getBlock(new Vector(x, y, z));
}
}

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function.util;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.FlatRegionFunction;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Offsets the position parameter by adding a given offset vector.
*/

View File

@ -19,12 +19,12 @@
package com.sk89q.worldedit.function.util;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Offsets the position parameter by adding a given offset vector.
*/

View File

@ -1,190 +1,221 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.visitor;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.BlockVector;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.IntegerTrio;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Queue;
import java.util.Set;
/**
* Performs a breadth-first search starting from points added with
* {@link #visit(com.sk89q.worldedit.Vector)}. The search continues
* to a certain adjacent point provided that the method
* {@link #isVisitable(com.sk89q.worldedit.Vector, com.sk89q.worldedit.Vector)}
* returns true for that point.
*
* <p>As an abstract implementation, this class can be used to implement
* functionality that starts at certain points and extends outward from
* those points.</p>
*/
public abstract class BreadthFirstSearch implements Operation {
private final RegionFunction function;
private final Queue<BlockVector> queue = new ArrayDeque<>();
private final Set<BlockVector> visited = new HashSet<>();
private final List<Vector> directions = new ArrayList<>();
private int affected = 0;
public static final Vector[] DEFAULT_DIRECTIONS = new Vector[6];
public static final Vector[] DIAGONAL_DIRECTIONS;
/**
* Create a new instance.
*
* @param function the function to apply to visited blocks
*/
protected BreadthFirstSearch(RegionFunction function) {
checkNotNull(function);
this.function = function;
addAxes();
}
/**
* Get the list of directions will be visited.
*
* <p>Directions are {@link com.sk89q.worldedit.Vector}s that determine
* what adjacent points area available. Vectors should not be
* unit vectors. An example of a valid direction is
* {@code new Vector(1, 0, 1)}.</p>
*
* <p>The list of directions can be cleared.</p>
*
* @return the list of directions
*/
protected Collection<Vector> getDirections() {
return directions;
}
/**
* Add the directions along the axes as directions to visit.
*/
protected void addAxes() {
directions.add(new Vector(0, -1, 0));
directions.add(new Vector(0, 1, 0));
directions.add(new Vector(-1, 0, 0));
directions.add(new Vector(1, 0, 0));
directions.add(new Vector(0, 0, -1));
directions.add(new Vector(0, 0, 1));
}
/**
* Add the diagonal directions as directions to visit.
*/
protected void addDiagonal() {
directions.add(new Vector(1, 0, 1));
directions.add(new Vector(-1, 0, -1));
directions.add(new Vector(1, 0, -1));
directions.add(new Vector(-1, 0, 1));
}
/**
* Add the given location to the list of locations to visit, provided
* that it has not been visited. The position passed to this method
* will still be visited even if it fails
* {@link #isVisitable(com.sk89q.worldedit.Vector, com.sk89q.worldedit.Vector)}.
*
* <p>This method should be used before the search begins, because if
* the position <em>does</em> fail the test, and the search has already
* visited it (because it is connected to another root point),
* the search will mark the position as "visited" and a call to this
* method will do nothing.</p>
*
* @param position the position
*/
public void visit(Vector position) {
BlockVector blockVector = position.toBlockVector();
if (!visited.contains(blockVector)) {
queue.add(blockVector);
visited.add(blockVector);
}
}
/**
* Try to visit the given 'to' location.
*
* @param from the origin block
* @param to the block under question
*/
private void visit(Vector from, Vector to) {
BlockVector blockVector = to.toBlockVector();
if (!visited.contains(blockVector)) {
visited.add(blockVector);
if (isVisitable(from, to)) {
queue.add(blockVector);
static {
DEFAULT_DIRECTIONS[0] = (new MutableBlockVector(0, -1, 0));
DEFAULT_DIRECTIONS[1] = (new MutableBlockVector(0, 1, 0));
DEFAULT_DIRECTIONS[2] = (new MutableBlockVector(-1, 0, 0));
DEFAULT_DIRECTIONS[3] = (new MutableBlockVector(1, 0, 0));
DEFAULT_DIRECTIONS[4] = (new MutableBlockVector(0, 0, -1));
DEFAULT_DIRECTIONS[5] = (new MutableBlockVector(0, 0, 1));
List<MutableBlockVector> list = new ArrayList<>();
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
if (x != 0 || y != 0 || z != 0) {
MutableBlockVector pos = new MutableBlockVector(x, y, z);
if (!list.contains(pos)) {
list.add(pos);
}
}
}
}
}
Collections.sort(list, new Comparator<Vector>() {
@Override
public int compare(Vector o1, Vector o2) {
return (int) Math.signum(o1.lengthSq() - o2.lengthSq());
}
});
DIAGONAL_DIRECTIONS = list.toArray(new Vector[list.size()]);
}
/**
* Return whether the given 'to' block should be visited, starting from the
* 'from' block.
*
* @param from the origin block
* @param to the block under question
* @return true if the 'to' block should be visited
*/
protected abstract boolean isVisitable(Vector from, Vector to);
private final RegionFunction function;
private List<Vector> directions = new ArrayList<>();
private BlockVectorSet visited;
private final MappedFaweQueue mFaweQueue;
private BlockVectorSet queue;
private int currentDepth = 0;
private final int maxDepth;
private int affected = 0;
private int maxBranch = Integer.MAX_VALUE;
/**
* Get the number of affected objects.
*
* @return the number of affected
*/
public int getAffected() {
return affected;
public BreadthFirstSearch(final RegionFunction function) {
this(function, Integer.MAX_VALUE);
}
public BreadthFirstSearch(final RegionFunction function, int maxDepth) {
this(function, maxDepth, null);
}
public BreadthFirstSearch(final RegionFunction function, int maxDepth, HasFaweQueue faweQueue) {
FaweQueue fq = faweQueue != null ? faweQueue.getQueue() : null;
this.mFaweQueue = fq instanceof MappedFaweQueue ? (MappedFaweQueue) fq : null;
this.queue = new BlockVectorSet();
this.visited = new BlockVectorSet();
this.function = function;
this.directions.addAll(Arrays.asList(DEFAULT_DIRECTIONS));
this.maxDepth = maxDepth;
}
public abstract boolean isVisitable(Vector from, Vector to);
public Collection<Vector> getDirections() {
return this.directions;
}
public void setDirections(List<Vector> directions) {
this.directions = directions;
}
private IntegerTrio[] getIntDirections() {
IntegerTrio[] array = new IntegerTrio[directions.size()];
for (int i = 0; i < array.length; i++) {
Vector dir = directions.get(i);
array[i] = new IntegerTrio(dir.getBlockX(), dir.getBlockY(), dir.getBlockZ());
}
return array;
}
public void visit(final Vector pos) {
if (!isVisited(pos)) {
isVisitable(pos, pos); // Ignore this, just to initialize mask on this point
queue.add(pos);
visited.add(pos);
}
}
public void resetVisited() {
queue.clear();
visited.clear();
affected = 0;
}
public void setVisited(BlockVectorSet set) {
this.visited = set;
}
public BlockVectorSet getVisited() {
return visited;
}
public boolean isVisited(Vector pos) {
return visited.contains(pos);
}
public void setMaxBranch(int maxBranch) {
this.maxBranch = maxBranch;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
Vector position;
while ((position = queue.poll()) != null) {
if (function.apply(position)) {
affected++;
MutableBlockVector mutable = new MutableBlockVector();
MutableBlockVector mutable2 = new MutableBlockVector();
boolean shouldTrim = false;
IntegerTrio[] dirs = getIntDirections();
BlockVectorSet tempQueue = new BlockVectorSet();
BlockVectorSet chunkLoadSet = new BlockVectorSet();
for (currentDepth = 0; !queue.isEmpty() && currentDepth <= maxDepth; currentDepth++) {
if (mFaweQueue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS > 1) {
int cx = Integer.MIN_VALUE;
int cz = Integer.MIN_VALUE;
for (Vector from : queue) {
for (IntegerTrio direction : dirs) {
int x = from.getBlockX() + direction.x;
int z = from.getBlockZ() + direction.z;
if (cx != (cx = x >> 4) || cz != (cz = z >> 4)) {
int y = from.getBlockY() + direction.y;
if (y < 0 || y >= 256) {
continue;
}
if (!visited.contains(x, y, z)) {
chunkLoadSet.add(cx, 0, cz);
}
}
}
}
for (Vector chunk : chunkLoadSet) {
mFaweQueue.queueChunkLoad(chunk.getBlockX(), chunk.getBlockZ());
}
}
for (Vector from : queue) {
if (function.apply(from)) affected++;
for (int i = 0, j = 0; i < dirs.length && j < maxBranch; i++) {
IntegerTrio direction = dirs[i];
int y = from.getBlockY() + direction.y;
if (y < 0 || y >= 256) {
continue;
}
int x = from.getBlockX() + direction.x;
int z = from.getBlockZ() + direction.z;
if (!visited.contains(x, y, z)) {
mutable2.mutX(x);
mutable2.mutY(y);
mutable2.mutZ(z);
if (isVisitable(from, mutable2)) {
j++;
visited.add(x, y, z);
tempQueue.add(x, y, z);
}
}
}
}
if (currentDepth == maxDepth) {
break;
}
int size = queue.size();
BlockVectorSet tmp = queue;
queue = tempQueue;
tmp.clear();
chunkLoadSet.clear();
tempQueue = tmp;
for (Vector dir : directions) {
visit(position, position.add(dir));
}
}
return null;
}
public int getDepth() {
return currentDepth;
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(BBC.VISITOR_BLOCK.format(getAffected()));
}
public int getAffected() {
return this.affected;
}
@Override
public void cancel() {
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(getAffected() + " blocks affected");
public static Class<?> inject() {
return BreadthFirstSearch.class;
}
}

View File

@ -0,0 +1,86 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Visits adjacent points on the same X-Z plane as long as the points
* pass the given mask, and then executes the provided region
* function on the entire column.
* <p>
* <p>This is used by {@code //fill}.</p>
*/
public class DirectionalVisitor extends RecursiveVisitor {
private final Vector origin;
private final Vector dirVec;
public DirectionalVisitor(Mask mask, RegionFunction function, Vector origin, Vector direction) {
this(mask, function, origin, direction, Integer.MAX_VALUE, null);
}
public DirectionalVisitor(Mask mask, RegionFunction function, Vector origin, Vector direction, int distance, HasFaweQueue hasFaweQueue) {
super(mask, function, distance, hasFaweQueue);
checkNotNull(mask);
this.origin = origin;
this.dirVec = new MutableBlockVector(direction);
final Collection<Vector> directions = this.getDirections();
directions.clear();
directions.add(new Vector(1, 0, 0));
directions.add(new Vector(-1, 0, 0));
directions.add(new Vector(0, 0, 1));
directions.add(new Vector(0, 0, -1));
directions.add(new Vector(0, -1, 0));
directions.add(new Vector(0, 1, 0));
}
@Override
public boolean isVisitable(final Vector from, final Vector to) {
int dx = to.getBlockX() - from.getBlockX();
int dz = to.getBlockZ() - from.getBlockZ();
int dy = to.getBlockY() - from.getBlockY();
if (dx != 0) {
if (dirVec.getBlockX() != 0 && dirVec.getBlockX() != dx) {
return false;
}
}
if (dy != 0) {
if (dirVec.getBlockY() != 0 && dirVec.getBlockY() != dy) {
return false;
}
}
if (dz != 0) {
if (dirVec.getBlockZ() != 0 && dirVec.getBlockZ() != dz) {
return false;
}
}
return super.isVisitable(from, to);
}
}

View File

@ -19,39 +19,42 @@
package com.sk89q.worldedit.function.visitor;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Visits adjacent points on the same X-Z plane as long as the points
* pass the given mask, and then executes the provided region
* function on the entire column.
*
* <p>
* <p>This is used by {@code //fill}.</p>
*/
public class DownwardVisitor extends RecursiveVisitor {
private int baseY;
private final int baseY;
/**
* Create a new visitor.
*
* @param mask the mask
* @param mask the mask
* @param function the function
* @param baseY the base Y
* @param baseY the base Y
*/
public DownwardVisitor(Mask mask, RegionFunction function, int baseY) {
super(mask, function);
this(mask, function, baseY, Integer.MAX_VALUE, null);
}
public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, HasFaweQueue hasFaweQueue) {
super(mask, function, depth, hasFaweQueue);
checkNotNull(mask);
this.baseY = baseY;
Collection<Vector> directions = getDirections();
final Collection<Vector> directions = this.getDirections();
directions.clear();
directions.add(new Vector(1, 0, 0));
directions.add(new Vector(-1, 0, 0));
@ -61,8 +64,12 @@ public class DownwardVisitor extends RecursiveVisitor {
}
@Override
protected boolean isVisitable(Vector from, Vector to) {
int fromY = from.getBlockY();
return (fromY == baseY || to.subtract(from).getBlockY() < 0) && super.isVisitable(from, to);
public boolean isVisitable(final Vector from, final Vector to) {
final int fromY = from.getBlockY();
return ((fromY == this.baseY) || (to.getBlockY() - from.getBlockY() < 0)) && super.isVisitable(from, to);
}
public static Class<?> inject() {
return DownwardVisitor.class;
}
}

View File

@ -19,25 +19,27 @@
package com.sk89q.worldedit.function.visitor;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.config.BBC;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.EntityFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.operation.RunContext;
import java.util.Iterator;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Visits entities as provided by an {@code Iterator}.
*/
public class EntityVisitor implements Operation {
private final Iterator<? extends Entity> iterator;
private final EntityFunction function;
private int affected = 0;
private final Iterator<? extends Entity> iterator;
/**
* Create a new instance.
@ -45,11 +47,12 @@ public class EntityVisitor implements Operation {
* @param iterator the iterator
* @param function the function
*/
public EntityVisitor(Iterator<? extends Entity> iterator, EntityFunction function) {
public EntityVisitor(final Iterator<? extends Entity> iterator, final EntityFunction function) {
checkNotNull(iterator);
checkNotNull(function);
this.iterator = iterator;
this.function = function;
this.iterator = iterator;
}
/**
@ -58,17 +61,16 @@ public class EntityVisitor implements Operation {
* @return the number of affected
*/
public int getAffected() {
return affected;
return this.affected;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
while (iterator.hasNext()) {
if (function.apply(iterator.next())) {
public Operation resume(final RunContext run) throws WorldEditException {
while (this.iterator.hasNext()) {
if (this.function.apply(this.iterator.next())) {
affected++;
}
}
return null;
}
@ -77,8 +79,11 @@ public class EntityVisitor implements Operation {
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(getAffected() + " entities affected");
public void addStatusMessages(final List<String> messages) {
messages.add(BBC.VISITOR_ENTITY.format(getAffected()));
}
public static Class<?> inject() {
return Operations.class;
}
}

View File

@ -19,38 +19,53 @@
package com.sk89q.worldedit.function.visitor;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.visitor.Fast2DIterator;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.FlatRegionFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.regions.FlatRegion;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Applies region functions to columns in a {@link FlatRegion}.
*/
public class FlatRegionVisitor implements Operation {
private final FlatRegion flatRegion;
private final FlatRegionFunction function;
private MappedFaweQueue queue;
private int affected = 0;
private final Iterable<Vector2D> iterator;
/**
* Create a new visitor.
*
* @param flatRegion a flat region
* @param function a function to apply to columns
* @param function a function to apply to columns
*/
public FlatRegionVisitor(FlatRegion flatRegion, FlatRegionFunction function) {
public FlatRegionVisitor(final FlatRegion flatRegion, final FlatRegionFunction function) {
checkNotNull(flatRegion);
checkNotNull(function);
this.flatRegion = flatRegion;
this.function = function;
this.iterator = flatRegion.asFlatRegion();
}
public FlatRegionVisitor(final FlatRegion flatRegion, final FlatRegionFunction function, HasFaweQueue hasFaweQueue) {
checkNotNull(flatRegion);
checkNotNull(function);
this.function = function;
this.iterator = flatRegion.asFlatRegion();
FaweQueue queue = hasFaweQueue.getQueue();
this.queue = (MappedFaweQueue) (queue instanceof MappedFaweQueue ? queue : null);
}
/**
@ -59,17 +74,20 @@ public class FlatRegionVisitor implements Operation {
* @return the number of affected
*/
public int getAffected() {
return affected;
return this.affected;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
for (Vector2D pt : flatRegion.asFlatRegion()) {
if (function.apply(pt)) {
affected++;
public Operation resume(final RunContext run) throws WorldEditException {
if (this.queue != null) {
for (final Vector2D pt : new Fast2DIterator(this.iterator, queue)) {
if (this.function.apply(pt)) affected++;
}
} else {
for (final Vector2D pt : this.iterator) {
if (this.function.apply(pt)) affected++;
}
}
return null;
}
@ -78,9 +96,12 @@ public class FlatRegionVisitor implements Operation {
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(getAffected() + " columns affected");
public void addStatusMessages(final List<String> messages) {
messages.add(BBC.VISITOR_FLAT.format(getAffected()));
}
public static Class<?> inject() {
return Operations.class;
}
}

View File

@ -0,0 +1,27 @@
package com.sk89q.worldedit.function.visitor;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
public class IntersectRegionFunction implements RegionFunction {
private final RegionFunction[] functions;
public IntersectRegionFunction(RegionFunction... functions) {
this.functions = functions;
}
@Override
public boolean apply(Vector position) throws WorldEditException {
boolean ret = false;
for (RegionFunction function : functions) {
if (!function.apply(position)) {
return ret;
} else {
ret = true;
}
}
return ret;
}
}

View File

@ -19,9 +19,6 @@
package com.sk89q.worldedit.function.visitor;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.WorldEditException;
@ -29,44 +26,48 @@ import com.sk89q.worldedit.function.LayerFunction;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.regions.FlatRegion;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Visits the layers within a region.
*
* <p>
* <p>This class works by iterating over all the columns in a {@link FlatRegion},
* finding the first ground block in each column (searching from a given
* maximum Y down to a minimum Y), and then applies a {@link LayerFunction} to
* each layer.</p>
*/
@Deprecated
public class LayerVisitor implements Operation {
private final FlatRegion flatRegion;
private final LayerFunction function;
private Mask2D mask = Masks.alwaysTrue2D();
private int minY;
private int maxY;
private final int minY;
private final int maxY;
private final Iterable<Vector2D> iterator;
/**
* Create a new visitor.
*
* @param flatRegion the flat region to visit
* @param minY the minimum Y to stop the search at
* @param maxY the maximum Y to begin the search at
* @param function the layer function to apply t blocks
* @param minY the minimum Y to stop the search at
* @param maxY the maximum Y to begin the search at
* @param function the layer function to apply t blocks
*/
public LayerVisitor(FlatRegion flatRegion, int minY, int maxY, LayerFunction function) {
public LayerVisitor(final FlatRegion flatRegion, final int minY, final int maxY, final LayerFunction function) {
checkNotNull(flatRegion);
checkArgument(minY <= maxY, "minY <= maxY required");
checkNotNull(function);
this.flatRegion = flatRegion;
this.minY = minY;
this.maxY = maxY;
this.function = function;
this.iterator = flatRegion.asFlatRegion();
}
/**
@ -76,7 +77,7 @@ public class LayerVisitor implements Operation {
* @return a 2D mask
*/
public Mask2D getMask() {
return mask;
return this.mask;
}
/**
@ -85,42 +86,41 @@ public class LayerVisitor implements Operation {
*
* @param mask a 2D mask
*/
public void setMask(Mask2D mask) {
public void setMask(final Mask2D mask) {
checkNotNull(mask);
this.mask = mask;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
for (Vector2D column : flatRegion.asFlatRegion()) {
if (!mask.test(column)) {
public Operation resume(final RunContext run) throws WorldEditException {
for (final Vector2D column : this.iterator) {
if (!this.mask.test(column)) {
continue;
}
// Abort if we are underground
if (function.isGround(column.toVector(maxY + 1))) {
if (this.function.isGround(column.toVector(this.maxY + 1))) {
return null;
}
boolean found = false;
int groundY = 0;
for (int y = maxY; y >= minY; --y) {
Vector test = column.toVector(y);
for (int y = this.maxY; y >= this.minY; --y) {
final Vector test = column.toVector(y);
if (!found) {
if (function.isGround(test)) {
if (this.function.isGround(test)) {
found = true;
groundY = y;
}
}
if (found) {
if (!function.apply(test, groundY - y)) {
if (!this.function.apply(test, groundY - y)) {
break;
}
}
}
}
return null;
}
@ -129,7 +129,10 @@ public class LayerVisitor implements Operation {
}
@Override
public void addStatusMessages(List<String> messages) {
public void addStatusMessages(final List<String> messages) {
}
public static Class<?> inject() {
return Operations.class;
}
}

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import java.util.Collection;
/**
@ -33,12 +33,17 @@ public class NonRisingVisitor extends RecursiveVisitor {
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param mask the mask
* @param function the function
*/
public NonRisingVisitor(Mask mask, RegionFunction function) {
super(mask, function);
Collection<Vector> directions = getDirections();
this(mask, function, Integer.MAX_VALUE, null);
}
public NonRisingVisitor(Mask mask, RegionFunction function, int depth, HasFaweQueue hasFaweQueue) {
super(mask, function, depth, hasFaweQueue);
final Collection<Vector> directions = this.getDirections();
directions.clear();
directions.add(new Vector(1, 0, 0));
directions.add(new Vector(-1, 0, 0));
@ -47,4 +52,8 @@ public class NonRisingVisitor extends RecursiveVisitor {
directions.add(new Vector(0, -1, 0));
}
public static Class<?> inject() {
return NonRisingVisitor.class;
}
}

View File

@ -19,12 +19,14 @@
package com.sk89q.worldedit.function.visitor;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.object.HasFaweQueue;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.mask.Mask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An implementation of an {@link BreadthFirstSearch} that uses a mask to
* determine where a block should be visited.
@ -33,20 +35,32 @@ public class RecursiveVisitor extends BreadthFirstSearch {
private final Mask mask;
public RecursiveVisitor(final Mask mask, final RegionFunction function) {
this(mask, function, Integer.MAX_VALUE);
}
/**
* Create a new recursive visitor.
*
* @param mask the mask
* @param mask the mask
* @param function the function
*/
public RecursiveVisitor(Mask mask, RegionFunction function) {
super(function);
public RecursiveVisitor(final Mask mask, final RegionFunction function, int maxDepth) {
this(mask, function, maxDepth, null);
}
public RecursiveVisitor(final Mask mask, final RegionFunction function, int maxDepth, HasFaweQueue faweQueue) {
super(function, maxDepth, faweQueue);
checkNotNull(mask);
this.mask = mask;
}
@Override
protected boolean isVisitable(Vector from, Vector to) {
return mask.test(to);
public boolean isVisitable(final Vector from, final Vector to) {
return this.mask.test(to);
}
}
public static Class<?> inject() {
return RecursiveVisitor.class;
}
}

View File

@ -19,13 +19,22 @@
package com.sk89q.worldedit.function.visitor;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.example.MappedFaweQueue;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.HasFaweQueue;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.RegionFunction;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.regions.Region;
import java.util.Iterator;
import java.util.List;
/**
@ -33,13 +42,37 @@ import java.util.List;
*/
public class RegionVisitor implements Operation {
private final Region region;
private final RegionFunction function;
private int affected = 0;
public final Region region;
public final Iterable<? extends Vector> iterable;
public final RegionFunction function;
private final MappedFaweQueue queue;
private boolean useCuboidIterator = false;
public int affected = 0;
/**
* Deprecated in favor of the other constructors which will preload chunks during iteration
*
* @param region
* @param function
*/
@Deprecated
public RegionVisitor(Region region, RegionFunction function) {
this.region = region;
this(region, function, (FaweQueue) null);
}
public RegionVisitor(Region region, RegionFunction function, EditSession editSession) {
this(region, function, editSession != null ? editSession.getQueue() : null);
}
public RegionVisitor(Region region, RegionFunction function, FaweQueue queue) {
this((Iterable<BlockVector>) region, function, queue);
}
public RegionVisitor(Iterable<? extends Vector> iterable, RegionFunction function, HasFaweQueue hasQueue) {
region = (iterable instanceof Region) ? (Region) iterable : null;
this.iterable = iterable;
this.function = function;
this.queue = hasQueue != null && hasQueue.getQueue() instanceof MappedFaweQueue ? (MappedFaweQueue) hasQueue.getQueue() : null;
}
/**
@ -48,28 +81,124 @@ public class RegionVisitor implements Operation {
* @return the number of affected
*/
public int getAffected() {
return affected;
return this.affected;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
for (Vector pt : region) {
if (function.apply(pt)) {
affected++;
public Operation resume(final RunContext run) throws WorldEditException {
if (queue != null && Settings.IMP.QUEUE.PRELOAD_CHUNKS > 1) {
/*
* The following is done to reduce iteration cost
* - Preload chunks just in time
* - Only check every 16th block for potential chunk loads
* - Stop iteration on exception instead of hasNext
* - Do not calculate the stacktrace as it is expensive
*/
Iterator<? extends Vector> trailIter = iterable.iterator();
Iterator<? extends Vector> leadIter = iterable.iterator();
int lastTrailChunkX = Integer.MIN_VALUE;
int lastTrailChunkZ = Integer.MIN_VALUE;
int lastLeadChunkX = Integer.MIN_VALUE;
int lastLeadChunkZ = Integer.MIN_VALUE;
int loadingTarget = Settings.IMP.QUEUE.PRELOAD_CHUNKS;
try {
for (; ; ) {
Vector pt = trailIter.next();
apply(pt);
int cx = pt.getBlockX() >> 4;
int cz = pt.getBlockZ() >> 4;
if (cx != lastTrailChunkX || cz != lastTrailChunkZ) {
lastTrailChunkX = cx;
lastTrailChunkZ = cz;
int amount;
if (lastLeadChunkX == Integer.MIN_VALUE) {
lastLeadChunkX = cx;
lastLeadChunkZ = cz;
amount = loadingTarget;
} else {
amount = 1;
}
for (int count = 0; count < amount; ) {
Vector v = leadIter.next();
int vcx = v.getBlockX() >> 4;
int vcz = v.getBlockZ() >> 4;
if (vcx != lastLeadChunkX || vcz != lastLeadChunkZ) {
lastLeadChunkX = vcx;
lastLeadChunkZ = vcz;
queue.queueChunkLoad(vcx, vcz);
count++;
}
// Skip the next 15 blocks
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
leadIter.next();
}
}
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
apply(trailIter.next());
}
} catch (FaweException e) {
throw new RuntimeException(e);
} catch (Throwable ignore) {
}
try {
for (; ; ) {
apply(trailIter.next());
apply(trailIter.next());
}
} catch (FaweException e) {
throw new RuntimeException(e);
} catch (Throwable ignore) {
}
} else {
for (Vector pt : iterable) {
apply(pt);
}
}
return null;
}
private void apply(Vector pt) throws WorldEditException {
if (function.apply(pt)) {
affected++;
}
}
@Override
public void cancel() {
}
@Override
public void addStatusMessages(List<String> messages) {
messages.add(getAffected() + " blocks affected");
public void addStatusMessages(final List<String> messages) {
messages.add(BBC.VISITOR_BLOCK.format(getAffected()));
}
public static Class<?> inject() {
return Operations.class;
}
}