From 22a7ad750316ab5c2d8b91650577f4b11719e9f6 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Thu, 18 Jul 2019 22:36:50 +1000 Subject: [PATCH] optimized biome streaming to schematics --- .../java/com/boydti/fawe/util/IOUtil.java | 8 ++ .../java/com/sk89q/worldedit/EditSession.java | 3 +- .../clipboard/io/SpongeSchematicWriter.java | 95 +++++++++++-------- .../worldedit/world/biome/BiomeTypes.java | 10 ++ 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java index d1427079a..a02be0890 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/IOUtil.java @@ -37,6 +37,14 @@ public final class IOUtil { return i; } + public static void writeVarInt(OutputStream out, int value) throws IOException { + while ((value & -128) != 0) { + out.write(value & 127 | 128); + value >>>= 7; + } + out.write(value); + } + public static void copy(InputStream in, OutputStream out) throws IOException { byte[] buf = new byte[8192]; while (true) { 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 60dfd22b9..69d6965a5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -127,6 +127,7 @@ import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.eventbus.EventBus; +import com.sk89q.worldedit.world.SimpleWorld; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; @@ -166,7 +167,7 @@ import static com.google.common.base.Preconditions.checkNotNull; * using the {@link ChangeSetExtent}.

*/ @SuppressWarnings({"FieldCanBeLocal"}) -public class EditSession extends AbstractDelegateExtent SimpleWorld, AutoCloseable { +public class EditSession extends AbstractDelegateExtent implements SimpleWorld, AutoCloseable { private static final Logger log = LoggerFactory.getLogger(EditSession.class); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java index 72313a09b..0a0240e4e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicWriter.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extent.clipboard.io; +import com.boydti.fawe.jnbt.NBTStreamer; import com.boydti.fawe.object.clipboard.FaweClipboard; import com.boydti.fawe.util.IOUtil; @@ -37,6 +38,7 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -51,11 +53,13 @@ import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Writes schematic files using the Sponge schematic format. @@ -175,11 +179,7 @@ public class SpongeSchematicWriter implements ClipboardWriter { palette[ordinal] = value = (char) size; paletteList.add(ordinal); } - while ((value & -128) != 0) { - blocksOut.write(value & 127 | 128); - value >>>= 7; - } - blocksOut.write(value); + IOUtil.writeVarInt(blocksOut, value); } catch (IOException e) { throw new RuntimeException(e); } @@ -231,7 +231,6 @@ public class SpongeSchematicWriter implements ClipboardWriter { writeBiomes(clipboard, out); } - TODO optimize List entities = new ArrayList<>(); for (Entity entity : clipboard.getEntities()) { BaseEntity state = entity.getState(); @@ -263,49 +262,63 @@ public class SpongeSchematicWriter implements ClipboardWriter { }); } - private void writeBiomes(Clipboard clipboard, NBTOutputStream schematic) throws IOException { - TODO optimize - BlockVector3 min = clipboard.getMinimumPoint(); - int width = clipboard.getRegion().getWidth(); - int length = clipboard.getRegion().getLength(); + private void writeBiomes(Clipboard clipboard, NBTOutputStream out) throws IOException { + ByteArrayOutputStream biomesCompressed = new ByteArrayOutputStream(); + DataOutputStream biomesOut = new DataOutputStream(new LZ4BlockOutputStream(biomesCompressed)); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * length); - - int paletteMax = 0; - Map palette = new HashMap<>(); - - for (int z = 0; z < length; z++) { - int z0 = min.getBlockZ() + z; - for (int x = 0; x < width; x++) { - int x0 = min.getBlockX() + x; - BlockVector2 pt = BlockVector2.at(x0, z0); - BiomeType biome = clipboard.getBiome(pt); - - String biomeKey = biome.getId(); - int biomeId; - if (palette.containsKey(biomeKey)) { - biomeId = palette.get(biomeKey); - } else { - biomeId = paletteMax; - palette.put(biomeKey, biomeId); - paletteMax++; + List paletteList = new ArrayList<>(); + int[] palette = new int[BiomeTypes.getMaxId() + 1]; + Arrays.fill(palette, Integer.MAX_VALUE); + int[] paletteMax = {0}; + NBTStreamer.ByteReader task = new NBTStreamer.ByteReader() { + @Override + public void run(int index, int ordinal) { + try { + int value = palette[ordinal]; + if (value == Integer.MAX_VALUE) { + int size = paletteMax[0]++; + palette[ordinal] = value = size; + paletteList.add(ordinal); + } + IOUtil.writeVarInt(biomesOut, value); + } catch (IOException e) { + e.printStackTrace(); } - - while ((biomeId & -128) != 0) { - buffer.write(biomeId & 127 | 128); - biomeId >>>= 7; + } + }; + if (clipboard instanceof BlockArrayClipboard) { + ((BlockArrayClipboard) clipboard).IMP.streamBiomes(task); + } else { + BlockVector3 min = clipboard.getMinimumPoint(); + int width = clipboard.getRegion().getWidth(); + int length = clipboard.getRegion().getLength(); + for (int z = 0, i = 0; z < length; z++) { + int z0 = min.getBlockZ() + z; + for (int x = 0; x < width; x++, i++) { + int x0 = min.getBlockX() + x; + BlockVector2 pt = BlockVector2.at(x0, z0); + BiomeType biome = clipboard.getBiome(pt); + task.run(i, biome.getInternalId()); } - buffer.write(biomeId); } } + biomesOut.close(); - schematic.writeNamedTag("BiomePaletteMax", new IntTag(paletteMax)); + out.writeNamedTag("BiomePaletteMax", paletteMax[0]); - Map paletteTag = new HashMap<>(); - palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); + out.writeLazyCompoundTag("BiomePalette", out12 -> { + for (int i = 0; i < paletteList.size(); i++) { + int ordinal = paletteList.get(i); + BiomeType state = BiomeTypes.get(ordinal); + out12.writeNamedTag(state.getId(), i); + } + }); - schematic.writeNamedTag("BiomePalette", new CompoundTag(paletteTag)); - schematic.writeNamedTag("BiomeData", new ByteArrayTag(buffer.toByteArray())); + out.writeNamedTagName("BiomeData", NBTConstants.TYPE_BYTE_ARRAY); + out.writeInt(biomesOut.size()); + try (LZ4BlockInputStream in = new LZ4BlockInputStream(new ByteArrayInputStream(biomesCompressed.toByteArray()))) { + IOUtil.copy(in, (DataOutput) out); + } } private void writeEntities(Clipboard clipboard, NBTOutputStream schematic) throws IOException { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java index cd82c891e..61a288049 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java @@ -135,6 +135,16 @@ public final class BiomeTypes { return BiomeType.REGISTRY.values(); } + public static int getMaxId() { + int maxBiomeId = 0; + for (BiomeType type : BiomeType.REGISTRY.values()) { + if (type.getInternalId() > maxBiomeId) { + maxBiomeId = type.getInternalId(); + } + } + return maxBiomeId; + } + static { OCEAN.setLegacyId(0); PLAINS.setLegacyId(1);