diff --git a/.gitignore b/.gitignore index 24f46cd56..3423dd222 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,4 @@ p2error.txt *.bat Nukkit/build/resources/main/plugin.yml docs/ +build/ diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitLocalQueue.java b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitLocalQueue.java index 3f93ebb63..c8d6d929f 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitLocalQueue.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/queue/BukkitLocalQueue.java @@ -31,6 +31,7 @@ import com.plotsquared.core.PlotSquared; import com.plotsquared.core.queue.BasicLocalBlockQueue; import com.plotsquared.core.util.BlockUtil; import com.plotsquared.core.util.MainUtil; +import com.plotsquared.core.util.task.TaskManager; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; @@ -50,7 +51,6 @@ import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.Container; import org.bukkit.block.data.BlockData; -import org.bukkit.inventory.BlockInventoryHolder; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; @@ -117,9 +117,6 @@ public class BukkitLocalQueue extends BasicLocalBlockQueue { @Override public final void setComponents(LocalChunk lc) throws ExecutionException, InterruptedException { setBaseBlocks(lc); - if (setBiome() && lc.biomes != null) { - setBiomes(lc); - } } public void setBaseBlocks(LocalChunk localChunk) { @@ -165,6 +162,22 @@ public class BukkitLocalQueue extends BasicLocalBlockQueue { } } } + if (setBiome() && localChunk.biomes != null) { + for (int x = 0; x < localChunk.biomes.length; x++) { + BiomeType[] biomeZ = localChunk.biomes[x]; + if (biomeZ != null) { + for (int z = 0; z < biomeZ.length; z++) { + if (biomeZ[z] != null) { + BiomeType biomeType = biomeZ[z]; + + Biome biome = BukkitAdapter.adapt(biomeType); + worldObj.setBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z, + biome); + } + } + } + } + } }; if (isForceSync()) { chunkConsumer.accept(getChunk(worldObj, localChunk)); diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java b/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java index 86fbd732e..27ce9139f 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/schematic/BukkitSchematicHandler.java @@ -25,258 +25,15 @@ */ package com.plotsquared.bukkit.schematic; -import com.plotsquared.bukkit.util.BukkitUtil; -import com.plotsquared.core.location.Location; import com.plotsquared.core.queue.LocalBlockQueue; -import com.plotsquared.core.util.MainUtil; import com.plotsquared.core.util.SchematicHandler; -import com.plotsquared.core.util.task.RunnableVal; -import com.plotsquared.core.util.task.TaskManager; -import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntArrayTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; - -import java.io.ByteArrayOutputStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.IntStream; /** * Schematic Handler. */ public class BukkitSchematicHandler extends SchematicHandler { - @Override - public void getCompoundTag(final String world, final Set regions, - final RunnableVal whenDone) { - // async - TaskManager.runTaskAsync(new Runnable() { - @Override public void run() { - // Main positions - Location[] corners = MainUtil.getCorners(world, regions); - final Location bot = corners[0]; - final Location top = corners[1]; - - CuboidRegion cuboidRegion = - new CuboidRegion(BukkitUtil.IMP.getWeWorld(world), bot.getBlockVector3(), - top.getBlockVector3()); - - final int width = cuboidRegion.getWidth(); - int height = cuboidRegion.getHeight(); - final int length = cuboidRegion.getLength(); - Map schematic = new HashMap<>(); - schematic.put("Version", new IntTag(2)); - schematic.put("DataVersion", new IntTag(WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.WORLD_EDITING).getDataVersion())); - - Map metadata = new HashMap<>(); - metadata.put("WEOffsetX", new IntTag(0)); - metadata.put("WEOffsetY", new IntTag(0)); - metadata.put("WEOffsetZ", new IntTag(0)); - - schematic.put("Metadata", new CompoundTag(metadata)); - - schematic.put("Width", new ShortTag((short) width)); - schematic.put("Height", new ShortTag((short) height)); - schematic.put("Length", new ShortTag((short) length)); - - // The Sponge format Offset refers to the 'min' points location in the world. That's our 'Origin' - schematic.put("Offset", new IntArrayTag(new int[] {0, 0, 0,})); - - Map palette = new HashMap<>(); - Map biomePalette = new HashMap<>(); - - List tileEntities = new ArrayList<>(); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length); - ByteArrayOutputStream biomeBuffer = new ByteArrayOutputStream(width * length); - // Queue - final ArrayDeque queue = new ArrayDeque<>(regions); - TaskManager.runTask(new Runnable() { - @Override public void run() { - if (queue.isEmpty()) { - TaskManager.runTaskAsync(() -> { - schematic.put("PaletteMax", new IntTag(palette.size())); - - Map paletteTag = new HashMap<>(); - palette.forEach( - (key, value) -> paletteTag.put(key, new IntTag(value))); - - schematic.put("Palette", new CompoundTag(paletteTag)); - schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray())); - schematic.put("TileEntities", - new ListTag(CompoundTag.class, tileEntities)); - - schematic.put("BiomePaletteMax", new IntTag(biomePalette.size())); - - Map biomePaletteTag = new HashMap<>(); - biomePalette.forEach( - (key, value) -> biomePaletteTag.put(key, new IntTag(value))); - - schematic.put("BiomePalette", new CompoundTag(biomePaletteTag)); - schematic - .put("BiomeData", new ByteArrayTag(biomeBuffer.toByteArray())); - whenDone.value = new CompoundTag(schematic); - TaskManager.runTask(whenDone); - }); - return; - } - final Runnable regionTask = this; - CuboidRegion region = queue.poll(); - Location pos1 = new Location(world, region.getMinimumPoint().getX(), - region.getMinimumPoint().getY(), region.getMinimumPoint().getZ()); - Location pos2 = new Location(world, region.getMaximumPoint().getX(), - region.getMaximumPoint().getY(), region.getMaximumPoint().getZ()); - final int p1x = pos1.getX(); - final int sy = pos1.getY(); - final int p1z = pos1.getZ(); - final int p2x = pos2.getX(); - final int p2z = pos2.getZ(); - final int ey = pos2.getY(); - Iterator yiter = IntStream.range(sy, ey + 1).iterator(); - final Runnable yTask = new Runnable() { - @Override public void run() { - long ystart = System.currentTimeMillis(); - while (yiter.hasNext() - && System.currentTimeMillis() - ystart < 20) { - final int y = yiter.next(); - Iterator ziter = - IntStream.range(p1z, p2z + 1).iterator(); - final Runnable zTask = new Runnable() { - @Override public void run() { - long zstart = System.currentTimeMillis(); - while (ziter.hasNext() - && System.currentTimeMillis() - zstart < 20) { - final int z = ziter.next(); - Iterator xiter = - IntStream.range(p1x, p2x + 1).iterator(); - final Runnable xTask = new Runnable() { - @Override public void run() { - long xstart = System.currentTimeMillis(); - final int ry = y - sy; - final int rz = z - p1z; - while (xiter.hasNext() - && System.currentTimeMillis() - xstart - < 20) { - final int x = xiter.next(); - final int rx = x - p1x; - BlockVector3 point = - BlockVector3.at(x, y, z); - BaseBlock block = - cuboidRegion.getWorld() - .getFullBlock(point); - if (block.getNbtData() != null) { - Map values = - new HashMap<>(); - for (Map.Entry entry : block - .getNbtData().getValue() - .entrySet()) { - values.put(entry.getKey(), - entry.getValue()); - } - // Remove 'id' if it exists. We want 'Id' - values.remove("id"); - - // Positions are kept in NBT, we don't want that. - values.remove("x"); - values.remove("y"); - values.remove("z"); - - values.put("Id", new StringTag( - block.getNbtId())); - values.put("Pos", new IntArrayTag( - new int[] {rx, ry, rz})); - - tileEntities - .add(new CompoundTag(values)); - } - String blockKey = - block.toImmutableState() - .getAsString(); - int blockId; - if (palette.containsKey(blockKey)) { - blockId = palette.get(blockKey); - } else { - blockId = palette.size(); - palette - .put(blockKey, palette.size()); - } - - while ((blockId & -128) != 0) { - buffer.write(blockId & 127 | 128); - blockId >>>= 7; - } - buffer.write(blockId); - - if (ry == sy) { - BlockVector2 pt = - BlockVector2.at(x, z); - BiomeType biome = - cuboidRegion.getWorld() - .getBiome(pt); - String biomeStr = biome.getId(); - int biomeId; - if (biomePalette - .containsKey(biomeStr)) { - biomeId = - biomePalette.get(biomeStr); - } else { - biomeId = biomePalette.size(); - biomePalette - .put(biomeStr, biomeId); - } - while ((biomeId & -128) != 0) { - biomeBuffer - .write(biomeId & 127 | 128); - biomeId >>>= 7; - } - biomeBuffer.write(biomeId); - } - } - if (xiter.hasNext()) { - this.run(); - } - } - }; - xTask.run(); - } - if (ziter.hasNext()) { - this.run(); - } - } - }; - zTask.run(); - } - if (yiter.hasNext()) { - TaskManager.runTaskLater(this, 1); - } else { - regionTask.run(); - } - } - }; - yTask.run(); - } - }); - } - }); - } - @Override public boolean restoreTile(LocalBlockQueue queue, CompoundTag ct, int x, int y, int z) { return new StateWrapper(ct).restoreTag(queue.getWorld(), x, y, z); diff --git a/Core/src/main/java/com/plotsquared/core/PlotSquared.java b/Core/src/main/java/com/plotsquared/core/PlotSquared.java index 67c5cab90..4d632ff2f 100644 --- a/Core/src/main/java/com/plotsquared/core/PlotSquared.java +++ b/Core/src/main/java/com/plotsquared/core/PlotSquared.java @@ -267,7 +267,7 @@ public class PlotSquared { // create setup util class SetupUtils.manager = this.IMP.initSetupUtils(); // Set block - GlobalBlockQueue.IMP = new GlobalBlockQueue(IMP.initBlockQueue(), 1); + GlobalBlockQueue.IMP = new GlobalBlockQueue(IMP.initBlockQueue(), 1, Settings.QUEUE.TARGET_TIME); GlobalBlockQueue.IMP.runTask(); // Set chunk ChunkManager.manager = this.IMP.initChunkManager(); diff --git a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java index 85e493088..f295c465f 100644 --- a/Core/src/main/java/com/plotsquared/core/configuration/Settings.java +++ b/Core/src/main/java/com/plotsquared/core/configuration/Settings.java @@ -499,6 +499,13 @@ public class Settings extends Config { public static boolean TILE_ENTITY_CHECK = true; } + @Comment("Settings relating to PlotSquared's GlobalBlockQueue") + public static final class QUEUE { + @Comment({"Average time per tick spent completing chunk tasks in ms. Target average TPS = 20 * 50 / TARGET_TIME.", + "Waits (chunk task time / target_time) ticks before completely the next task."}) + public static int TARGET_TIME = 65; + } + @Comment({"Enable or disable parts of the plugin", "Note: A cache will use some memory if enabled"}) diff --git a/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java b/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java index 022f7037d..52557144e 100644 --- a/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java +++ b/Core/src/main/java/com/plotsquared/core/generator/ClassicPlotManager.java @@ -36,6 +36,7 @@ import com.plotsquared.core.queue.GlobalBlockQueue; import com.plotsquared.core.queue.LocalBlockQueue; import com.plotsquared.core.util.BlockUtil; import com.plotsquared.core.util.MathMan; +import com.plotsquared.core.util.RegionManager; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.world.block.BlockTypes; @@ -92,75 +93,38 @@ public class ClassicPlotManager extends SquarePlotManager { public boolean setFloor(PlotId plotId, Pattern blocks) { Plot plot = classicPlotWorld.getPlotAbs(plotId); - LocalBlockQueue queue = classicPlotWorld.getQueue(false); if (plot.isBasePlot()) { - for (CuboidRegion region : plot.getRegions()) { - Location pos1 = - new Location(classicPlotWorld.getWorldName(), region.getMinimumPoint().getX(), - classicPlotWorld.PLOT_HEIGHT, region.getMinimumPoint().getZ()); - Location pos2 = - new Location(classicPlotWorld.getWorldName(), region.getMaximumPoint().getX(), - classicPlotWorld.PLOT_HEIGHT, region.getMaximumPoint().getZ()); - queue.setCuboid(pos1, pos2, blocks); - } + return RegionManager.manager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, + classicPlotWorld.PLOT_HEIGHT, classicPlotWorld.PLOT_HEIGHT); } - return queue.enqueue(); + return false; } public boolean setAll(PlotId plotId, Pattern blocks) { Plot plot = classicPlotWorld.getPlotAbs(plotId); - if (!plot.isBasePlot()) { - return false; + if (plot.isBasePlot()) { + return RegionManager.manager + .setCuboids(classicPlotWorld, plot.getRegions(), blocks, 1, getWorldHeight()); } - LocalBlockQueue queue = classicPlotWorld.getQueue(false); - int maxY = getWorldHeight(); - for (CuboidRegion region : plot.getRegions()) { - Location pos1 = - new Location(classicPlotWorld.getWorldName(), region.getMinimumPoint().getX(), 1, - region.getMinimumPoint().getZ()); - Location pos2 = - new Location(classicPlotWorld.getWorldName(), region.getMaximumPoint().getX(), maxY, - region.getMaximumPoint().getZ()); - queue.setCuboid(pos1, pos2, blocks); - } - return queue.enqueue(); + return false; } public boolean setAir(PlotId plotId, Pattern blocks) { Plot plot = classicPlotWorld.getPlotAbs(plotId); - if (!plot.isBasePlot()) { - return false; + if (plot.isBasePlot()) { + return RegionManager.manager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, + classicPlotWorld.PLOT_HEIGHT + 1, getWorldHeight()); } - LocalBlockQueue queue = classicPlotWorld.getQueue(false); - int maxY = getWorldHeight(); - for (CuboidRegion region : plot.getRegions()) { - Location pos1 = - new Location(classicPlotWorld.getWorldName(), region.getMinimumPoint().getX(), - classicPlotWorld.PLOT_HEIGHT + 1, region.getMinimumPoint().getZ()); - Location pos2 = - new Location(classicPlotWorld.getWorldName(), region.getMaximumPoint().getX(), maxY, - region.getMaximumPoint().getZ()); - queue.setCuboid(pos1, pos2, blocks); - } - return queue.enqueue(); + return false; } public boolean setMain(PlotId plotId, Pattern blocks) { Plot plot = classicPlotWorld.getPlotAbs(plotId); - if (!plot.isBasePlot()) { - return false; + if (plot.isBasePlot()) { + return RegionManager.manager.setCuboids(classicPlotWorld, plot.getRegions(), blocks, 1, + classicPlotWorld.PLOT_HEIGHT - 1); } - LocalBlockQueue queue = classicPlotWorld.getQueue(false); - for (CuboidRegion region : plot.getRegions()) { - Location pos1 = - new Location(classicPlotWorld.getWorldName(), region.getMinimumPoint().getX(), 1, - region.getMinimumPoint().getZ()); - Location pos2 = - new Location(classicPlotWorld.getWorldName(), region.getMaximumPoint().getX(), - classicPlotWorld.PLOT_HEIGHT - 1, region.getMaximumPoint().getZ()); - queue.setCuboid(pos1, pos2, blocks); - } - return queue.enqueue(); + return false; } public boolean setMiddle(PlotId plotId, Pattern blocks) { diff --git a/Core/src/main/java/com/plotsquared/core/queue/GlobalBlockQueue.java b/Core/src/main/java/com/plotsquared/core/queue/GlobalBlockQueue.java index 68a059583..3b63a94c0 100644 --- a/Core/src/main/java/com/plotsquared/core/queue/GlobalBlockQueue.java +++ b/Core/src/main/java/com/plotsquared/core/queue/GlobalBlockQueue.java @@ -44,8 +44,8 @@ public class GlobalBlockQueue { private final ConcurrentLinkedDeque inactiveQueues; private final ConcurrentLinkedDeque runnables; private final AtomicBoolean running; + private final int targetTime; private QueueProvider provider; - /** * Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the * server @@ -53,6 +53,7 @@ public class GlobalBlockQueue { private long last; private long secondLast; private long lastSuccess; + private double lastPeriod = 0; private final RunnableVal2 SET_TASK = new RunnableVal2() { @Override public void run(Long free, LocalBlockQueue queue) { @@ -65,17 +66,19 @@ public class GlobalBlockQueue { } return; } - } while (((GlobalBlockQueue.this.secondLast = System.currentTimeMillis()) - - GlobalBlockQueue.this.last) < free); + } while ((lastPeriod = + ((GlobalBlockQueue.this.secondLast = System.currentTimeMillis()) + - GlobalBlockQueue.this.last)) < free); } }; - public GlobalBlockQueue(QueueProvider provider, int threads) { + public GlobalBlockQueue(QueueProvider provider, int threads, int targetTime) { this.provider = provider; this.activeQueues = new ConcurrentLinkedDeque<>(); this.inactiveQueues = new ConcurrentLinkedDeque<>(); this.runnables = new ConcurrentLinkedDeque<>(); this.running = new AtomicBoolean(); + this.targetTime = targetTime; this.PARALLEL_THREADS = threads; } @@ -112,9 +115,15 @@ public class GlobalBlockQueue { @Override public void run() { if (inactiveQueues.isEmpty() && activeQueues.isEmpty()) { lastSuccess = System.currentTimeMillis(); + lastPeriod = 0; GlobalBlockQueue.this.runEmptyTasks(); return; } + // Server laggy? Skip. + if (lastPeriod > targetTime) { + lastPeriod -= targetTime; + return; + } SET_TASK.value1 = 50 + Math.min( (50 + GlobalBlockQueue.this.last) - (GlobalBlockQueue.this.last = System.currentTimeMillis()), diff --git a/Core/src/main/java/com/plotsquared/core/util/RegionManager.java b/Core/src/main/java/com/plotsquared/core/util/RegionManager.java index 5ab1a8350..ab3333a19 100644 --- a/Core/src/main/java/com/plotsquared/core/util/RegionManager.java +++ b/Core/src/main/java/com/plotsquared/core/util/RegionManager.java @@ -28,8 +28,11 @@ package com.plotsquared.core.util; import com.plotsquared.core.PlotSquared; import com.plotsquared.core.location.Location; import com.plotsquared.core.plot.Plot; +import com.plotsquared.core.plot.PlotArea; +import com.plotsquared.core.queue.LocalBlockQueue; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.world.biome.BiomeType; @@ -147,6 +150,19 @@ public abstract class RegionManager { }); } + public boolean setCuboids(final PlotArea area, final Set regions, + final Pattern blocks, int minY, int maxY) { + LocalBlockQueue queue = area.getQueue(false); + for (CuboidRegion region : regions) { + Location pos1 = new Location(area.getWorldName(), region.getMinimumPoint().getX(), minY, + region.getMinimumPoint().getZ()); + Location pos2 = new Location(area.getWorldName(), region.getMaximumPoint().getX(), maxY, + region.getMaximumPoint().getZ()); + queue.setCuboid(pos1, pos2, blocks); + } + return queue.enqueue(); + } + /** * Copy a region to a new location (in the same world) */ 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 78cc7192c..547741e6f 100644 --- a/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java +++ b/Core/src/main/java/com/plotsquared/core/util/SchematicHandler.java @@ -35,9 +35,18 @@ import com.plotsquared.core.plot.schematic.Schematic; import com.plotsquared.core.queue.LocalBlockQueue; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; +import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; @@ -54,6 +63,7 @@ import org.json.JSONArray; import org.json.JSONException; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -66,15 +76,19 @@ import java.net.URL; import java.net.URLConnection; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -104,10 +118,6 @@ public abstract class SchematicHandler { final Plot plot = i.next(); i.remove(); - PlotSquared.get().getImpromptuUUIDPipeline().getSingle(plot.getOwnerAbs(), (username, throwable) -> { - - }); - final String owner; if (plot.hasOwner()) { owner = plot.getOwnerAbs().toString(); @@ -126,12 +136,14 @@ public abstract class SchematicHandler { .replaceAll("%idy%", plot.getId().y + "") .replaceAll("%world%", plot.getArea().toString()); } + final String directory; if (outputDir == null) { directory = Settings.Paths.SCHEMATICS; } else { directory = outputDir.getAbsolutePath(); } + final Runnable THIS = this; SchematicHandler.manager.getCompoundTag(plot, new RunnableVal() { @Override public void run(final CompoundTag value) { @@ -471,8 +483,190 @@ public abstract class SchematicHandler { return true; } - public abstract void getCompoundTag(String world, Set regions, - RunnableVal whenDone); + public void getCompoundTag(final String world, final Set regions, + final RunnableVal whenDone) { + // async + TaskManager.runTaskAsync(() -> { + // Main positions + Location[] corners = MainUtil.getCorners(world, regions); + final Location bot = corners[0]; + final Location top = corners[1]; + + CuboidRegion cuboidRegion = + new CuboidRegion(WorldUtil.IMP.getWeWorld(world), bot.getBlockVector3(), + top.getBlockVector3()); + + final int width = cuboidRegion.getWidth(); + int height = cuboidRegion.getHeight(); + final int length = cuboidRegion.getLength(); + Map schematic = new HashMap<>(); + schematic.put("Version", new IntTag(2)); + schematic.put("DataVersion", new IntTag(WorldEdit.getInstance().getPlatformManager() + .queryCapability(Capability.WORLD_EDITING).getDataVersion())); + + Map metadata = new HashMap<>(); + metadata.put("WEOffsetX", new IntTag(0)); + metadata.put("WEOffsetY", new IntTag(0)); + metadata.put("WEOffsetZ", new IntTag(0)); + + schematic.put("Metadata", new CompoundTag(metadata)); + + schematic.put("Width", new ShortTag((short) width)); + schematic.put("Height", new ShortTag((short) height)); + schematic.put("Length", new ShortTag((short) length)); + + // The Sponge format Offset refers to the 'min' points location in the world. That's our 'Origin' + schematic.put("Offset", new IntArrayTag(new int[] {0, 0, 0,})); + + Map palette = new HashMap<>(); + Map biomePalette = new HashMap<>(); + + List tileEntities = new ArrayList<>(); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length); + ByteArrayOutputStream biomeBuffer = new ByteArrayOutputStream(width * length); + // Queue + final ArrayDeque queue = new ArrayDeque<>(regions); + TaskManager.runTask(new Runnable() { + @Override public void run() { + if (queue.isEmpty()) { + TaskManager.runTaskAsync(() -> { + schematic.put("PaletteMax", new IntTag(palette.size())); + + Map paletteTag = new HashMap<>(); + palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); + + schematic.put("Palette", new CompoundTag(paletteTag)); + schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray())); + schematic + .put("TileEntities", new ListTag(CompoundTag.class, tileEntities)); + + schematic.put("BiomePaletteMax", new IntTag(biomePalette.size())); + + Map biomePaletteTag = new HashMap<>(); + biomePalette.forEach( + (key, value) -> biomePaletteTag.put(key, new IntTag(value))); + + schematic.put("BiomePalette", new CompoundTag(biomePaletteTag)); + schematic.put("BiomeData", new ByteArrayTag(biomeBuffer.toByteArray())); + whenDone.value = new CompoundTag(schematic); + TaskManager.runTask(whenDone); + }); + return; + } + final Runnable regionTask = this; + CuboidRegion region = queue.poll(); + Location pos1 = new Location(world, region.getMinimumPoint().getX(), + region.getMinimumPoint().getY(), region.getMinimumPoint().getZ()); + Location pos2 = new Location(world, region.getMaximumPoint().getX(), + region.getMaximumPoint().getY(), region.getMaximumPoint().getZ()); + final int p1x = pos1.getX(); + final int sy = pos1.getY(); + final int p1z = pos1.getZ(); + final int p2x = pos2.getX(); + final int p2z = pos2.getZ(); + final int ey = pos2.getY(); + Iterator yiter = IntStream.range(sy, ey + 1).iterator(); + final Runnable yTask = () -> { + long ystart = System.currentTimeMillis(); + while (yiter.hasNext() && System.currentTimeMillis() - ystart < 20) { + final int y = yiter.next(); + Iterator ziter = IntStream.range(p1z, p2z + 1).iterator(); + final Runnable zTask = () -> { + long zstart = System.currentTimeMillis(); + while (ziter.hasNext() + && System.currentTimeMillis() - zstart < 20) { + final int z = ziter.next(); + Iterator xiter = + IntStream.range(p1x, p2x + 1).iterator(); + final Runnable xTask = () -> { + long xstart = System.currentTimeMillis(); + final int ry = y - sy; + final int rz = z - p1z; + while (xiter.hasNext() + && System.currentTimeMillis() - xstart < 20) { + final int x = xiter.next(); + final int rx = x - p1x; + BlockVector3 point = BlockVector3.at(x, y, z); + BaseBlock block = + cuboidRegion.getWorld().getFullBlock(point); + if (block.getNbtData() != null) { + Map values = new HashMap<>(); + for (Map.Entry entry : block + .getNbtData().getValue().entrySet()) { + values.put(entry.getKey(), entry.getValue()); + } + // Remove 'id' if it exists. We want 'Id' + values.remove("id"); + + // Positions are kept in NBT, we don't want that. + values.remove("x"); + values.remove("y"); + values.remove("z"); + + values.put("Id", new StringTag(block.getNbtId())); + values.put("Pos", + new IntArrayTag(new int[] {rx, ry, rz})); + + tileEntities.add(new CompoundTag(values)); + } + String blockKey = + block.toImmutableState().getAsString(); + int blockId; + if (palette.containsKey(blockKey)) { + blockId = palette.get(blockKey); + } else { + blockId = palette.size(); + palette.put(blockKey, palette.size()); + } + + while ((blockId & -128) != 0) { + buffer.write(blockId & 127 | 128); + blockId >>>= 7; + } + buffer.write(blockId); + + if (ry > 0) { + continue; + } + BlockVector2 pt = BlockVector2.at(x, z); + BiomeType biome = cuboidRegion.getWorld().getBiome(pt); + String biomeStr = biome.getId(); + int biomeId; + if (biomePalette.containsKey(biomeStr)) { + biomeId = biomePalette.get(biomeStr); + } else { + biomeId = biomePalette.size(); + biomePalette.put(biomeStr, biomeId); + } + while ((biomeId & -128) != 0) { + biomeBuffer.write(biomeId & 127 | 128); + biomeId >>>= 7; + } + biomeBuffer.write(biomeId); + } + if (xiter.hasNext()) { + this.run(); + } + }; + xTask.run(); + } + if (ziter.hasNext()) { + this.run(); + } + }; + zTask.run(); + } + if (yiter.hasNext()) { + TaskManager.runTaskLater(this, 1); + } else { + regionTask.run(); + } + }; + yTask.run(); + } + }); + }); + } public void getCompoundTag(final Plot plot, final RunnableVal whenDone) { getCompoundTag(plot.getWorldName(), plot.getRegions(), new RunnableVal() { @@ -502,7 +696,6 @@ public abstract class SchematicHandler { public UnsupportedFormatException(String message, Throwable cause) { super(message, cause); } - } }