diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java index df7d65949..208c27dd0 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/Regenerator.java @@ -266,8 +266,8 @@ public abstract class Regenerator { 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; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index af8b3f0b3..0a14b0d94 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -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(); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 32297718d..fdffeea0e 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -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 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 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 } diff --git a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar index 1dd840f1c..737a3f354 100644 Binary files a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar and b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar differ diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweAPI.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweAPI.java index 6bf5a2d51..36be7b077 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweAPI.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweAPI.java @@ -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 regionSet = Sets.newHashSet(bounds); ArrayList 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) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index 6e7048e7e..0c355d01c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -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]; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlendBall.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlendBall.java index 1695ebb8d..358eda0a5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlendBall.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/BlendBall.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java index 5165f3087..c8ea45f2a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CommandBrush.java @@ -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 { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CopyPastaBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CopyPastaBrush.java index 7d809e094..35f6785fb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CopyPastaBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/CopyPastaBrush.java @@ -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 diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FallingSphere.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FallingSphere.java index 98c1d160c..374458ef9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FallingSphere.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FallingSphere.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FlattenBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FlattenBrush.java index 172b5c5fc..60d5a17e3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FlattenBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/FlattenBrush.java @@ -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 diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java index 47615319b..2a92e7bb8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java @@ -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); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ImageBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ImageBrush.java index 318ab0bb7..230be7993 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ImageBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ImageBrush.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/LayerBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/LayerBrush.java index e9301692c..e2db42374 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/LayerBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/LayerBrush.java @@ -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); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/RecurseBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/RecurseBrush.java index c9234084e..97bf3fa0e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/RecurseBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/RecurseBrush.java @@ -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(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterBrush.java index 54aaf864e..3d8f15aa3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ScatterBrush.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SplatterBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SplatterBrush.java index 135c067a8..930ba96dc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SplatterBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SplatterBrush.java @@ -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)); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/StencilBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/StencilBrush.java index b4a81bef7..7bb17ad84 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/StencilBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/StencilBrush.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSphereBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSphereBrush.java index 234a2d497..26b654957 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSphereBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSphereBrush.java @@ -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)); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSpline.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSpline.java index aff11cf6c..3be389898 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSpline.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/SurfaceSpline.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java index 750f8bd63..d5a8dc909 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/AdjacentMaskParser.java @@ -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 { 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); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java index 4bea571e1..f6a21ef6f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichOffsetMaskParser.java @@ -43,7 +43,7 @@ public class RichOffsetMaskParser extends RichParser { 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()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java index d7adf2c6b..ea2a37b6b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/OffsetPatternParser.java @@ -58,10 +58,7 @@ public class OffsetPatternParser extends RichParser { } 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()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java index e3efcf6e3..2250c982a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RandomOffsetPatternParser.java @@ -58,8 +58,7 @@ public class RandomOffsetPatternParser extends RichParser { } 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()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java index e998a6db9..688eeebd5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/RelativePatternParser.java @@ -41,8 +41,7 @@ public class RelativePatternParser extends RichParser { )); } 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()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java index bd8115ea7..45b3dda76 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SolidRandomOffsetPatternParser.java @@ -58,8 +58,7 @@ public class SolidRandomOffsetPatternParser extends RichParser { } 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()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java index 50c100e80..ab5a882ef 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/SurfaceRandomOffsetPatternParser.java @@ -47,8 +47,7 @@ public class SurfaceRandomOffsetPatternParser extends RichParser { } 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()); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java index 647df1ae5..dee157658 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java @@ -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)); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java index f7cdb7844..c775bf438 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java index a91bab7bc..2cd997fc5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/filter/block/ChunkFilterBlock.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/LimitExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/LimitExtent.java index 308e0ae93..9a91bedf5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/LimitExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/LimitExtent.java @@ -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; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ArrayHeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ArrayHeightMap.java index 6b977cce8..6bce75ba1 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ArrayHeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ArrayHeightMap.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/FlatScalableHeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/FlatScalableHeightMap.java index 0c790436e..a32285a09 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/FlatScalableHeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/FlatScalableHeightMap.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightMap.java index f98288743..421abbba4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/HeightMap.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ScalableHeightMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ScalableHeightMap.java index 00b05e894..ff70dcf35 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ScalableHeightMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/heightmap/ScalableHeightMap.java @@ -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 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); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java index 385f2292e..b595e5291 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java @@ -60,11 +60,12 @@ public class NMSRelighter implements Relighter { private final ConcurrentHashMap 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 queue, boolean calculateHeightMaps) { + public NMSRelighter(IQueueExtent 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 { + private static class RelightSkyEntry implements Comparable { 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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java index 57af21a1a..2f46d1c90 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/RelightProcessor.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/CavesGen.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/CavesGen.java index 374895bd2..6cc3eef7f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/CavesGen.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/CavesGen.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/OreGen.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/OreGen.java index 3ac9a9726..73262f306 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/OreGen.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/OreGen.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java index cba37c0f2..dcf2d7a36 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/generator/SchemGen.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AdjacentAnyMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AdjacentAnyMask.java index 877890018..a72150d65 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AdjacentAnyMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AdjacentAnyMask.java @@ -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); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AngleMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AngleMask.java index 10c73bd80..b10285b69 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AngleMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/AngleMask.java @@ -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; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/CachedMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/CachedMask.java index e2fa01756..7d3adbed0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/CachedMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/CachedMask.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/MaskedTargetBlock.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/MaskedTargetBlock.java index 6e7c48291..fc558aee7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/MaskedTargetBlock.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/MaskedTargetBlock.java @@ -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 { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceMask.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceMask.java index 1bfc6e8eb..6d935e103 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceMask.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/SurfaceMask.java @@ -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) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AngleColorPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AngleColorPattern.java index 892e1830a..a13061951 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AngleColorPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AngleColorPattern.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AnglePattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AnglePattern.java index 6c8ca56dc..0e4d5262b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AnglePattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/AnglePattern.java @@ -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 > 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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java index eda121d6d..894f9d06a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java @@ -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); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java index aae82e9fe..3eb5c3b77 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java index 3501d1645..cda875f04 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java @@ -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); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java index a8be79b62..12b64a497 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java index 6b531ccf5..73327f94a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java @@ -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)); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/AboveVisitor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/AboveVisitor.java index a41985332..d2503447b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/AboveVisitor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/AboveVisitor.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/DirectionalVisitor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/DirectionalVisitor.java index c7ee64002..b7b14059b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/DirectionalVisitor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/visitor/DirectionalVisitor.java @@ -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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java index f8e7381e3..7ea8dfbdc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java @@ -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()); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index e7e16f5d9..221001daf 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -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) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java index 8534c7d81..c780297cc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java index 49dc09e27..775f96e4d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/BlockVector3ChunkMap.java @@ -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 implements IAdaptedMap { +public class BlockVector3ChunkMap implements IAdaptedMap { - private final Short2ObjectArrayMap map = new Short2ObjectArrayMap<>(); + private final Int2ObjectArrayMap map = new Int2ObjectArrayMap<>(); @Override - public Map getParent() { + public Map 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 implements IAdaptedMap 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 implements if (!entries.hasNext()) { return Collections.emptyIterator(); } - return new Iterator() { + return new Iterator<>() { Int2ObjectMap.Entry entry = entries.next(); Iterator entryIter = entry.getValue().iterator(); final MutableBlockVector3 mutable = new MutableBlockVector3(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java index fcef656e5..92b904610 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/LocalBlockVectorSet.java @@ -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 { + 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 { } public boolean contains(int x, int y, int z) { - return set.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ)); + // take 128 to fit -256 { 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> 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 { 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 { 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> 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 { @Override public Iterator iterator() { return new Iterator() { + 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 { 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> 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 { 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> 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 { 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 { 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 { } private int getIndex(BlockVector3 vector) { - return MathMan.tripleSearchCoords(vector.getBlockX() - offsetX, vector.getBlockY(), vector.getBlockZ() - offsetZ); + // take 128 to fit -256 { if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { return false; } - int index = MathMan.tripleSearchCoords(relX, y, relZ); + // take 128 to fit -256 { 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> 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 { 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> 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 { set.clear(); } + public interface BlockVectorSetVisitor { + + void run(int x, int y, int z, int index); + + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index 392990f1a..8345d3bde 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -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++; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java index 25aa33470..437370e71 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBlocks.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java index bd789eec6..d61d40c45 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkExtent.java @@ -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 extends Extent { T getOrCreateChunk(int chunkX, int chunkZ); @Override - default > boolean setBlock(int x, @Range(from = 0, to = 255) int y, int z, B state) { + default > 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); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java index 7f18ba27b..8004f7098 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkGet.java @@ -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(); + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java index ffa633725..318e8ebeb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkSet.java @@ -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); + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/Flood.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/Flood.java index cc2c901bd..096477d4a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/Flood.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/Flood.java @@ -25,11 +25,17 @@ public class Flood { private int chunkYLayer; private int chunkZ; private final ConcurrentLinkedQueue 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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 782251bd6..ed6cf8609 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -52,6 +52,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen // Chunks currently being queued / worked on private final Long2ObjectLinkedOpenHashMap chunks = new Long2ObjectLinkedOpenHashMap<>(); + private World world = null; + private int minY = 0; + private int maxY = 255; + private IChunkCache cacheGet; private IChunkCache 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 get, IChunkCache set) { reset(); + this.minY = extent.getMinY(); + this.maxY = extent.getMaxY(); currentThread = Thread.currentThread(); if (get == null) { get = (x, z) -> { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java index c441aa63b..96a2e4b83 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/BitSetBlocks.java @@ -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 > 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; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java index 0c90c4849..5665c8789 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharBlocks.java @@ -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; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java index 11dfd5b46..e443d6bd7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharGetBlocks.java @@ -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; + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java index cca3f4bc2..81256fdcb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/CharSetBlocks.java @@ -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 > boolean setBlock(int x, @Range(from = 0, to = 255) int y, int z, T holder) { + public > 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; + } + } + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java index e79d724d4..1e1e14dc8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/blocks/NullChunkGet.java @@ -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() { } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index f9b2be467..5f1dab37b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -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> implements IQueueChunk { 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> implements IQueueChunk { } @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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { } @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> implements IQueueChunk { } @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> implements IQueueChunk { public > 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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { } @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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { } @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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { 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> implements IQueueChunk { int[] getHeightMap(ChunkHolder chunk, HeightMapType type); - void flushLightToGet(ChunkHolder chunk, boolean heightmaps); + void flushLightToGet(ChunkHolder chunk); void setLightingToGet(ChunkHolder chunk, char[][] lighting); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java index 766e8ae4b..9f8b5ade0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/NullChunk.java @@ -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 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() { } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FuzzyRegion.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FuzzyRegion.java index 7511287ee..5fc7c543f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FuzzyRegion.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/FuzzyRegion.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/RegionWrapper.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/RegionWrapper.java index 83e2a68e9..a5d38deee 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/RegionWrapper.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/regions/RegionWrapper.java @@ -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) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MathMan.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MathMan.java index d7c7d1fbc..c53df7ec7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MathMan.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MathMan.java @@ -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) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/MemBlockSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/MemBlockSet.java index a3803ae47..232dd7017 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/MemBlockSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/MemBlockSet.java @@ -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 getChunks() { - return new AbstractSet() { + return new AbstractSet<>() { @Nonnull @Override public Iterator iterator() { - return new Iterator() { + 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 getChunkCubes() { - return new AbstractSet() { + return new AbstractSet<>() { @Override public Iterator iterator() { - return new Iterator() { + 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 iterator() { - return new Iterator() { + 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]; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/SimpleWorld.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/SimpleWorld.java index 61cfd5f15..70c7abd9c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/SimpleWorld.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/world/SimpleWorld.java @@ -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); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java index 3793a4e92..43616cdb5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java @@ -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 diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java index d3c5960bd..8374cba7c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java @@ -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(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 91e215e44..b834508dc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -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 > 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) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 510fe90dc..ede31707f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -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 diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 2fe324eb0..6d0b7ee8b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -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) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index 1cc269847..b408418f2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -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())); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java index 0336cb627..1d97c13d2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java @@ -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> edits = database.getEdits(other, minTime, bot, top, false, false); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index f69a93b64..b51e82113 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -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 flat = Regions.asFlatRegion(region).asFlatRegion(); Iterator 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); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index d1cd86615..f5e8758e9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -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", diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index 173334e96..24def83d0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -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; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java index c15d4dc6f..d6c44fa66 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/BrushTool.java @@ -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; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java index bb0b9a46a..7986a7a1d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/FloodFillTool.java @@ -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 diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java index 1b2ea3f5f..9fad39f9d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/RecursivePickaxe.java @@ -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); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java index 72bba1d95..b82601288 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/tool/brush/GravityBrush.java @@ -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; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java index ccf875833..a9c12235c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java @@ -69,7 +69,7 @@ public class OffsetMaskParser extends InputParser 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 } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java index 4bbb20354..660c8331d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/input/ParserContext.java @@ -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 } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index d414c2a96..643cec495 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -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 > 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); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index cb6ed7634..dfda01b0f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -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) + *

+ * 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(); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index 890bafbaf..79280819e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -347,7 +347,7 @@ public interface Clipboard extends Extent, Iterable, 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); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java index b7d8006ad..91e92964f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/OffsetMask.java @@ -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 diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java index 7e73eaa75..aadf915a3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/BreadthFirstSearch.java @@ -95,6 +95,8 @@ public abstract class BreadthFirstSearch implements Operation { private BlockVectorSet visited = new BlockVectorSet(); private BlockVector3[] directions; //FAWE end + protected final int minY; + protected final int maxY; private int affected = 0; //FAWE start private int currentDepth = 0; @@ -107,21 +109,21 @@ public abstract class BreadthFirstSearch implements Operation { * @param function the function to apply to visited blocks */ public BreadthFirstSearch(RegionFunction function) { - //FAWE start - this(function, Integer.MAX_VALUE); - //FAWE end - checkNotNull(function); + //FAWE start - int depth, min/max y + this(function, Integer.MAX_VALUE, 0, 255, null); } - //FAWE start + //FAWE start - int depth, min/max y, preloading /** * Create a new instance. * * @param function the function to apply to visited blocks - * @param maxDepth the maximum number of iterations + * @param depth maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. */ - public BreadthFirstSearch(RegionFunction function, int maxDepth) { - this(function, maxDepth, null); + public BreadthFirstSearch(RegionFunction function, int depth, int minY, int maxY) { + this(function, depth, minY, maxY, null); } /** @@ -129,13 +131,17 @@ public abstract class BreadthFirstSearch implements Operation { * * @param function the function to apply to visited blocks * @param maxDepth the maximum number of iterations + * @param minY minimum y value to visit. Inclusive. + * @param maxY maximum y value to visit. Inclusive. * @param extent extent to use for preloading */ - public BreadthFirstSearch(RegionFunction function, int maxDepth, Extent extent) { + public BreadthFirstSearch(RegionFunction function, int maxDepth, int minY, int maxY, Extent extent) { checkNotNull(function); this.function = function; this.directions = DEFAULT_DIRECTIONS; this.maxDepth = maxDepth; + this.minY = minY; + this.maxY = maxY; if (extent != null) { ExtentTraverser queueTraverser = new ExtentTraverser<>(extent).find(ParallelQueueExtent.class); this.singleQueue = queueTraverser != null ? (SingleThreadQueueExtent) queueTraverser.get().getExtent() : null; @@ -314,7 +320,7 @@ public abstract class BreadthFirstSearch implements Operation { for (int i = 0, j = 0; i < dirs.length && j < maxBranch; i++) { BlockVector3 direction = dirs[i]; int y = from.getBlockY() + direction.getY(); - if (y < 0 || y >= 256) { + if (y < minY || y > maxY) { continue; } int x = from.getBlockX() + direction.getX(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java index 5e1d12971..45a81c9a0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/DownwardVisitor.java @@ -37,19 +37,22 @@ public class DownwardVisitor extends RecursiveVisitor { private final int baseY; - //FAWE start - /** * Create a new visitor. * * @param mask the mask * @param function the function * @param baseY the base Y + * @deprecated Use {@link DownwardVisitor#DownwardVisitor(Mask, RegionFunction, int, int, int, int)} */ + @Deprecated public DownwardVisitor(Mask mask, RegionFunction function, int baseY) { - this(mask, function, baseY, Integer.MAX_VALUE); + //FAWE start - int depth, min/max y + this(mask, function, baseY, Integer.MAX_VALUE, 0, 255, null); + //FAWE end } + //FAWE start - int depth, min/max y, preloading /** * Create a new visitor. * @@ -57,9 +60,11 @@ public class DownwardVisitor extends RecursiveVisitor { * @param function the function * @param baseY the base Y * @param depth maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. */ - public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth) { - this (mask, function, baseY, depth, null); + public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, int minY, int maxY) { + this(mask, function, baseY, depth, minY, maxY, null); } /** @@ -69,10 +74,12 @@ public class DownwardVisitor extends RecursiveVisitor { * @param function the function * @param baseY the base Y * @param depth maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. * @param extent extent for preloading */ - public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, Extent extent) { - super(mask, function, depth, extent); + public DownwardVisitor(Mask mask, RegionFunction function, int baseY, int depth, int minY, int maxY, Extent extent) { + super(mask, function, depth, minY, maxY, extent); checkNotNull(mask); this.baseY = baseY; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java index eafc86ffe..f1a358f36 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/NonRisingVisitor.java @@ -29,29 +29,32 @@ import com.sk89q.worldedit.math.BlockVector3; */ public class NonRisingVisitor extends RecursiveVisitor { - //FAWE start - max int - /** - * Create a new recursive visitor. + * Create a new resursive visitor. * * @param mask the mask * @param function the function + * @deprecated Use {@link NonRisingVisitor#NonRisingVisitor(Mask, RegionFunction, int, int, int, Extent)} */ + @Deprecated public NonRisingVisitor(Mask mask, RegionFunction function) { - this(mask, function, Integer.MAX_VALUE); + //FAWE start - int depth, y min/max + this(mask, function, Integer.MAX_VALUE, 0, 255, null); + //FAWE end } - //FAWE end - //FAWE start - int depth, preloading + //FAWE start - int depth, preloading, min/max y /** * Create a new recursive visitor. * * @param mask the mask * @param function the function * @param depth the maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. */ - public NonRisingVisitor(Mask mask, RegionFunction function, int depth) { - this(mask, function, depth, null); + public NonRisingVisitor(Mask mask, RegionFunction function, int depth, int minY, int maxY) { + this(mask, function, Integer.MAX_VALUE, minY, maxY, null); } /** @@ -60,10 +63,12 @@ public class NonRisingVisitor extends RecursiveVisitor { * @param mask the mask * @param function the function * @param depth the maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. * @param extent the extent for preloading */ - public NonRisingVisitor(Mask mask, RegionFunction function, int depth, Extent extent) { - super(mask, function, depth, extent); + public NonRisingVisitor(Mask mask, RegionFunction function, int depth, int minY, int maxY, Extent extent) { + super(mask, function, depth, minY, maxY, extent); setDirections( BlockVector3.UNIT_X, BlockVector3.UNIT_MINUS_X, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RecursiveVisitor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RecursiveVisitor.java index 346b312ac..ba80448a0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RecursiveVisitor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/visitor/RecursiveVisitor.java @@ -34,7 +34,6 @@ public class RecursiveVisitor extends BreadthFirstSearch { private final Mask mask; - //FAWE start /** * Create a new recursive visitor. * @@ -42,30 +41,37 @@ public class RecursiveVisitor extends BreadthFirstSearch { * @param function the function */ public RecursiveVisitor(Mask mask, RegionFunction function) { - this(mask, function, Integer.MAX_VALUE); + this(mask, function, Integer.MAX_VALUE, 0, 255, null); + //FAWE end } + //FAWE start - int depth, min/max y /** * Create a new recursive visitor. * * @param mask the mask * @param function the function * @param maxDepth the maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. */ - public RecursiveVisitor(Mask mask, RegionFunction function, int maxDepth) { - this(mask, function, maxDepth, null); + public RecursiveVisitor(Mask mask, RegionFunction function, int maxDepth, int minY, int maxY) { + this(mask, function, maxDepth, minY, maxY, null); } + //FAWE start - int depth, min/max y /** * Create a new recursive visitor. * * @param mask the mask * @param function the function * @param maxDepth the maximum number of iterations + * @param minY minimum allowable y to visit. Inclusive. + * @param maxY maximum allowable y to visit. Inclusive. * @param extent the extent for preloading */ - public RecursiveVisitor(Mask mask, RegionFunction function, int maxDepth, Extent extent) { - super(function, maxDepth, extent); + public RecursiveVisitor(Mask mask, RegionFunction function, int maxDepth, int minY, int maxY, Extent extent) { + super(function, maxDepth, minY, maxY, extent); checkNotNull(mask); this.mask = mask; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/BiomeMath.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/BiomeMath.java index c043a8874..a79347a90 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/BiomeMath.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/util/BiomeMath.java @@ -19,53 +19,54 @@ package com.sk89q.worldedit.internal.util; -import net.royawesome.jlibnoise.MathHelper; - public class BiomeMath { - // From BiomeArray / BiomeContainer - public static final int HORIZONTAL_SECTION_COUNT = (int) Math.round(Math.log(16.0D) / Math.log(2.0D)) - 2; - public static final int VERTICAL_SECTION_COUNT = (int) Math.round(Math.log(256.0D) / Math.log(2.0D)) - 2; - public static final int HORIZONTAL_BIT_MASK = (1 << HORIZONTAL_SECTION_COUNT) - 1; - public static final int VERTICAL_BIT_MASK = (1 << VERTICAL_SECTION_COUNT) - 1; + //FAWE - Removed because it's unneeded, and it would require effort to have it work with worlds of differing heights across + // different game versions - private BiomeMath() { - } - - /** - * Compute the index into the MC biome array, for non-extended-height worlds. - * - * @param x the block x coordinate - * @param y the block y coordinate - * @param z the block z coordinate - * @return the index into the standard MC biome array - */ - public static int computeBiomeIndex(int x, int y, int z) { - int l = (x >> 2) & HORIZONTAL_BIT_MASK; - int m = MathHelper.clamp(y >> 2, 0, VERTICAL_BIT_MASK); - int n = (z >> 2) & HORIZONTAL_BIT_MASK; - return m << HORIZONTAL_SECTION_COUNT + HORIZONTAL_SECTION_COUNT - | n << HORIZONTAL_SECTION_COUNT - | l; - } - - /** - * Compute the index into the MC biome array, for extended-height worlds. - * - * @param x the block x coordinate - * @param y the block y coordinate - * @param z the block z coordinate - * @param minY minimum y of the world - * @param maxY maximum y of the world - * @return the index into the standard MC biome array - */ - public static int computeBiomeIndex(int x, int y, int z, int minY, int maxY) { - int l = (x >> 2) & HORIZONTAL_BIT_MASK; - int m = MathHelper.clamp((y >> 2) - minY, 0, maxY); - int n = (z >> 2) & HORIZONTAL_BIT_MASK; - return m << HORIZONTAL_SECTION_COUNT + HORIZONTAL_SECTION_COUNT - | n << HORIZONTAL_SECTION_COUNT - | l; - } +// // From BiomeArray / BiomeContainer +// public static final int HORIZONTAL_SECTION_COUNT = (int) Math.round(Math.log(16.0D) / Math.log(2.0D)) - 2; +// public static final int VERTICAL_SECTION_COUNT = (int) Math.round(Math.log(256.0D) / Math.log(2.0D)) - 2; +// public static final int HORIZONTAL_BIT_MASK = (1 << HORIZONTAL_SECTION_COUNT) - 1; +// public static final int VERTICAL_BIT_MASK = (1 << VERTICAL_SECTION_COUNT) - 1; +// +// private BiomeMath() { +// } +// +// /** +// * Compute the index into the MC biome array, for non-extended-height worlds. +// * +// * @param x the block x coordinate +// * @param y the block y coordinate +// * @param z the block z coordinate +// * @return the index into the standard MC biome array +// */ +// public static int computeBiomeIndex(int x, int y, int z) { +// int l = (x >> 2) & HORIZONTAL_BIT_MASK; +// int m = MathHelper.clamp(y >> 2, 0, VERTICAL_BIT_MASK); +// int n = (z >> 2) & HORIZONTAL_BIT_MASK; +// return m << HORIZONTAL_SECTION_COUNT + HORIZONTAL_SECTION_COUNT +// | n << HORIZONTAL_SECTION_COUNT +// | l; +// } +// +// /** +// * Compute the index into the MC biome array, for extended-height worlds. +// * +// * @param x the block x coordinate +// * @param y the block y coordinate +// * @param z the block z coordinate +// * @param minY minimum y of the world +// * @param maxY maximum y of the world +// * @return the index into the standard MC biome array +// */ +// public static int computeBiomeIndex(int x, int y, int z, int minY, int maxY) { +// int l = (x >> 2) & HORIZONTAL_BIT_MASK; +// int m = MathHelper.clamp((y >> 2) - minY, 0, maxY); +// int n = (z >> 2) & HORIZONTAL_BIT_MASK; +// return m << HORIZONTAL_SECTION_COUNT + HORIZONTAL_SECTION_COUNT +// | n << HORIZONTAL_SECTION_COUNT +// | l; +// } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java index d280f3699..fc8ada868 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/convolution/HeightMap.java @@ -97,18 +97,18 @@ public class HeightMap { int bx = min.getBlockX(); int bz = min.getBlockZ(); Iterator flat = Regions.asFlatRegion(region).asFlatRegion().iterator(); - int layer = 0; + int layer = session.getMinY(); while (flat.hasNext()) { BlockVector2 pos = flat.next(); int x = pos.getBlockX(); int z = pos.getBlockZ(); - layer = session.getNearestSurfaceLayer(x, z, (layer + 7) >> 3, 0, maxY); + layer = session.getNearestSurfaceLayer(x, z, (layer + 7) >> 3, session.getMinY(), maxY); data[(z - bz) * width + (x - bx)] = layer; } } else { // Store current heightmap data int index = 0; - int yTmp = 255; + int yTmp = session.getMaxY(); for (int z = 0; z < height; ++z) { for (int x = 0; x < width; ++x, index++) { if (mask != null) { @@ -324,7 +324,7 @@ public class HeightMap { int y0 = newHeight - 1; for (int setY = y0, getY = curHeight - 1; setY >= curHeight; setY--, getY--) { BlockState get; - if (getY >= 0 && getY < 256) { + if (getY >= session.getMinY() && getY <= session.getMaxY()) { get = session.getBlock(xr, getY, zr); } else { get = BlockTypes.AIR.getDefaultState(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index d13fc5b8f..92ddcc65c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.regions; -import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.math.BlockVectorSet; @@ -745,7 +744,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { if (bx >= minX && tx <= maxX && bz >= minZ && tz <= maxZ) { // contains all X/Z - if (minY <= 0 && maxY >= 255) { + if (minY <= set.getMinSectionIndex() << 4 && maxY >= (set.getMaxSectionIndex() << 4) + 15) { return set; } trimY(set, minY, maxY); @@ -766,7 +765,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { boolean trimX = lowerX != 0 || upperX != 15; boolean trimZ = lowerZ != 0 || upperZ != 15; - for (int layer = 0; layer < FaweCache.IMP.CHUNK_LAYERS; layer++) { + for (int layer = get.getMinSectionIndex(); layer < get.getMaxSectionIndex(); layer++) { if (set.hasSection(layer)) { char[] arr = set.load(layer); if (trimX || trimZ) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 8ca6493a6..b9d080749 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -244,11 +244,11 @@ public interface Region extends Iterable, Cloneable, IBatchProcess final IChunkSet set, boolean full ) { - int minSection = Math.max(0, getMinimumY() >> 4); - int maxSection = Math.min(15, getMaximumY() >> 4); + int minSection = Math.max(get.getMinSectionIndex(), getMinimumY() >> 4); + int maxSection = Math.min(get.getMaxSectionIndex(), getMaximumY() >> 4); block = block.initChunk(chunk.getX(), chunk.getZ()); for (int layer = minSection; layer <= maxSection; layer++) { - if ((!full && !get.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { + if ((!full && !set.hasSection(layer)) || !filter.appliesLayer(chunk, layer)) { return; } block = block.initLayer(get, set, layer); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java index a0fb78882..cb83d5873 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/TargetBlock.java @@ -178,7 +178,7 @@ public class TargetBlock { } else { if (searchForLastBlock) { lastBlock = getCurrentBlock(); - if (lastBlock.getBlockY() <= 0 || lastBlock.getBlockY() >= world.getMaxY()) { + if (lastBlock.getBlockY() <= world.getMinY() || lastBlock.getBlockY() >= world.getMaxY()) { searchForLastBlock = false; } } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 01d618d94..1574eaf65 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -131,6 +131,10 @@ "fawe.error.no-failure": "This shouldn't result in any failure", "fawe.error.invalid-bracketing": "Invalid bracketing, are you missing a '{0}'.", "fawe.error.too-simple": "Complexity must be in the range 0-100", + "fawe.error.outside-range": "Argument {0} outside of range {1}-{2}.", + "fawe.error.outside-range-lower": "Argument {0} may not be less than {1}", + "fawe.error.outside-range-upper": "Argument {0} may not be greater than {1}", + "fawe.error.argument-size-mismatch": "Argument {0} may not be greater than argument {1}", "fawe.error.input-parser-exception": "Invalid empty string instead of boolean.", "fawe.error.invalid-boolean": "Invalid boolean {0}", "fawe.error.schematic.not.found": "Schematic {0} not found.",