Remove hardcoding of world limits (#1199)

* Remove hardcoding of world limits
 - seems to be working fine without the datapack for world height changing
 - particular attention should be given to LocalBlockVectorSet and MathMan changes

* update adapters

* Override getMinY in various classes and ensure selections have a world attached to them

* no message

* Address comments
 - Fix for lighting mode 1

* A few more changes

* Fix LocalBlockVectorSet

* Fix range statement

* Various fixes/comment-addressing
- There's not much point in having a different file name now for history. We've broken it before...
- Fix history read/write
- Fix range on for loops in CharBlocks

* undo bad CharBlocks change

* Fix history y level

* Fix biome history

* Fix lighting

* Fix /up

* Make regen fail not because of these changes

* Fixes for y < 0

* Fix isEmpty where only the uppermost chunksection is edited

* Fix javadocs/FAWE annotations

* Better explain why BiomeMath is removed

* If history task throws an error, it should only be caught and printed if not completing now.

* Min|max world heights for new patterns

* Load biomes from NMS instead of bukkit (#1200)

* Update adapters

* Update adapters

* Don't initialise BlockTypes when biomes aren't set up yet so all BiomeTypes.BIOME are no longer null thanks.

* Address some comments.

* rename layer -> sectionIndex to imply inclusivity

* Javadoctored.

Co-authored-by: NotMyFault <mc.cache@web.de>
Co-authored-by: Hannes Greule <SirYwell@users.noreply.github.com>
This commit is contained in:
dordsor21 2021-08-17 22:13:51 +01:00 committed by GitHub
parent 5b2bd45d86
commit 1d9b1a3d5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 1489 additions and 677 deletions

View File

@ -266,8 +266,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
});
}
source = new SingleThreadQueueExtent();
source.init(null, initSourceQueueCache(), null);
source = new SingleThreadQueueExtent(originalBukkitWorld.getMinHeight(), originalBukkitWorld.getMaxHeight());
source.init(target, initSourceQueueCache(), null);
return true;
}

View File

@ -11,6 +11,7 @@ import javax.annotation.Nonnull;
public class MinecraftVersion implements Comparable<MinecraftVersion> {
public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16);
public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17);
private final int major;
private final int minor;

View File

@ -49,6 +49,7 @@ import com.sk89q.worldedit.util.lifecycle.Lifecycled;
import com.sk89q.worldedit.util.lifecycle.SimpleLifecycled;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockCategory;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.gamemode.GameModes;
@ -59,6 +60,7 @@ import org.apache.logging.log4j.Logger;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
import org.bukkit.block.Biome;
import org.bukkit.command.BlockCommandSender;
@ -234,17 +236,18 @@ public class WorldEditPlugin extends JavaPlugin {
// datapacks aren't loaded until just before the world is, and bukkit has no event for this
// so the earliest we can do this is in WorldInit
setupTags();
setupBiomes(); // FAWE - load biomes later
WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent(platform));
}
@SuppressWarnings({"deprecation", "unchecked"})
private void initializeRegistries() {
// Biome
/* // FAWE start - move Biomes to their own method
for (Biome biome : Biome.values()) {
String lowerCaseBiomeName = biome.name().toLowerCase(Locale.ROOT);
BiomeType.REGISTRY.register("minecraft:" + lowerCaseBiomeName, new BiomeType("minecraft:" + lowerCaseBiomeName));
}
/*
// FAWE end
// Block & Item
for (Material material : Material.values()) {
@ -304,6 +307,30 @@ public class WorldEditPlugin extends JavaPlugin {
"The version of Spigot/Paper you are using doesn't support Tags. The usage of tags with WorldEdit will not work until you update.");
}
}
// FAWE start
private void setupBiomes() {
if (this.adapter.value().isPresent()) {
// We don't know which world is the one with the data packs
// so we just loop over them. Doesn't hurt
for (org.bukkit.World world : Bukkit.getWorlds()) {
// cast is needed, thanks to raw types <3
for (final NamespacedKey biome : ((BukkitImplAdapter<?>) adapter.value().get()).getRegisteredBiomes(world)) {
if (BiomeType.REGISTRY.get(biome.toString()) == null) { // only register once
BiomeType.REGISTRY.register(biome.toString(), new BiomeType(biome.toString()));
}
}
}
} else {
LOGGER.warn("Failed to load biomes via adapter (not present). Will load via bukkit");
for (Biome biome : Biome.values()) {
if (BiomeType.REGISTRY.get(biome.toString()) == null) { // only register once
BiomeType.REGISTRY.register(biome.getKey().toString(), new BiomeType(biome.getKey().toString()));
}
}
}
}
// FAWE end
private void loadAdapter() {
WorldEdit worldEdit = WorldEdit.getInstance();

View File

@ -50,7 +50,9 @@ import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial;
import org.bukkit.Keyed;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.block.Biome;
@ -60,9 +62,11 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Map;
import java.util.OptionalInt;
import java.util.Set;
import java.util.stream.Collectors;
/**
* An interface for adapters of various Bukkit implementations.
@ -304,6 +308,18 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
return Biome.BADLANDS.ordinal();
}
/**
* Returns an iterable of all biomes known to the server.
*
* @param world the world to load the registered biomes from.
* @return all biomes known to the server.
*/
default Iterable<NamespacedKey> getRegisteredBiomes(World world) {
return Arrays.stream(Biome.values())
.map(Keyed::getKey)
.collect(Collectors.toList());
}
default RelighterFactory getRelighterFactory() {
return new NMSRelighterFactory(); // TODO implement in adapters instead
}

View File

@ -270,17 +270,22 @@ public class FaweAPI {
RegionWrapper bounds = new RegionWrapper(
origin.getBlockX() - radius,
origin.getBlockX() + radius,
extent.getMinY(),
extent.getMaxY(),
origin.getBlockZ() - radius,
origin.getBlockZ() + radius
);
RegionWrapper boundsPlus = new RegionWrapper(bounds.minX - 64, bounds.maxX + 512, bounds.minZ - 64, bounds.maxZ + 512);
RegionWrapper boundsPlus = new RegionWrapper(bounds.minX - 64, bounds.maxX + 512, bounds.minY, bounds.maxY,
bounds.minZ - 64,
bounds.maxZ + 512);
HashSet<RegionWrapper> regionSet = Sets.<RegionWrapper>newHashSet(bounds);
ArrayList<DiskStorageHistory> result = new ArrayList<>();
for (File file : files) {
UUID uuid = UUID.fromString(file.getParentFile().getName());
DiskStorageHistory dsh = new DiskStorageHistory(world, uuid, Integer.parseInt(file.getName().split("\\.")[0]));
SimpleChangeSetSummary summary = dsh.summarize(boundsPlus, shallow);
RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, summary.minZ, summary.maxZ);
RegionWrapper region = new RegionWrapper(summary.minX, summary.maxX, extent.getMinY(), extent.getMaxY(), summary.minZ,
summary.maxZ);
boolean encompassed = false;
boolean isIn = false;
for (RegionWrapper allowed : regionSet) {

View File

@ -64,9 +64,6 @@ public enum FaweCache implements Trimable {
private static final Logger LOGGER = LogManagerCompat.getLogger();
public final int BLOCKS_PER_LAYER = 4096;
public final int CHUNK_LAYERS = 16;
public final int WORLD_HEIGHT = CHUNK_LAYERS << 4;
public final int WORLD_MAX_Y = WORLD_HEIGHT - 1;
public final char[] EMPTY_CHAR_4096 = new char[4096];

View File

@ -24,7 +24,8 @@ public class BlendBall implements Brush {
int[] frequency = new int[BlockTypes.size()];
int maxY = editSession.getMaximumPoint().getBlockY();
int maxY = editSession.getMaxY();
int minY = editSession.getMinY();
for (int x = -outsetSize; x <= outsetSize; x++) {
int x0 = x + tx;
@ -43,7 +44,7 @@ public class BlendBall implements Brush {
for (int ox = -1; ox <= 1; ox++) {
for (int oz = -1; oz <= 1; oz++) {
for (int oy = -1; oy <= 1; oy++) {
if (oy + y0 < 0 || oy + y0 > maxY) {
if (oy + y0 < minY || oy + y0 > maxY) {
continue;
}
BlockState state = editSession.getBlock(x0 + ox, y0 + oy, z0 + oz);

View File

@ -41,7 +41,8 @@ public class CommandBrush implements Brush {
.replace("{size}", Integer.toString(radius));
Player player = editSession.getPlayer();
Location face = player.getBlockTraceFace(256, true);
//Use max world height to allow full coverage of the world height
Location face = player.getBlockTraceFace(editSession.getWorld().getMaxY(), true);
if (face == null) {
position = position.add(0, 1, 1);
} else {

View File

@ -78,7 +78,8 @@ public class CopyPastaBrush implements Brush, ResettableTool {
};
// Add origin
mask.test(position);
RecursiveVisitor visitor = new RecursiveVisitor(mask, new NullRegionFunction(), (int) size);
RecursiveVisitor visitor = new RecursiveVisitor(mask, new NullRegionFunction(), (int) size, editSession.getMinY(),
editSession.getMaxY());
visitor.visit(position);
Operations.completeBlindly(visitor);
// Build the clipboard

View File

@ -16,6 +16,7 @@ public class FallingSphere implements Brush {
int py = position.getBlockY();
int pz = position.getBlockZ();
int maxY = editSession.getMaxY();
int minY = editSession.getMinY();
int radius = (int) Math.round(size);
int radiusSqr = (int) Math.round(size * size);
@ -37,10 +38,10 @@ public class FallingSphere implements Brush {
}
int yRadius = MathMan.usqrt(remainingY);
int startY = Math.max(0, py - yRadius);
int startY = Math.max(minY, py - yRadius);
int endY = Math.min(maxY, py + yRadius);
int heightY = editSession.getHighestTerrainBlock(ax, az, 0, endY);
int heightY = editSession.getHighestTerrainBlock(ax, az, startY, endY);
if (heightY < startY) {
int diff = startY - heightY;
startY -= diff;

View File

@ -21,9 +21,11 @@ public class FlattenBrush extends HeightBrush {
boolean layers,
boolean smooth,
Clipboard clipboard,
ScalableHeightMap.Shape shape
ScalableHeightMap.Shape shape,
int minY,
int maxY
) {
super(stream, rotation, yscale, layers, smooth, clipboard, shape);
super(stream, rotation, yscale, layers, smooth, clipboard, shape, minY, maxY);
}
@Override

View File

@ -27,8 +27,9 @@ public class HeightBrush implements Brush {
public final boolean layers;
public final boolean smooth;
public HeightBrush(InputStream stream, int rotation, double yscale, boolean layers, boolean smooth, Clipboard clipboard) {
this(stream, rotation, yscale, layers, smooth, clipboard, ScalableHeightMap.Shape.CONE);
public HeightBrush(InputStream stream, int rotation, double yscale, boolean layers, boolean smooth, Clipboard clipboard,
int minY, int maxY) {
this(stream, rotation, yscale, layers, smooth, clipboard, ScalableHeightMap.Shape.CONE, minY, maxY);
}
public HeightBrush(
@ -38,7 +39,9 @@ public class HeightBrush implements Brush {
boolean layers,
boolean smooth,
Clipboard clipboard,
ScalableHeightMap.Shape shape
ScalableHeightMap.Shape shape,
int minY,
int maxY
) {
this.rotation = (rotation / 90) % 4;
this.yscale = yscale;
@ -46,14 +49,14 @@ public class HeightBrush implements Brush {
this.smooth = smooth;
if (stream != null) {
try {
heightMap = ScalableHeightMap.fromPNG(stream);
heightMap = ScalableHeightMap.fromPNG(stream, minY, maxY);
} catch (IOException e) {
throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid"));
}
} else if (clipboard != null) {
heightMap = ScalableHeightMap.fromClipboard(clipboard);
heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY);
} else {
heightMap = ScalableHeightMap.fromShape(shape);
heightMap = ScalableHeightMap.fromShape(shape, minY, maxY);
}
}

View File

@ -102,7 +102,7 @@ public class ImageBrush implements Brush {
colorFunction,
editSession,
session.getTextureUtil()
), vector -> true, Integer.MAX_VALUE);
), vector -> true, Integer.MAX_VALUE, editSession.getMinY(), editSession.getMaxY());
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
visitor.visit(center);
Operations.completeBlindly(visitor);

View File

@ -36,10 +36,12 @@ public class LayerBrush implements Brush {
BlockTypes.AIR,
BlockTypes.CAVE_AIR,
BlockTypes.VOID_AIR
));
), editSession.getMinY(), editSession.getMaxY());
final SolidBlockMask solid = new SolidBlockMask(editSession);
final RadiusMask radius = new RadiusMask(0, (int) size);
visitor = new RecursiveVisitor(new MaskIntersection(adjacent, solid, radius), function -> true);
visitor = new RecursiveVisitor(new MaskIntersection(adjacent, solid, radius), funcion -> true, Integer.MAX_VALUE,
editSession.getMinY(),
editSession.getMaxY());
visitor.visit(position);
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
Operations.completeBlindly(visitor);
@ -48,7 +50,7 @@ public class LayerBrush implements Brush {
int depth = visitor.getDepth();
Pattern currentPattern = layers[depth];
return currentPattern.apply(editSession, pos, pos);
}, layers.length - 1);
}, layers.length - 1, editSession.getMinY(), editSession.getMaxY());
for (BlockVector3 pos : visited) {
visitor.visit(pos);
}

View File

@ -48,7 +48,7 @@ public class RecurseBrush implements Brush {
visitor.visit(position);
Operations.completeBlindly(visitor);
} else {
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, radius) {
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, radius, editSession.getMinY(), editSession.getMaxY()) {
@Override
public boolean isVisitable(BlockVector3 from, BlockVector3 to) {
int y = to.getBlockY();

View File

@ -52,7 +52,8 @@ public class ScatterBrush implements Brush {
final int distance = Math.min((int) size, this.distance);
RecursiveVisitor visitor = new RecursiveVisitor(new MaskIntersection(radius, surface), function -> true);
RecursiveVisitor visitor = new RecursiveVisitor(new MaskIntersection(radius, surface), function -> true,
Integer.MAX_VALUE, editSession.getMinY(), editSession.getMaxY());
visitor.visit(position);
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
Operations.completeBlindly(visitor);

View File

@ -42,7 +42,7 @@ public class SplatterBrush extends ScatterBrush {
SurfaceMask surface = new SurfaceMask(editSession);
RecursiveVisitor visitor = new RecursiveVisitor(new SplatterBrushMask(editSession, position, size2, surface, placed),
vector -> editSession.setBlock(vector, finalPattern), recursion
vector -> editSession.setBlock(vector, finalPattern), recursion, editSession.getMinY(), editSession.getMaxY()
);
visitor.setMaxBranch(2);
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));

View File

@ -21,8 +21,9 @@ public class StencilBrush extends HeightBrush {
private final boolean onlyWhite;
public StencilBrush(InputStream stream, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard) {
super(stream, rotation, yscale, false, true, clipboard);
public StencilBrush(InputStream stream, int rotation, double yscale, boolean onlyWhite, Clipboard clipboard, int minY,
int maxY) {
super(stream, rotation, yscale, false, true, clipboard, minY, maxY);
this.onlyWhite = onlyWhite;
}
@ -32,15 +33,16 @@ public class StencilBrush extends HeightBrush {
int size = (int) sizeDouble;
int size2 = (int) (sizeDouble * sizeDouble);
int maxY = editSession.getMaxY();
int minY = editSession.getMinY();
int add;
if (yscale < 0) {
add = maxY;
add = maxY - minY;
} else {
add = 0;
}
final HeightMap map = getHeightMap();
map.setSize(size);
int cutoff = onlyWhite ? maxY : 0;
int cutoff = onlyWhite ? maxY - minY : 0;
final SolidBlockMask solid = new SolidBlockMask(editSession);
Location loc = editSession.getPlayer().getLocation();
@ -48,7 +50,7 @@ public class StencilBrush extends HeightBrush {
float pitch = loc.getPitch();
AffineTransform transform = new AffineTransform().rotateY((-yaw) % 360).rotateX(pitch - 90).inverse();
double scale = (yscale / sizeDouble) * (maxY + 1);
double scale = (yscale / sizeDouble) * (maxY - minY + 1);
RecursiveVisitor visitor =
new RecursiveVisitor(new StencilBrushMask(
editSession,
@ -63,7 +65,7 @@ public class StencilBrush extends HeightBrush {
maxY,
pattern
),
vector -> true, Integer.MAX_VALUE
vector -> true, Integer.MAX_VALUE, editSession.getMinY(), editSession.getMaxY()
);
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));
visitor.visit(center);

View File

@ -25,7 +25,7 @@ public class SurfaceSphereBrush implements Brush {
final RadiusMask radius = new RadiusMask(0, (int) size);
RecursiveVisitor visitor = new RecursiveVisitor(
new MaskIntersection(surface, radius),
vector -> editSession.setBlock(vector, pattern)
vector -> editSession.setBlock(vector, pattern), Integer.MAX_VALUE, editSession.getMinY(), editSession.getMaxY()
);
visitor.visit(position);
visitor.setDirections(Arrays.asList(BreadthFirstSearch.DIAGONAL_DIRECTIONS));

View File

@ -37,13 +37,14 @@ public class SurfaceSpline implements Brush {
public void build(EditSession editSession, BlockVector3 pos, Pattern pattern, double radius) throws
MaxChangedBlocksException {
int maxY = editSession.getMaxY();
int minY = editSession.getMinY();
if (path.isEmpty() || !pos.equals(path.get(path.size() - 1))) {
int max = editSession.getNearestSurfaceTerrainBlock(
pos.getBlockX(),
pos.getBlockZ(),
pos.getBlockY(),
0,
editSession.getMaxY()
minY,
maxY
);
if (max == -1) {
return;
@ -71,7 +72,7 @@ public class SurfaceSpline implements Brush {
final int tipx = MathMan.roundInt(tipv.getX());
final int tipz = (int) tipv.getZ();
int tipy = MathMan.roundInt(tipv.getY());
tipy = editSession.getNearestSurfaceTerrainBlock(tipx, tipz, tipy, 0, maxY);
tipy = editSession.getNearestSurfaceTerrainBlock(tipx, tipz, tipy, minY, maxY);
if (tipy == -1) {
continue;
}

View File

@ -5,9 +5,13 @@ import com.fastasyncworldedit.core.function.mask.AdjacentAnyMask;
import com.fastasyncworldedit.core.function.mask.AdjacentMask;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.command.util.SuggestionHelper;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extension.platform.Locatable;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nonnull;
import java.util.stream.Stream;
@ -43,7 +47,7 @@ public class AdjacentMaskParser extends RichParser<Mask> {
max = min;
}
if (max >= 8 && min == 1) {
return new AdjacentAnyMask(subMask);
return new AdjacentAnyMask(subMask, context.getMinY(), context.getMaxY());
}
return new AdjacentMask(subMask, min, max);
}

View File

@ -43,7 +43,7 @@ public class RichOffsetMaskParser extends RichParser<Mask> {
int y = Integer.parseInt(arguments[1]);
int z = Integer.parseInt(arguments[2]);
Mask submask = worldEdit.getMaskFactory().parseFromInput(arguments[3], context);
return new OffsetMask(submask, BlockVector3.at(x, y, z));
return new OffsetMask(submask, BlockVector3.at(x, y, z), context.getMinY(), context.getMaxY());
}
}

View File

@ -58,10 +58,7 @@ public class OffsetPatternParser extends RichParser<Pattern> {
} else {
x = y = z = Integer.parseInt(arguments[1]);
}
Extent extent = context.requireExtent();
int minY = extent.getMinY();
int maxY = extent.getMaxY();
return new OffsetPattern(inner, x, y, z, minY, maxY);
return new OffsetPattern(inner, x, y, z, context.getMinY(), context.getMaxY());
}
}

View File

@ -58,8 +58,7 @@ public class RandomOffsetPatternParser extends RichParser<Pattern> {
} else {
x = y = z = Integer.parseInt(arguments[1]);
}
Extent extent = context.requireExtent();
return new RandomOffsetPattern(inner, x, y, z, extent.getMinY(), extent.getMaxY());
return new RandomOffsetPattern(inner, x, y, z, context.getMinY(), context.getMaxY());
}
}

View File

@ -41,8 +41,7 @@ public class RelativePatternParser extends RichParser<Pattern> {
));
}
Pattern inner = this.worldEdit.getPatternFactory().parseFromInput(input[0], context);
Extent extent = context.requireExtent();
return new RelativePattern(inner, extent.getMinY(), extent.getMaxY());
return new RelativePattern(inner, context.getMinY(), context.getMaxY());
}
}

View File

@ -58,8 +58,7 @@ public class SolidRandomOffsetPatternParser extends RichParser<Pattern> {
} else {
x = y = z = Integer.parseInt(arguments[1]);
}
Extent extent = context.requireExtent();
return new SolidRandomOffsetPattern(inner, x, y, z, extent.getMinY(), extent.getMaxY());
return new SolidRandomOffsetPattern(inner, x, y, z, context.getMinY(), context.getMaxY());
}
}

View File

@ -47,8 +47,7 @@ public class SurfaceRandomOffsetPatternParser extends RichParser<Pattern> {
}
Pattern inner = this.worldEdit.getPatternFactory().parseFromInput(arguments[0], context);
int distance = Integer.parseInt(arguments[1]);
Extent extent = context.requireExtent();
return new SurfaceRandomOffsetPattern(inner, distance, extent.getMinY(), extent.getMaxY());
return new SurfaceRandomOffsetPattern(inner, distance, context.getMinY(), context.getMaxY());
}
}

View File

@ -44,7 +44,7 @@ public class ExtentHeightCacher extends PassthroughExtent {
index = rx + (rz << 8);
}
int result = cacheHeights[index] & 0xFF;
if (result == 0) {
if (result == minY) {
cacheHeights[index] = (byte) (result = lastY = super
.getNearestSurfaceTerrainBlock(x, z, lastY, minY, maxY));
}

View File

@ -217,6 +217,11 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor {
throw reason;
}
@Override
public int getMinY() {
throw reason;
}
@Override
public BlockArrayClipboard lazyCopy(Region region) {
throw reason;
@ -273,21 +278,6 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor {
throw reason;
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
throw reason;
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
throw reason;
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
throw reason;
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
throw reason;

View File

@ -71,7 +71,7 @@ public abstract class ChunkFilterBlock extends AbstractExtentFilterBlock {
*/
public final IChunkSet filter(IChunk chunk, IChunkGet get, IChunkSet set, Filter filter) {
initChunk(chunk.getX(), chunk.getZ());
for (int layer = 0; layer < 16; layer++) {
for (int layer = get.getMinSectionIndex(); layer <= get.getMaxSectionIndex(); layer++) {
if (set.hasSection(layer)) {
initLayer(get, set, layer);
filter(filter);
@ -87,7 +87,7 @@ public abstract class ChunkFilterBlock extends AbstractExtentFilterBlock {
if (region != null) {
region.filter(chunk, filter, this, get, set, full);
} else {
for (int layer = 0; layer < 16; layer++) {
for (int layer = get.getMinSectionIndex(); layer <= get.getMaxSectionIndex(); layer++) {
if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) {
continue;
}

View File

@ -119,92 +119,92 @@ public class LimitExtent extends AbstractDelegateExtent {
@Override
public int getHighestTerrainBlock(int x, int z, int minY, int maxY) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getHighestTerrainBlock(x, z, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@Override
public int getHighestTerrainBlock(int x, int z, int minY, int maxY, Mask filter) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getHighestTerrainBlock(x, z, minY, maxY, filter);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@Override
public int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getNearestSurfaceLayer(x, z, y, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, ignoreAir);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@Override
public int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, mask);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}
@ -219,14 +219,14 @@ public class LimitExtent extends AbstractDelegateExtent {
int failedMax,
boolean ignoreAir
) {
limit.THROW_MAX_CHECKS(FaweCache.IMP.WORLD_HEIGHT);
limit.THROW_MAX_CHECKS(maxY - minY + 1);
try {
return super.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, ignoreAir);
} catch (FaweException e) {
if (!limit.MAX_FAILS()) {
throw e;
}
return 0;
return minY;
}
}

View File

@ -11,7 +11,15 @@ public class ArrayHeightMap extends ScalableHeightMap {
private double rx;
private double rz;
public ArrayHeightMap(byte[][] height) {
/**
* New height map represented by byte array[][] of values x*z to be scaled given a set size
*
* @param height array of height values
* @param minY min y value allowed to be set. Inclusive.
* @param maxY max y value allowed to be set. Inclusive.
*/
public ArrayHeightMap(byte[][] height, int minY, int maxY) {
super(minY, maxY);
setSize(5);
this.height = height;
this.width = height.length;

View File

@ -2,8 +2,15 @@ package com.fastasyncworldedit.core.extent.processor.heightmap;
public class FlatScalableHeightMap extends ScalableHeightMap {
public FlatScalableHeightMap() {
super();
/**
* New height map where the returned height is the minmum height value if outside the size, otherwise returns height equal
* to size.
*
* @param minY min y value allowed to be set. Inclusive.
* @param maxY max y value allowed to be set. Inclusive.
*/
public FlatScalableHeightMap(int minY, int maxY) {
super(minY, maxY);
}
@Override
@ -12,7 +19,7 @@ public class FlatScalableHeightMap extends ScalableHeightMap {
int dz = Math.abs(z);
int d2 = dx * dx + dz * dz;
if (d2 > size2) {
return 0;
return minY;
}
return size;
}

View File

@ -18,7 +18,6 @@ public interface HeightMap {
void setSize(int size);
default void perform(
EditSession session,
Mask mask,
@ -83,8 +82,8 @@ public interface HeightMap {
boolean towards,
final boolean layers
) {
BlockVector3 top = session.getMaximumPoint();
int maxY = top.getBlockY();
int maxY = session.getMaxY();
int minY = session.getMinY();
int diameter = 2 * size + 1;
int centerX = pos.getBlockX();
int centerZ = pos.getBlockZ();
@ -121,15 +120,15 @@ public interface HeightMap {
}
int height;
if (layers) {
height = tmpY = session.getNearestSurfaceLayer(xx, zz, tmpY, 0, maxY);
height = tmpY = session.getNearestSurfaceLayer(xx, zz, tmpY, minY, maxY);
} else {
height = tmpY = session.getNearestSurfaceTerrainBlock(xx, zz, tmpY, 0, maxY);
height = tmpY = session.getNearestSurfaceTerrainBlock(xx, zz, tmpY, minY, maxY);
if (height == -1) {
continue;
}
}
oldData[index] = height;
if (height == 0) {
if (height == minY) {
newData[index] = centerY;
continue;
}
@ -137,8 +136,9 @@ public interface HeightMap {
int diff = targetY - height;
double raiseScaled = diff * (raisePow * sizePowInv);
double raiseScaledAbs = Math.abs(raiseScaled);
int random = ThreadLocalRandom.current().nextInt(256) < (int) ((Math.ceil(raiseScaledAbs) - Math.floor(
raiseScaledAbs)) * 256) ? (diff > 0 ? 1 : -1) : 0;
int random =
ThreadLocalRandom.current().nextInt(maxY + 1 - minY) - minY < (int) ((Math.ceil(raiseScaledAbs) - Math.floor(
raiseScaledAbs)) * (maxY + 1 - minY)) ? (diff > 0 ? 1 : -1) : 0;
int raiseScaledInt = (int) raiseScaled + random;
newData[index] = height + raiseScaledInt;
}
@ -166,20 +166,22 @@ public interface HeightMap {
break;
}
if (layers) {
height = session.getNearestSurfaceLayer(xx, zz, height, 0, maxY);
height = session.getNearestSurfaceLayer(xx, zz, height, minY, maxY);
} else {
height = session.getNearestSurfaceTerrainBlock(xx, zz, height, 0, maxY);
if (height == -1) {
height = session.getNearestSurfaceTerrainBlock(xx, zz, height, minY, maxY);
if (height == minY - 1) {
continue;
}
}
oldData[index] = height;
if (height == 0) {
if (height == minY) {
newData[index] = centerY;
continue;
}
raise = (yscale * raise);
int random = ThreadLocalRandom.current().nextInt(256) < (int) ((raise - (int) raise) * (256)) ? 1 : 0;
int random =
ThreadLocalRandom.current().nextInt(maxY + 1 - minY) - minY < (int) ((raise - (int) raise) * (maxY - minY + 1))
? 1 : 0;
int newHeight = height + (int) raise + random;
newData[index] = newHeight;
}

View File

@ -18,20 +18,26 @@ public class ScalableHeightMap implements HeightMap {
public int size2;
public int size;
protected int minY;
protected int maxY;
public enum Shape {
CONE,
CYLINDER,
}
public ScalableHeightMap() {
/**
* New height map.
*
* @param minY min y value allowed to be set. Inclusive.
* @param maxY max y value allowed to be set. Inclusive.
*/
public ScalableHeightMap(final int minY, final int maxY) {
this.minY = minY;
this.maxY = maxY;
setSize(5);
}
public ScalableHeightMap(int size) {
setSize(size);
}
@Override
public void setSize(int size) {
this.size = size;
@ -44,29 +50,29 @@ public class ScalableHeightMap implements HeightMap {
int dz = Math.abs(z);
int d2 = dx * dx + dz * dz;
if (d2 > size2) {
return 0;
return minY;
}
return Math.max(0, size - MathMan.sqrtApprox(d2));
return Math.max(minY, size - MathMan.sqrtApprox(d2));
}
public static ScalableHeightMap fromShape(Shape shape) {
public static ScalableHeightMap fromShape(Shape shape, int minY, int maxY) {
switch (shape) {
default:
case CONE:
return new ScalableHeightMap();
return new ScalableHeightMap(minY, maxY);
case CYLINDER:
return new FlatScalableHeightMap();
return new FlatScalableHeightMap(minY, maxY);
}
}
public static ScalableHeightMap fromClipboard(Clipboard clipboard) {
public static ScalableHeightMap fromClipboard(Clipboard clipboard, int minY, int maxY) {
BlockVector3 dim = clipboard.getDimensions();
byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()];
int minX = clipboard.getMinimumPoint().getBlockX();
int minZ = clipboard.getMinimumPoint().getBlockZ();
int minY = clipboard.getMinimumPoint().getBlockY();
int maxY = clipboard.getMaximumPoint().getBlockY();
int clipHeight = maxY - minY + 1;
int clipMinX = clipboard.getMinimumPoint().getBlockX();
int clipMinZ = clipboard.getMinimumPoint().getBlockZ();
int clipMinY = clipboard.getMinimumPoint().getBlockY();
int clipMaxY = clipboard.getMaximumPoint().getBlockY();
int clipHeight = clipMaxY - clipMinY + 1;
HashSet<IntPair> visited = new HashSet<>();
MutableBlockVector3 bv = new MutableBlockVector3();
for (BlockVector3 pos : clipboard.getRegion()) {
@ -77,24 +83,24 @@ public class ScalableHeightMap implements HeightMap {
visited.add(pair);
int xx = pos.getBlockX();
int zz = pos.getBlockZ();
int highestY = minY;
int highestY = clipMinY;
bv.setComponents(pos);
for (int y = minY; y <= maxY; y++) {
for (int y = clipMinY; y <= clipMaxY; y++) {
bv.mutY(y);
BlockState block = clipboard.getBlock(bv);
if (!block.getBlockType().getMaterial().isAir()) {
highestY = y + 1;
}
}
int pointHeight = Math.min(255, (256 * (highestY - minY)) / clipHeight);
int x = xx - minX;
int z = zz - minZ;
int pointHeight = Math.min(clipMaxY, ((maxY - minY + 1 ) * (highestY - clipMinY)) / clipHeight);
int x = xx - clipMinX;
int z = zz - clipMinZ;
heightArray[x][z] = (byte) pointHeight;
}
return new ArrayHeightMap(heightArray);
return new ArrayHeightMap(heightArray, minY, maxY);
}
public static ScalableHeightMap fromPNG(InputStream stream) throws IOException {
public static ScalableHeightMap fromPNG(InputStream stream, int minY, int maxY) throws IOException {
BufferedImage heightFile = MainUtil.readImage(stream);
int width = heightFile.getWidth();
int length = heightFile.getHeight();
@ -113,7 +119,7 @@ public class ScalableHeightMap implements HeightMap {
array[x][z] = (byte) intensity;
}
}
return new ArrayHeightMap(array);
return new ArrayHeightMap(array, minY, maxY);
}
}

View File

@ -60,11 +60,12 @@ public class NMSRelighter implements Relighter {
private final ConcurrentHashMap<Long, long[][][]> concurrentLightQueue;
private final RelightMode relightMode;
private final int maxY;
private final int minY;
private final ReentrantLock lightingLock;
private final AtomicBoolean finished = new AtomicBoolean(false);
private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue, boolean calculateHeightMaps) {
public NMSRelighter(IQueueExtent<IQueueChunk> queue) {
this(queue, null);
}
@ -75,6 +76,7 @@ public class NMSRelighter implements Relighter {
this.chunksToSend = new Long2ObjectOpenHashMap<>(12);
this.concurrentLightQueue = new ConcurrentHashMap<>(12);
this.maxY = queue.getMaxY();
this.minY = queue.getMinY();
this.relightMode = relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE);
this.lightingLock = new ReentrantLock();
}
@ -118,6 +120,8 @@ public class NMSRelighter implements Relighter {
if (m2 == null) {
m2 = m1[x] = new long[4];
}
// Account for negative y values by "adding" minY
y -= minY;
m2[y >> 6] |= 1L << y;
}
@ -132,6 +136,8 @@ public class NMSRelighter implements Relighter {
this.lightQueue.put(index, currentMap);
}
set(x & 15, y, z & 15, currentMap);
this.lightQueue.putAll(concurrentLightQueue);
concurrentLightQueue.clear();
} finally {
lightLock.set(false);
}
@ -154,7 +160,7 @@ public class NMSRelighter implements Relighter {
}
public boolean addChunk(int cx, int cz, byte[] fix, int bitmask) {
RelightSkyEntry toPut = new RelightSkyEntry(cx, cz, fix, bitmask);
RelightSkyEntry toPut = new RelightSkyEntry(cx, cz, fix, bitmask, minY, maxY);
extendSkyToRelight.add(toPut);
return true;
}
@ -188,7 +194,7 @@ public class NMSRelighter implements Relighter {
if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z);
}
for (int i = 0; i < 16; i++) {
for (int i = minY >> 4; i <= maxY >> 4; i++) {
iChunk.removeSectionLighting(i, true);
}
iter.remove();
@ -238,7 +244,7 @@ public class NMSRelighter implements Relighter {
for (int j = 0; j < 64; j++) {
if (((value >> j) & 1) == 1) {
int x = lx + bx;
int y = yStart + j;
int y = yStart + j + minY;
int z = lz + bz;
int oldLevel = iChunk.getEmittedLight(lx, y, lz);
int newLevel = iChunk.getBrightness(lx, y, lz);
@ -287,7 +293,7 @@ public class NMSRelighter implements Relighter {
removalVisited,
visited
);
if (node.getY() > 0) {
if (node.getY() > minY) {
this.computeRemoveBlockLight(
node.getX(),
node.getY() - 1,
@ -299,7 +305,7 @@ public class NMSRelighter implements Relighter {
visited
);
}
if (node.getY() < 255) {
if (node.getY() < maxY) {
this.computeRemoveBlockLight(
node.getX(),
node.getY() + 1,
@ -650,7 +656,7 @@ public class NMSRelighter implements Relighter {
this.computeSpreadBlockLight(x, y - 1, z, currentLight, queue, visited);
}
state = this.queue.getBlock(x, y + 1, z);
if (y < 255 && !top && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, true)) {
if (y < maxY && !top && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, true)) {
this.computeSpreadBlockLight(x, y + 1, z, currentLight, queue, visited);
}
}
@ -696,7 +702,7 @@ public class NMSRelighter implements Relighter {
this.computeSpreadBlockLight(x, y - 1, z, currentLight, queue, visited);
}
state = this.queue.getBlock(x, y + 1, z);
if (y < 255 && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, false)) {
if (y < maxY && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, false)) {
this.computeSpreadBlockLight(x, y + 1, z, currentLight, queue, visited);
}
}
@ -944,7 +950,7 @@ public class NMSRelighter implements Relighter {
int z = MathMan.unpairIntY(pair);
ChunkHolder<?> chunk = (ChunkHolder<?>) queue.getOrCreateChunk(x, z);
chunk.setBitMask(bitMask);
chunk.flushLightToGet(true);
chunk.flushLightToGet();
Fawe.imp().getPlatformAdapter().sendChunk(chunk.getOrCreateGet(), bitMask, true);
iter.remove();
}
@ -984,7 +990,7 @@ public class NMSRelighter implements Relighter {
}
}
public void fill(byte[] mask, int chunkX, int y, int chunkZ, byte reason) {
public void fill(byte[] mask, ChunkHolder<?> iChunk, int y, byte reason) {
if (y >= 16) {
Arrays.fill(mask, (byte) 15);
return;
@ -995,12 +1001,10 @@ public class NMSRelighter implements Relighter {
return;
}
case SkipReason.AIR: {
int bx = chunkX << 4;
int bz = chunkZ << 4;
int index = 0;
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
mask[index++] = (byte) queue.getSkyLight(bx + x, y, bz + z);
mask[index++] = (byte) iChunk.getSkyLight(x, y, z);
}
}
}
@ -1026,19 +1030,19 @@ public class NMSRelighter implements Relighter {
}
}
}
for (int y = 255; y > 0; y--) {
for (int y = maxY; y > minY; y--) {
for (RelightSkyEntry chunk : chunks) { // Propagate skylight
int layer = y >> 4;
int layer = (y - minY) >> 4;
byte[] mask = chunk.mask;
if (chunk.fix[layer] != SkipReason.NONE) {
if ((y & 15) == 0 && layer != 0 && chunk.fix[layer - 1] == SkipReason.NONE) {
fill(mask, chunk.x, y, chunk.z, chunk.fix[layer]);
}
continue;
}
int bx = chunk.x << 4;
int bz = chunk.z << 4;
ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z);
if (chunk.fix[layer] != SkipReason.NONE) {
if ((y & 15) == 0 && layer != 0 && chunk.fix[layer - 1] == SkipReason.NONE) {
fill(mask, iChunk, y, chunk.fix[layer]);
}
continue;
}
if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z);
}
@ -1157,29 +1161,29 @@ public class NMSRelighter implements Relighter {
}
byte value = mask[j];
if (x != 0 && z != 0) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 0 && z == 0) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 0) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
@ -1203,29 +1207,29 @@ public class NMSRelighter implements Relighter {
}
byte value = mask[j];
if (x != 15 && z != 15) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 15 && z == 15) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else if (x == 15) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
}
} else {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) {
} else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) < 14) {
value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value);
}
if (value > mask[j]) {
iChunk.setSkyLight(x, y, z, mask[j] = value);
@ -1235,7 +1239,7 @@ public class NMSRelighter implements Relighter {
}
}
private class RelightSkyEntry implements Comparable<RelightSkyEntry> {
private static class RelightSkyEntry implements Comparable<RelightSkyEntry> {
public final int x;
public final int z;
@ -1244,7 +1248,7 @@ public class NMSRelighter implements Relighter {
public int bitmask;
public boolean smooth;
public RelightSkyEntry(int x, int z, byte[] fix, int bitmask) {
private RelightSkyEntry(int x, int z, byte[] fix, int bitmask, int minY, int maxY) {
this.x = x;
this.z = z;
byte[] array = new byte[256];
@ -1252,13 +1256,14 @@ public class NMSRelighter implements Relighter {
this.mask = array;
this.bitmask = bitmask;
if (fix == null) {
this.fix = new byte[(maxY + 1) >> 4];
this.fix = new byte[(maxY - minY + 1) >> 4];
Arrays.fill(this.fix, SkipReason.NONE);
} else {
this.fix = fix;
}
}
//Following are public because they are public in Object. NONE of this nested class is API.
@Override
public String toString() {
return x + "," + z;

View File

@ -28,11 +28,12 @@ public class RelightProcessor implements IBatchProcessor {
if (Settings.IMP.LIGHTING.MODE == 2) {
relighter.addChunk(chunk.getX(), chunk.getZ(), null, chunk.getBitMask());
} else if (Settings.IMP.LIGHTING.MODE == 1) {
byte[] fix = new byte[16];
byte[] fix = new byte[get.getSectionCount()];
boolean relight = false;
for (int i = 15; i >= 0; i--) {
for (int i = get.getMaxSectionIndex(); i >= get.getMinSectionIndex(); i--) {
if (!set.hasSection(i)) {
fix[i] = Relighter.SkipReason.AIR;
// Array index cannot be < 0 so "add" the min
fix[i - get.getMinSectionIndex()] = Relighter.SkipReason.AIR;
continue;
}
relight = true;

View File

@ -189,11 +189,11 @@ public class CavesGen extends GenBase {
n = 16;
}
if (i1 < 1) {
i1 = 1;
if (i1 < chunk.getMinY() + 1) {
i1 = chunk.getMinY();
}
if (i2 > 256 - 8) {
i2 = 256 - 8;
if (i2 >= chunk.getMaxY() - 8) {
i2 = chunk.getMaxY() - 8;
}
if (i3 < 0) {
i3 = 0;
@ -207,7 +207,7 @@ public class CavesGen extends GenBase {
for (int local_x = m; !waterFound && local_x < n; local_x++) {
for (int local_z = i3; !waterFound && local_z < i4; local_z++) {
for (int local_y = i2 + 1; !waterFound && local_y >= i1 - 1; local_y--) {
if (local_y < 255) {
if (local_y < chunk.getMaxY()) {
BlockState material = chunk.getBlock(bx + local_x, local_y, bz + local_z);
if (material.getBlockType() == BlockTypes.WATER) {
waterFound = true;

View File

@ -78,11 +78,11 @@ public class OreGen implements Resource {
double d12o2 = d12 * ONE_2;
int minX = MathMan.floorZero(d7 - d11o2);
int minY = Math.max(1, MathMan.floorZero(d8 - d12o2));
int minY = Math.max(this.minY + 1, MathMan.floorZero(d8 - d12o2));
int minZ = MathMan.floorZero(d9 - d11o2);
int maxX = MathMan.floorZero(d7 + d11o2);
int maxY = Math.min(255, MathMan.floorZero(d8 + d12o2));
int maxY = Math.min(this.maxY, MathMan.floorZero(d8 + d12o2));
int maxZ = MathMan.floorZero(d9 + d11o2);
double id11o2 = 1.0 / (d11o2);

View File

@ -33,7 +33,7 @@ public class SchemGen implements Resource {
public boolean spawn(Random random, int x, int z) throws WorldEditException {
mutable.mutX(x);
mutable.mutZ(z);
int y = extent.getNearestSurfaceTerrainBlock(x, z, mutable.getBlockY(), 0, 255);
int y = extent.getNearestSurfaceTerrainBlock(x, z, mutable.getBlockY(), this.extent.getMinY(), this.extent.getMaxY());
if (y == -1) {
return false;
}

View File

@ -12,10 +12,14 @@ public class AdjacentAnyMask extends AbstractMask implements ResettableMask {
private final CachedMask mask;
private final MutableBlockVector3 mutable;
private final int minY;
private final int maxY;
public AdjacentAnyMask(Mask mask) {
public AdjacentAnyMask(Mask mask, int minY, int maxY) {
this.mask = CachedMask.cache(mask);
mutable = new MutableBlockVector3();
this.minY = minY;
this.maxY = maxY;
}
@Override
@ -44,9 +48,9 @@ public class AdjacentAnyMask extends AbstractMask implements ResettableMask {
return mutable.setComponents(0, 0, 1);
} else if (mask.test(x, y, z - 1)) {
return mutable.setComponents(0, 0, -1);
} else if (y < 256 && mask.test(x, y + 1, z)) {
} else if (y < maxY && mask.test(x, y + 1, z)) {
return mutable.setComponents(0, 1, 0);
} else if (y > 0 && mask.test(x, y - 1, z)) {
} else if (y > minY && mask.test(x, y - 1, z)) {
return mutable.setComponents(0, -1, 0);
} else {
return null;
@ -55,7 +59,7 @@ public class AdjacentAnyMask extends AbstractMask implements ResettableMask {
@Override
public Mask copy() {
return new AdjacentAnyMask(mask.copy());
return new AdjacentAnyMask(mask.copy(), minY, maxY);
}
}

View File

@ -18,6 +18,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
protected final boolean overlay;
protected final boolean checkFirst;
protected final int maxY;
protected final int minY;
protected final int distance;
public AngleMask(Extent extent, double min, double max, boolean overlay, int distance) {
@ -26,7 +27,8 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
this.min = min;
this.max = max;
this.checkFirst = max >= (Math.tan(90 * (Math.PI / 180)));
this.maxY = extent.getMaximumPoint().getBlockY();
this.maxY = extent.getMaxY();
this.minY = extent.getMinY();
this.overlay = overlay;
this.distance = distance;
}
@ -77,7 +79,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
}
int result = cacheHeights[index] & 0xFF;
if (y > result) {
cacheHeights[index] = (byte) (result = lastY = extent.getNearestSurfaceTerrainBlock(x, z, lastY, 0, maxY));
cacheHeights[index] = (byte) (result = lastY = extent.getNearestSurfaceTerrainBlock(x, z, lastY, minY, maxY));
}
return result;
} catch (Throwable e) {
@ -141,10 +143,10 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
if (!mask.test(x, y, z - 1)) {
return true;
}
if (y < 255 && !mask.test(x, y + 1, z)) {
if (y < maxY && !mask.test(x, y + 1, z)) {
return true;
}
return y > 0 && !mask.test(x, y - 1, z);
return y > minY && !mask.test(x, y - 1, z);
}
@Override
@ -164,7 +166,7 @@ public class AngleMask extends SolidBlockMask implements ResettableMask {
return false;
}
if (overlay) {
if (y < 255 && !adjacentAir(vector)) {
if (y < maxY && !adjacentAir(vector)) {
return lastValue = false;
}
}

View File

@ -57,9 +57,10 @@ public class CachedMask extends AbstractDelegateMask implements ResettableMask {
return result;
} catch (UnsupportedOperationException ignored) {
boolean result = getMask().test(mutable.setComponents(x, y, z));
if (y < 0 || y > 255) {
return result;
}
// Assume that the mask won't be given y outside the world range
// if (y < 0 || y > 255) {
// return result;
//}
resetCache();
cache_checked.setOffset(x, z);
cache_results.setOffset(x, z);

View File

@ -25,10 +25,10 @@ public class MaskedTargetBlock extends TargetBlock {
if (!mask.test(current.toBlockPoint())) {
if (searchForLastBlock) {
lastBlock = current;
if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) {
if (lastBlock.getBlockY() <= world.getMinY() || lastBlock.getBlockY() >= world.getMaxY()) {
searchForLastBlock = false;
}
} else if (current.getBlockY() <= 0) {
} else if (current.getBlockY() <= world.getMinY()) {
break;
}
} else {

View File

@ -9,7 +9,7 @@ import com.sk89q.worldedit.world.block.BlockTypes;
public class SurfaceMask extends AdjacentAnyMask {
public SurfaceMask(Extent extent) {
super(getMask(extent));
super(getMask(extent), extent.getMinY(), extent.getMaxY());
}
public static AbstractExtentMask getMask(Extent extent) {

View File

@ -45,8 +45,8 @@ public class AngleColorPattern extends AnglePattern {
int x = vector.getBlockX();
int y = vector.getBlockY();
int z = vector.getBlockZ();
int height = extent.getNearestSurfaceTerrainBlock(x, z, y, 0, maxY);
if (height > 0) {
int height = extent.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY);
if (height > minY) {
BlockState below = extent.getBlock(x, height - 1, z);
if (!below.getBlockType().getMaterial().isMovementBlocker()) {
return Integer.MAX_VALUE;

View File

@ -13,6 +13,7 @@ public abstract class AnglePattern extends AbstractPattern {
public final double factor;
public final Extent extent;
public final int maxY;
public final int minY;
public final int distance;
/**
@ -23,9 +24,10 @@ public abstract class AnglePattern extends AbstractPattern {
*/
public AnglePattern(Extent extent, int distance) {
this.extent = new ExtentHeightCacher(extent);
this.maxY = extent.getMaximumPoint().getBlockY();
this.maxY = extent.getMaxY();
this.minY = extent.getMinY();
this.distance = distance;
this.factor = (1D / distance) * (1D / 255);
this.factor = (1D / distance) * (1D / maxY);
}
public <T extends BlockStateHolder<T>> int getSlope(T block, BlockVector3 vector, Extent extent) {
@ -36,29 +38,29 @@ public abstract class AnglePattern extends AbstractPattern {
return -1;
}
int slope = Math.abs(
extent.getNearestSurfaceTerrainBlock(x + distance, z, y, 0, maxY) - extent
.getNearestSurfaceTerrainBlock(x - distance, z, y, 0, maxY)) * 7;
extent.getNearestSurfaceTerrainBlock(x + distance, z, y, minY, maxY) - extent
.getNearestSurfaceTerrainBlock(x - distance, z, y, minY, maxY)) * 7;
slope += Math.abs(extent.getNearestSurfaceTerrainBlock(
x,
z + distance,
y,
0,
minY,
maxY
) - extent.getNearestSurfaceTerrainBlock(x, z - distance, y, 0, maxY)) * 7;
) - extent.getNearestSurfaceTerrainBlock(x, z - distance, y, minY, maxY)) * 7;
slope += Math.abs(extent.getNearestSurfaceTerrainBlock(
x + distance,
z + distance,
y,
0,
minY,
maxY
) - extent.getNearestSurfaceTerrainBlock(x - distance, z - distance, y, 0, maxY)) * 5;
) - extent.getNearestSurfaceTerrainBlock(x - distance, z - distance, y, minY, maxY)) * 5;
slope += Math.abs(extent.getNearestSurfaceTerrainBlock(
x - distance,
z + distance,
y,
0,
minY,
maxY
) - extent.getNearestSurfaceTerrainBlock(x + distance, z - distance, y, 0, maxY)) * 5;
) - extent.getNearestSurfaceTerrainBlock(x + distance, z - distance, y, minY, maxY)) * 5;
return slope;
}

View File

@ -7,6 +7,7 @@ import com.sk89q.worldedit.function.pattern.AbstractPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
public class OffsetPattern extends AbstractPattern {
@ -42,6 +43,9 @@ public class OffsetPattern extends AbstractPattern {
mutable.mutX(position.getX() + dx);
mutable.mutY(position.getY() + dy);
mutable.mutZ(position.getZ() + dz);
if (mutable.getY() < minY || mutable.getY() > maxY) {
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
return pattern.applyBlock(mutable);
}
@ -49,10 +53,10 @@ public class OffsetPattern extends AbstractPattern {
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
mutable.mutX(get.getX() + dx);
mutable.mutY(get.getY() + dy);
if (mutable.getY() < minY || mutable.getY() > maxY) {
mutable.mutZ(get.getZ() + dz);
if (mutable.getY() < extent.getMinY() || mutable.getY() > extent.getMaxY()) {
return false;
}
mutable.mutZ(get.getZ() + dz);
return pattern.apply(extent, get, mutable);
}

View File

@ -7,6 +7,7 @@ import com.sk89q.worldedit.function.pattern.AbstractPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.SplittableRandom;
@ -32,8 +33,8 @@ public class RandomOffsetPattern extends AbstractPattern {
* @param dx offset x
* @param dy offset y
* @param dz offset z
* @param minY min applicable y (inclusive
* @param maxY max applicable y (inclusive
* @param minY min applicable y (inclusive)
* @param maxY max applicable y (inclusive)
*/
public RandomOffsetPattern(Pattern pattern, int dx, int dy, int dz, int minY, int maxY) {
this.pattern = pattern;
@ -54,6 +55,9 @@ public class RandomOffsetPattern extends AbstractPattern {
mutable.mutX((position.getX() + r.nextInt(dx2) - dx));
mutable.mutY((position.getY() + r.nextInt(dy2) - dy));
mutable.mutZ((position.getZ() + r.nextInt(dz2) - dz));
if (mutable.getY() < minY || mutable.getY() > maxY) {
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
return pattern.applyBlock(mutable);
}
@ -62,7 +66,7 @@ public class RandomOffsetPattern extends AbstractPattern {
mutable.mutX((set.getX() + r.nextInt(dx2) - dx));
mutable.mutY((set.getY() + r.nextInt(dy2) - dy));
mutable.mutZ((set.getZ() + r.nextInt(dz2) - dz));
if (mutable.getY() < minY || mutable.getY() > maxY) {
if (mutable.getY() < extent.getMinY() || mutable.getY() > extent.getMaxY()) {
return false;
}
return pattern.apply(extent, get, mutable);

View File

@ -7,6 +7,7 @@ import com.sk89q.worldedit.function.pattern.AbstractPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
public class RelativePattern extends AbstractPattern implements ResettablePattern {
@ -37,6 +38,9 @@ public class RelativePattern extends AbstractPattern implements ResettablePatter
mutable.mutX(pos.getX() - origin.getX());
mutable.mutY(pos.getY() - origin.getY());
mutable.mutZ(pos.getZ() - origin.getZ());
if (mutable.getY() < minY || mutable.getY() > maxY) {
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
return pattern.applyBlock(mutable);
}
@ -47,10 +51,10 @@ public class RelativePattern extends AbstractPattern implements ResettablePatter
}
mutable.mutX(set.getX() - origin.getX());
mutable.mutY(set.getY() - origin.getY());
if (mutable.getY() < minY || mutable.getY() > maxY) {
mutable.mutZ(set.getZ() - origin.getZ());
if (mutable.getY() < extent.getMinY() || mutable.getY() > extent.getMaxY()) {
return false;
}
mutable.mutZ(set.getZ() - origin.getZ());
return pattern.apply(extent, get, mutable);
}

View File

@ -66,6 +66,12 @@ public class SolidRandomOffsetPattern extends AbstractPattern {
mutable.mutX(position.getX() + r.nextInt(dx2) - dx);
mutable.mutY(position.getY() + r.nextInt(dy2) - dy);
mutable.mutZ(position.getZ() + r.nextInt(dz2) - dz);
if (mutable.getY() < minY || mutable.getY() > maxY) {
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
if (mutable.getY() < minY || mutable.getY() > maxY) {
return BlockTypes.AIR.getDefaultState().toBaseBlock();
}
BaseBlock block = pattern.applyBlock(mutable);
if (block.getMaterial().isSolid()) {
return block;
@ -77,10 +83,10 @@ public class SolidRandomOffsetPattern extends AbstractPattern {
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
mutable.mutX(set.getX() + r.nextInt(dx2) - dx);
mutable.mutY(set.getY() + r.nextInt(dy2) - dy);
if (mutable.getY() < minY || mutable.getY() > maxY) {
mutable.mutZ(set.getZ() + r.nextInt(dz2) - dz);
if (mutable.getY() < extent.getMinY() || mutable.getY() > extent.getMaxY()) {
return false;
}
mutable.mutZ(set.getZ() + r.nextInt(dz2) - dz);
BaseBlock block = pattern.applyBlock(mutable);
if (block.getMaterial().isSolid()) {
return pattern.apply(extent, get, mutable);

View File

@ -1,6 +1,8 @@
package com.fastasyncworldedit.core.function.pattern;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.pattern.AbstractPattern;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.visitor.BreadthFirstSearch;
@ -41,6 +43,11 @@ public class SurfaceRandomOffsetPattern extends AbstractPattern {
allowed = new MutableBlockVector3[buffer.length];
}
@Override
public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException {
return super.apply(extent, get, set);
}
@Override
public BaseBlock applyBlock(BlockVector3 position) {
return pattern.applyBlock(travel(position));

View File

@ -22,13 +22,12 @@ public class AboveVisitor extends RecursiveVisitor {
* @param mask the mask
* @param function the function
* @param baseY the base Y
* @param depth maximum number of iterations
* @param minY min visitable y value. Inclusive.
* @param maxY max visitable y value. Inclusive.
*/
public AboveVisitor(Mask mask, RegionFunction function, int baseY) {
this(mask, function, baseY, Integer.MAX_VALUE);
}
public AboveVisitor(Mask mask, RegionFunction function, int baseY, int depth) {
super(mask, function, depth);
public AboveVisitor(Mask mask, RegionFunction function, int baseY, int depth, int minY, int maxY) {
super(mask, function, depth, minY, maxY);
checkNotNull(mask);
this.baseY = baseY;

View File

@ -19,12 +19,22 @@ public class DirectionalVisitor extends RecursiveVisitor {
private final BlockVector3 origin;
private final BlockVector3 dirVec;
public DirectionalVisitor(Mask mask, RegionFunction function, BlockVector3 origin, BlockVector3 direction) {
this(mask, function, origin, direction, Integer.MAX_VALUE);
}
public DirectionalVisitor(Mask mask, RegionFunction function, BlockVector3 origin, BlockVector3 direction, int distance) {
super(mask, function, distance);
/**
* New visitor. Only visits in the given direction
*
* @param mask block mask
* @param function function to apply
* @param origin start position
* @param direction allowable direction to visit between
* @param distance max number of iterations
* @param minY min visitable y value. Inclusive.
* @param maxY max visitable y value. Inclusive.
*/
public DirectionalVisitor(
Mask mask, RegionFunction function, BlockVector3 origin, BlockVector3 direction, int distance,
int minY, int maxY
) {
super(mask, function, distance, minY, maxY);
checkNotNull(mask);
this.origin = origin;
this.dirVec = direction;

View File

@ -102,6 +102,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
nbttFile = new File(folder, index + ".nbtt");
entfFile = new File(folder, index + ".entf");
enttFile = new File(folder, index + ".entt");
//Switch file ending due to new (sort-of) format. (Added e for Extended height)
bdFile = new File(folder, index + ".bd");
bioFile = new File(folder, index + ".bio");
}
@ -431,6 +432,8 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
final FaweInputStream gis = MainUtil.getCompressedIS(fis);
// skip mode
gis.skipFully(1);
// skip version
gis.skipFully(1);
// origin
ox = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read());
oz = ((gis.read() << 24) + (gis.read() << 16) + (gis.read() << 8) + gis.read());

View File

@ -157,7 +157,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
addEntityCreate(tag);
}
}
for (int layer = 0; layer < 16; layer++) {
for (int layer = get.getMinSectionIndex(); layer <= get.getMaxSectionIndex(); layer++) {
if (!set.hasSection(layer)) {
continue;
}
@ -172,6 +172,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
char[] blocksSet;
System.arraycopy(set.load(layer), 0, (blocksSet = new char[4096]), 0, 4096);
// Account for negative layers
int by = layer << 4;
for (int y = 0, index = 0; y < 16; y++) {
int yy = y + by;
@ -195,14 +196,21 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
BiomeType[] biomes = set.getBiomes();
if (biomes != null) {
for (int y = 0, index = 0; y < 64; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 4; x++, index++) {
BiomeType newBiome = biomes[index];
if (newBiome != null) {
BiomeType oldBiome = get.getBiomeType(x, y, z);
if (oldBiome != newBiome) {
addBiomeChange(bx + (x << 2), y << 2, bz + (z << 2), oldBiome, newBiome);
int index = 0;
for (int layer = get.getMinSectionIndex(); layer <= get.getMaxSectionIndex(); layer++) {
if (!set.hasBiomes(layer)) {
continue;
}
int yy = layer << 4;
for (int y = 0; y < 4; y++) {
for (int z = 0; z < 4; z++) {
for (int x = 0; x < 4; x++, index++) {
BiomeType newBiome = biomes[index];
if (newBiome != null) {
BiomeType oldBiome = get.getBiomeType(x, y, z);
if (oldBiome != newBiome) {
addBiomeChange(bx + (x << 2), yy + (y << 2), bz + (z << 2), oldBiome, newBiome);
}
}
}
}
@ -341,11 +349,17 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
return addWriteTask(writeTask, Fawe.isMainThread());
}
public Future<?> addWriteTask(Runnable writeTask, boolean completeNow) {
public Future<?> addWriteTask(final Runnable writeTask, final boolean completeNow) {
AbstractChangeSet.this.waitingCombined.incrementAndGet();
Runnable wrappedTask = () -> {
try {
writeTask.run();
} catch (Throwable t) {
if (completeNow) {
throw t;
} else {
t.printStackTrace();
}
} finally {
if (AbstractChangeSet.this.waitingCombined.decrementAndGet() <= 0) {
synchronized (AbstractChangeSet.this.waitingAsync) {

View File

@ -28,11 +28,16 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* FAWE stream ChangeSet offering support for extended-height worlds
*/
public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public static final int HEADER_SIZE = 9;
private static final int version = 1;
private int mode;
private final int compression;
private final int minY;
protected FaweStreamIdDelegate idDel;
protected FaweStreamPositionDelegate posDel;
@ -44,6 +49,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public FaweStreamChangeSet(World world, int compression, boolean storeRedo, boolean smallLoc) {
super(world);
this.compression = compression;
this.minY = world.getMinY();
init(storeRedo, smallLoc);
}
@ -139,6 +145,10 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override
public void write(OutputStream out, int x, int y, int z) throws IOException {
if (y < 0 || y > 255) {
throw new UnsupportedOperationException("y cannot be outside range 0-255 for " +
"small-edits=true");
}
int rx = -lx + (lx = x);
int ry = -ly + (ly = y);
int rz = -lz + (lz = z);
@ -174,7 +184,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
};
} else {
posDel = new FaweStreamPositionDelegate() {
final byte[] buffer = new byte[5];
final byte[] buffer = new byte[6];
int lx;
int ly;
int lz;
@ -188,7 +198,8 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
stream.write(((rx) >> 8) & 0xff);
stream.write((rz) & 0xff);
stream.write(((rz) >> 8) & 0xff);
stream.write((byte) ry);
stream.write((ry) & 0xff);
stream.write(((ry) >> 8) & 0xff);
}
@Override
@ -199,7 +210,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
@Override
public int readY(FaweInputStream is) throws IOException {
return (ly = (ly + (buffer[4]))) & 0xFF;
return ly = (ly + (buffer[4] & 0xFF) + (buffer[5] << 8));
}
@Override
@ -212,6 +223,8 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public void writeHeader(OutputStream os, int x, int y, int z) throws IOException {
os.write(mode);
// Allows for version detection of history in case of changes to format.
os.write(version);
setOrigin(x, z);
os.write((byte) (x >> 24));
os.write((byte) (x >> 16));
@ -227,6 +240,10 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public void readHeader(InputStream is) throws IOException {
// skip mode
int mode = is.read();
int version = is.read();
if (version != FaweStreamChangeSet.version) {
throw new UnsupportedOperationException(String.format("Version %s history not supported!", version));
}
// origin
int x = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
@ -290,10 +307,6 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
public abstract NBTInputStream getTileRemoveIS() throws IOException;
protected int blockSize;
public int entityCreateSize;
public int entityRemoveSize;
public int tileCreateSize;
public int tileRemoveSize;
private int originX;
private int originZ;
@ -325,9 +338,12 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
}
@Override
public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) {
public void addBiomeChange(int bx, int by, int bz, BiomeType from, BiomeType to) {
blockSize++;
try {
int x = bx >> 2;
int y = by >> 2;
int z = bz >> 2;
FaweOutputStream os = getBiomeOS();
os.write((byte) (x >> 24));
os.write((byte) (x >> 16));
@ -337,7 +353,9 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
os.write((byte) (z >> 16));
os.write((byte) (z >> 8));
os.write((byte) (z));
os.write((byte) (y));
// only need to store biomes in the 4x4x4 chunks so only need one byte for y still (signed byte -128 -> 127)
// means -512 -> 508. Add 128 to avoid negative value casting.
os.write((byte) (y + 128));
os.writeVarInt(from.getInternalId());
os.writeVarInt(to.getInternalId());
} catch (Throwable e) {
@ -465,9 +483,9 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
try {
int int1 = is.read();
if (int1 != -1) {
int x = ((int1 << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read());
int y = is.read();
int x = ((int1 << 24) + (is.read() << 16) + (is.read() << 8) + is.read()) << 2;
int z = ((is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read()) << 2;
int y = (is.read() - 128) << 2;
int from = is.readVarInt();
int to = is.readVarInt();
change.setBiome(x, y, z, from, to);

View File

@ -3,26 +3,26 @@ package com.fastasyncworldedit.core.math;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.collection.IAdaptedMap;
import com.sk89q.worldedit.math.BlockVector3;
import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import java.util.Map;
public class BlockVector3ChunkMap<T> implements IAdaptedMap<BlockVector3, T, Short, T> {
public class BlockVector3ChunkMap<T> implements IAdaptedMap<BlockVector3, T, Integer, T> {
private final Short2ObjectArrayMap<T> map = new Short2ObjectArrayMap<>();
private final Int2ObjectArrayMap<T> map = new Int2ObjectArrayMap<>();
@Override
public Map<Short, T> getParent() {
public Map<Integer, T> getParent() {
return map;
}
@Override
public Short adaptKey(BlockVector3 key) {
public Integer adaptKey(BlockVector3 key) {
return MathMan.tripleBlockCoord(key.getX(), key.getY(), key.getZ());
}
@Override
public BlockVector3 adaptKey2(Short key) {
public BlockVector3 adaptKey2(Integer key) {
int x = MathMan.untripleBlockCoordX(key);
int y = MathMan.untripleBlockCoordY(key);
int z = MathMan.untripleBlockCoordZ(key);
@ -40,23 +40,23 @@ public class BlockVector3ChunkMap<T> implements IAdaptedMap<BlockVector3, T, Sho
}
public T put(int x, int y, int z, T value) {
short key = MathMan.tripleBlockCoord(x, y, z);
int key = MathMan.tripleBlockCoord(x, y, z);
return map.put(key, value);
}
public T get(int x, int y, int z) {
short key = MathMan.tripleBlockCoord(x, y, z);
int key = MathMan.tripleBlockCoord(x, y, z);
return map.get(key);
}
public T remove(int x, int y, int z) {
short key = MathMan.tripleBlockCoord(x, y, z);
int key = MathMan.tripleBlockCoord(x, y, z);
return map.remove(key);
}
public boolean contains(int x, int y, int z) {
short key = MathMan.tripleBlockCoord(x, y, z);
int key = MathMan.tripleBlockCoord(x, y, z);
return map.containsKey(key);
}

View File

@ -44,14 +44,14 @@ public class BlockVectorSet extends AbstractCollection<BlockVector3> implements
int newSize = count + size;
if (newSize > index) {
int localIndex = index - count;
BlockVector3 pos = set.getIndex(localIndex);
MutableBlockVector3 pos = set.getIndex(localIndex);
if (pos != null) {
int pair = entry.getIntKey();
int cx = MathMan.unpairX(pair);
int cz = MathMan.unpairY(pair);
pos = pos.mutX((cx << 11) + pos.getBlockX());
pos = pos.mutZ((cz << 11) + pos.getBlockZ());
return pos;
pos.mutX((cx << 11) + pos.getBlockX());
pos.mutZ((cz << 11) + pos.getBlockZ());
return pos.toImmutable();
}
}
count += newSize;
@ -91,7 +91,7 @@ public class BlockVectorSet extends AbstractCollection<BlockVector3> implements
if (!entries.hasNext()) {
return Collections.emptyIterator();
}
return new Iterator<BlockVector3>() {
return new Iterator<>() {
Int2ObjectMap.Entry<LocalBlockVectorSet> entry = entries.next();
Iterator<BlockVector3> entryIter = entry.getValue().iterator();
final MutableBlockVector3 mutable = new MutableBlockVector3();

View File

@ -11,14 +11,14 @@ import java.util.Set;
/**
* The LocalBlockVectorSet is a Memory and CPU optimized Set for storing BlockVectors which are all in a local region
* - All vectors must be in a 2048 * 2048 area centered around the first entry
* - All vectors must be in a 2048 * 512 * 2048 area centered around the first entry
* - This will use 8 bytes for every 64 BlockVectors (about 800x less than a HashSet)
*/
public class LocalBlockVectorSet implements Set<BlockVector3> {
private final SparseBitSet set;
private int offsetX;
private int offsetZ;
private final SparseBitSet set;
public LocalBlockVectorSet() {
offsetX = offsetZ = Integer.MAX_VALUE;
@ -42,7 +42,8 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
}
public boolean contains(int x, int y, int z) {
return set.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ));
// take 128 to fit -256<y<255
return set.get(MathMan.tripleSearchCoords(x - offsetX, y - 128, z - offsetZ));
}
@Override
@ -67,12 +68,15 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
if (size() < length * length * length) {
int index = -1;
while ((index = set.nextSetBit(index + 1)) != -1) {
int b1 = (byte) (index >> 0) & 0xFF;
int b2 = (byte) (index >> 8) & 0x7F;
int b3 = (byte) (index >> 15) & 0xFF;
int b4 = (byte) (index >> 23) & 0xFF;
if (Math.abs((offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21)) - x) <= radius && Math.abs((offsetZ + (((b4 + ((MathMan
.unpair8y(b2)) << 8)) << 21) >> 21)) - z) <= radius && Math.abs((b1) - y) <= radius) {
int b1 = (index & 0xFF);
int b2 = (index >> 8) & 0xff;
int b3 = (index >> 15) & 0xFF;
int b4 = (index >> 23) & 0xFF;
int ix = (offsetX + (b3 + (((b2 & 0x7)) << 8)) << 21) >> 21;
// Add 128 as we shift y by 128 to fit -256<y<255
int iy = 128 + b1 * (((b2 >> 6) & 0x1) == 0 ? 1 : -1);
int iz = (offsetZ + (b4 + (((b2 >> 3) & 0x7) << 8)) << 21) >> 21;
if (Math.abs(ix - x) <= radius && Math.abs(iz - z) <= radius && Math.abs(iy - y) <= radius) {
return true;
}
}
@ -100,7 +104,7 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
this.offsetZ = z;
}
protected BlockVector3 getIndex(int getIndex) {
protected MutableBlockVector3 getIndex(int getIndex) {
int size = size();
if (getIndex > size) {
return null;
@ -110,13 +114,15 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
index = set.nextSetBit(index + 1);
}
if (index != -1) {
int b1 = (byte) (index >> 0) & 0xFF;
int b2 = (byte) (index >> 8) & 0x7F;
int b3 = (byte) (index >> 15) & 0xFF;
int b4 = (byte) (index >> 23) & 0xFF;
int x = offsetX + (((b3 + (MathMan.unpair8x(b2) << 8)) << 21) >> 21);
int z = offsetZ + (((b4 + (MathMan.unpair8y(b2) << 8)) << 21) >> 21);
return MutableBlockVector3.get(x, b1, z);
int b1 = (index & 0xFF);
int b2 = (index >> 8) & 0xff;
int b3 = (index >> 15) & 0xFF;
int b4 = (index >> 23) & 0xFF;
int x = (offsetX + (b3 + (((b2 & 0x7)) << 8)) << 21) >> 21;
// Add 128 as we shift y by 128 to fit -256<y<255
int y = 128 + b1 * (((b2 >> 6) & 0x1) == 0 ? 1 : -1);
int z = (offsetZ + (b4 + (((b2 >> 3) & 0x7) << 8)) << 21) >> 21;
return MutableBlockVector3.get(x, y, z);
}
return null;
}
@ -125,9 +131,9 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
@Override
public Iterator<BlockVector3> iterator() {
return new Iterator<BlockVector3>() {
final MutableBlockVector3 mutable = new MutableBlockVector3(0, 0, 0);
int index = set.nextSetBit(0);
int previous = -1;
final MutableBlockVector3 mutable = new MutableBlockVector3(0, 0, 0);
@Override
public void remove() {
@ -143,12 +149,16 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
public BlockVector3 next() {
if (index != -1) {
int b1 = (index & 0xFF);
int b2 = ((byte) (index >> 8)) & 0x7F;
int b3 = ((byte) (index >> 15)) & 0xFF;
int b4 = ((byte) (index >> 23)) & 0xFF;
mutable.mutX(offsetX + (((b3 + (MathMan.unpair8x(b2) << 8)) << 21) >> 21));
mutable.mutY(b1);
mutable.mutZ(offsetZ + (((b4 + (MathMan.unpair8y(b2) << 8)) << 21) >> 21));
int b2 = (index >> 8) & 0xff;
int b3 = (index >> 15) & 0xFF;
int b4 = (index >> 23) & 0xFF;
int x = (offsetX + (b3 + (((b2 & 0x7)) << 8)) << 21) >> 21;
// Add 128 as we shift y by 128 to fit -256<y<255
int y = 128 + b1 * (((b2 >> 6) & 0x1) == 0 ? 1 : -1);
int z = (offsetZ + (b4 + (((b2 >> 3) & 0x7) << 8)) << 21) >> 21;
mutable.mutX(x);
mutable.mutY(y);
mutable.mutZ(z);
previous = index;
index = set.nextSetBit(index + 1);
return mutable;
@ -175,12 +185,14 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
for (int i = 0; i < size; i++) {
index = set.nextSetBit(index);
int b1 = (index & 0xFF);
int b2 = ((byte) (index >> 8)) & 0x7F;
int b3 = ((byte) (index >> 15)) & 0xFF;
int b4 = ((byte) (index >> 23)) & 0xFF;
int x = offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21);
int z = offsetZ + (((b4 + ((MathMan.unpair8y(b2)) << 8)) << 21) >> 21);
array[i] = (T) BlockVector3.at(x, b1, z);
int b2 = (index >> 8) & 0xff;
int b3 = (index >> 15) & 0xFF;
int b4 = (index >> 23) & 0xFF;
int x = (offsetX + (b3 + (((b2 & 0x7)) << 8)) << 21) >> 21;
// Add 128 as we shift y by 128 to fit -256<y<255
int y = 128 + b1 * (((b2 >> 6) & 0x1) == 0 ? 1 : -1);
int z = (offsetZ + (b4 + (((b2 >> 3) & 0x7) << 8)) << 21) >> 21;
array[i] = (T) BlockVector3.at(x, y, z);
index++;
}
return array;
@ -195,7 +207,7 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) {
return false;
}
return y >= 0 && y <= 256;
return y >= -128 && y <= 383;
}
public boolean add(int x, int y, int z) {
@ -209,8 +221,8 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
throw new UnsupportedOperationException(
"LocalVectorSet can only contain vectors within 1024 blocks (cuboid) of the first entry. ");
}
if (y < 0 || y > 255) {
throw new UnsupportedOperationException("LocalVectorSet can only contain vectors from y elem:[0,255]");
if (y < -128 || y > 383) {
throw new UnsupportedOperationException("LocalVectorSet can only contain vectors from y elem:[-128,383]");
}
int index = getIndex(x, y, z);
if (set.get(index)) {
@ -227,11 +239,17 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
}
private int getIndex(BlockVector3 vector) {
return MathMan.tripleSearchCoords(vector.getBlockX() - offsetX, vector.getBlockY(), vector.getBlockZ() - offsetZ);
// take 128 to fit -256<y<255
return MathMan.tripleSearchCoords(
vector.getBlockX() - offsetX,
vector.getBlockY() - 128,
vector.getBlockZ() - offsetZ
);
}
private int getIndex(int x, int y, int z) {
return MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ);
// take 128 to fit -256<y<255
return MathMan.tripleSearchCoords(x - offsetX, y - 128, z - offsetZ);
}
public boolean remove(int x, int y, int z) {
@ -240,7 +258,8 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) {
return false;
}
int index = MathMan.tripleSearchCoords(relX, y, relZ);
// take 128 to fit -256<y<255
int index = MathMan.tripleSearchCoords(relX, y - 128, relZ);
boolean value = set.get(index);
set.clear(index);
return value;
@ -283,12 +302,16 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
for (int i = 0; i < size; i++) {
index = set.nextSetBit(index + 1);
int b1 = (index & 0xFF);
int b2 = ((byte) (index >> 8)) & 0x7F;
int b3 = ((byte) (index >> 15)) & 0xFF;
int b4 = ((byte) (index >> 23)) & 0xFF;
mVec.mutX(offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21));
mVec.mutY(b1);
mVec.mutZ(offsetZ + (((b4 + ((MathMan.unpair8y(b2)) << 8)) << 21) >> 21));
int b2 = (index >> 8) & 0xff;
int b3 = (index >> 15) & 0xFF;
int b4 = (index >> 23) & 0xFF;
int x = (offsetX + (b3 + (((b2 & 0x7)) << 8)) << 21) >> 21;
// Add 128 as we shift y by 128 to fit -256<y<255
int y = 128 + b1 * (((b2 >> 6) & 0x1) == 0 ? 1 : -1);
int z = (offsetZ + (b4 + (((b2 >> 3) & 0x7) << 8)) << 21) >> 21;
mVec.mutX(x);
mVec.mutY(y);
mVec.mutZ(z);
if (!c.contains(mVec)) {
result = true;
set.clear(index);
@ -312,21 +335,17 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
for (int i = 0; i < size; i++) {
index = set.nextSetBit(index + 1);
int b1 = (index & 0xFF);
int b2 = ((byte) (index >> 8)) & 0x7F;
int b3 = ((byte) (index >> 15)) & 0xFF;
int b4 = ((byte) (index >> 23)) & 0xFF;
int x = offsetX + (((b3 + ((MathMan.unpair8x(b2)) << 8)) << 21) >> 21);
int z = offsetZ + (((b4 + ((MathMan.unpair8y(b2)) << 8)) << 21) >> 21);
visitor.run(x, b1, z, index);
int b2 = (index >> 8) & 0xff;
int b3 = (index >> 15) & 0xFF;
int b4 = (index >> 23) & 0xFF;
int x = (offsetX + (b3 + (((b2 & 0x7)) << 8)) << 21) >> 21;
// Add 128 as we shift y by 128 to fit -256<y<255
int y = 128 + b1 * (((b2 >> 6) & 0x1) == 0 ? 1 : -1);
int z = (offsetZ + (b4 + (((b2 >> 3) & 0x7) << 8)) << 21) >> 21;
visitor.run(x, y, z, index);
}
}
public interface BlockVectorSetVisitor {
void run(int x, int y, int z, int index);
}
@Override
public void clear() {
offsetZ = Integer.MAX_VALUE;
@ -334,4 +353,10 @@ public class LocalBlockVectorSet implements Set<BlockVector3> {
set.clear();
}
public interface BlockVectorSetVisitor {
void run(int x, int y, int z, int index);
}
}

View File

@ -1,6 +1,5 @@
package com.fastasyncworldedit.core.queue;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor;
import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor;
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
@ -42,7 +41,7 @@ public interface IBatchProcessor {
*/
default boolean trimY(IChunkSet set, int minY, int maxY) {
int minLayer = (minY - 1) >> 4;
for (int layer = 0; layer <= minLayer; layer++) {
for (int layer = set.getMinSectionIndex(); layer <= minLayer; layer++) {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.load(layer);
@ -57,7 +56,7 @@ public interface IBatchProcessor {
}
}
int maxLayer = (maxY + 1) >> 4;
for (int layer = maxLayer; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
for (int layer = maxLayer; layer < set.getMaxSectionIndex(); layer++) {
if (set.hasSection(layer)) {
if (layer == minLayer) {
char[] arr = set.load(layer);
@ -74,10 +73,8 @@ public interface IBatchProcessor {
try {
int layer = (minY - 15) >> 4;
while (layer < (maxY + 15) >> 4) {
if (layer > -1) {
if (set.hasSection(layer)) {
return true;
}
if (set.hasSection(layer)) {
return true;
}
layer++;
}

View File

@ -11,7 +11,6 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.registry.BlockRegistry;
import org.jetbrains.annotations.Range;
import java.io.IOException;
import java.util.Map;
@ -23,7 +22,14 @@ import java.util.stream.IntStream;
*/
public interface IBlocks extends Trimable {
boolean hasSection(@Range(from = 0, to = 15) int layer);
/**
* Returns if the chunk has a BLOCKS section at the given layer. May not be indicative of presence
* of entities, tile entites, biomes, etc.
*
* @param layer chunk section layer
* @return if blocks/a block section is present
*/
boolean hasSection(int layer);
char[] load(int layer);
@ -38,7 +44,7 @@ public interface IBlocks extends Trimable {
BiomeType getBiomeType(int x, int y, int z);
default int getBitMask() {
return IntStream.range(0, FaweCache.IMP.CHUNK_LAYERS).filter(this::hasSection)
return IntStream.range(getMinSectionIndex(), getMaxSectionIndex() + 1).filter(this::hasSection)
.map(layer -> (1 << layer)).sum();
}
@ -48,6 +54,21 @@ public interface IBlocks extends Trimable {
IBlocks reset();
/**
* Get the number of stores sections
*/
int getSectionCount();
/**
* Max ChunkSection array index
*/
int getMaxSectionIndex();
/**
* Min ChunkSection array index
*/
int getMinSectionIndex();
default byte[] toByteArray(boolean full, boolean stretched) {
return toByteArray(null, getBitMask(), full, stretched);
}
@ -61,7 +82,7 @@ public interface IBlocks extends Trimable {
.queryCapability(Capability.GAME_HOOKS).getRegistries().getBlockRegistry();
FastByteArrayOutputStream sectionByteArray = new FastByteArrayOutputStream(buffer);
try (FaweOutputStream sectionWriter = new FaweOutputStream(sectionByteArray)) {
for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) {
for (int layer = 0; layer < this.getSectionCount(); layer++) {
if (!this.hasSection(layer) || (bitMask & (1 << layer)) == 0) {
continue;
}

View File

@ -17,7 +17,6 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.jetbrains.annotations.Range;
import java.util.ArrayList;
import java.util.HashMap;
@ -37,7 +36,7 @@ public interface IChunkExtent<T extends IChunk> extends Extent {
T getOrCreateChunk(int chunkX, int chunkZ);
@Override
default <B extends BlockStateHolder<B>> boolean setBlock(int x, @Range(from = 0, to = 255) int y, int z, B state) {
default <B extends BlockStateHolder<B>> boolean setBlock(int x, int y, int z, B state) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.setBlock(x & 15, y, z & 15, state);
}

View File

@ -48,19 +48,43 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
CompoundTag getEntity(UUID uuid);
void setCreateCopy(boolean createCopy);
boolean isCreateCopy();
void setCreateCopy(boolean createCopy);
@Nullable
default IChunkGet getCopy() {
return null;
}
void setLightingToGet(char[][] lighting);
/**
* Flush the block lighting array (section*blocks) to the chunk GET between the given section indices. Negative allowed.
*
* @param lighting lighting array
* @param startSectionIndex lowest section index
* @param endSectionIndex highest section index
*/
void setLightingToGet(char[][] lighting, int startSectionIndex, int endSectionIndex);
void setSkyLightingToGet(char[][] lighting);
/**
* Flush the sky lighting array (section*blocks) to the chunk GET between the given section indices. Negative allowed.
*
* @param lighting sky lighting array
* @param startSectionIndex lowest section index
* @param endSectionIndex highest section index
*/
void setSkyLightingToGet(char[][] lighting, int startSectionIndex, int endSectionIndex);
void setHeightmapToGet(HeightMapType type, int[] data);
/**
* Max y value for the chunk's world (inclusive)
*/
int getMaxY();
/**
* Min y value for the chunk's world (inclusive)
*/
int getMinY();
}

View File

@ -107,4 +107,12 @@ public interface IChunkSet extends IBlocks, OutputExtent {
return null;
}
/**
* If the given layer has biomes stored to be set to the world. Can be negative
*
* @param layer layer to check
* @return if the layer has biomes stored to be set to the world
*/
boolean hasBiomes(int layer);
}

View File

@ -25,11 +25,17 @@ public class Flood {
private int chunkYLayer;
private int chunkZ;
private final ConcurrentLinkedQueue<int[]> queuePool = new ConcurrentLinkedQueue<>();
private final int minSectionIndex;
private final int maxSectionIndex;
private final int sectionCount;
public Flood(int maxBranch, int maxDepth, Direction[] directions) {
public Flood(int maxBranch, int maxDepth, Direction[] directions, int minSectionIndex, int maxSectionIndex) {
this.maxBranch = maxBranch;
this.maxDepth = maxDepth;
this.directions = directions;
this.minSectionIndex = minSectionIndex;
this.maxSectionIndex = maxSectionIndex;
this.sectionCount = maxSectionIndex - minSectionIndex + 1;
this.queues = new int[27][];
this.visits = new long[27][];
@ -64,7 +70,7 @@ public class Flood {
int chunkX = x >> 4;
int chunkZ = z >> 4;
long pair = MathMan.pairInt(chunkX, chunkZ);
int layer = y >> 4;
int layer = (y >> 4) - minSectionIndex;
int[] section = getOrCreateQueue(pair, layer);
int val = (x & 15) + ((z & 15) << 4) + ((y & 15) << 8) + (depth << 12);
push(section, val);
@ -73,7 +79,7 @@ public class Flood {
private int[] getOrCreateQueue(long pair, int layer) {
int[][] arrs = chunkQueues.get(pair);
if (arrs == null) {
chunkQueues.put(pair, arrs = new int[16][]);
chunkQueues.put(pair, arrs = new int[sectionCount][]);
}
int[] section = arrs[layer];
if (section == null) {
@ -154,7 +160,7 @@ public class Flood {
if (visit == null || queue == null) {
long pair = MathMan.pairInt(this.chunkX + nextX, this.chunkZ + nextZ);
int layer = this.chunkYLayer + nextY;
if (layer < 0 || layer > 15) {
if (layer < minSectionIndex || layer > maxSectionIndex) {
continue;
}
queues[sectionIndex] = queue = getOrCreateQueue(pair, layer);

View File

@ -52,6 +52,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
// Chunks currently being queued / worked on
private final Long2ObjectLinkedOpenHashMap<IQueueChunk> chunks = new Long2ObjectLinkedOpenHashMap<>();
private World world = null;
private int minY = 0;
private int maxY = 255;
private IChunkCache<IChunkGet> cacheGet;
private IChunkCache<IChunkSet> cacheSet;
private boolean initialized;
@ -68,7 +72,15 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
private final ReentrantLock getChunkLock = new ReentrantLock();
private World world = null;
public SingleThreadQueueExtent() {}
/**
* New instance given inclusive world height bounds.
*/
public SingleThreadQueueExtent(int minY, int maxY) {
this.minY = minY;
this.maxY = maxY;
}
/**
* Safety check to ensure that the thread being used matches the one being initialized on. - Can
@ -111,6 +123,16 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
return fastmode;
}
@Override
public int getMinY() {
return minY;
}
@Override
public int getMaxY() {
return maxY;
}
/**
* Resets the queue.
*/
@ -142,6 +164,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
@Override
public synchronized void init(Extent extent, IChunkCache<IChunkGet> get, IChunkCache<IChunkSet> set) {
reset();
this.minY = extent.getMinY();
this.maxY = extent.getMaxY();
currentThread = Thread.currentThread();
if (get == null) {
get = (x, z) -> {

View File

@ -20,14 +20,21 @@ public class BitSetBlocks implements IChunkSet {
private final MemBlockSet.RowZ row;
private final BlockState blockState;
private final int minSectionIndex;
private final int maxSectionIndex;
private final int layers;
public BitSetBlocks(BlockState blockState) {
this.row = new MemBlockSet.RowZ();
public BitSetBlocks(BlockState blockState, int minSectionIndex, int maxSectionIndex) {
this.row = new MemBlockSet.RowZ(minSectionIndex, maxSectionIndex);
this.blockState = blockState;
this.minSectionIndex = minSectionIndex;
this.maxSectionIndex = maxSectionIndex;
this.layers = maxSectionIndex - minSectionIndex + 1;
}
@Override
public boolean hasSection(int layer) {
layer -= minSectionIndex;
return row.rows[layer] != MemBlockSet.NULL_ROW_Y;
}
@ -39,19 +46,21 @@ public class BitSetBlocks implements IChunkSet {
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T holder) {
row.set(null, x, y, z);
y -= minSectionIndex << 4;
row.set(null, x, y, z, minSectionIndex, maxSectionIndex);
return true;
}
@Override
public void setBlocks(int layer, char[] data) {
layer -= minSectionIndex;
row.reset(layer);
int by = layer << 4;
for (int y = 0, index = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++, index++) {
if (data[index] != 0) {
row.set(null, x, by + y, z);
row.set(null, x, by + y, z, minSectionIndex, maxSectionIndex);
}
}
}
@ -114,6 +123,7 @@ public class BitSetBlocks implements IChunkSet {
@Override
public char[] load(int layer) {
layer -= minSectionIndex;
char[] arr = FaweCache.IMP.SECTION_BITS_TO_CHAR.get();
MemBlockSet.IRow nullRowY = row.getRow(layer);
if (nullRowY instanceof MemBlockSet.RowY) {
@ -189,6 +199,26 @@ public class BitSetBlocks implements IChunkSet {
return this;
}
@Override
public boolean hasBiomes(final int layer) {
return false;
}
@Override
public int getSectionCount() {
return layers;
}
@Override
public int getMaxSectionIndex() {
return minSectionIndex;
}
@Override
public int getMinSectionIndex() {
return maxSectionIndex;
}
@Override
public boolean trim(boolean aggressive) {
return false;

View File

@ -7,7 +7,6 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Range;
public abstract class CharBlocks implements IBlocks {
@ -15,30 +14,30 @@ public abstract class CharBlocks implements IBlocks {
protected static final Section FULL = new Section() {
@Override
public final char[] get(CharBlocks blocks, int layer) {
public char[] get(CharBlocks blocks, int layer) {
return blocks.blocks[layer];
}
// Ignore aggressive switch here.
@Override
public char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, boolean aggressive) {
public char[] get(CharBlocks blocks, int layer, boolean aggressive) {
return blocks.blocks[layer];
}
@Override
public final boolean isFull() {
public boolean isFull() {
return true;
}
};
protected final Section empty = new Section() {
@Override
public final synchronized char[] get(CharBlocks blocks, int layer) {
public synchronized char[] get(CharBlocks blocks, int layer) {
// Defaults to aggressive as it should only be avoided where we know we've reset a chunk during an edit
return get(blocks, layer, true);
}
@Override
public synchronized char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, boolean aggressive) {
public synchronized char[] get(CharBlocks blocks, int layer, boolean aggressive) {
char[] arr = blocks.blocks[layer];
if (arr == null) {
arr = blocks.blocks[layer] = blocks.update(layer, null, aggressive);
@ -58,17 +57,26 @@ public abstract class CharBlocks implements IBlocks {
}
@Override
public final boolean isFull() {
public boolean isFull() {
return false;
}
};
public final char[][] blocks;
public final Section[] sections;
public char[][] blocks;
public Section[] sections;
protected int minSectionIndex;
protected int maxSectionIndex;
protected int sectionCount;
public CharBlocks() {
blocks = new char[16][];
sections = new Section[16];
for (int i = 0; i < 16; i++) {
/**
* New instance given initial min/max section indices. Can be negative.
*/
public CharBlocks(int minSectionIndex, int maxSectionIndex) {
this.minSectionIndex = minSectionIndex;
this.maxSectionIndex = maxSectionIndex;
this.sectionCount = maxSectionIndex - minSectionIndex + 1;
blocks = new char[sectionCount][];
sections = new Section[sectionCount];
for (int i = 0; i < sectionCount; i++) {
sections[i] = empty;
}
}
@ -76,7 +84,7 @@ public abstract class CharBlocks implements IBlocks {
@Override
public synchronized boolean trim(boolean aggressive) {
boolean result = true;
for (int i = 0; i < 16; i++) {
for (int i = 0; i < sectionCount; i++) {
if (!sections[i].isFull() && blocks[i] != null) {
blocks[i] = null;
} else {
@ -99,13 +107,14 @@ public abstract class CharBlocks implements IBlocks {
@Override
public synchronized IChunkSet reset() {
for (int i = 0; i < 16; i++) {
for (int i = 0; i < sectionCount; i++) {
sections[i] = empty;
}
return null;
}
public synchronized void reset(@Range(from = 0, to = 15) int layer) {
public synchronized void reset(int layer) {
layer -= minSectionIndex;
sections[layer] = empty;
}
@ -121,12 +130,14 @@ public abstract class CharBlocks implements IBlocks {
// Not synchronized as any subsequent methods called from this class will be, or the section shouldn't appear as loaded anyway.
@Override
public boolean hasSection(@Range(from = 0, to = 15) int layer) {
return sections[layer].isFull();
public boolean hasSection(int layer) {
layer -= minSectionIndex;
return layer >= 0 && layer < sections.length && sections[layer].isFull();
}
@Override
public char[] load(@Range(from = 0, to = 15) int layer) {
public char[] load(int layer) {
layer -= minSectionIndex;
synchronized (sections[layer]) {
return sections[layer].get(this, layer);
}
@ -137,17 +148,17 @@ public abstract class CharBlocks implements IBlocks {
return BlockTypesCache.states[get(x, y, z)];
}
public char get(int x, @Range(from = 0, to = 255) int y, int z) {
final int layer = y >> 4;
public char get(int x, int y, int z) {
int layer = y >> 4;
final int index = (y & 15) << 8 | z << 4 | x;
if (layer >= sections.length || layer < 0) {
if (layer > maxSectionIndex || layer < minSectionIndex) {
return 0;
}
return sections[layer].get(this, layer, index);
return get(layer, index);
}
// Not synchronized as it refers to a synchronized method and includes nothing that requires synchronization
public void set(int x, @Range(from = 0, to = 255) int y, int z, char value) {
public void set(int x, int y, int z, char value) {
final int layer = y >> 4;
final int index = (y & 15) << 8 | z << 4 | x;
try {
@ -163,24 +174,26 @@ public abstract class CharBlocks implements IBlocks {
Section
*/
public final char get(@Range(from = 0, to = 15) int layer, int index) {
public final char get(int layer, int index) {
layer -= minSectionIndex;
return sections[layer].get(this, layer, index);
}
public synchronized final void set(@Range(from = 0, to = 15) int layer, int index, char value) throws
public synchronized final void set(int layer, int index, char value) throws
ArrayIndexOutOfBoundsException {
layer -= minSectionIndex;
sections[layer].set(this, layer, index, value);
}
public abstract static class Section {
public abstract char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer);
public abstract char[] get(CharBlocks blocks, int layer);
public abstract char[] get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, boolean aggressive);
public abstract char[] get(CharBlocks blocks, int layer, boolean aggressive);
public abstract boolean isFull();
public final char get(CharBlocks blocks, @Range(from = 0, to = 15) int layer, int index) {
public final char get(CharBlocks blocks, int layer, int index) {
char[] section = get(blocks, layer);
if (section == null) {
blocks.reset(layer);
@ -189,7 +202,7 @@ public abstract class CharBlocks implements IBlocks {
return section[index];
}
public final void set(CharBlocks blocks, @Range(from = 0, to = 15) int layer, int index, char value) {
public final void set(CharBlocks blocks, int layer, int index, char value) {
get(blocks, layer)[index] = value;
}

View File

@ -10,6 +10,13 @@ import java.util.Arrays;
public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
/**
* New instance given the min/max section indices
*/
public CharGetBlocks(final int minSectionIndex, final int maxSectionIndex) {
super(minSectionIndex, maxSectionIndex);
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
BlockState state = BlockTypesCache.states[get(x, y, z)];
@ -18,7 +25,7 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
@Override
public boolean trim(boolean aggressive) {
for (int i = 0; i < 16; i++) {
for (int i = 0; i < sectionCount; i++) {
sections[i] = empty;
blocks[i] = null;
}
@ -36,6 +43,7 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
@Override
public synchronized boolean trim(boolean aggressive, int layer) {
layer -= minSectionIndex;
sections[layer] = empty;
blocks[layer] = null;
return true;
@ -47,4 +55,9 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
return null;
}
@Override
public int getSectionCount() {
return sectionCount;
}
}

View File

@ -11,7 +11,6 @@ import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.jetbrains.annotations.Range;
import java.util.Arrays;
import java.util.Collections;
@ -45,6 +44,8 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
private int bitMask = -1;
private CharSetBlocks() {
// Expand as we go
super(0, 15);
}
@Override
@ -59,9 +60,10 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public BiomeType getBiomeType(int x, int y, int z) {
if (biomes == null) {
if (biomes == null || (y >> 4) < minSectionIndex || (y >> 4) > maxSectionIndex) {
return null;
}
y -= minSectionIndex << 4;
return biomes[(y >> 2) << 4 | (z >> 2) << 2 | x >> 2];
}
@ -92,15 +94,18 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public boolean setBiome(int x, int y, int z, BiomeType biome) {
updateSectionIndexRange(y >> 4);
y -= minSectionIndex << 4;
if (biomes == null) {
biomes = new BiomeType[1024];
biomes = new BiomeType[64 * sectionCount];
}
biomes[(y >> 2) << 4 | (z >> 2) << 2 | x >> 2] = biome;
return true;
}
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(int x, @Range(from = 0, to = 255) int y, int z, T holder) {
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T holder) {
updateSectionIndexRange(y >> 4);
set(x, y, z, holder.getOrdinalChar());
holder.applyTileEntity(this, x, y, z);
return true;
@ -108,6 +113,8 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public void setBlocks(int layer, char[] data) {
updateSectionIndexRange(layer);
layer -= minSectionIndex;
this.blocks[layer] = data;
this.sections[layer] = data == null ? empty : FULL;
}
@ -123,38 +130,41 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
if (tiles == null) {
tiles = new BlockVector3ChunkMap<>();
}
updateSectionIndexRange(y >> 4);
tiles.put(x, y, z, tile);
return true;
}
@Override
public void setBlockLight(int x, int y, int z, int value) {
updateSectionIndexRange(y >> 4);
if (light == null) {
light = new char[16][];
light = new char[sectionCount][];
}
final int layer = y >> 4;
final int layer = (y >> 4) - minSectionIndex;
if (light[layer] == null) {
char[] c = new char[4096];
Arrays.fill(c, (char) 16);
light[layer] = c;
}
final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
light[y >> 4][index] = (char) value;
light[layer][index] = (char) value;
}
@Override
public void setSkyLight(int x, int y, int z, int value) {
updateSectionIndexRange(y >> 4);
if (skyLight == null) {
skyLight = new char[16][];
skyLight = new char[sectionCount][];
}
final int layer = y >> 4;
final int layer = (y >> 4) - minSectionIndex;
if (skyLight[layer] == null) {
char[] c = new char[4096];
Arrays.fill(c, (char) 16);
skyLight[layer] = c;
}
final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
skyLight[y >> 4][index] = (char) value;
skyLight[layer][index] = (char) value;
}
@Override
@ -167,17 +177,21 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public void setLightLayer(int layer, char[] toSet) {
updateSectionIndexRange(layer);
if (light == null) {
light = new char[16][];
light = new char[sectionCount][];
}
layer -= minSectionIndex;
light[layer] = toSet;
}
@Override
public void setSkyLightLayer(int layer, char[] toSet) {
updateSectionIndexRange(layer);
if (skyLight == null) {
skyLight = new char[16][];
skyLight = new char[sectionCount][];
}
layer -= minSectionIndex;
skyLight[layer] = toSet;
}
@ -193,8 +207,10 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public void removeSectionLighting(int layer, boolean sky) {
updateSectionIndexRange(layer);
layer -= minSectionIndex;
if (light == null) {
light = new char[16][];
light = new char[sectionCount][];
}
if (light[layer] == null) {
light[layer] = new char[4096];
@ -202,7 +218,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
Arrays.fill(light[layer], (char) 0);
if (sky) {
if (skyLight == null) {
skyLight = new char[16][];
skyLight = new char[sectionCount][];
}
if (skyLight[layer] == null) {
skyLight[layer] = new char[4096];
@ -213,14 +229,16 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
@Override
public void setFullBright(int layer) {
updateSectionIndexRange(layer);
layer -= minSectionIndex;
if (light == null) {
light = new char[16][];
light = new char[sectionCount][];
}
if (light[layer] == null) {
light[layer] = new char[4096];
}
if (skyLight == null) {
skyLight = new char[16][];
skyLight = new char[sectionCount][];
}
if (skyLight[layer] == null) {
skyLight[layer] = new char[4096];
@ -275,7 +293,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
if (biomes != null || light != null || skyLight != null) {
return false;
}
return IntStream.range(0, 16).noneMatch(this::hasSection);
return IntStream.range(minSectionIndex, maxSectionIndex + 1).noneMatch(this::hasSection);
}
@Override
@ -288,4 +306,98 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
return null;
}
@Override
public boolean hasBiomes(int layer) {
layer -= minSectionIndex;
if (layer < 0 || layer >= sections.length) {
return false;
}
return biomes != null;
}
@Override
public char[] load(final int layer) {
updateSectionIndexRange(layer);
return super.load(layer);
}
@Override
public int getSectionCount() {
return sectionCount;
}
@Override
public int getMaxSectionIndex() {
return maxSectionIndex;
}
@Override
public int getMinSectionIndex() {
return minSectionIndex;
}
// Checks and updates the various section arrays against the new layer index
private void updateSectionIndexRange(int layer) {
if (layer >= minSectionIndex && layer <= maxSectionIndex) {
return;
}
if (layer < minSectionIndex) {
int diff = minSectionIndex - layer;
sectionCount += diff;
char[][] tmpBlocks = new char[sectionCount][];
Section[] tmpSections = new Section[sectionCount];
System.arraycopy(blocks, 0, tmpBlocks, diff, blocks.length);
System.arraycopy(sections, 0, tmpSections, diff, sections.length);
for (int i = 0; i < diff; i++) {
tmpSections[i] = empty;
}
blocks = tmpBlocks;
sections = tmpSections;
minSectionIndex = layer;
if (biomes != null) {
BiomeType[] tmpBiomes = new BiomeType[sectionCount * 64];
System.arraycopy(biomes, 0, tmpBiomes, 64*diff, biomes.length);
biomes = tmpBiomes;
}
if (light != null) {
char[][] tmplight = new char[sectionCount][];
System.arraycopy(light, 0, tmplight, diff, light.length);
light = tmplight;
}
if (skyLight != null) {
char[][] tmplight = new char[sectionCount][];
System.arraycopy(skyLight, 0, tmplight, diff, skyLight.length);
skyLight = tmplight;
}
} else {
int diff = layer - maxSectionIndex;
sectionCount += diff;
char[][] tmpBlocks = new char[sectionCount][];
Section[] tmpSections = new Section[sectionCount];
System.arraycopy(blocks, 0, tmpBlocks, 0, blocks.length);
System.arraycopy(sections, 0, tmpSections, 0, sections.length);
for (int i = sectionCount - diff; i < sectionCount; i++) {
tmpSections[i] = empty;
}
blocks = tmpBlocks;
sections = tmpSections;
maxSectionIndex = layer;
if (biomes != null) {
BiomeType[] tmpBiomes = new BiomeType[sectionCount * 64];
System.arraycopy(biomes, 0, tmpBiomes, 0, biomes.length);
biomes = tmpBiomes;
}
if (light != null) {
char[][] tmplight = new char[sectionCount][];
System.arraycopy(light, 0, tmplight, 0, light.length);
light = tmplight;
}
if (skyLight != null) {
char[][] tmplight = new char[sectionCount][];
System.arraycopy(skyLight, 0, tmplight, 0, skyLight.length);
skyLight = tmplight;
}
}
}
}

View File

@ -78,17 +78,37 @@ public final class NullChunkGet implements IChunkGet {
}
@Override
public void setLightingToGet(char[][] lighting) {
public void setLightingToGet(char[][] lighting, int startSectionIndex, int endSectionIndex) {
}
@Override
public void setSkyLightingToGet(char[][] lighting) {
public void setSkyLightingToGet(char[][] lighting, int minSectionIndex, int maxSectionIndex) {
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
}
@Override
public int getMaxY() {
return 0;
}
@Override
public int getMinY() {
return 0;
}
@Override
public int getMaxSectionIndex() {
return 0;
}
@Override
public int getMinSectionIndex() {
return 0;
}
public boolean trim(boolean aggressive) {
return true;
}
@ -129,6 +149,11 @@ public final class NullChunkGet implements IChunkGet {
return null;
}
@Override
public int getSectionCount() {
return 0;
}
private NullChunkGet() {
}

View File

@ -19,7 +19,6 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.jetbrains.annotations.Range;
import javax.annotation.Nullable;
import java.util.Map;
@ -140,6 +139,12 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return bitMask;
}
@Override
public boolean hasBiomes(final int layer) {
// No need to go through delegate. hasBiomes is SET only.
return getOrCreateSet().hasBiomes(layer);
}
public boolean isInit() {
return isInit;
}
@ -160,12 +165,12 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
}
@Override
public void setLightingToGet(char[][] lighting) {
public void setLightingToGet(char[][] lighting, int minSectionIndex, int maxSectionIndex) {
delegate.setLightingToGet(this, lighting);
}
@Override
public void setSkyLightingToGet(char[][] lighting) {
public void setSkyLightingToGet(char[][] lighting, int minSectionIndex, int maxSectionIndex) {
delegate.setSkyLightingToGet(this, lighting);
}
@ -174,8 +179,28 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
delegate.setHeightmapToGet(this, type, data);
}
public void flushLightToGet(boolean heightmaps) {
delegate.flushLightToGet(this, heightmaps);
@Override
public int getMaxY() {
return getOrCreateGet().getMaxY();
}
@Override
public int getMinY() {
return getOrCreateGet().getMinY();
}
@Override
public int getMaxSectionIndex() {
return getOrCreateGet().getMaxSectionIndex();
}
@Override
public int getMinSectionIndex() {
return getOrCreateGet().getMinSectionIndex();
}
public void flushLightToGet() {
delegate.flushLightToGet(this);
}
private static final IBlockDelegate BOTH = new IBlockDelegate() {
@ -260,6 +285,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getSkyLight() != null) {
int layer = y >> 4;
layer -= chunk.chunkSet.getMinSectionIndex();
if (chunk.chunkSet.getSkyLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getSkyLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
@ -274,6 +300,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public int getEmittedLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getLight() != null) {
int layer = y >> 4;
layer -= chunk.chunkSet.getMinSectionIndex();
if (chunk.chunkSet.getLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
@ -300,19 +327,21 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight());
chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight());
public void flushLightToGet(ChunkHolder chunk) {
chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight(), chunk.chunkSet.getMinSectionIndex(),
chunk.chunkSet.getMaxSectionIndex());
chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight(), chunk.chunkSet.getMinSectionIndex(),
chunk.chunkSet.getMaxSectionIndex());
}
@Override
public void setLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setLightingToGet(lighting);
chunk.chunkExisting.setLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setSkyLightingToGet(lighting);
chunk.chunkExisting.setSkyLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
@ -447,18 +476,18 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
public void flushLightToGet(ChunkHolder chunk) {
// Do nothing as no lighting to flush to GET
}
@Override
public void setLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setLightingToGet(lighting);
chunk.chunkExisting.setLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setSkyLightingToGet(lighting);
chunk.chunkExisting.setSkyLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
@ -493,7 +522,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public <B extends BlockStateHolder<B>> boolean setBlock(
ChunkHolder chunk,
int x,
@Range(from = 0, to = 255) int y,
int y,
int z,
B block
) {
@ -568,6 +597,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getSkyLight() != null) {
int layer = y >> 4;
layer -= chunk.chunkSet.getMinSectionIndex();
if (chunk.chunkSet.getSkyLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getSkyLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
@ -585,6 +615,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
public int getEmittedLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getLight() != null) {
int layer = y >> 4;
layer -= chunk.chunkSet.getMinSectionIndex();
if (chunk.chunkSet.getLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
@ -623,12 +654,11 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
public void flushLightToGet(ChunkHolder chunk) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight());
chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight());
chunk.flushLightToGet();
}
@Override
@ -636,7 +666,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setLightingToGet(lighting);
chunk.setLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
@ -644,7 +674,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setSkyLightingToGet(lighting);
chunk.setSkyLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
@ -652,7 +682,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setHeightmapToGet(type, data);
chunk.setHeightmapToGet(type, data);
}
};
@ -804,7 +834,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
public void flushLightToGet(ChunkHolder chunk) {
// Do nothing as no light to flush
}
@ -813,7 +843,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
chunk.setLightingToGet(lighting);
chunk.setLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
@ -821,7 +851,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
chunk.setSkyLightingToGet(lighting);
chunk.setSkyLightingToGet(lighting, chunk.chunkSet.getMinSectionIndex(), chunk.chunkSet.getMaxSectionIndex());
}
@Override
@ -889,6 +919,11 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return this.trim(aggressive);
}
@Override
public int getSectionCount() {
return getOrCreateGet().getSectionCount();
}
@Override
public boolean isEmpty() {
return chunkSet == null || chunkSet.isEmpty();
@ -1117,7 +1152,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
int[] getHeightMap(ChunkHolder chunk, HeightMapType type);
void flushLightToGet(ChunkHolder chunk, boolean heightmaps);
void flushLightToGet(ChunkHolder chunk);
void setLightingToGet(ChunkHolder chunk, char[][] lighting);

View File

@ -82,6 +82,11 @@ public final class NullChunk implements IQueueChunk {
return new char[0][];
}
@Override
public boolean hasBiomes(final int layer) {
return false;
}
@Nonnull
public int[] getHeightMap(@Nullable HeightMapType type) {
return new int[256];
@ -182,17 +187,37 @@ public final class NullChunk implements IQueueChunk {
}
@Override
public void setLightingToGet(char[][] lighting) {
public void setLightingToGet(char[][] lighting, int minSectionIndex, int maxSectionIndex) {
}
@Override
public void setSkyLightingToGet(char[][] lighting) {
public void setSkyLightingToGet(char[][] lighting, int minSectionIndex, int maxSectionIndex) {
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
}
@Override
public int getMaxY() {
return 0;
}
@Override
public int getMinY() {
return 0;
}
@Override
public int getMaxSectionIndex() {
return 0;
}
@Override
public int getMinSectionIndex() {
return 0;
}
@Nullable
public <T extends Future<T>> T call(@Nullable IChunkSet set, @Nullable Runnable finalize) {
return null;
@ -206,6 +231,11 @@ public final class NullChunk implements IQueueChunk {
return true;
}
@Override
public int getSectionCount() {
return 0;
}
private NullChunk() {
}

View File

@ -51,7 +51,7 @@ public class FuzzyRegion extends AbstractRegion {
RecursiveVisitor search = new RecursiveVisitor(mask, p -> {
setMinMax(p.getBlockX(), p.getBlockY(), p.getBlockZ());
return true;
}, 256);
}, 256, extent.getMinY(), extent.getMaxY());
search.setVisited(set);
search.visit(BlockVector3.at(x, y, z));
Operations.completeBlindly(search);

View File

@ -7,6 +7,8 @@ import com.sk89q.worldedit.regions.CuboidRegion;
public class RegionWrapper extends CuboidRegion {
private static final RegionWrapper GLOBAL = new RegionWrapper(
Integer.MIN_VALUE,
Integer.MAX_VALUE,
Integer.MIN_VALUE,
Integer.MAX_VALUE,
Integer.MIN_VALUE,
@ -20,16 +22,16 @@ public class RegionWrapper extends CuboidRegion {
public int minZ;
public int maxZ;
public static RegionWrapper GLOBAL() {
return GLOBAL;
}
/**
* @deprecated use {@link RegionWrapper#RegionWrapper(int, int, int, int, int, int)}
*/
@Deprecated
public RegionWrapper(final int minX, final int maxX, final int minZ, final int maxZ) {
this(minX, maxX, 0, 255, minZ, maxZ);
}
public RegionWrapper(final int minX, final int maxX, final int minY, final int maxY, final int minZ, final int maxZ) {
this(BlockVector3.at(minX, 0, minZ), BlockVector3.at(maxX, 255, maxZ));
this(BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ));
}
public RegionWrapper(final BlockVector3 pos1, final BlockVector3 pos2) {
@ -42,6 +44,10 @@ public class RegionWrapper extends CuboidRegion {
this.maxY = Math.max(pos1.getBlockY(), pos2.getBlockY());
}
public static RegionWrapper GLOBAL() {
return GLOBAL;
}
@Override
protected void recalculate() {
super.recalculate();
@ -134,7 +140,8 @@ public class RegionWrapper extends CuboidRegion {
@Override
public boolean isGlobal() {
return minX == Integer.MIN_VALUE && minZ == Integer.MIN_VALUE && maxX == Integer.MAX_VALUE && maxZ == Integer.MAX_VALUE && minY <= 0 && maxY >= 255;
return minX == Integer.MIN_VALUE && minY == Integer.MIN_VALUE && minZ == Integer.MIN_VALUE
&& maxX == Integer.MAX_VALUE && maxY == Integer.MAX_VALUE && maxZ == Integer.MAX_VALUE;
}
public boolean contains(RegionWrapper current) {

View File

@ -152,52 +152,65 @@ public class MathMan {
}
public static long tripleWorldCoord(int x, int y, int z) {
return y + (((long) x & 0x3FFFFFF) << 8) + (((long) z & 0x3FFFFFF) << 34);
return ((y + 256) & 0xffff) + (((long) x & 0xffffff) << 16) + (((long) z & 0xffffff) << 40);
}
public static long untripleWorldCoordX(long triple) {
return (((triple >> 8) & 0x3FFFFFF) << 38) >> 38;
return (((triple >> 16) & 0xffffff) << 38) >> 38;
}
public static long untripleWorldCoordY(long triple) {
return triple & 0xFF;
return (triple & 0xffff) - 256;
}
public static long untripleWorldCoordZ(long triple) {
return (((triple >> 34) & 0x3FFFFFF) << 38) >> 38;
return (((triple >> 40) & 0xffffff) << 38) >> 38;
}
public static short tripleBlockCoord(int x, int y, int z) {
return (short) ((x & 15) << 12 | (z & 15) << 8 | y);
public static int tripleBlockCoord(int x, int y, int z) {
// account for the fact y can be negative now. Assume it won't be less than -256
y += 256;
return ((x & 15) << 16 | (z & 15) << 12 | y);
}
public static char tripleBlockCoordChar(int x, int y, int z) {
return (char) ((x & 15) << 12 | (z & 15) << 8 | y);
return (char) ((x & 15) << 16 | (z & 15) << 12 | y);
}
public static int untripleBlockCoordX(int triple) {
return (triple >> 12) & 0xF;
return (triple >> 16) & 0xF;
}
public static int untripleBlockCoordY(int triple) {
return (triple & 0xFF);
return (triple & 0x1ff) - 256;
}
public static int untripleBlockCoordZ(int triple) {
return (triple >> 8) & 0xF;
return (triple >> 12) & 0xF;
}
/**
* Obtain an integer representation of 3 ints, x, y and z. y is represented by the right-most 9 bits and can
* be within the range -256 to 255 (inclusive). x and z are represented by 11 bits each and can be within the range
* -1024 to 1023 (inclusive).
*
* @param x x value
* @param y y value
* @param z z value
* @return integer representation of x y z
*/
public static int tripleSearchCoords(int x, int y, int z) {
byte b1 = (byte) y;
byte b3 = (byte) (x);
byte b4 = (byte) (z);
int x16 = (x >> 8) & 0x7;
int z16 = (z >> 8) & 0x7;
byte b2 = MathMan.pair8(x16, z16);
return ((b1 & 0xFF)
+ ((b2 & 0x7F) << 8)
+ ((b3 & 0xFF) << 15)
+ ((b4 & 0xFF) << 23));
if (x > 1023 || x < -1024 || y > 255 || y < -256 || z > 1023 || z < -1024) {
throw new IndexOutOfBoundsException(String.format("Check range on x=%s, y=%s and z=%s!", x, y, z));
}
int b1 = Math.abs(y) & 0xff;
int b3 = x & 0xff;
int b4 = z & 0xff;
int x16 = (((x >> 8) & 0x3) | (x < 0 ? 0x4 : 0x00));
int z16 = (((z >> 8) & 0x3) | (z < 0 ? 0x4 : 0x00));
int y16 = (y < 0 ? 0x1 : 0x00);
int b2 = ((x16 + (z16 << 3) + (y16 << 6)));
return (((b1) | (b2 << 8)) | (b3 << 15)) | (b4 << 23);
}
public static int pairSearchCoords(int x, int y) {

View File

@ -28,17 +28,17 @@ public final class MemBlockSet extends BlockSet {
public static final IRow NULL_ROW_Y = new NullRowY();
public final IRow[] rows;
public final MutableBlockVector3 mutable;
private final int minSectionIndex;
private final int maxSectionIndex;
public MemBlockSet() {
this(16, 0, 0);
}
public MemBlockSet(int size, int offsetX, int offsetZ) {
public MemBlockSet(int size, int offsetX, int offsetZ, int minSectionIndex, int maxSectionIndex) {
super(offsetX, offsetZ);
this.rows = new IRow[size];
for (int i = 0; i < size; i++) {
rows[i] = NULL_ROW_X;
}
this.minSectionIndex = minSectionIndex;
this.maxSectionIndex = maxSectionIndex;
this.mutable = new MutableBlockVector3();
}
@ -53,14 +53,14 @@ public final class MemBlockSet extends BlockSet {
public boolean add(int x, int y, int z) {
x -= getBlockOffsetX();
z -= getBlockOffsetZ();
return rows[x >> 4].add(this.rows, x, y, z - getBlockOffsetZ());
return rows[x >> 4].add(this.rows, x, y, z - getBlockOffsetZ(), minSectionIndex, maxSectionIndex);
}
@Override
public void set(int x, int y, int z) {
x -= getBlockOffsetX();
z -= getBlockOffsetZ();
rows[x >> 4].set(this.rows, x, y, z - getBlockOffsetZ());
rows[x >> 4].set(this.rows, x, y, z - getBlockOffsetZ(), minSectionIndex, maxSectionIndex);
}
@Override
@ -88,11 +88,11 @@ public final class MemBlockSet extends BlockSet {
@Override
public Set<BlockVector2> getChunks() {
return new AbstractSet<BlockVector2>() {
return new AbstractSet<>() {
@Nonnull
@Override
public Iterator<BlockVector2> iterator() {
return new Iterator<BlockVector2>() {
return new Iterator<>() {
private final MutableBlockVector2 mutable = new MutableBlockVector2();
private boolean hasNext;
private int X;
@ -181,10 +181,10 @@ public final class MemBlockSet extends BlockSet {
}
public Set<BlockVector3> getChunkCubes() {
return new AbstractSet<BlockVector3>() {
return new AbstractSet<>() {
@Override
public Iterator<BlockVector3> iterator() {
return new Iterator<BlockVector3>() {
return new Iterator<>() {
private final MutableBlockVector3 mutable = new MutableBlockVector3();
private boolean hasNext;
private int X;
@ -234,7 +234,7 @@ public final class MemBlockSet extends BlockSet {
@Override
public BlockVector3 next() {
mutable.setComponents(
setX + getBlockOffsetX(), setY, setZ + getBlockOffsetX());
setX + getBlockOffsetX(), setY - (minSectionIndex << 4), setZ + getBlockOffsetX());
init();
return mutable;
}
@ -282,7 +282,7 @@ public final class MemBlockSet extends BlockSet {
if (rowx instanceof RowX) {
IRow rowz = ((RowX) rowx).rows[other.getZ()];
if (rowz instanceof RowZ) {
return ((RowZ) rowz).rows[other.getY() - getChunkOffsetZ()] instanceof RowY;
return ((RowZ) rowz).rows[other.getY() - (minSectionIndex << 4) - getChunkOffsetZ()] instanceof RowY;
}
}
}
@ -293,7 +293,7 @@ public final class MemBlockSet extends BlockSet {
@Override
public int getMinimumY() {
int maxY = 15;
int maxY = maxSectionIndex;
int maxy = 16;
int by = Integer.MAX_VALUE;
for (IRow nullRowX : rows) {
@ -357,7 +357,7 @@ public final class MemBlockSet extends BlockSet {
}
RowZ rowz = (RowZ) nullRowZ;
outer:
for (int Y = 15; Y >= maxY; Y--) {
for (int Y = maxSectionIndex; Y >= maxY; Y--) {
IRow nullRowY = rowz.rows[Y];
if (!(nullRowY instanceof RowY)) {
continue;
@ -376,8 +376,8 @@ public final class MemBlockSet extends BlockSet {
maxy = y + 1;
}
by = (Y << 4) + y;
if (by == FaweCache.IMP.WORLD_MAX_Y) {
return FaweCache.IMP.WORLD_MAX_Y;
if (by == (maxSectionIndex << 4) + 15) {
return (maxSectionIndex << 4) + 15;
}
break outer;
}
@ -582,7 +582,7 @@ public final class MemBlockSet extends BlockSet {
if (!(nullRowY instanceof RowY)) {
continue;
}
int by = Y << 4;
int by = ((Y - minSectionIndex) << 4);
RowY rowY = (RowY) nullRowY;
for (int y = 0, i = 0; y < 16; y++) {
for (int z = 0; z < 16; z += 4, i++) {
@ -615,7 +615,7 @@ public final class MemBlockSet extends BlockSet {
@Override
public Iterator<BlockVector3> iterator() {
return new Iterator<BlockVector3>() {
return new Iterator<>() {
private int bx;
private int by;
private int bz;
@ -817,10 +817,10 @@ public final class MemBlockSet extends BlockSet {
return false;
}
void set(IRow[] rows, int x, int y, int z);
void set(IRow[] rows, int x, int y, int z, int minSectionIndex, int maxSectionIndex);
default boolean add(IRow[] rows, int x, int y, int z) {
set(rows, x, y, z);
default boolean add(IRow[] rows, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
set(rows, x, y, z, minSectionIndex, maxSectionIndex);
return true;
}
@ -837,10 +837,10 @@ public final class MemBlockSet extends BlockSet {
public static final class NullRowX implements IRow {
@Override
public void set(IRow[] parent, int x, int y, int z) {
public void set(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
IRow row = new RowX(parent.length);
parent[x >> 4] = row;
row.set(parent, x, y, z);
row.set(parent, x, y, z, minSectionIndex, maxSectionIndex);
}
}
@ -848,10 +848,10 @@ public final class MemBlockSet extends BlockSet {
public static final class NullRowZ implements IRow {
@Override
public void set(IRow[] parent, int x, int y, int z) {
IRow row = new RowZ();
public void set(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
IRow row = new RowZ(minSectionIndex, maxSectionIndex);
parent[z >> 4] = row;
row.set(parent, x, y, z);
row.set(parent, x, y, z, minSectionIndex, maxSectionIndex);
}
}
@ -859,10 +859,10 @@ public final class MemBlockSet extends BlockSet {
public static final class NullRowY implements IRow {
@Override
public void set(IRow[] parent, int x, int y, int z) {
public void set(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
IRow row = new RowY();
parent[y >> 4] = row;
row.set(parent, x, y, z);
row.set(parent, x, y, z, minSectionIndex, maxSectionIndex);
}
}
@ -884,13 +884,13 @@ public final class MemBlockSet extends BlockSet {
}
@Override
public void set(IRow[] parent, int x, int y, int z) {
this.rows[z >> 4].set(this.rows, x, y, z);
public void set(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
this.rows[z >> 4].set(this.rows, x, y, z, minSectionIndex, maxSectionIndex);
}
@Override
public boolean add(IRow[] parent, int x, int y, int z) {
return this.rows[z >> 4].add(this.rows, x, y, z);
public boolean add(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
return this.rows[z >> 4].add(this.rows, x, y, z, minSectionIndex, maxSectionIndex);
}
@Override
@ -909,8 +909,8 @@ public final class MemBlockSet extends BlockSet {
public final IRow[] rows;
public RowZ() {
this.rows = new IRow[FaweCache.IMP.CHUNK_LAYERS];
public RowZ(int minSectionIndex, int maxSectionIndex) {
this.rows = new IRow[maxSectionIndex - minSectionIndex + 1];
reset();
}
@ -924,18 +924,19 @@ public final class MemBlockSet extends BlockSet {
}
@Override
public void set(IRow[] parent, int x, int y, int z) {
this.rows[y >> 4].set(this.rows, x, y, z);
public void set(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
this.rows[y >> 4].set(this.rows, x, y, z, minSectionIndex, maxSectionIndex);
}
@Override
public boolean add(IRow[] parent, int x, int y, int z) {
return this.rows[y >> 4].add(this.rows, x, y, z);
public boolean add(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
return this.rows[y >> 4].add(this.rows, x, y, z, minSectionIndex, maxSectionIndex);
}
@Override
public void clear(IRow[] parent, int x, int y, int z) {
this.rows[y >> 4].set(this.rows, x, y, z);
// min/amx layer does not matter here.
this.rows[y >> 4].set(this.rows, x, y, z, 0, 0);
}
@Override
@ -957,9 +958,7 @@ public final class MemBlockSet extends BlockSet {
}
public void reset() {
for (int i = 0; i < FaweCache.IMP.CHUNK_LAYERS; i++) {
rows[i] = NULL_ROW_Y;
}
Arrays.fill(rows, NULL_ROW_Y);
}
}
@ -983,13 +982,13 @@ public final class MemBlockSet extends BlockSet {
}
@Override
public void set(IRow[] parent, int x, int y, int z) {
public void set(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
int i = ((y & 15) << 8) | ((z & 15) << 4) | (x & 15);
bits[i >> 6] |= (1L << (i & 0x3F));
}
@Override
public boolean add(IRow[] parent, int x, int y, int z) {
public boolean add(IRow[] parent, int x, int y, int z, int minSectionIndex, int maxSectionIndex) {
int i = ((y & 15) << 8) | ((z & 15) << 4) | (x & 15);
int offset = i >> 6;
long value = bits[offset];

View File

@ -61,6 +61,11 @@ public interface SimpleWorld extends World {
return getMaximumPoint().getBlockY();
}
@Override
default int getMinY() {
return getMinimumPoint().getBlockY();
}
@Override
default Mask createLiquidMask() {
return new BlockMask(this).add(BlockTypes.LAVA, BlockTypes.WATER);

View File

@ -85,8 +85,8 @@ public class AsyncPlayer extends PlayerProxy {
public boolean ascendToCeiling(int clearance, boolean alwaysGlass) {
Location pos = getBlockLocation();
int x = pos.getBlockX();
int initialY = Math.max(0, pos.getBlockY());
int y = Math.max(0, pos.getBlockY() + 2);
int initialY = Math.max(getWorld().getMinY(), pos.getBlockY());
int y = Math.max(getWorld().getMinY(), pos.getBlockY() + 2);
int z = pos.getBlockZ();
Extent world = getLocation().getExtent();
@ -121,15 +121,15 @@ public class AsyncPlayer extends PlayerProxy {
public boolean ascendUpwards(int distance, boolean alwaysGlass) {
final Location pos = getBlockLocation();
final int x = pos.getBlockX();
final int initialY = Math.max(0, pos.getBlockY());
int y = Math.max(0, pos.getBlockY() + 1);
final int initialY = Math.max(getWorld().getMinY(), pos.getBlockY());
int y = Math.max(getWorld().getMinY(), pos.getBlockY() + 1);
final int z = pos.getBlockZ();
final int maxY = Math.min(getWorld().getMaxY() + 1, initialY + distance);
final Extent world = getLocation().getExtent();
MutableBlockVector3 mutable = new MutableBlockVector3(x, y, z);
while (y <= world.getMaximumPoint().getY() + 2) {
while (y <= world.getMaxY() + 2) {
if (world.getBlock(mutable.mutY(y)).getBlockType().getMaterial()
.isMovementBlocker()) {
break; // Hit something

View File

@ -96,6 +96,11 @@ public class WorldWrapper extends AbstractWorld {
return parent.getMaxY();
}
@Override
public int getMinY() {
return parent.getMinY();
}
@Override
public Mask createLiquidMask() {
return parent.createLiquidMask();

View File

@ -1184,6 +1184,16 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
return getBlockChangeCount();
}
@Override
public BlockVector3 getMinimumPoint() {
return getWorld().getMinimumPoint();
}
@Override
public BlockVector3 getMaximumPoint() {
return getWorld().getMaximumPoint();
}
//FAWE start
public void setSize(int size) {
this.changes = size;
@ -1276,7 +1286,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
public <B extends BlockStateHolder<B>> int fall(final Region region, boolean fullHeight, final B replace) {
FlatRegion flat = asFlatRegion(region);
final int startPerformY = region.getMinimumPoint().getBlockY();
final int startCheckY = fullHeight ? 0 : startPerformY;
final int startCheckY = fullHeight ? getMinY() : startPerformY;
final int endY = region.getMaximumPoint().getBlockY();
RegionVisitor visitor = new RegionVisitor(flat, pos -> {
int x = pos.getX();
@ -1353,7 +1363,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
final BlockReplace replace = new BlockReplace(EditSession.this, pattern);
// Pick how we're going to visit blocks
RecursiveVisitor visitor = new DirectionalVisitor(mask, replace, origin, direction, (int) (radius * 2 + 1));
RecursiveVisitor visitor = new DirectionalVisitor(mask, replace, origin, direction, (int) (radius * 2 + 1), minY, maxY);
// Start at the origin
visitor.visit(origin);
@ -1406,8 +1416,8 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
Mask mask = new MaskIntersection(
new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
new BoundedHeightMask(
Math.max(origin.getBlockY() - depth + 1, getMinimumPoint().getBlockY()),
Math.min(getMaxY(), origin.getBlockY())
Math.max(origin.getBlockY() - depth + 1, minY),
Math.min(maxY, origin.getBlockY())
),
Masks.negate(new ExistingBlockMask(this))
);
@ -1417,11 +1427,11 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
// Pick how we're going to visit blocks
RecursiveVisitor visitor;
//FAWE start - provide extent for preloading
//FAWE start - provide extent for preloading, min/max y
if (recursive) {
visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this);
visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), minY, maxY, this);
} else {
visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), this);
visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), minY, maxY, this);
}
//FAWE end
@ -1938,7 +1948,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
//FAWE end
}
Mask mask = new MaskIntersection(
new BoundedHeightMask(getWorld().getMinY(), getWorld().getMaxY()),
new BoundedHeightMask(minY, maxY),
new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
//FAWE start
liquidMask
@ -1950,8 +1960,8 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
} else {
replace = new BlockReplace(this, BlockTypes.AIR.getDefaultState());
}
//FAWE start - provide extent for preloading
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), this);
//FAWE start - provide extent for preloading, min/max y
RecursiveVisitor visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), minY, maxY, this);
//FAWE end
// Around the origin in a 3x3 block
@ -1989,14 +1999,14 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
// There are boundaries that the routine needs to stay in
Mask mask = new MaskIntersection(
new BoundedHeightMask(getWorld().getMinY(), Math.min(origin.getBlockY(), getWorld().getMaxY())),
new BoundedHeightMask(minY, Math.min(origin.getBlockY(), maxY)),
new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
blockMask
);
BlockReplace replace = new BlockReplace(this, fluid.getDefaultState());
//FAWE start - provide extent for preloading
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace, Integer.MAX_VALUE, this);
//FAWE start - provide extent for preloading, world min/maxY
NonRisingVisitor visitor = new NonRisingVisitor(mask, replace, Integer.MAX_VALUE, minY, maxY, this);
//FAWE end
// Around the origin in a 3x3 block
@ -2411,7 +2421,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
}
}
}
if (y != 0 && (yy = py - y) >= 0) {
if (y != 0 && (yy = py - y) >= minY) {
this.setBlock(px + x, yy, pz + z, block);
if (x != 0) {
this.setBlock(px - x, yy, pz + z, block);
@ -2506,9 +2516,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
BlockState air = BlockTypes.AIR.getDefaultState();
BlockState water = BlockTypes.WATER.getDefaultState();
int centerY = Math.max(getWorld().getMinY(), Math.min(getWorld().getMaxY(), oy));
int minY = Math.max(getWorld().getMinY(), centerY - height);
int maxY = Math.min(getWorld().getMaxY(), centerY + height);
int centerY = Math.max(minY, Math.min(maxY, oy));
int minY = Math.max(this.minY, centerY - height);
int maxY = Math.min(this.maxY, centerY + height);
//FAWE start - mutable
MutableBlockVector3 mutable = new MutableBlockVector3();
@ -2535,8 +2545,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
++affected;
}
} else if (id == BlockTypes.SNOW) {
//FAWE start
if (setBlock(mutable, air)) {
if (y > 0) {
if (y > getMinY()) {
BlockState block = getBlock(mutable2);
if (block.getStates().containsKey(snowy)) {
if (setBlock(mutable2, block.with(snowy, false))) {
@ -2649,9 +2660,9 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
final BlockState grass = BlockTypes.GRASS_BLOCK.getDefaultState();
final int centerY = Math.max(getWorld().getMinY(), Math.min(getWorld().getMaxY(), oy));
final int minY = Math.max(getWorld().getMinY(), centerY - height);
final int maxY = Math.min(getWorld().getMaxY(), centerY + height);
final int centerY = Math.max(minY, Math.min(maxY, oy));
final int minY = Math.max(this.minY, centerY - height);
final int maxY = Math.min(this.maxY, centerY + height);
//FAWE start - mutable
MutableBlockVector3 mutable = new MutableBlockVector3();
@ -2952,7 +2963,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
int zv = (int) (z.getValue() * unit.getZ() + zero2.getZ());
BlockState get;
if (yv >= 0 && yv < 256) {
if (yv >= minY && yv <= maxY) {
get = getBlock(xv, yv, zv);
} else {
get = BlockTypes.AIR.getDefaultState();
@ -3538,7 +3549,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
int xx = x + bx;
for (int z = 0; z < 16; z++) {
int zz = z + bz;
for (int y = 0; y < maxY + 1; y++) {
for (int y = minY; y < maxY + 1; y++) {
BaseBlock block = getFullBlock(mutable.setComponents(xx, y, zz));
fcs.add(mutable, block, BlockTypes.AIR.getDefaultState().toBaseBlock());
}
@ -3561,7 +3572,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
for (int z = 0; z < 16; z++) {
int zz = z + bz;
mutable.mutZ(zz);
for (int y = 0; y < maxY + 1; y++) {
for (int y = minY; y < maxY + 1; y++) {
mutable.mutY(y);
boolean contains = (fe == null || fe.contains(xx, y, zz)) && region.contains(mutable);
if (contains) {

View File

@ -211,6 +211,13 @@ public class LocalSession implements TextureHolder {
if (defaultSelector != null) {
this.selector = defaultSelector.createSelector();
}
//FAWE start
if (worldOverride != null) {
this.selector.setWorld(worldOverride);
} else {
this.selector.setWorld(currentWorld);
}
//FAWE end
}
//FAWE start

View File

@ -457,6 +457,7 @@ public class BrushCommands {
)
@CommandPermissions("worldedit.brush.stencil")
public void stencilBrush(
Player player,
LocalSession session, InjectedValueAccess context,
@Arg(desc = "Pattern")
Pattern fill,
@ -478,13 +479,15 @@ public class BrushCommands {
worldEdit.checkMaxBrushRadius(radius);
InputStream stream = getHeightmapStream(image);
HeightBrush brush;
int minY = player.getWorld().getMinY();
int maxY = player.getWorld().getMaxY();
try {
brush = new StencilBrush(stream, rotation, yscale, onlyWhite,
"#clipboard".equalsIgnoreCase(image)
? session.getClipboard().getClipboard() : null
? session.getClipboard().getClipboard() : null, minY, maxY
);
} catch (EmptyClipboardException ignored) {
brush = new StencilBrush(stream, rotation, yscale, onlyWhite, null);
brush = new StencilBrush(stream, rotation, yscale, onlyWhite, null, minY, maxY);
}
if (randomRotate) {
brush.setRandomRotate(true);
@ -710,6 +713,7 @@ public class BrushCommands {
)
@CommandPermissions("worldedit.brush.height")
public void heightBrush(
Player player,
LocalSession session,
@Arg(desc = "Expression", def = "5")
Expression radius,
@ -728,7 +732,7 @@ public class BrushCommands {
boolean dontSmooth, InjectedValueAccess context
)
throws WorldEditException, FileNotFoundException {
terrainBrush(session, radius, image, rotation, yscale, false, randomRotate, layers,
terrainBrush(player, session, radius, image, rotation, yscale, false, randomRotate, layers,
!dontSmooth, ScalableHeightMap.Shape.CONE, context
);
}
@ -741,6 +745,7 @@ public class BrushCommands {
)
@CommandPermissions("worldedit.brush.height")
public void cliffBrush(
Player player,
LocalSession session,
@Arg(desc = "Expression", def = "5")
Expression radius,
@ -760,7 +765,7 @@ public class BrushCommands {
boolean dontSmooth, InjectedValueAccess context
)
throws WorldEditException, FileNotFoundException {
terrainBrush(session, radius, image, rotation, yscale, true, randomRotate, layers,
terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers,
!dontSmooth, ScalableHeightMap.Shape.CYLINDER, context
);
}
@ -775,6 +780,7 @@ public class BrushCommands {
)
@CommandPermissions("worldedit.brush.height")
public void flattenBrush(
Player player,
LocalSession session,
@Arg(desc = "Expression", def = "5")
Expression radius,
@ -794,12 +800,13 @@ public class BrushCommands {
boolean dontSmooth, InjectedValueAccess context
)
throws WorldEditException, FileNotFoundException {
terrainBrush(session, radius, image, rotation, yscale, true, randomRotate, layers,
terrainBrush(player, session, radius, image, rotation, yscale, true, randomRotate, layers,
!dontSmooth, ScalableHeightMap.Shape.CONE, context
);
}
private void terrainBrush(
Player player,
LocalSession session,
Expression radius,
String image,
@ -816,23 +823,25 @@ public class BrushCommands {
worldEdit.checkMaxBrushRadius(radius);
InputStream stream = getHeightmapStream(image);
HeightBrush brush;
int minY = player.getWorld().getMinY();
int maxY = player.getWorld().getMaxY();
if (flat) {
try {
brush = new FlattenBrush(stream, rotation, yscale, layers, smooth,
"#clipboard".equalsIgnoreCase(image)
? session.getClipboard().getClipboard() : null, shape
? session.getClipboard().getClipboard() : null, shape, minY, maxY
);
} catch (EmptyClipboardException ignored) {
brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, null, shape);
brush = new FlattenBrush(stream, rotation, yscale, layers, smooth, null, shape, minY, maxY);
}
} else {
try {
brush = new HeightBrush(stream, rotation, yscale, layers, smooth,
"#clipboard".equalsIgnoreCase(image)
? session.getClipboard().getClipboard() : null
? session.getClipboard().getClipboard() : null, minY, maxY
);
} catch (EmptyClipboardException ignored) {
brush = new HeightBrush(stream, rotation, yscale, layers, smooth, null);
brush = new HeightBrush(stream, rotation, yscale, layers, smooth, null, minY, maxY);
}
}
if (randomRotate) {

View File

@ -635,12 +635,18 @@ public class GenerationCommands {
@Arg(desc = "Ore vein size") @Range(from = 0, to = Integer.MAX_VALUE) int size,
@Arg(desc = "Ore vein frequency (number of times to attempt to place ore)", def = "10") @Range(from = 0, to = Integer.MAX_VALUE) int freq,
@Arg(desc = "Ore vein rarity (% chance each attempt is placed)", def = "100") @Range(from = 0, to = 100) int rarity,
@Arg(desc = "Ore vein min y", def = "0") @Range(from = 0, to = 255) int minY,
@Arg(desc = "Ore vein max y", def = "63") @Range(from = 0, to = 255) int maxY
@Arg(desc = "Ore vein min y", def = "0") int minY,
@Arg(desc = "Ore vein max y", def = "63") int maxY
) throws WorldEditException {
if (mask instanceof AbstractExtentMask) {
((AbstractExtentMask) mask).setExtent(editSession);
}
checkCommandArgument(minY >= editSession.getMinY(), Caption.of("fawe.error.outside-range-lower", "miny",
editSession.getMinY()));
checkCommandArgument(maxY <= editSession.getMaxY(), Caption.of("fawe.error.outside-range-upper", "maxy",
editSession.getMaxY()));
checkCommandArgument(minY < maxY, Caption.of("fawe.error.argument-size-mismatch", "miny",
"maxy"));
editSession.addOre(region, mask, material, size, freq, rarity, minY, maxY);
actor.print(Caption.of("fawe.worldedit.visitor.visitor.block", editSession.getBlockChangeCount()));
}

View File

@ -133,8 +133,8 @@ public class HistorySubCommands {
Location origin = player.getLocation();
BlockVector3 bot = origin.toBlockPoint().subtract(radius, radius, radius);
BlockVector3 top = origin.toBlockPoint().add(radius, radius, radius);
bot = bot.clampY(0, world.getMaxY());
top = top.clampY(0, world.getMaxY());
bot = bot.clampY(world.getMinY(), world.getMaxY());
top = top.clampY(world.getMinY(), world.getMaxY());
// TODO mask the regions bot / top to the bottom and top coord in the allowedRegions
// TODO: then mask the edit to the bot / top
// if (allowedRegions.length != 1 || !allowedRegions[0].isGlobal()) {
@ -196,9 +196,9 @@ public class HistorySubCommands {
.summarize(RegionWrapper.GLOBAL(), false);
if (summary != null) {
rollback.setDimensions(
BlockVector3.at(summary.minX, 0, summary.minZ),
BlockVector3.at(summary.minX, world.getMinY(), summary.minZ),
BlockVector3
.at(summary.maxX, 255, summary.maxZ)
.at(summary.maxX, world.getMaxY(), summary.maxZ)
);
rollback.setTime(historyFile.lastModified());
RollbackDatabase db = DBHandler.IMP
@ -410,8 +410,8 @@ public class HistorySubCommands {
BlockVector3 bot = origin.toBlockPoint().subtract(radius, radius, radius);
BlockVector3 top = origin.toBlockPoint().add(radius, radius, radius);
bot = bot.clampY(0, world.getMaxY());
top = top.clampY(0, world.getMaxY());
bot = bot.clampY(world.getMinY(), world.getMaxY());
top = top.clampY(world.getMinY(), world.getMaxY());
long minTime = System.currentTimeMillis() - timeDiff;
Iterable<Supplier<RollbackOptimizedHistory>> edits = database.getEdits(other, minTime, bot, top, false, false);

View File

@ -363,17 +363,23 @@ public class RegionCommands {
@Selection Region region,
@Arg(name = "pattern", desc = "The pattern of blocks to lay") Pattern patternArg
) throws WorldEditException {
BlockVector3 max = region.getMaximumPoint();
int maxY = max.getBlockY();
//FAWE start - world min/maxY
int maxY = region.getMaximumY();
int minY = region.getMinimumY();
//FAWE end
Iterable<BlockVector2> flat = Regions.asFlatRegion(region).asFlatRegion();
Iterator<BlockVector2> iter = flat.iterator();
int y = 0;
//FAWE start - world min/maxY
int y = minY;
//FAWE end
int affected = 0;
while (iter.hasNext()) {
BlockVector2 pos = iter.next();
int x = pos.getBlockX();
int z = pos.getBlockZ();
y = editSession.getNearestSurfaceTerrainBlock(x, z, y, 0, maxY);
//FAWE start - world min/maxY
y = editSession.getNearestSurfaceTerrainBlock(x, z, y, minY, maxY);
//FAWE end
editSession.setBlock(x, y, z, patternArg);
affected++;
}
@ -856,6 +862,7 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.region.flora")
@Logging(REGION)
@Preload(Preload.PreloadCheck.PRELOAD)
@Confirm(Confirm.Processor.REGION)
public int flora(
Actor actor, EditSession editSession, @Selection Region region,
@ -867,7 +874,7 @@ public class RegionCommands {
FloraGenerator generator = new FloraGenerator(editSession);
GroundFunction ground = new GroundFunction(new ExistingBlockMask(editSession), generator);
//FAWE start - provide extent for preloading
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground, editSession);
LayerVisitor visitor = new LayerVisitor(asFlatRegion(region), minimumBlockY(region), maximumBlockY(region), ground);
//FAWE end
visitor.setMask(new NoiseFilter2D(new RandomNoise(), density));
Operations.completeLegacy(visitor);

View File

@ -124,9 +124,9 @@ public class SelectionCommands {
Location pos;
//FAWE start - clamp
if (coordinates != null) {
pos = new Location(world, coordinates.toVector3().clampY(0, world.getMaxY()));
pos = new Location(world, coordinates.toVector3().clampY(world.getMinY(), world.getMaxY()));
} else if (actor instanceof Locatable) {
pos = ((Locatable) actor).getBlockLocation().clampY(0, world.getMaxY());
pos = ((Locatable) actor).getBlockLocation().clampY(world.getMinY(), world.getMaxY());
//FAWE end
} else {
actor.print(Caption.of("worldedit.pos.console-require-coords"));
@ -157,9 +157,9 @@ public class SelectionCommands {
Location pos;
if (coordinates != null) {
//FAWE start - clamp
pos = new Location(world, coordinates.toVector3().clampY(0, world.getMaxY()));
pos = new Location(world, coordinates.toVector3().clampY(world.getMinY(), world.getMaxY()));
} else if (actor instanceof Locatable) {
pos = ((Locatable) actor).getBlockLocation().clampY(0, world.getMaxY());
pos = ((Locatable) actor).getBlockLocation().clampY(world.getMinY(), world.getMaxY());
//Fawe end
} else {
actor.print(Caption.of("worldedit.pos.console-require-coords"));
@ -258,7 +258,7 @@ public class SelectionCommands {
.clampY(minChunkY, maxChunkY);
min = minChunk.shl(CHUNK_SHIFTS, CHUNK_SHIFTS_Y, CHUNK_SHIFTS);
max = maxChunk.shl(CHUNK_SHIFTS, CHUNK_SHIFTS_Y, CHUNK_SHIFTS).add(15, world.getMaxY(), 15);
max = maxChunk.shl(CHUNK_SHIFTS, CHUNK_SHIFTS_Y, CHUNK_SHIFTS).add(15, 255, 15);
actor.print(Caption.of(
"worldedit.chunk.selected-multiple",
@ -286,7 +286,7 @@ public class SelectionCommands {
}
min = minChunk.shl(CHUNK_SHIFTS, CHUNK_SHIFTS_Y, CHUNK_SHIFTS);
max = min.add(15, world.getMaxY(), 15);
max = min.add(15, 255, 15);
actor.print(Caption.of(
"worldedit.chunk.selected",

View File

@ -413,8 +413,8 @@ public class UtilityCommands {
) throws WorldEditException {
size = Math.max(1, size);
we.checkMaxRadius(size);
height = height != null ? Math.min((world.getMaxY() + 1), height + 1) : (world.getMaxY() + 1);
height = height != null ? Math.min((world.getMaxY() - world.getMinY() + 1), height + 1) : (world.getMaxY() - world.getMinY() + 1);
int affected = editSession.removeAbove(session.getPlacementPosition(actor), size, height);
actor.print(Caption.of("worldedit.removeabove.removed", TextComponent.of(affected)));
return affected;
@ -436,8 +436,8 @@ public class UtilityCommands {
) throws WorldEditException {
size = Math.max(1, size);
we.checkMaxRadius(size);
height = height != null ? Math.min((world.getMaxY() + 1), height + 1) : (world.getMaxY() + 1);
height = height != null ? Math.min((world.getMaxY() - world.getMinY() + 1), height + 1) : (world.getMaxY() - world.getMinY() + 1);
int affected = editSession.removeBelow(session.getPlacementPosition(actor), size, height);
actor.print(Caption.of("worldedit.removebelow.removed", TextComponent.of(affected)));
return affected;

View File

@ -388,7 +388,7 @@ public class BrushTool
final int x = loc.getBlockX();
final int z = loc.getBlockZ();
int y;
for (y = height; y > 0; y--) {
for (y = height; y > editSession.getMinY(); y--) {
BlockType block = editSession.getBlockType(x, y, z);
if (block.getMaterial().isMovementBlocker()) {
break;

View File

@ -87,7 +87,8 @@ public class FloodFillTool implements BlockTool {
//FAWE start - Respect masks
Mask mask = initialType.toMask(editSession);
BlockReplace function = new BlockReplace(editSession, pattern);
RecursiveVisitor visitor = new RecursiveVisitor(mask, function, range, editSession);
RecursiveVisitor visitor = new RecursiveVisitor(mask, function, range, editSession.getMinY(),
editSession.getMaxY(), editSession);
visitor.visit(origin);
Operations.completeLegacy(visitor);
//FAWE end

View File

@ -86,7 +86,14 @@ public class RecursivePickaxe implements BlockTool {
final int radius = (int) range;
final BlockReplace replace = new BlockReplace(editSession, (BlockTypes.AIR.getDefaultState()));
editSession.setMask(null);
RecursiveVisitor visitor = new RecursiveVisitor(new IdMask(editSession), replace, radius, editSession);
RecursiveVisitor visitor = new RecursiveVisitor(
new IdMask(editSession),
replace,
radius,
editSession.getMinY(),
editSession.getMaxY(),
editSession
);
//TODO: Fix below
//visitor.visit(pos);
//Operations.completeBlindly(visitor);

View File

@ -39,8 +39,8 @@ public class GravityBrush implements Brush {
MaxChangedBlocksException {
//FAWE start - Ours operates differently to upstream, but does the same
double endY = position.getY() + size;
double startPerformY = Math.max(0, position.getY() - size);
double startCheckY = fullHeight ? 0 : startPerformY;
double startPerformY = Math.max(editSession.getMinY(), position.getY() - size);
double startCheckY = fullHeight ? editSession.getMinY() : startPerformY;
for (double x = position.getX() + size; x > position.getX() - size; --x) {
for (double z = position.getZ() + size; z > position.getZ() - size; --z) {
double freeSpot = startCheckY;

View File

@ -69,7 +69,7 @@ public class OffsetMaskParser extends InputParser<Mask> implements AliasedParser
submask = new ExistingBlockMask(context.requireExtent());
}
//FAWE start - OffsetMask > OffsetsMask
return new OffsetMask(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0));
return new OffsetMask(submask, BlockVector3.at(0, firstChar == '>' ? -1 : 1, 0), context.getMinY(), context.getMaxY());
//FAWE end
}

View File

@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.configuration.Caption;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.extension.factory.MaskFactory;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Locatable;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import org.enginehub.piston.inject.InjectedValueAccess;
@ -50,6 +51,8 @@ public class ParserContext {
private boolean preferringWildcard;
//Fawe start
private InjectedValueAccess injected;
private int minY = Integer.MIN_VALUE;
private int maxY = Integer.MAX_VALUE;
//FAWE end
/**
@ -270,5 +273,69 @@ public class ParserContext {
public InjectedValueAccess getInjected() {
return injected;
}
/**
* Attempts to resolve the minimum Y value associated with this context or returns 0.
* Caches both min and max y values.
*
* @return Minimum y value (inclusive) or 0
*/
public int getMinY() {
if (minY != Integer.MIN_VALUE) {
return minY;
}
Extent extent = null;
if (actor instanceof Locatable) {
extent = ((Locatable) actor).getExtent();
} else if (world != null) {
extent = world;
} else if (this.extent != null) {
extent = this.extent;
}
if (extent != null) {
minY = extent.getMinY();
maxY = extent.getMaxY();
} else {
minY = 0;
maxY = 255;
}
return minY;
}
/**
* Attempts to resolve the maximum Y value associated with this context or returns 255.
* Caches both min and max y values.
*
* @return Maximum y value (inclusive) or 255
*/
public int getMaxY() {
if (maxY != Integer.MAX_VALUE) {
return maxY;
}
Extent extent = null;
if (actor instanceof Locatable) {
extent = ((Locatable) actor).getExtent();
} else if (world != null) {
extent = world;
} else if (this.extent != null) {
extent = this.extent;
}
if (extent != null) {
minY = extent.getMinY();
maxY = extent.getMaxY();
} else {
minY = 0;
maxY = 255;
}
return maxY;
}
//FAWE end
}

View File

@ -202,6 +202,11 @@ public class AbstractDelegateExtent implements Extent {
return extent.getMaxY();
}
@Override
public int getMinY() {
return extent.getMinY();
}
@Override
public boolean relight(int x, int y, int z) {
return extent.relight(x, y, z);
@ -317,7 +322,7 @@ public class AbstractDelegateExtent implements Extent {
@Override
public <T extends BlockStateHolder<T>> boolean setBlock(
int x, @Range(from = 0, to = 255) int y,
int x, int y,
int z, T block
) throws WorldEditException {
return extent.setBlock(x, y, z, block);

View File

@ -200,9 +200,16 @@ public interface Extent extends InputExtent, OutputExtent {
- TODO: actually optimize these
*/
/**
* Returns the highest solid 'terrain' block.
*
* @param x the X coordinate
* @param z the Z coordinate
* @param minY minimal height
* @param maxY maximal height
* @return height of highest block found or 'minY'
*/
default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY) {
maxY = Math.min(maxY, Math.max(0, maxY));
minY = Math.max(0, minY);
for (int y = maxY; y >= minY; --y) {
BlockState block = getBlock(x, y, z);
if (block.getBlockType().getMaterial().isMovementBlocker()) {
@ -212,9 +219,19 @@ public interface Extent extends InputExtent, OutputExtent {
return minY;
}
/**
* Returns the highest solid 'terrain' block.
*
* @param x the X coordinate
* @param z the Z coordinate
* @param minY minimal height
* @param maxY maximal height
* @param filter a mask of blocks to consider, or null to consider any solid (movement-blocking) block
* @return height of highest block found or 'minY'
*/
default int getHighestTerrainBlock(final int x, final int z, int minY, int maxY, Mask filter) {
maxY = Math.min(maxY, Math.max(0, maxY));
minY = Math.max(0, minY);
maxY = Math.min(maxY, getMaxY());
minY = Math.max(getMinY(), minY);
MutableBlockVector3 mutable = new MutableBlockVector3();
@ -226,6 +243,18 @@ public interface Extent extends InputExtent, OutputExtent {
return minY;
}
/**
* Returns the nearest surface layer (up/down from start)
* <p>
* TODO: Someone understand this..?
*
* @param x x to search from
* @param z y to search from
* @param y z to search from
* @param minY min y to search (inclusive)
* @param maxY max y to search (inclusive)
* @return nearest surface layer
*/
default int getNearestSurfaceLayer(int x, int z, int y, int minY, int maxY) {
int clearanceAbove = maxY - y;
int clearanceBelow = y - minY;
@ -255,7 +284,7 @@ public interface Extent extends InputExtent, OutputExtent {
for (int layer = y - clearance - 1; layer >= minY; layer--) {
block = getBlock(x, layer, z);
if (block.getBlockType().getMaterial().isMovementBlocker() == state) {
return ((layer + offset) << 4) + 0;
return (layer + offset) << 4;
}
data1 = PropertyGroup.LEVEL.get(block);
}
@ -272,18 +301,20 @@ public interface Extent extends InputExtent, OutputExtent {
return (state ? minY : maxY) << 4;
}
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, minY, maxY, ignoreAir);
}
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, minY, maxY);
}
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, true);
}
/**
* Gets y value for the nearest block that is considered the surface of the terrain (cave roof/floor, mountain surface,
* etc) where the block conforms to a given mask. Searches in the x,z column given.
*
* @param x column x
* @param z column z
* @param y start y
* @param minY minimum y height to consider. Inclusive.
* @param maxY maximum y height to consider. Inclusive.
* @param failedMin if nothing found, the minimum y value to return if returning min
* @param failedMax if nothing found, the maximum y value to return if returning max
* @param mask mask to test blocks against
* @return The y value of the nearest terrain block
*/
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax, Mask mask) {
y = Math.max(minY, Math.min(maxY, y));
int clearanceAbove = maxY - y;
@ -320,6 +351,68 @@ public interface Extent extends InputExtent, OutputExtent {
return state ? failedMin : failedMax;
}
/**
* Gets y value for the nearest block that is considered the surface of the terrain (cave roof/floor, mountain surface,
* etc). Searches in the x,z column given.
*
* @param x column x
* @param z column z
* @param y start y
* @param minY minimum y height to consider. Inclusive.
* @param maxY maximum y height to consider. Inclusive.
* @param ignoreAir if air at the final value if no block found should be considered for return, else return -1
* @return The y value of the nearest terrain block
*/
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, boolean ignoreAir) {
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, minY, maxY, ignoreAir);
}
/**
* Gets y value for the nearest block that is considered the surface of the terrain (cave roof/floor, mountain surface,
* etc). Searches in the x,z column given.
*
* @param x column x
* @param z column z
* @param y start y
* @param minY minimum y height to consider. Inclusive.
* @param maxY maximum y height to consider. Inclusive.
* @return The y value of the nearest terrain block
*/
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY) {
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, minY, maxY);
}
/**
* Gets y value for the nearest block that is considered the surface of the terrain (cave roof/floor, mountain surface,
* etc). Searches in the x,z column given.
*
* @param x column x
* @param z column z
* @param y start y
* @param minY minimum y height to consider. Inclusive.
* @param maxY maximum y height to consider. Inclusive.
* @param failedMin if nothing found, the minimum y value to return if returning min
* @param failedMax if nothing found, the maximum y value to return if returning max
* @return The y value of the nearest terrain block
*/
default int getNearestSurfaceTerrainBlock(int x, int z, int y, int minY, int maxY, int failedMin, int failedMax) {
return getNearestSurfaceTerrainBlock(x, z, y, minY, maxY, failedMin, failedMax, true);
}
/**
* Gets y value for the nearest block that is considered the surface of the terrain (cave roof/floor, mountain surface,
* etc). Searches in the x,z column given.
*
* @param x column x
* @param z column z
* @param y start y
* @param minY minimum y height to consider. Inclusive.
* @param maxY maximum y height to consider. Inclusive.
* @param failedMin if nothing found, the minimum y value to return if returning min
* @param failedMax if nothing found, the maximum y value to return if returning max
* @param ignoreAir if air at the final value if no block found should be considered for return, else return -1
* @return The y value of the nearest terrain block
*/
default int getNearestSurfaceTerrainBlock(
int x,
int z,
@ -367,7 +460,7 @@ public interface Extent extends InputExtent, OutputExtent {
}
}
int result = state ? failedMin : failedMax;
if (result > 0 && !ignoreAir) {
if (result > minY && !ignoreAir) {
block = getBlock(x, result, z);
return block.getBlockType().getMaterial().isAir() ? -1 : result;
}
@ -444,12 +537,13 @@ public interface Extent extends InputExtent, OutputExtent {
spawnResource(region, new OreGen(this, mask, material, size, minY, maxY), rarity, frequency);
}
//TODO: probably update these for 1.18 etc.
default void addOres(Region region, Mask mask) throws WorldEditException {
addOre(region, mask, BlockTypes.DIRT.getDefaultState(), 33, 10, 100, 0, 255);
addOre(region, mask, BlockTypes.GRAVEL.getDefaultState(), 33, 8, 100, 0, 255);
addOre(region, mask, BlockTypes.ANDESITE.getDefaultState(), 33, 10, 100, 0, 79);
addOre(region, mask, BlockTypes.DIORITE.getDefaultState(), 33, 10, 100, 0, 79);
addOre(region, mask, BlockTypes.GRANITE.getDefaultState(), 33, 10, 100, 0, 79);
addOre(region, mask, BlockTypes.DIRT.getDefaultState(), 33, 10, 100, getMinY(), getMaxY());
addOre(region, mask, BlockTypes.GRAVEL.getDefaultState(), 33, 8, 100, getMinY(), getMaxY());
addOre(region, mask, BlockTypes.ANDESITE.getDefaultState(), 33, 10, 100, getMinY(), 79);
addOre(region, mask, BlockTypes.DIORITE.getDefaultState(), 33, 10, 100, getMinY(), 79);
addOre(region, mask, BlockTypes.GRANITE.getDefaultState(), 33, 10, 100, getMinY(), 79);
addOre(region, mask, BlockTypes.COAL_ORE.getDefaultState(), 17, 20, 100, 0, 127);
addOre(region, mask, BlockTypes.IRON_ORE.getDefaultState(), 9, 20, 100, 0, 63);
addOre(region, mask, BlockTypes.GOLD_ORE.getDefaultState(), 9, 2, 100, 0, 31);
@ -556,11 +650,11 @@ public interface Extent extends InputExtent, OutputExtent {
}
default int getMinY() {
return 0;
return getMinimumPoint().getY();
}
default int getMaxY() {
return 255;
return getMaximumPoint().getY();
}
/**

View File

@ -347,7 +347,7 @@ public interface Clipboard extends Extent, Iterable<BlockVector3>, Closeable {
if (!pasteAir && block.getBlockType().getMaterial().isAir()) {
continue;
}
if (pos.getY() < 0) {
if (pos.getY() < extent.getMinY()) {
throw new RuntimeException("Y-Position cannot be less than 0!");
}
extent.setBlock(xx, yy, zz, block);

View File

@ -31,6 +31,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/
public class OffsetMask extends AbstractMask {
//FAWE start - ignore resultant position outside world height range
private final int minY;
private final int maxY;
//FAWE end
private Mask mask;
private BlockVector3 offset;
@ -39,12 +43,30 @@ public class OffsetMask extends AbstractMask {
*
* @param mask the mask
* @param offset the offset
* @deprecated use {@link OffsetMask#OffsetMask(Mask, BlockVector3, int, int)}
*/
@Deprecated
public OffsetMask(Mask mask, BlockVector3 offset) {
this(mask, offset, 0, 255);
}
/**
* Create a new instance.
*
* @param mask the mask
* @param offset the offset
* @param minY minimum allowable y value to be set. Inclusive.
* @param maxY maximum allowable y value to be set. Inclusive.
*/
//FAWE start - ignore resultant position outside world height range
public OffsetMask(Mask mask, BlockVector3 offset, int minY, int maxY) {
checkNotNull(mask);
checkNotNull(offset);
this.mask = mask;
this.offset = offset;
this.minY = minY;
this.maxY = maxY;
//FAWE end
}
/**
@ -87,11 +109,13 @@ public class OffsetMask extends AbstractMask {
@Override
public boolean test(BlockVector3 vector) {
//FAWE start - ignore resultant position outside world height range
BlockVector3 testPos = vector.add(offset);
if (testPos.getBlockY() < 0 || testPos.getBlockY() > 255) {
if (testPos.getBlockY() < minY || testPos.getBlockY() > maxY) {
return false;
}
return getMask().test(vector.add(offset));
return getMask().test(testPos);
//FAWE end
}
@Nullable
@ -108,7 +132,7 @@ public class OffsetMask extends AbstractMask {
//FAWE start
@Override
public Mask copy() {
return new OffsetMask(mask.copy(), offset.toImmutable());
return new OffsetMask(mask.copy(), offset.toImmutable(), minY, maxY);
}
//FAWE end

Some files were not shown because too many files have changed in this diff Show More