diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java index 4335d5948..532bc6af9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/OncePerChunkExtent.java @@ -52,12 +52,12 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch } private boolean shouldRun(int chunkX, int chunkZ) { - final long pair = (long) chunkX << 32 | chunkZ & 0xffffffffL; - if (pair == lastPair) { - return false; - } - lastPair = pair; synchronized (set) { + final long pair = (long) chunkX << 32 | chunkZ & 0xffffffffL; + if (pair == lastPair) { + return false; + } + lastPair = pair; return set.add(chunkX, chunkZ); } } @@ -68,10 +68,16 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch } } + /** + * Get the task run once per chunk. + */ public Consumer getTask() { return task; } + /** + * Set the task to be run once per chunk + */ public void setTask(Consumer task) { this.task = task; } @@ -181,4 +187,12 @@ public class OncePerChunkExtent extends AbstractDelegateExtent implements IBatch super.setSkyLight(x, y, z, value); } + /** + * Reset the chunks visited + */ + public void reset() { + lastPair = Long.MAX_VALUE; + set.clear(); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java index aa608be05..0f777196e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/ReadOnlyClipboard.java @@ -45,17 +45,17 @@ public abstract class ReadOnlyClipboard extends SimpleClipboard { return of(() -> extent, region); } - public static ReadOnlyClipboard of(Extent extent, final Region region, boolean copyEntities, boolean copyBiomes) { - Fawe.instance().getQueueHandler().unCache(); - return of(() -> extent, region, copyEntities, copyBiomes); - } - public static ReadOnlyClipboard of(Supplier supplier, final Region region) { return of(supplier, region, true, false); } public static ReadOnlyClipboard of(Supplier supplier, final Region region, boolean copyEntities, boolean copyBiomes) { - return new WorldCopyClipboard(supplier, region, copyEntities, copyBiomes); + return of(supplier.get(), region, copyEntities, copyBiomes); + } + + public static ReadOnlyClipboard of(Extent extent, final Region region, boolean copyEntities, boolean copyBiomes) { + Fawe.instance().getQueueHandler().unCache(); + return WorldCopyClipboard.of(extent, region, copyEntities, copyBiomes); } private static Supplier supply() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java index 4bb50ca73..55a017604 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/WorldCopyClipboard.java @@ -1,14 +1,22 @@ package com.fastasyncworldedit.core.extent.clipboard; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.EditSessionBuilder; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -16,26 +24,47 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { private final boolean hasBiomes; private final boolean hasEntities; - private Extent extent; - private Supplier supplier; + private final Extent extent; + /** + * @deprecated use {@link WorldCopyClipboard#of(Extent, Region)} + */ + @Deprecated(forRemoval = true, since = "TODO") public WorldCopyClipboard(Supplier supplier, Region region) { this(supplier, region, true, false); } + /** + * @deprecated use {@link WorldCopyClipboard#of(Extent, Region, boolean, boolean)} + */ + @Deprecated(forRemoval = true, since = "TODO") public WorldCopyClipboard(Supplier supplier, Region region, boolean hasEntities, boolean hasBiomes) { super(region); this.hasBiomes = hasBiomes; this.hasEntities = hasEntities; - this.supplier = supplier; + this.extent = supplier.get(); + } + + private WorldCopyClipboard(Extent extent, Region region, boolean hasEntities, boolean hasBiomes) { + super(region); + this.hasBiomes = hasBiomes; + this.hasEntities = hasEntities; + this.extent = extent; + } + + public static WorldCopyClipboard of(Extent extent, Region region) { + return of(extent, region, true); + } + + public static WorldCopyClipboard of(Extent extent, Region region, boolean hasEntities) { + return of(extent, region, hasEntities, false); + } + + public static WorldCopyClipboard of(Extent extent, Region region, boolean hasEntities, boolean hasBiomes) { + return new WorldCopyClipboard(extent, region, hasEntities, hasBiomes); } public Extent getExtent() { - if (extent != null) { - return extent; - } - extent = supplier.get(); - supplier = null; return extent; } @@ -60,6 +89,7 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override + @Deprecated public List getEntities() { if (!hasEntities) { return new ArrayList<>(); @@ -72,4 +102,69 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { return hasBiomes; } + @Override + public void paste(Extent toExtent, BlockVector3 to, boolean pasteAir, boolean pasteEntities, boolean pasteBiomes) { + boolean close = false; + if (toExtent instanceof World) { + close = true; + EditSessionBuilder builder = WorldEdit + .getInstance() + .newEditSessionBuilder() + .world((World) toExtent) + .checkMemory(false) + .allowedRegionsEverywhere() + .limitUnlimited() + .changeSetNull(); + toExtent = builder.build(); + } + + Extent source = getExtent(); + + Collection entities = pasteEntities ? ForwardExtentCopy.getEntities(source, region) : Collections.emptySet(); + + final BlockVector3 origin = this.getOrigin(); + + // To must be relative to the clipboard origin ( player location - clipboard origin ) (as the locations supplied are relative to the world origin) + final int relx = to.getBlockX() - origin.getBlockX(); + final int rely = to.getBlockY() - origin.getBlockY(); + final int relz = to.getBlockZ() - origin.getBlockZ(); + + pasteBiomes &= this.hasBiomes(); + + for (BlockVector3 pos : this) { + BaseBlock block = pos.getFullBlock(this); + int xx = pos.getX() + relx; + int yy = pos.getY() + rely; + int zz = pos.getZ() + relz; + if (pasteBiomes) { + toExtent.setBiome(xx, yy, zz, pos.getBiome(this)); + } + if (!pasteAir && block.getBlockType().getMaterial().isAir()) { + continue; + } + toExtent.setBlock(xx, yy, zz, block); + } + // Entity offset is the paste location subtract the clipboard origin (entity's location is already relative to the world origin) + final int entityOffsetX = to.getBlockX() - origin.getBlockX(); + final int entityOffsetY = to.getBlockY() - origin.getBlockY(); + final int entityOffsetZ = to.getBlockZ() - origin.getBlockZ(); + // entities + for (Entity entity : entities) { + // skip players on pasting schematic + if (entity.getState() != null && entity.getState().getType().getId() + .equals("minecraft:player")) { + continue; + } + Location pos = entity.getLocation(); + Location newPos = new Location(pos.getExtent(), pos.getX() + entityOffsetX, + pos.getY() + entityOffsetY, pos.getZ() + entityOffsetZ, pos.getYaw(), + pos.getPitch() + ); + toExtent.createEntity(newPos, entity.getState()); + } + if (close) { + ((EditSession) toExtent).close(); + } + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 56a238166..da43ff205 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -266,7 +266,7 @@ public class ParallelQueueExtent extends PassthroughExtent { */ @Override public Clipboard lazyCopy(Region region) { - Clipboard clipboard = new WorldCopyClipboard(() -> this, region); + Clipboard clipboard = WorldCopyClipboard.of(this, region); clipboard.setOrigin(region.getMinimumPoint()); return clipboard; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java index 6ae147765..dc103dc79 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java @@ -2,6 +2,8 @@ package com.fastasyncworldedit.core.util; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -9,6 +11,8 @@ import java.lang.reflect.Field; public class ExtentTraverser { + private static final Logger LOGGER = LogManagerCompat.getLogger(); + private final T root; private final ExtentTraverser parent; @@ -98,9 +102,8 @@ public class ExtentTraverser { @SuppressWarnings("unchecked") public ExtentTraverser next() { try { - if (root instanceof AbstractDelegateExtent) { - AbstractDelegateExtent root = (AbstractDelegateExtent) this.root; - T value = (T) root.getExtent(); + if (root instanceof AbstractDelegateExtent abstractDelegateExtent) { + T value = (T) abstractDelegateExtent.getExtent(); if (value == null) { return null; } @@ -113,4 +116,23 @@ public class ExtentTraverser { } } + public static void printNestedExtents(Extent extent) { + String nested = printNestedExtent(new StringBuilder("Extent tree:"), new ExtentTraverser<>(extent), 0).toString(); + LOGGER.info(nested); + } + + private static StringBuilder printNestedExtent(StringBuilder builder, ExtentTraverser traverser, int depth) { + if (traverser == null || !traverser.exists()) { + return builder; + } + String indent = " ".repeat(Math.max(0, depth)); // Adjust the indentation as needed + + Extent extent = traverser.get(); + builder.append("\n").append(indent).append("- ").append(extent.getClass().getSimpleName()); + + // Recursively print nested extents + printNestedExtent(builder, traverser.next(), depth + 1); + return builder; + } + } 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 85fbde830..75e837f22 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -3885,7 +3885,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { @Override public Clipboard lazyCopy(Region region) { - WorldCopyClipboard faweClipboard = new WorldCopyClipboard(() -> this, region); + WorldCopyClipboard faweClipboard = WorldCopyClipboard.of(this, region); faweClipboard.setOrigin(region.getMinimumPoint()); return faweClipboard; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index bb42e7095..9bda82380 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -29,6 +29,7 @@ import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard; import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; +import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.ImgurUtility; @@ -192,7 +193,7 @@ public class ClipboardCommands { throw FaweCache.MAX_CHECKS; } session.setClipboard(null); - ReadOnlyClipboard lazyClipboard = ReadOnlyClipboard.of(region, !skipEntities, copyBiomes); + ReadOnlyClipboard lazyClipboard = WorldCopyClipboard.of(editSession, region, !skipEntities, copyBiomes); lazyClipboard.setOrigin(session.getPlacementPosition(actor)); session.setClipboard(new ClipboardHolder(lazyClipboard)); 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 45f604f71..47ab909dc 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 @@ -692,7 +692,7 @@ public interface Extent extends InputExtent, OutputExtent { * @return */ default Clipboard lazyCopy(Region region) { - WorldCopyClipboard faweClipboard = new WorldCopyClipboard(() -> this, region); + WorldCopyClipboard faweClipboard = WorldCopyClipboard.of(this, region); faweClipboard.setOrigin(region.getMinimumPoint()); return faweClipboard; } 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 cac9f4a7b..be702ae4d 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 @@ -381,7 +381,9 @@ public interface Clipboard extends Extent, Iterable, Closeable, Fl } default void paste(Extent extent, BlockVector3 to, boolean pasteAir, boolean pasteEntities, boolean pasteBiomes) { + boolean close = false; if (extent instanceof World) { + close = true; EditSessionBuilder builder = WorldEdit .getInstance() .newEditSessionBuilder() @@ -435,6 +437,9 @@ public interface Clipboard extends Extent, Iterable, Closeable, Fl extent.createEntity(newPos, entity.getState()); } } + if (close) { + ((EditSession) extent).close(); + } } //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java index 5403b3807..f57a536bb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/ForwardExtentCopy.java @@ -24,18 +24,21 @@ import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.BlockTranslateExtent; import com.fastasyncworldedit.core.extent.OncePerChunkExtent; import com.fastasyncworldedit.core.extent.PositionTransformExtent; +import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard; import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; import com.fastasyncworldedit.core.function.RegionMaskTestFunction; import com.fastasyncworldedit.core.function.block.BiomeCopy; import com.fastasyncworldedit.core.function.block.CombinedBlockCopy; import com.fastasyncworldedit.core.function.block.SimpleBlockCopy; import com.fastasyncworldedit.core.function.visitor.IntersectRegionFunction; +import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.MaskTraverser; +import com.fastasyncworldedit.core.util.ProcessorTraverser; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEditException; @@ -57,6 +60,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Identity; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.FlatRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; @@ -69,7 +73,9 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; +import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -422,54 +428,7 @@ public class ForwardExtentCopy implements Operation { blockCopy = new RegionVisitor(region, copy, preloader); } - Collection entities; - if (copyingEntities) { - IQueueExtent queue; - Extent ext = source instanceof AbstractDelegateExtent ex ? ex.getExtent() : source; - ParallelQueueExtent parallel = new ExtentTraverser<>(source).findAndGet(ParallelQueueExtent.class); - if (parallel != null) { - queue = parallel.getExtent(); - } else { - queue = new ExtentTraverser<>(source).findAndGet(SingleThreadQueueExtent.class); - } - if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS && queue != null) { - entities = new LinkedBlockingQueue<>(); - OncePerChunkExtent oncePer = new OncePerChunkExtent( - ext, - queue, - (get) -> { - if (region.containsChunk(get.getX(), get.getZ())) { - entities.addAll(get.getFullEntities()); - } else { - get.getFullEntities().forEach(e -> { - if (region.contains(e.getLocation().toBlockPoint())) { - entities.add(e); - } - }); - } - } - ); - ExtentBatchProcessorHolder batchExtent = - new ExtentTraverser<>(source).findAndGet(ExtentBatchProcessorHolder.class); - if (batchExtent != null) { - batchExtent.getProcessor().join(oncePer); - } else { - new ExtentTraverser(source).setNext(oncePer); - } - } else { - if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS) { - LOGGER.warn("Could not find IQueueExtent instance for entity retrieval, falling back to default method."); - } - // filter players since they can't be copied - entities = new HashSet<>(source.getEntities(region)); - entities.removeIf(entity -> { - EntityProperties properties = entity.getFacet(EntityProperties.class); - return properties != null && !properties.isPasteable(); - }); - } - } else { - entities = Collections.emptySet(); - } + Collection entities = copyingEntities ? getEntities(source, region) : Collections.emptySet(); for (int i = 0; i < repetitions; i++) { Operations.completeBlindly(blockCopy); @@ -508,6 +467,78 @@ public class ForwardExtentCopy implements Operation { return null; } + /** + * If setting enabled, Creates a new OncePerChunkExtent instance to retain a list of entities for the given source extent, + * then add it to the source extent. If setting is not set simply returns the entities from {@link Extent#getEntities()} Accepts an + * optional region for entities to be within. + * + * @param source Source extent + * @param region Optional regions for entities to be within + * @return Collection of entities (may not be filled until an operation completes on the chunks) + * @since TODO + */ + public static Collection getEntities(Extent source, Region region) { + Extent extent = source; + if (source instanceof WorldCopyClipboard clip) { + extent = clip.getExtent(); + } + IQueueExtent queue = null; + if (Settings.settings().EXPERIMENTAL.IMPROVED_ENTITY_EDITS) { + ParallelQueueExtent parallel = new ExtentTraverser<>(extent).findAndGet(ParallelQueueExtent.class); + if (parallel != null) { + queue = parallel.getExtent(); + } else { + queue = new ExtentTraverser<>(extent).findAndGet(SingleThreadQueueExtent.class); + } + if (queue == null) { + LOGGER.warn("Could not find IQueueExtent instance for entity retrieval, OncePerChunkExtent will not work."); + } + } + if (queue == null) { + Set entities = new HashSet<>(region != null ? source.getEntities(region) : source.getEntities()); + entities.removeIf(entity -> { + EntityProperties properties = entity.getFacet(EntityProperties.class); + return properties != null && !properties.isPasteable(); + }); + return entities; + } + LinkedBlockingQueue entities = new LinkedBlockingQueue<>(); + Consumer task = (get) -> { + if (region == null || region instanceof CuboidRegion cuboid && cuboid.chunkContainedBy( + get.getX(), + get.getZ(), + get.getMinY(), + get.getMaxY() + )) { + entities.addAll(get.getFullEntities()); + } else { + get.getFullEntities().forEach(e -> { + if (region.contains(e.getLocation().toBlockPoint())) { + entities.add(e); + } + }); + } + }; + Extent ext = extent instanceof AbstractDelegateExtent ex ? ex.getExtent() : extent; + ExtentBatchProcessorHolder batchExtent = new ExtentTraverser<>(extent).findAndGet(ExtentBatchProcessorHolder.class); + OncePerChunkExtent oncePer = new ExtentTraverser<>(extent).findAndGet(OncePerChunkExtent.class); + if (batchExtent != null && oncePer == null) { + oncePer = new ProcessorTraverser<>(batchExtent).find(OncePerChunkExtent.class); + } + if (oncePer != null) { + oncePer.reset(); + oncePer.setTask(task); + } else { + oncePer = new OncePerChunkExtent(ext, queue, task); + if (false && batchExtent != null) { + batchExtent.getProcessor().join(oncePer); + } else { + new ExtentTraverser(extent).setNext(oncePer); + } + } + return entities; + } + @Override public void cancel() { } 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 4b0894d91..3c0879a80 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 @@ -744,6 +744,26 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { return maxZ; } + /** + * Check if an entire chunk is contained by this region + * + * @param chunkX chunk x coord + * @param chunkZ chunk z coord + * @param chunkMinY The minimum Y level of the chunk + * @param chunkMaxY The maximum Y level of the chunk + * @return If the entire chunk is contained by this region + * @since TODO + */ + public boolean chunkContainedBy(int chunkX, int chunkZ, int chunkMinY, int chunkMaxY) { + int bx = chunkX << 4; + int bz = chunkZ << 4; + int tx = bx + 15; + int tz = bz + 15; + BlockVector3 min = getMinimumPoint(); + BlockVector3 max = getMaximumPoint(); + return min.getY() <= chunkMinY && max.getY() >= chunkMaxY && min.getX() <= bx && max.getX() >= tx && min.getZ() <= bz && max.getZ() >= tz; + } + @Override public void filter( final IChunk chunk,