From 207e56969ba090a229343c627486927aeeb39a0a Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 19 Jul 2020 16:03:40 +0100 Subject: [PATCH] Remove ChunkManager#chunkTask --- .../core/generator/HybridPlotManager.java | 70 +++++---- .../core/generator/HybridUtils.java | 138 +++++++++--------- .../core/queue/ChunkCoordinatorBuilder.java | 1 - .../plotsquared/core/util/ChunkManager.java | 95 +----------- .../com/plotsquared/core/util/ChunkUtil.java | 20 +++ .../core/util/SchematicHandler.java | 91 ++++++------ 6 files changed, 174 insertions(+), 241 deletions(-) diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java b/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java index 67bf3c011..3955a00ee 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridPlotManager.java @@ -35,13 +35,12 @@ import com.plotsquared.core.plot.PlotAreaTerrainType; import com.plotsquared.core.plot.PlotAreaType; import com.plotsquared.core.plot.PlotId; import com.plotsquared.core.queue.QueueCoordinator; -import com.plotsquared.core.util.ChunkManager; +import com.plotsquared.core.util.ChunkUtil; import com.plotsquared.core.util.FileBytes; import com.plotsquared.core.util.FileUtils; import com.plotsquared.core.util.MathMan; import com.plotsquared.core.util.RegionManager; import com.plotsquared.core.util.WorldUtil; -import com.plotsquared.core.util.task.RunnableVal; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -228,8 +227,8 @@ public class HybridPlotManager extends ClassicPlotManager { } } final String world = hybridPlotWorld.getWorldName(); - Location pos1 = plot.getBottomAbs(); - Location pos2 = plot.getExtendedTopAbs(); + final Location pos1 = plot.getBottomAbs(); + final Location pos2 = plot.getExtendedTopAbs(); // If augmented final boolean canRegen = (hybridPlotWorld.getType() == PlotAreaType.AUGMENTED) && (hybridPlotWorld.getTerrain() @@ -248,39 +247,38 @@ public class HybridPlotManager extends ClassicPlotManager { final BiomeType biome = hybridPlotWorld.getPlotBiome(); final QueueCoordinator queue = hybridPlotWorld.getQueue(); - ChunkManager.chunkTask(pos1, pos2, new RunnableVal() { - @Override public void run(int[] value) { - // If the chunk isn't near the edge and it isn't an augmented world we can just regen the whole chunk - if (canRegen && (value[6] == 0)) { - queue.regenChunk(value[0], value[1]); - return; - } - /* Otherwise we need to set each component, as we don't want to regenerate the road or other plots that share the same chunk.*/ - // Set the biome - WorldUtil.setBiome(world, value[2], value[3], value[4], value[5], biome); - // These two locations are for each component (e.g. bedrock, main block, floor, air) - Location bot = Location.at(world, value[2], 0, value[3]); - Location top = Location.at(world, value[4], 1, value[5]); - queue.setCuboid(bot, top, bedrock); - // Each component has a different layer - bot = bot.withY(1); - top = top.withY(hybridPlotWorld.PLOT_HEIGHT); - queue.setCuboid(bot, top, filling); - bot = bot.withY(hybridPlotWorld.PLOT_HEIGHT); - top = top.withY(hybridPlotWorld.PLOT_HEIGHT + 1); - queue.setCuboid(bot, top, plotfloor); - bot = bot.withY(hybridPlotWorld.PLOT_HEIGHT + 1); - top = top.withY(getWorldHeight()); - queue.setCuboid(bot, top, air); - // And finally set the schematic, the y value is unimportant for this function - pastePlotSchematic(queue, bot, top); + queue.setChunkConsumer(blockVector2 -> { + // If the chunk isn't near the edge and it isn't an augmented world we can just regen the whole chunk + if (canRegen && ChunkUtil.isWholeChunk(pos1, pos2, blockVector2)) { + queue.regenChunk(blockVector2.getX(), blockVector2.getZ()); + return; } - }, () -> { - // And notify whatever called this when plot clearing is done - queue.setCompleteTask(whenDone); - queue.enqueue(); - }, 10); - return true; + /* Otherwise we need to set each component, as we don't want to regenerate the road or other plots that share the same chunk.*/ + // Set the biome + int x1 = Math.max(pos1.getX(), blockVector2.getX() << 4); + int z1 = Math.max(pos1.getZ(), blockVector2.getZ() << 4); + int x2 = Math.min(pos2.getX(), blockVector2.getX() << 4); + int z2 = Math.min(pos2.getZ(), blockVector2.getZ() << 4); + WorldUtil.setBiome(world, x1, z1, x2, z2, biome); + // These two locations are for each component (e.g. bedrock, main block, floor, air) + Location bot = Location.at(world, x1, 0, z1); + Location top = Location.at(world, x2, 1, z2); + queue.setCuboid(bot, top, bedrock); + // Each component has a different layer + bot = bot.withY(1); + top = top.withY(hybridPlotWorld.PLOT_HEIGHT); + queue.setCuboid(bot, top, filling); + bot = bot.withY(hybridPlotWorld.PLOT_HEIGHT); + top = top.withY(hybridPlotWorld.PLOT_HEIGHT + 1); + queue.setCuboid(bot, top, plotfloor); + bot = bot.withY(hybridPlotWorld.PLOT_HEIGHT + 1); + top = top.withY(getWorldHeight()); + queue.setCuboid(bot, top, air); + // And finally set the schematic, the y value is unimportant for this function + pastePlotSchematic(queue, bot, top); + }); + queue.setCompleteTask(whenDone); + return queue.enqueue(); } public void pastePlotSchematic(QueueCoordinator queue, Location bottom, Location top) { diff --git a/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java b/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java index e89e221c2..c54e494aa 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java +++ b/Core/src/main/java/com/plotsquared/core/generator/HybridUtils.java @@ -64,8 +64,8 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import javax.annotation.Nonnull; import java.io.File; import java.util.ArrayDeque; import java.util.ArrayList; @@ -80,7 +80,8 @@ import java.util.concurrent.atomic.AtomicInteger; public class HybridUtils { - private static final Logger logger = LoggerFactory.getLogger("P2/" + HybridUtils.class.getSimpleName()); + private static final Logger logger = + LoggerFactory.getLogger("P2/" + HybridUtils.class.getSimpleName()); public static HybridUtils manager; public static Set regions; @@ -98,7 +99,8 @@ public class HybridUtils { @Inject public HybridUtils(@Nonnull final PlotAreaManager plotAreaManager, @Nonnull final ChunkManager chunkManager, @Nonnull final GlobalBlockQueue blockQueue, - @Nonnull final WorldUtil worldUtil, @Nonnull final RegionManager regionManager, @Nonnull final SchematicHandler schematicHandler) { + @Nonnull final WorldUtil worldUtil, @Nonnull final RegionManager regionManager, + @Nonnull final SchematicHandler schematicHandler) { this.plotAreaManager = plotAreaManager; this.chunkManager = chunkManager; this.blockQueue = blockQueue; @@ -127,8 +129,6 @@ public class HybridUtils { * */ TaskManager.runTaskAsync(() -> { - final QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(world)); - final BlockVector3 bot = region.getMinimumPoint(); final BlockVector3 top = region.getMaximumPoint(); @@ -160,6 +160,55 @@ public class HybridUtils { System.gc(); System.gc(); + QueueCoordinator queue = area.getQueue(); + + queue.setChunkConsumer(blockVector2 -> { + int X = blockVector2.getX(); + int Z = blockVector2.getZ(); + int minX; + if (X == cbx) { + minX = bx & 15; + } else { + minX = 0; + } + int minZ; + if (Z == cbz) { + minZ = bz & 15; + } else { + minZ = 0; + } + int maxX; + if (X == ctx) { + maxX = tx & 15; + } else { + maxX = 16; + } + int maxZ; + if (Z == ctz) { + maxZ = tz & 15; + } else { + maxZ = 16; + } + + int chunkBlockX = X << 4; + int chunkBlockZ = Z << 4; + + int xb = chunkBlockX - bx; + int zb = chunkBlockZ - bz; + for (int x = minX; x <= maxX; x++) { + int xx = chunkBlockX + x; + for (int z = minZ; z <= maxZ; z++) { + int zz = chunkBlockZ + z; + for (int y = 0; y < 256; y++) { + BlockState block = queue.getBlock(xx, y, zz); + int xr = xb + x; + int zr = zb + z; + newBlocks[y][xr][zr] = block; + } + } + } + }); + final Runnable run = () -> TaskManager.runTaskAsync(() -> { int size = width * length; int[] changes = new int[size]; @@ -248,57 +297,8 @@ public class HybridUtils { whenDone.value = analysis; whenDone.run(); }); - System.gc(); - Location botLoc = Location.at(world, bot.getX(), bot.getY(), bot.getZ()); - Location topLoc = Location.at(world, top.getX(), top.getY(), top.getZ()); - ChunkManager.chunkTask(botLoc, topLoc, new RunnableVal() { - @Override public void run(int[] value) { - int X = value[0]; - int Z = value[1]; - int minX; - if (X == cbx) { - minX = bx & 15; - } else { - minX = 0; - } - int minZ; - if (Z == cbz) { - minZ = bz & 15; - } else { - minZ = 0; - } - int maxX; - if (X == ctx) { - maxX = tx & 15; - } else { - maxX = 16; - } - int maxZ; - if (Z == ctz) { - maxZ = tz & 15; - } else { - maxZ = 16; - } - - int cbx = X << 4; - int cbz = Z << 4; - - int xb = cbx - bx; - int zb = cbz - bz; - for (int x = minX; x <= maxX; x++) { - int xx = cbx + x; - for (int z = minZ; z <= maxZ; z++) { - int zz = cbz + z; - for (int y = 0; y < 256; y++) { - BlockState block = queue.getBlock(xx, y, zz); - int xr = xb + x; - int zr = zb + z; - newBlocks[y][xr][zr] = block; - } - } - } - } - }, () -> TaskManager.runTaskAsync(run), 5); + queue.setCompleteTask(run); + queue.enqueue(); }); } @@ -466,9 +466,11 @@ public class HybridUtils { BlockVector2 loc = iterator.next(); iterator.remove(); if (Settings.DEBUG) { - logger.info("[P2] Updating .mcr: {}, {} (approx 1024 chunks)", - loc.getX(), loc.getZ()); - logger.info("[P2] - Remaining: {}", HybridUtils.regions.size()); + logger + .info("[P2] Updating .mcr: {}, {} (approx 1024 chunks)", + loc.getX(), loc.getZ()); + logger.info("[P2] - Remaining: {}", + HybridUtils.regions.size()); } chunks.addAll(getChunks(loc)); System.gc(); @@ -482,8 +484,7 @@ public class HybridUtils { .isEmpty()) { final BlockVector2 chunk = iterator.next(); iterator.remove(); - boolean regenedRoads = - regenerateRoad(area, chunk, extend); + boolean regenedRoads = regenerateRoad(area, chunk, extend); if (!regenedRoads && Settings.DEBUG) { logger.info("[P2] Failed to regenerate road"); } @@ -496,13 +497,15 @@ public class HybridUtils { Iterator iterator = HybridUtils.regions.iterator(); BlockVector2 loc = iterator.next(); iterator.remove(); - logger.error("[P2] Error! Could not update '{}/region/r.{}.{}.mca' (Corrupt chunk?)", + logger.error( + "[P2] Error! Could not update '{}/region/r.{}.{}.mca' (Corrupt chunk?)", area.getWorldHash(), loc.getX(), loc.getZ()); int sx = loc.getX() << 5; int sz = loc.getZ() << 5; for (int x = sx; x < sx + 32; x++) { for (int z = sz; z < sz + 32; z++) { - chunkManager.unloadChunk(area.getWorldName(), BlockVector2.at(x, z), + chunkManager + .unloadChunk(area.getWorldName(), BlockVector2.at(x, z), true); } } @@ -544,7 +547,8 @@ public class HybridUtils { this.schematicHandler.getCompoundTag(world, sideRoad, new RunnableVal() { @Override public void run(CompoundTag value) { schematicHandler.save(value, dir + "sideroad.schem"); - schematicHandler.getCompoundTag(world, intersection, new RunnableVal() { + schematicHandler + .getCompoundTag(world, intersection, new RunnableVal() { @Override public void run(CompoundTag value) { schematicHandler.save(value, dir + "intersection.schem"); plotworld.ROAD_SCHEMATIC_ENABLED = true; @@ -604,7 +608,8 @@ public class HybridUtils { z -= plotWorld.ROAD_OFFSET_Z; final int finalX = x; final int finalZ = z; - QueueCoordinator queue = this.blockQueue.getNewQueue(worldUtil.getWeWorld(plotWorld.getWorldName())); + QueueCoordinator queue = + this.blockQueue.getNewQueue(worldUtil.getWeWorld(plotWorld.getWorldName())); if (id1 == null || id2 == null || id1 != id2) { this.chunkManager.loadChunk(area.getWorldName(), chunk, false).thenRun(() -> { if (id1 != null) { @@ -643,7 +648,8 @@ public class HybridUtils { } if (condition) { BaseBlock[] blocks = plotWorld.G_SCH.get(MathMan.pair(absX, absZ)); - int minY = Settings.Schematics.PASTE_ROAD_ON_TOP ? plotWorld.SCHEM_Y : 1; + int minY = + Settings.Schematics.PASTE_ROAD_ON_TOP ? plotWorld.SCHEM_Y : 1; int maxY = Math.max(extend, blocks.length); for (int y = 0; y < maxY; y++) { if (y > blocks.length - 1) { diff --git a/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java b/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java index cbfab298a..861ccc8ed 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java +++ b/Core/src/main/java/com/plotsquared/core/queue/ChunkCoordinatorBuilder.java @@ -29,7 +29,6 @@ import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.plotsquared.core.inject.factory.ChunkCoordinatorFactory; import com.plotsquared.core.location.Location; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.world.World; import org.jetbrains.annotations.NotNull; diff --git a/Core/src/main/java/com/plotsquared/core/util/ChunkManager.java b/Core/src/main/java/com/plotsquared/core/util/ChunkManager.java index 2d5b2371e..e2e5c36e9 100644 --- a/Core/src/main/java/com/plotsquared/core/util/ChunkManager.java +++ b/Core/src/main/java/com/plotsquared/core/util/ChunkManager.java @@ -31,12 +31,8 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.queue.QueueCoordinator; import com.plotsquared.core.queue.ScopedQueueCoordinator; import com.plotsquared.core.util.task.RunnableVal; -import com.plotsquared.core.util.task.TaskManager; -import com.plotsquared.core.util.task.TaskTime; import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.regions.CuboidRegion; -import java.util.ArrayList; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -50,8 +46,8 @@ public abstract class ChunkManager { public static void setChunkInPlotArea(RunnableVal force, RunnableVal add, String world, BlockVector2 loc) { - QueueCoordinator queue = - PlotSquared.platform().getGlobalBlockQueue().getNewQueue(PlotSquared.platform().getWorldUtil().getWeWorld(world)); + QueueCoordinator queue = PlotSquared.platform().getGlobalBlockQueue() + .getNewQueue(PlotSquared.platform().getWorldUtil().getWeWorld(world)); if (PlotSquared.get().getPlotAreaManager().isAugmented(world) && PlotSquared.get() .isNonStandardGeneration(world, loc)) { int blockX = loc.getX() << 4; @@ -99,93 +95,10 @@ public abstract class ChunkManager { return false; } - public static void chunkTask(final Plot plot, final RunnableVal task, - final Runnable whenDone, final int allocate) { - final ArrayList regions = new ArrayList<>(plot.getRegions()); - Runnable smallTask = new Runnable() { - @Override public void run() { - if (regions.isEmpty()) { - TaskManager.runTask(whenDone); - return; - } - CuboidRegion value = regions.remove(0); - Location pos1 = Location.at(plot.getWorldName(), value.getMinimumPoint().getX(), 0, - value.getMinimumPoint().getZ()); - Location pos2 = Location.at(plot.getWorldName(), value.getMaximumPoint().getX(), 0, - value.getMaximumPoint().getZ()); - chunkTask(pos1, pos2, task, this, allocate); - } - }; - smallTask.run(); - } - - /** - * The int[] will be in the form: [chunkX, chunkZ, pos1x, pos1z, pos2x, pos2z, isEdge] and will represent the bottom and top parts of the chunk - * - * @param pos1 - * @param pos2 - * @param task - * @param whenDone - */ - public static void chunkTask(Location pos1, Location pos2, final RunnableVal task, - final Runnable whenDone, final int allocate) { - final int p1x = pos1.getX(); - final int p1z = pos1.getZ(); - final int p2x = pos2.getX(); - final int p2z = pos2.getZ(); - final int bcx = p1x >> 4; - final int bcz = p1z >> 4; - final int tcx = p2x >> 4; - final int tcz = p2z >> 4; - final ArrayList chunks = new ArrayList<>(); - - for (int x = bcx; x <= tcx; x++) { - for (int z = bcz; z <= tcz; z++) { - chunks.add(BlockVector2.at(x, z)); - } - } - TaskManager.runTask(new Runnable() { - @Override public void run() { - long start = System.currentTimeMillis(); - while (!chunks.isEmpty() && ((System.currentTimeMillis() - start) < allocate)) { - BlockVector2 chunk = chunks.remove(0); - task.value = new int[7]; - task.value[0] = chunk.getX(); - task.value[1] = chunk.getZ(); - task.value[2] = task.value[0] << 4; - task.value[3] = task.value[1] << 4; - task.value[4] = task.value[2] + 15; - task.value[5] = task.value[3] + 15; - if (task.value[0] == bcx) { - task.value[2] = p1x; - task.value[6] = 1; - } - if (task.value[0] == tcx) { - task.value[4] = p2x; - task.value[6] = 1; - } - if (task.value[1] == bcz) { - task.value[3] = p1z; - task.value[6] = 1; - } - if (task.value[1] == tcz) { - task.value[5] = p2z; - task.value[6] = 1; - } - task.run(); - } - if (!chunks.isEmpty()) { - TaskManager.runTaskLater(this, TaskTime.ticks(1L)); - } else { - TaskManager.runTask(whenDone); - } - } - }); - } - + @Deprecated public abstract CompletableFuture loadChunk(String world, BlockVector2 loc, boolean force); - public abstract void unloadChunk(String world, BlockVector2 loc, boolean save); + @Deprecated public abstract void unloadChunk(String world, BlockVector2 loc, boolean save); public Plot hasPlot(String world, BlockVector2 chunk) { int x1 = chunk.getX() << 4; diff --git a/Core/src/main/java/com/plotsquared/core/util/ChunkUtil.java b/Core/src/main/java/com/plotsquared/core/util/ChunkUtil.java index 6c1a1415d..fc73c7ba1 100644 --- a/Core/src/main/java/com/plotsquared/core/util/ChunkUtil.java +++ b/Core/src/main/java/com/plotsquared/core/util/ChunkUtil.java @@ -25,6 +25,8 @@ */ package com.plotsquared.core.util; +import com.plotsquared.core.location.Location; +import com.sk89q.worldedit.math.BlockVector2; import lombok.experimental.UtilityClass; import org.jetbrains.annotations.Range; @@ -115,4 +117,22 @@ public class ChunkUtil { public static int getZ(@Range(from = 0, to = 4095) int j) { return z_loc[j]; } + + /** + * Returns true if the region pos1 -> pos2 contains the chunk + * + * @param pos1 Region minimum point + * @param pos2 Region maximum point + * @param chunk BlockVector2 of chunk coordinates + * @return true if the region pos1 -> pos2 contains the chunk + */ + public static boolean isWholeChunk(Location pos1, Location pos2, BlockVector2 chunk) { + int x1 = pos1.getX(); + int z1 = pos1.getZ(); + int x2 = pos2.getX(); + int z2 = pos2.getZ(); + int cx = chunk.getX() << 4; + int cz = chunk.getZ() << 4; + return cx > x1 && cz > z1 && cx < x2 && cz < z2; + } } diff --git a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java index 2b570c9ed..0381945fa 100644 --- a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java +++ b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java @@ -224,7 +224,8 @@ public abstract class SchematicHandler { final String name; if (namingScheme == null) { name = - plot.getId().getX() + ";" + plot.getId().getY() + ',' + plot.getArea() + ',' + owner; + plot.getId().getX() + ";" + plot.getId().getY() + ',' + plot.getArea() + ',' + + owner; } else { name = namingScheme.replaceAll("%id%", plot.getId().toString()) .replaceAll("%idx%", plot.getId().getX() + "") @@ -330,57 +331,53 @@ public abstract class SchematicHandler { final int tcx = p2x >> 4; final int tcz = p2z >> 4; - ChunkManager.chunkTask(pos1, pos2, new RunnableVal() { - @Override public void run(int[] value) { - BlockVector2 chunk = BlockVector2.at(value[0], value[1]); - int x = chunk.getX(); - int z = chunk.getZ(); - int xxb = x << 4; - int zzb = z << 4; - int xxt = xxb + 15; - int zzt = zzb + 15; - if (x == bcx) { - xxb = p1x; - } - if (x == tcx) { - xxt = p2x; - } - if (z == bcz) { - zzb = p1z; - } - if (z == tcz) { - zzt = p2z; - } - // Paste schematic here + queue.setChunkConsumer(blockVector2 -> { + int x = blockVector2.getX(); + int z = blockVector2.getZ(); + int xxb = x << 4; + int zzb = z << 4; + int xxt = xxb + 15; + int zzt = zzb + 15; + if (x == bcx) { + xxb = p1x; + } + if (x == tcx) { + xxt = p2x; + } + if (z == bcz) { + zzb = p1z; + } + if (z == tcz) { + zzt = p2z; + } + // Paste schematic here - for (int ry = 0; ry < Math.min(256, HEIGHT); ry++) { - int yy = y_offset_actual + ry; - if (yy > 255) { - continue; - } - for (int rz = zzb - p1z; rz <= (zzt - p1z); rz++) { - for (int rx = xxb - p1x; rx <= (xxt - p1x); rx++) { - int xx = p1x + xOffset + rx; - int zz = p1z + zOffset + rz; - BaseBlock id = blockArrayClipboard - .getFullBlock(BlockVector3.at(rx, ry, rz)); - queue.setBlock(xx, yy, zz, id); - if (ry == 0) { - BiomeType biome = - blockArrayClipboard.getBiome(BlockVector2.at(rx, rz)); - queue.setBiome(xx, zz, biome); - } + for (int ry = 0; ry < Math.min(256, HEIGHT); ry++) { + int yy = y_offset_actual + ry; + if (yy > 255) { + continue; + } + for (int rz = zzb - p1z; rz <= (zzt - p1z); rz++) { + for (int rx = xxb - p1x; rx <= (xxt - p1x); rx++) { + int xx = p1x + xOffset + rx; + int zz = p1z + zOffset + rz; + BaseBlock id = + blockArrayClipboard.getFullBlock(BlockVector3.at(rx, ry, rz)); + queue.setBlock(xx, yy, zz, id); + if (ry == 0) { + BiomeType biome = + blockArrayClipboard.getBiome(BlockVector2.at(rx, rz)); + queue.setBiome(xx, zz, biome); } } } - queue.enqueue(); } - }, () -> { - if (whenDone != null) { - whenDone.value = true; - whenDone.run(); - } - }, 10); + }); + if (whenDone != null) { + whenDone.value = true; + } + queue.setCompleteTask(whenDone); + queue.enqueue(); } catch (Exception e) { e.printStackTrace(); TaskManager.runTask(whenDone);