From b1e791a81aaec4a41b4a635da34afde82b158a18 Mon Sep 17 00:00:00 2001 From: Pierre Maurice Schwang Date: Wed, 26 Jun 2024 22:59:44 +0200 Subject: [PATCH] chore: update schematic and clipboard logic for linbus changes --- .../clipboard/io/BuiltInClipboardFormat.java | 205 ++++++++---------- .../extent/clipboard/io/ClipboardFormat.java | 25 ++- .../clipboard/io/NBTSchematicReader.java | 4 + .../clipboard/io/sponge/ReaderUtil.java | 186 ++++++++-------- .../io/sponge/SpongeSchematicV1Reader.java | 107 ++++----- .../io/sponge/SpongeSchematicV2Reader.java | 122 ++++------- .../io/sponge/SpongeSchematicV2Writer.java | 157 ++++++-------- .../io/sponge/SpongeSchematicV3Reader.java | 157 +++++--------- .../io/sponge/SpongeSchematicV3Writer.java | 158 ++++++-------- .../clipboard/io/sponge/WriterUtil.java | 104 ++++----- 10 files changed, 544 insertions(+), 681 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java index 3b2aad499..8a57411d6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/BuiltInClipboardFormat.java @@ -27,13 +27,10 @@ import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStruct import com.fastasyncworldedit.core.extent.clipboard.io.schematic.PNGWriter; import com.fastasyncworldedit.core.internal.io.ResettableFileInputStream; import com.google.common.collect.ImmutableSet; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NamedTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV1Reader; import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader; import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Writer; @@ -41,18 +38,19 @@ import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader; import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Writer; import it.unimi.dsi.fastutil.io.FastBufferedInputStream; import org.anarres.parallelgzip.ParallelGZIPOutputStream; +import org.enginehub.linbus.stream.LinBinaryIO; +import org.enginehub.linbus.tree.LinRootEntry; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.file.Files; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -65,7 +63,6 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { //FAWE start - register fast clipboard io FAST_V3("fast", "fawe", "schem") { - @Override public ClipboardReader getReader(InputStream inputStream) throws IOException { return new FastSchematicReaderV3(inputStream); @@ -85,9 +82,8 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { } @Override - public boolean isFormat(final File file) { - try (final DataInputStream stream = new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(Files.newInputStream( - file.toPath())))); + public boolean isFormat(final InputStream inputStream) { + try (final DataInputStream stream = new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(inputStream))); final NBTInputStream nbt = new NBTInputStream(stream)) { if (stream.readByte() != NBTConstants.TYPE_COMPOUND) { return false; @@ -151,31 +147,8 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { } @Override - public boolean isFormat(File file) { - try (final DataInputStream stream = - new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(Files.newInputStream(file.toPath())))); - final NBTInputStream nbt = new NBTInputStream(stream)) { - if (stream.readByte() != NBTConstants.TYPE_COMPOUND) { - return false; - } - stream.skipNBytes(2); // TAG name length ("Schematic" = 9) - stream.skipNBytes(9); // "Schematic" - - // We can't guarantee the specific order of nbt data, so scan and skip, if required - do { - byte type = stream.readByte(); - String name = stream.readUTF(); - if (type == NBTConstants.TYPE_END) { - return false; - } - if (type == NBTConstants.TYPE_INT && name.equals("Version")) { - return stream.readInt() == FastSchematicWriterV2.CURRENT_VERSION; - } - nbt.readTagPayloadLazy(type, 0); - } while (true); - } catch (IOException ignored) { - } - return false; + public boolean isFormat(InputStream inputStream) { + return detectOldSpongeSchematic(inputStream, FastSchematicWriterV2.CURRENT_VERSION); } }, @@ -205,9 +178,18 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { } @Override - public boolean isFormat(File file) { - String name = file.getName().toLowerCase(Locale.ROOT); - return name.endsWith(".schematic") || name.endsWith(".mcedit") || name.endsWith(".mce"); + public boolean isFormat(InputStream inputStream) { + LinRootEntry rootEntry; + try { + DataInputStream stream = new DataInputStream(new GZIPInputStream(inputStream)); + rootEntry = LinBinaryIO.readUsing(stream, LinRootEntry::readFrom); + } catch (Exception e) { + return false; + } + if (!rootEntry.name().equals("Schematic")) { + return false; + } + return rootEntry.value().value().containsKey("Materials"); } }, SPONGE_V1_SCHEMATIC("sponge.1") { @@ -218,8 +200,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { @Override public ClipboardReader getReader(InputStream inputStream) throws IOException { - NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream)); - return new SpongeSchematicV1Reader(nbtStream); + return new SpongeSchematicV1Reader(LinBinaryIO.read(new DataInputStream(new GZIPInputStream(inputStream)))); } @Override @@ -228,25 +209,8 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { } @Override - public boolean isFormat(File file) { - try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) { - NamedTag rootTag = str.readNamedTag(); - if (!rootTag.getName().equals("Schematic")) { - return false; - } - CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); - - // Check - Map schematic = schematicTag.getValue(); - Tag versionTag = schematic.get("Version"); - if (!(versionTag instanceof IntTag) || ((IntTag) versionTag).getValue() != 1) { - return false; - } - } catch (Exception e) { - return false; - } - - return true; + public boolean isFormat(InputStream inputStream) { + return detectOldSpongeSchematic(inputStream, 1); } }, @@ -256,7 +220,8 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { * Avoid using with any large schematics/clipboards for reading/writing. */ @Deprecated - SPONGE_V2_SCHEMATIC("slow.2", "safe.2", "sponge.2") { + SPONGE_V2_SCHEMATIC("slow.2", "safe.2", "sponge.2") { // FAWE - edit aliases for fast + @Override public String getPrimaryFileExtension() { return "schem"; @@ -264,38 +229,21 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { @Override public ClipboardReader getReader(InputStream inputStream) throws IOException { - NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream)); - return new SpongeSchematicV2Reader(nbtStream); + return new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(new GZIPInputStream(inputStream)))); } @Override public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { - NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream)); - return new SpongeSchematicV2Writer(nbtStream); + return new SpongeSchematicV2Writer(new DataOutputStream(new GZIPOutputStream(outputStream))); } @Override - public boolean isFormat(File file) { - try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) { - NamedTag rootTag = str.readNamedTag(); - if (!rootTag.getName().equals("Schematic")) { - return false; - } - CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); - - // Check - Map> schematic = schematicTag.getValue(); - if (!schematic.containsKey("Version")) { - return false; - } - } catch (Exception e) { - return false; - } - - return true; + public boolean isFormat(InputStream inputStream) { + return detectOldSpongeSchematic(inputStream, 2); } }, - SPONGE_V3_SCHEMATIC("sponge.3", "slow", "safe") { // FAWE edit aliases for fast + SPONGE_V3_SCHEMATIC("sponge.3", "slow", "safe") { // FAWE - edit aliases for fast + @Override public String getPrimaryFileExtension() { return "schem"; @@ -303,41 +251,19 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { @Override public ClipboardReader getReader(InputStream inputStream) throws IOException { - NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream)); - return new SpongeSchematicV3Reader(nbtStream); + return new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(new GZIPInputStream(inputStream)))); } @Override public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { - NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream)); - return new SpongeSchematicV3Writer(nbtStream); + return new SpongeSchematicV3Writer(new DataOutputStream(new GZIPOutputStream(outputStream))); } @Override public boolean isFormat(File file) { - try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) { - NamedTag rootTag = str.readNamedTag(); - CompoundTag rootCompoundTag = (CompoundTag) rootTag.getTag(); - if (!rootCompoundTag.containsKey("Schematic")) { - return false; - } - Tag schematicTag = rootCompoundTag.getValue() - .get("Schematic"); - if (!(schematicTag instanceof CompoundTag)) { - return false; - } - - // Check - Map schematic = ((CompoundTag) schematicTag).getValue(); - Tag versionTag = schematic.get("Version"); - if (!(versionTag instanceof IntTag) || ((IntTag) versionTag).getValue() != 3) { - return false; - } - } catch (Exception e) { - return false; - } - - return true; + //FAWE start - delegate to stream-based isFormat approach of fast impl + return FAST_V3.isFormat(file); + //FAWE end } }, //FAWE start - recover schematics with bad entity data & register other clipboard formats @@ -407,9 +333,37 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { } @Override - public boolean isFormat(File file) { - String name = file.getName().toLowerCase(Locale.ROOT); - return name.endsWith(".nbt"); + public boolean isFormat(InputStream inputStream) { + try (final DataInputStream stream = new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(inputStream))); + final NBTInputStream nbt = new NBTInputStream(stream)) { + if (stream.readByte() != NBTConstants.TYPE_COMPOUND) { + return false; + } + NamedTag namedTag = nbt.readNamedTag(); + if (!namedTag.getName().isEmpty()) { + return false; + } + + // We can't guarantee the specific order of nbt data, so scan and skip, if required + do { + byte type = stream.readByte(); + String name = stream.readUTF(); + if (type == NBTConstants.TYPE_END) { + return false; + } + if (type == NBTConstants.TYPE_LIST && name.equals("size")) { + return true; + } + nbt.readTagPayloadLazy(type, 0); + } while (true); + } catch (IOException ignored) { + } + return false; + } + + @Override + public boolean isFormat(final File file) { + return file.getName().toLowerCase(Locale.ROOT).endsWith(".nbt") && super.isFormat(file); } }, @@ -440,6 +394,33 @@ public enum BuiltInClipboardFormat implements ClipboardFormat { }; //FAWE end + private static boolean detectOldSpongeSchematic(InputStream inputStream, int version) { + //FAWE start - dont utilize linbus - WorldEdit approach is not really streamed + try (final DataInputStream stream = new DataInputStream(new FastBufferedInputStream(new GZIPInputStream(inputStream))); + final NBTInputStream nbt = new NBTInputStream(stream)) { + if (stream.readByte() != NBTConstants.TYPE_COMPOUND) { + return false; + } + stream.skipNBytes(2); // TAG name length ("Schematic" = 9) + stream.skipNBytes(9); // "Schematic" + + // We can't guarantee the specific order of nbt data, so scan and skip, if required + do { + byte type = stream.readByte(); + String name = stream.readUTF(); + if (type == NBTConstants.TYPE_END) { + return false; + } + if (type == NBTConstants.TYPE_INT && name.equals("Version")) { + return stream.readInt() == version; + } + nbt.readTagPayloadLazy(type, 0); + } while (true); + } catch (IOException ignored) { + } + return false; + } + /** * For backwards compatibility, this points to the Sponge Schematic Specification (Version 2) * format. This should not be used going forwards. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java index cb5cd1b7a..7b4b1bf3b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/ClipboardFormat.java @@ -35,6 +35,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URL; +import java.nio.file.Files; import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; @@ -82,7 +83,29 @@ public interface ClipboardFormat { * @param file the file * @return true if the given file is of this format */ - boolean isFormat(File file); + default boolean isFormat(File file) { + try (InputStream stream = Files.newInputStream(file.toPath())) { + return isFormat(stream); + } catch (IOException e) { + return false; + } + } + + /** + * Return whether the given stream is of this format. + * + * @apiNote The caller is responsible for the following: + *
    + *
  • Closing the input stream
  • + *
+ * + * @param inputStream The stream + * @return true if the given stream is of this format + * @since TODO + */ + default boolean isFormat(InputStream inputStream) { + return false; + } /** * Get the file extension this format primarily uses. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java index a6b9c1400..017f07e9c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/NBTSchematicReader.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extent.clipboard.io; import com.sk89q.jnbt.Tag; +import org.enginehub.linbus.tree.LinCompoundTag; import javax.annotation.Nullable; import java.io.IOException; @@ -27,7 +28,10 @@ import java.util.Map; /** * Base class for NBT schematic readers. + * + * @deprecated These utility methods are provided by {@link LinCompoundTag} now. */ +@Deprecated public abstract class NBTSchematicReader implements ClipboardReader { protected static > T requireTag(Map> items, String key, Class expected) throws IOException { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/ReaderUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/ReaderUtil.java index 09706e017..5c4e90264 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/ReaderUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/ReaderUtil.java @@ -19,14 +19,7 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -import com.sk89q.jnbt.AdventureNBTConverter; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.CompoundTagBuilder; -import com.sk89q.jnbt.IntArrayTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; + import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; @@ -39,25 +32,31 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.VarIntIterator; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.concurrency.LazyReference; import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.storage.NBTConversions; +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinIntArrayTag; +import org.enginehub.linbus.tree.LinIntTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinTagType; import java.io.IOException; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkState; -import static com.sk89q.worldedit.extent.clipboard.io.SchematicNbtUtil.getTag; -import static com.sk89q.worldedit.extent.clipboard.io.SchematicNbtUtil.requireTag; /** * Common code shared between schematic readers. @@ -66,9 +65,8 @@ public class ReaderUtil { //FAWE - make public private static final Logger LOGGER = LogManagerCompat.getLogger(); - static void checkSchematicVersion(int version, CompoundTag schematicTag) throws IOException { - int schematicVersion = requireTag(schematicTag.getValue(), "Version", IntTag.class) - .getValue(); + static void checkSchematicVersion(int version, LinCompoundTag schematicTag) throws IOException { + int schematicVersion = getSchematicVersion(schematicTag); checkState( version == schematicVersion, @@ -76,12 +74,19 @@ public class ReaderUtil { //FAWE - make public ); } + public static int getSchematicVersion(LinCompoundTag schematicTag) throws IOException { + return schematicTag.getTag("Version", LinTagType.intTag()).valueAsInt(); + } + static VersionedDataFixer getVersionedDataFixer( - Map schematic, Platform platform, + LinCompoundTag schematic, Platform platform, int liveDataVersion - ) throws IOException { - //FAWE - delegate to new method - return getVersionedDataFixer(requireTag(schematic, "DataVersion", IntTag.class).getValue(), platform, liveDataVersion); + ) { + //FAWE start - call fawe method without NBT component requirement + return getVersionedDataFixer(schematic.getTag("DataVersion", LinTagType.intTag()).valueAsInt(), platform, + liveDataVersion + ); + //FAWE end } //FAWE start - make getVersionedDataFixer without schematic compound + public @@ -119,7 +124,7 @@ public class ReaderUtil { //FAWE - make public //FAWE end static Map decodePalette( - Map paletteObject, VersionedDataFixer fixer + LinCompoundTag paletteObject, VersionedDataFixer fixer ) throws IOException { Map palette = new HashMap<>(); @@ -128,12 +133,15 @@ public class ReaderUtil { //FAWE - make public parserContext.setTryLegacy(false); parserContext.setPreferringWildcard(false); - for (String palettePart : paletteObject.keySet()) { - int id = requireTag(paletteObject, palettePart, IntTag.class).getValue(); - palettePart = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart); + for (var palettePart : paletteObject.value().entrySet()) { + if (!(palettePart.getValue() instanceof LinIntTag idTag)) { + throw new IOException("Invalid palette entry: " + palettePart); + } + int id = idTag.valueAsInt(); + String paletteName = fixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, palettePart.getKey()); BlockState state; try { - state = WorldEdit.getInstance().getBlockFactory().parseFromInput(palettePart, parserContext).toImmutableState(); + state = WorldEdit.getInstance().getBlockFactory().parseFromInput(paletteName, parserContext).toImmutableState(); } catch (InputParseException e) { LOGGER.warn("Invalid BlockState in palette: " + palettePart + ". Block will be replaced with air."); state = BlockTypes.AIR.getDefaultState(); @@ -144,43 +152,24 @@ public class ReaderUtil { //FAWE - make public } static void initializeClipboardFromBlocks( - Clipboard clipboard, Map palette, byte[] blocks, ListTag tileEntities, + Clipboard clipboard, Map palette, byte[] blocks, LinListTag tileEntities, VersionedDataFixer fixer, boolean dataIsNested ) throws IOException { - Map> tileEntitiesMap = new HashMap<>(); + Map tileEntitiesMap = new HashMap<>(); if (tileEntities != null) { - List> tileEntityTags = tileEntities.getValue().stream() - .map(tag -> (CompoundTag) tag) - .map(CompoundTag::getValue) - .collect(Collectors.toList()); - - for (Map tileEntity : tileEntityTags) { - int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue(); - final BlockVector3 pt = clipboard.getMinimumPoint().add(pos[0], pos[1], pos[2]); - Map values; - if (dataIsNested) { - CompoundTag dataTag = getTag(tileEntity, "Data", CompoundTag.class); - if (dataTag != null) { - values = new LinkedHashMap<>(dataTag.getValue()); - } else { - values = new LinkedHashMap<>(); - } - } else { - values = new LinkedHashMap<>(tileEntity); - values.remove("Id"); - values.remove("Pos"); - } - values.put("x", new IntTag(pt.getBlockX())); - values.put("y", new IntTag(pt.getBlockY())); - values.put("z", new IntTag(pt.getBlockZ())); - values.put("id", tileEntity.get("Id")); + for (LinCompoundTag tileEntity : tileEntities.value()) { + final BlockVector3 pt = clipboard.getMinimumPoint().add( + decodeBlockVector3(tileEntity.getTag("Pos", LinTagType.intArrayTag())) + ); + LinCompoundTag.Builder values = extractData(dataIsNested, tileEntity); + values.putInt("x", pt.x()); + values.putInt("y", pt.y()); + values.putInt("z", pt.z()); + values.put("id", tileEntity.value().get("Id")); if (fixer.isActive()) { - tileEntity = ((CompoundTag) AdventureNBTConverter.fromAdventure(fixer.fixUp( - DataFixer.FixTypes.BLOCK_ENTITY, - new CompoundTag(values).asBinaryTag() - ))).getValue(); + tileEntity = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, values.build()); } else { - tileEntity = values; + tileEntity = values.build(); } tileEntitiesMap.put(pt, tileEntity); } @@ -196,20 +185,26 @@ public class ReaderUtil { //FAWE - make public BlockVector3 rawPos = decodePositionFromDataIndex(width, length, index); try { BlockVector3 offsetPos = clipboard.getMinimumPoint().add(rawPos); - Map tileEntity = tileEntitiesMap.get(offsetPos); - if (tileEntity != null) { - clipboard.setBlock( - offsetPos, state.toBaseBlock(new CompoundTag(tileEntity)) - ); - } else { - clipboard.setBlock(offsetPos, state); - } + LinCompoundTag tileEntity = tileEntitiesMap.get(offsetPos); + clipboard.setBlock(offsetPos, state.toBaseBlock(tileEntity)); } catch (WorldEditException e) { throw new IOException("Failed to load a block in the schematic", e); } } } + private static LinCompoundTag.Builder extractData(boolean dataIsNested, LinCompoundTag tag) { + if (dataIsNested) { + LinCompoundTag dataTag = tag.findTag("Data", LinTagType.compoundTag()); + return dataTag != null ? dataTag.toBuilder() : LinCompoundTag.builder(); + } else { + LinCompoundTag.Builder values = tag.toBuilder(); + values.remove("Id"); + values.remove("Pos"); + return values; + } + } + static BlockVector3 decodePositionFromDataIndex(int width, int length, int index) { // index = (y * width * length) + (z * width) + x int y = index / (width * length); @@ -219,11 +214,11 @@ public class ReaderUtil { //FAWE - make public return BlockVector3.at(x, y, z); } - static BlockVector3 decodeBlockVector3(@Nullable IntArrayTag tag) throws IOException { + static BlockVector3 decodeBlockVector3(@Nullable LinIntArrayTag tag) throws IOException { if (tag == null) { return BlockVector3.ZERO; } - int[] parts = tag.getValue(); + int[] parts = tag.value(); if (parts.length != 3) { throw new IOException("Invalid block vector specified in schematic."); } @@ -231,46 +226,26 @@ public class ReaderUtil { //FAWE - make public } static void readEntities( - BlockArrayClipboard clipboard, List entList, + BlockArrayClipboard clipboard, List entList, VersionedDataFixer fixer, boolean positionIsRelative - ) throws IOException { + ) { if (entList.isEmpty()) { return; } - for (Tag et : entList) { - if (!(et instanceof CompoundTag)) { - continue; - } - CompoundTag entityTag = (CompoundTag) et; - Map tags = entityTag.getValue(); - String id = requireTag(tags, "Id", StringTag.class).getValue(); - CompoundTagBuilder dataTagBuilder = CompoundTagBuilder.create(); - if (positionIsRelative) { - // then we're in version 3 - CompoundTag subTag = getTag(entityTag.getValue(), "Data", CompoundTag.class); - if (subTag != null) { - dataTagBuilder.putAll(subTag.getValue()); - } - } else { - // version 2 - dataTagBuilder.putAll(tags); - dataTagBuilder.remove("Id"); - dataTagBuilder.remove("Pos"); - } - CompoundTag dataTag = dataTagBuilder.putString("id", id).build(); - dataTag = ((CompoundTag) AdventureNBTConverter.fromAdventure(fixer.fixUp( - DataFixer.FixTypes.ENTITY, - dataTag.asBinaryTag() - ))); + for (LinCompoundTag entityTag : entList) { + String id = entityTag.getTag("Id", LinTagType.stringTag()).value(); + LinCompoundTag.Builder values = extractData(positionIsRelative, entityTag); + LinCompoundTag dataTag = values.putString("id", id).build(); + dataTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, dataTag); EntityType entityType = EntityTypes.get(id); if (entityType != null) { Location location = NBTConversions.toLocation( clipboard, - requireTag(tags, "Pos", ListTag.class), - requireTag(dataTag.getValue(), "Rotation", ListTag.class) + entityTag.getListTag("Pos", LinTagType.doubleTag()), + dataTag.getListTag("Rotation", LinTagType.floatTag()) ); - BaseEntity state = new BaseEntity(entityType, dataTag); + BaseEntity state = new BaseEntity(entityType, LazyReference.computed(dataTag)); if (positionIsRelative) { location = location.setPosition( location.toVector().add(clipboard.getMinimumPoint().toVector3()) @@ -283,6 +258,25 @@ public class ReaderUtil { //FAWE - make public } } + static Int2ObjectMap readBiomePalette(VersionedDataFixer fixer, LinCompoundTag paletteTag, Logger logger) throws + IOException { + Int2ObjectMap palette = new Int2ObjectLinkedOpenHashMap<>(paletteTag.value().size()); + for (var palettePart : paletteTag.value().entrySet()) { + String key = palettePart.getKey(); + key = fixer.fixUp(DataFixer.FixTypes.BIOME, key); + BiomeType biome = BiomeTypes.get(key); + if (biome == null) { + logger.warn("Unknown biome type :" + key + + " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); + } + if (!(palettePart.getValue() instanceof LinIntTag idTag)) { + throw new IOException("Biome mapped to non-Int tag."); + } + palette.put(idTag.valueAsInt(), biome); + } + return palette; + } + private ReaderUtil() { } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV1Reader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV1Reader.java index d26a97369..7574bfe4c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV1Reader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV1Reader.java @@ -19,63 +19,54 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -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.NamedTag; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.NBTSchematicReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.block.BlockState; +import org.enginehub.linbus.stream.LinStream; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinIntTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinRootEntry; +import org.enginehub.linbus.tree.LinTagType; import java.io.IOException; import java.util.Map; import java.util.OptionalInt; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Reads schematic files using the Sponge Schematic Specification (Version 1). */ -public class SpongeSchematicV1Reader extends NBTSchematicReader { +public class SpongeSchematicV1Reader implements ClipboardReader { - private final NBTInputStream inputStream; + private final LinStream rootStream; - /** - * Create a new instance. - * - * @param inputStream the input stream to read from - */ - public SpongeSchematicV1Reader(NBTInputStream inputStream) { - checkNotNull(inputStream); - this.inputStream = inputStream; + public SpongeSchematicV1Reader(LinStream rootStream) { + this.rootStream = rootStream; } @Override public Clipboard read() throws IOException { - CompoundTag schematicTag = getBaseTag(); - ReaderUtil.checkSchematicVersion(1, getBaseTag()); + LinCompoundTag schematicTag = getBaseTag(); + ReaderUtil.checkSchematicVersion(1, schematicTag); - final Platform platform = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.WORLD_EDITING); + return doRead(schematicTag); + } + + // For legacy SpongeSchematicReader, can be inlined in WorldEdit 8 + public static BlockArrayClipboard doRead(LinCompoundTag schematicTag) throws IOException { + final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); // this is a relatively safe assumption unless someone imports a schematic from 1.12 // e.g. sponge 7.1- - VersionedDataFixer fixer = new VersionedDataFixer( - Constants.DATA_VERSION_MC_1_13_2, platform.getDataFixer() - ); + VersionedDataFixer fixer = new VersionedDataFixer(Constants.DATA_VERSION_MC_1_13_2, platform.getDataFixer()); return readVersion1(schematicTag, fixer); } @@ -90,64 +81,54 @@ public class SpongeSchematicV1Reader extends NBTSchematicReader { } } - private CompoundTag getBaseTag() throws IOException { - NamedTag rootTag = inputStream.readNamedTag(); - - return (CompoundTag) rootTag.getTag(); + private LinCompoundTag getBaseTag() throws IOException { + return LinRootEntry.readFrom(rootStream).value(); } - static BlockArrayClipboard readVersion1(CompoundTag schematicTag, VersionedDataFixer fixer) throws IOException { - Map schematic = schematicTag.getValue(); + static BlockArrayClipboard readVersion1(LinCompoundTag schematicTag, VersionedDataFixer fixer) throws IOException { + int width = schematicTag.getTag("Width", LinTagType.shortTag()).valueAsShort() & 0xFFFF; + int height = schematicTag.getTag("Height", LinTagType.shortTag()).valueAsShort() & 0xFFFF; + int length = schematicTag.getTag("Length", LinTagType.shortTag()).valueAsShort() & 0xFFFF; - int width = requireTag(schematic, "Width", ShortTag.class).getValue() & 0xFFFF; - int height = requireTag(schematic, "Height", ShortTag.class).getValue() & 0xFFFF; - int length = requireTag(schematic, "Length", ShortTag.class).getValue() & 0xFFFF; - - BlockVector3 min = ReaderUtil.decodeBlockVector3( - getTag(schematic, "Offset", IntArrayTag.class) - ); + BlockVector3 min = ReaderUtil.decodeBlockVector3(schematicTag.findTag("Offset", LinTagType.intArrayTag())); BlockVector3 offset = BlockVector3.ZERO; - CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class); - if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) { - // We appear to have WorldEdit Metadata - Map metadata = metadataTag.getValue(); - int offsetX = requireTag(metadata, "WEOffsetX", IntTag.class).getValue(); - int offsetY = requireTag(metadata, "WEOffsetY", IntTag.class).getValue(); - int offsetZ = requireTag(metadata, "WEOffsetZ", IntTag.class).getValue(); - offset = BlockVector3.at(offsetX, offsetY, offsetZ); + LinCompoundTag metadataTag = schematicTag.findTag("Metadata", LinTagType.compoundTag()); + if (metadataTag != null) { + LinIntTag offsetX = metadataTag.findTag("WEOffsetX", LinTagType.intTag()); + if (offsetX != null) { + int offsetY = metadataTag.getTag("WEOffsetY", LinTagType.intTag()).valueAsInt(); + int offsetZ = metadataTag.getTag("WEOffsetZ", LinTagType.intTag()).valueAsInt(); + offset = BlockVector3.at(offsetX.valueAsInt(), offsetY, offsetZ); + } } BlockVector3 origin = min.subtract(offset); Region region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)); - IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class); - Map paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue(); - if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) { + LinIntTag paletteMaxTag = schematicTag.findTag("PaletteMax", LinTagType.intTag()); + LinCompoundTag paletteObject = schematicTag.getTag("Palette", LinTagType.compoundTag()); + if (paletteMaxTag != null && paletteObject.value().size() != paletteMaxTag.valueAsInt()) { throw new IOException("Block palette size does not match expected size."); } - Map palette = ReaderUtil.decodePalette( - paletteObject, fixer - ); + Map palette = ReaderUtil.decodePalette(paletteObject, fixer); - byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue(); + byte[] blocks = schematicTag.getTag("BlockData", LinTagType.byteArrayTag()).value(); - ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class); + LinListTag tileEntities = schematicTag.findListTag("BlockEntities", LinTagType.compoundTag()); if (tileEntities == null) { - tileEntities = getTag(schematic, "TileEntities", ListTag.class); + tileEntities = schematicTag.findListTag("TileEntities", LinTagType.compoundTag()); } BlockArrayClipboard clipboard = new BlockArrayClipboard(region); clipboard.setOrigin(origin); - ReaderUtil.initializeClipboardFromBlocks( - clipboard, palette, blocks, tileEntities, fixer, false - ); + ReaderUtil.initializeClipboardFromBlocks(clipboard, palette, blocks, tileEntities, fixer, false); return clipboard; } @Override public void close() throws IOException { - inputStream.close(); } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Reader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Reader.java index df20edd69..2cd7bb1e1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Reader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Reader.java @@ -19,66 +19,56 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -import com.sk89q.jnbt.ByteArrayTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.NBTInputStream; -import com.sk89q.jnbt.NamedTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.NBTSchematicReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.VarIntIterator; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.stream.LinStream; +import org.enginehub.linbus.tree.LinByteArrayTag; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinIntTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinRootEntry; +import org.enginehub.linbus.tree.LinTagType; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; import java.util.OptionalInt; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Reads schematic files using the Sponge Schematic Specification (Version 2). */ -public class SpongeSchematicV2Reader extends NBTSchematicReader { +public class SpongeSchematicV2Reader implements ClipboardReader { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final NBTInputStream inputStream; + private final LinStream rootStream; - /** - * Create a new instance. - * - * @param inputStream the input stream to read from - */ - public SpongeSchematicV2Reader(NBTInputStream inputStream) { - checkNotNull(inputStream); - this.inputStream = inputStream; + public SpongeSchematicV2Reader(LinStream rootStream) { + this.rootStream = rootStream; } @Override public Clipboard read() throws IOException { - CompoundTag schematicTag = getBaseTag(); + LinCompoundTag schematicTag = getBaseTag(); ReaderUtil.checkSchematicVersion(2, schematicTag); - final Platform platform = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.WORLD_EDITING); + return doRead(schematicTag); + } + + // For legacy SpongeSchematicReader, can be inlined in WorldEdit 8 + public static Clipboard doRead(LinCompoundTag schematicTag) throws IOException { + final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); int liveDataVersion = platform.getDataVersion(); - VersionedDataFixer fixer = ReaderUtil.getVersionedDataFixer( - schematicTag.getValue(), platform, liveDataVersion - ); + VersionedDataFixer fixer = ReaderUtil.getVersionedDataFixer(schematicTag, platform, liveDataVersion); BlockArrayClipboard clip = SpongeSchematicV1Reader.readVersion1(schematicTag, fixer); return readVersion2(clip, schematicTag, fixer); } @@ -86,11 +76,10 @@ public class SpongeSchematicV2Reader extends NBTSchematicReader { @Override public OptionalInt getDataVersion() { try { - CompoundTag schematicTag = getBaseTag(); + LinCompoundTag schematicTag = getBaseTag(); ReaderUtil.checkSchematicVersion(2, schematicTag); - int dataVersion = requireTag(schematicTag.getValue(), "DataVersion", IntTag.class) - .getValue(); + int dataVersion = schematicTag.getTag("DataVersion", LinTagType.intTag()).valueAsInt(); if (dataVersion < 0) { return OptionalInt.empty(); } @@ -100,67 +89,48 @@ public class SpongeSchematicV2Reader extends NBTSchematicReader { } } - private CompoundTag getBaseTag() throws IOException { - NamedTag rootTag = inputStream.readNamedTag(); - - return (CompoundTag) rootTag.getTag(); + private LinCompoundTag getBaseTag() throws IOException { + return LinRootEntry.readFrom(rootStream).value(); } - private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag, - VersionedDataFixer fixer) throws IOException { - Map schematic = schematicTag.getValue(); - if (schematic.containsKey("BiomeData")) { - readBiomes2(version1, schematic, fixer); + private static Clipboard readVersion2( + BlockArrayClipboard version1, LinCompoundTag schematicTag, VersionedDataFixer fixer + ) throws IOException { + if (schematicTag.value().containsKey("BiomeData")) { + readBiomes2(version1, schematicTag, fixer); } - ListTag entities = getTag(schematic, "Entities", ListTag.class); + LinListTag entities = schematicTag.findListTag("Entities", LinTagType.compoundTag()); if (entities != null) { - ReaderUtil.readEntities( - version1, entities.getValue(), fixer, false - ); + ReaderUtil.readEntities(version1, entities.value(), fixer, false); } return version1; } - private void readBiomes2(BlockArrayClipboard clipboard, Map schematic, - VersionedDataFixer fixer) throws IOException { - ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class); - IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class); - CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class); + private static void readBiomes2( + BlockArrayClipboard clipboard, LinCompoundTag schematic, VersionedDataFixer fixer + ) throws IOException { + LinByteArrayTag dataTag = schematic.getTag("BiomeData", LinTagType.byteArrayTag()); + LinIntTag maxTag = schematic.getTag("BiomePaletteMax", LinTagType.intTag()); + LinCompoundTag paletteTag = schematic.getTag("BiomePalette", LinTagType.compoundTag()); - Map palette = new HashMap<>(); - if (maxTag.getValue() != paletteTag.getValue().size()) { + if (maxTag.valueAsInt() != paletteTag.value().size()) { throw new IOException("Biome palette size does not match expected size."); } - for (Entry palettePart : paletteTag.getValue().entrySet()) { - String key = palettePart.getKey(); - key = fixer.fixUp(DataFixer.FixTypes.BIOME, key); - BiomeType biome = BiomeTypes.get(key); - if (biome == null) { - LOGGER.warn("Unknown biome type :" + key - + " in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?"); - } - Tag idTag = palettePart.getValue(); - if (!(idTag instanceof IntTag)) { - throw new IOException("Biome mapped to non-Int tag."); - } - palette.put(((IntTag) idTag).getValue(), biome); - } + Int2ObjectMap palette = ReaderUtil.readBiomePalette(fixer, paletteTag, LOGGER); - int width = clipboard.getDimensions().getX(); + int width = clipboard.getDimensions().x(); - byte[] biomes = dataTag.getValue(); + byte[] biomes = dataTag.value(); BlockVector3 min = clipboard.getMinimumPoint(); int index = 0; for (VarIntIterator iter = new VarIntIterator(biomes); iter.hasNext(); index++) { int nextBiomeId = iter.nextInt(); BiomeType type = palette.get(nextBiomeId); // hack -- the x and y values from the 3d decode with length == 1 are equivalent - BlockVector3 hackDecode = ReaderUtil.decodePositionFromDataIndex( - width, 1, index - ); - int x = hackDecode.getX(); - int z = hackDecode.getY(); + BlockVector3 hackDecode = ReaderUtil.decodePositionFromDataIndex(width, 1, index); + int x = hackDecode.x(); + int z = hackDecode.y(); for (int y = 0; y < clipboard.getRegion().getHeight(); y++) { clipboard.setBiome(min.add(x, y, z), type); } @@ -169,6 +139,6 @@ public class SpongeSchematicV2Reader extends NBTSchematicReader { @Override public void close() throws IOException { - inputStream.close(); } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Writer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Writer.java index 3df86d98f..8504f0fe8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Writer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV2Writer.java @@ -19,18 +19,6 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriterV2; -import com.google.common.collect.ImmutableMap; -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.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.extension.platform.Platform; @@ -40,38 +28,30 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; +import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import org.enginehub.linbus.stream.LinBinaryIO; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinRootEntry; +import org.enginehub.linbus.tree.LinTagType; import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.google.common.base.Preconditions.checkNotNull; /** * Writes schematic files using the Sponge Schematic Specification (Version 2). - * - * @deprecated Slow, resource intensive, but sometimes safer than using the recommended - * {@link FastSchematicWriterV2}. - * Avoid using large clipboards to create schematics with this writer. */ -@Deprecated public class SpongeSchematicV2Writer implements ClipboardWriter { private static final int CURRENT_VERSION = 2; private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; - private final NBTOutputStream outputStream; + private final DataOutputStream outputStream; - /** - * Create a new schematic writer. - * - * @param outputStream the output stream to write to - */ - public SpongeSchematicV2Writer(NBTOutputStream outputStream) { - checkNotNull(outputStream); + public SpongeSchematicV2Writer(DataOutputStream outputStream) { this.outputStream = outputStream; } @@ -81,17 +61,16 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { // between upstream and FAWE clipboard.flush(); //FAWE end - // For now always write the latest version. Maybe provide support for earlier if more appear. - outputStream.writeNamedTag("Schematic", new CompoundTag(write2(clipboard))); + LinBinaryIO.write(outputStream, new LinRootEntry("Schematic", write2(clipboard))); } /** * Writes a version 2 schematic file. * * @param clipboard The clipboard - * @return The schematic map + * @return the schematic tag */ - private Map write2(Clipboard clipboard) { + private LinCompoundTag write2(Clipboard clipboard) { Region region = clipboard.getRegion(); BlockVector3 origin = clipboard.getOrigin(); BlockVector3 min = region.getMinimumPoint(); @@ -110,52 +89,49 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { throw new IllegalArgumentException("Length of region too large for a .schematic"); } - Map schematic = new HashMap<>(); - schematic.put("Version", new IntTag(CURRENT_VERSION)); - schematic.put("DataVersion", new IntTag( - WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion())); + LinCompoundTag.Builder schematic = LinCompoundTag.builder(); + schematic.putInt("Version", CURRENT_VERSION); + schematic.putInt("DataVersion", + WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion() + ); - Map metadata = new HashMap<>(); - metadata.put("WEOffsetX", new IntTag(offset.x())); - metadata.put("WEOffsetY", new IntTag(offset.y())); - metadata.put("WEOffsetZ", new IntTag(offset.z())); - metadata.put("FAWEVersion", new IntTag(Fawe.instance().getVersion().build)); + LinCompoundTag.Builder metadata = LinCompoundTag.builder(); + metadata.putInt("WEOffsetX", offset.x()); + metadata.putInt("WEOffsetY", offset.y()); + metadata.putInt("WEOffsetZ", offset.z()); - Map worldEditSection = new HashMap<>(); - worldEditSection.put("Version", new StringTag(WorldEdit.getVersion())); - worldEditSection.put("EditingPlatform", new StringTag(WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getId())); - worldEditSection.put("Offset", new IntArrayTag(new int[] { - offset.getBlockX(), offset.getBlockY(), offset.getBlockZ() - })); + LinCompoundTag.Builder worldEditSection = LinCompoundTag.builder(); + worldEditSection.putString("Version", WorldEdit.getVersion()); + worldEditSection.putString("EditingPlatform", + WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).id() + ); + worldEditSection.putIntArray("Offset", new int[]{offset.x(), offset.y(), offset.z()}); - Map platformsSection = new HashMap<>(); + LinCompoundTag.Builder platformsSection = LinCompoundTag.builder(); for (Platform platform : WorldEdit.getInstance().getPlatformManager().getPlatforms()) { - platformsSection.put(platform.getId(), new CompoundTag(ImmutableMap.of( - "Name", new StringTag(platform.getPlatformName()), - "Version", new StringTag(platform.getPlatformVersion()) - ))); + platformsSection.put(platform.id(), LinCompoundTag + .builder() + .putString("Name", platform.getPlatformName()) + .putString("Version", platform.getPlatformVersion()) + .build()); } - worldEditSection.put("Platforms", new CompoundTag(platformsSection)); + worldEditSection.put("Platforms", platformsSection.build()); - metadata.put("WorldEdit", new CompoundTag(worldEditSection)); + metadata.put("WorldEdit", worldEditSection.build()); - schematic.put("Metadata", new CompoundTag(metadata)); + schematic.put("Metadata", metadata.build()); - schematic.put("Width", new ShortTag((short) width)); - schematic.put("Height", new ShortTag((short) height)); - schematic.put("Length", new ShortTag((short) length)); + schematic.putShort("Width", (short) width); + schematic.putShort("Height", (short) height); + schematic.putShort("Length", (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[]{ - min.x(), - min.y(), - min.z(), - })); + schematic.putIntArray("Offset", new int[]{min.x(), min.y(), min.z(),}); int paletteMax = 0; - Map palette = new HashMap<>(); + Object2IntMap palette = new Object2IntLinkedOpenHashMap<>(); - List tileEntities = new ArrayList<>(); + LinListTag.Builder tileEntities = LinListTag.builder(LinTagType.compoundTag()); ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * height * length); @@ -167,8 +143,9 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { int x0 = min.x() + x; BlockVector3 point = BlockVector3.at(x0, y0, z0); BaseBlock block = clipboard.getFullBlock(point); - if (block.getNbtData() != null) { - Map values = new HashMap<>(block.getNbtData().getValue()); + LinCompoundTag nbt = block.getNbt(); + if (nbt != null) { + LinCompoundTag.Builder values = nbt.toBuilder(); values.remove("id"); // Remove 'id' if it exists. We want 'Id' @@ -177,16 +154,16 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { values.remove("y"); values.remove("z"); - values.put("Id", new StringTag(block.getNbtId())); - values.put("Pos", new IntArrayTag(new int[]{x, y, z})); + values.putString("Id", block.getNbtId()); + values.putIntArray("Pos", new int[]{x, y, z}); - tileEntities.add(new CompoundTag(values)); + tileEntities.add(values.build()); } String blockKey = block.toImmutableState().getAsString(); int blockId; if (palette.containsKey(blockKey)) { - blockId = palette.get(blockKey); + blockId = palette.getInt(blockKey); } else { blockId = paletteMax; palette.put(blockKey, blockId); @@ -202,14 +179,14 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { } } - schematic.put("PaletteMax", new IntTag(paletteMax)); + schematic.putInt("PaletteMax", paletteMax); - Map paletteTag = new HashMap<>(); - palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); + LinCompoundTag.Builder paletteTag = LinCompoundTag.builder(); + Object2IntMaps.fastForEach(palette, e -> paletteTag.putInt(e.getKey(), e.getIntValue())); - schematic.put("Palette", new CompoundTag(paletteTag)); - schematic.put("BlockData", new ByteArrayTag(buffer.toByteArray())); - schematic.put("BlockEntities", new ListTag(CompoundTag.class, tileEntities)); + schematic.put("Palette", paletteTag.build()); + schematic.putByteArray("BlockData", buffer.toByteArray()); + schematic.put("BlockEntities", tileEntities.build()); // version 2 stuff if (clipboard.hasBiomes()) { @@ -217,16 +194,16 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { } if (!clipboard.getEntities().isEmpty()) { - ListTag value = WriterUtil.encodeEntities(clipboard, false); + LinListTag value = WriterUtil.encodeEntities(clipboard, false); if (value != null) { schematic.put("Entities", value); } } - return schematic; + return schematic.build(); } - private void writeBiomes(Clipboard clipboard, Map schematic) { + private void writeBiomes(Clipboard clipboard, LinCompoundTag.Builder schematic) { BlockVector3 min = clipboard.getMinimumPoint(); int width = clipboard.getRegion().getWidth(); int length = clipboard.getRegion().getLength(); @@ -234,7 +211,7 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * length); int paletteMax = 0; - Map palette = new HashMap<>(); + Object2IntMap palette = new Object2IntLinkedOpenHashMap<>(); for (int z = 0; z < length; z++) { int z0 = min.z() + z; @@ -246,7 +223,7 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { String biomeKey = biome.id(); int biomeId; if (palette.containsKey(biomeKey)) { - biomeId = palette.get(biomeKey); + biomeId = palette.getInt(biomeKey); } else { biomeId = paletteMax; palette.put(biomeKey, biomeId); @@ -261,13 +238,13 @@ public class SpongeSchematicV2Writer implements ClipboardWriter { } } - schematic.put("BiomePaletteMax", new IntTag(paletteMax)); + schematic.putInt("BiomePaletteMax", paletteMax); - Map paletteTag = new HashMap<>(); - palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value))); + LinCompoundTag.Builder paletteTag = LinCompoundTag.builder(); + Object2IntMaps.fastForEach(palette, e -> paletteTag.putInt(e.getKey(), e.getIntValue())); - schematic.put("BiomePalette", new CompoundTag(paletteTag)); - schematic.put("BiomeData", new ByteArrayTag(buffer.toByteArray())); + schematic.put("BiomePalette", paletteTag.build()); + schematic.putByteArray("BiomeData", buffer.toByteArray()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java index 8def1e796..978cb8ba5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Reader.java @@ -19,82 +19,62 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -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.NamedTag; -import com.sk89q.jnbt.ShortTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.NBTSchematicReader; -import com.sk89q.worldedit.extent.clipboard.io.SchematicNbtUtil; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.internal.util.VarIntIterator; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import org.apache.logging.log4j.Logger; +import org.enginehub.linbus.stream.LinStream; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinRootEntry; +import org.enginehub.linbus.tree.LinTagType; import java.io.IOException; -import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.OptionalInt; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Reads schematic files using the Sponge Schematic Specification. */ -public class SpongeSchematicV3Reader extends NBTSchematicReader { +public class SpongeSchematicV3Reader implements ClipboardReader { private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final NBTInputStream inputStream; + private final LinStream rootStream; - /** - * Create a new instance. - * - * @param inputStream the input stream to read from - */ - public SpongeSchematicV3Reader(NBTInputStream inputStream) { - checkNotNull(inputStream); - this.inputStream = inputStream; + public SpongeSchematicV3Reader(LinStream rootStream) { + this.rootStream = rootStream; } @Override public Clipboard read() throws IOException { - CompoundTag schematicTag = getBaseTag(); + LinCompoundTag schematicTag = getBaseTag(); ReaderUtil.checkSchematicVersion(3, schematicTag); - final Platform platform = WorldEdit.getInstance().getPlatformManager() - .queryCapability(Capability.WORLD_EDITING); + final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); int liveDataVersion = platform.getDataVersion(); - VersionedDataFixer fixer = ReaderUtil.getVersionedDataFixer( - schematicTag.getValue(), platform, liveDataVersion - ); + VersionedDataFixer fixer = ReaderUtil.getVersionedDataFixer(schematicTag, platform, liveDataVersion); return readVersion3(schematicTag, fixer); } @Override public OptionalInt getDataVersion() { try { - CompoundTag schematicTag = getBaseTag(); + LinCompoundTag schematicTag = getBaseTag(); ReaderUtil.checkSchematicVersion(3, schematicTag); - int dataVersion = requireTag(schematicTag.getValue(), "DataVersion", IntTag.class) - .getValue(); + int dataVersion = schematicTag.getTag("DataVersion", LinTagType.intTag()).valueAsInt(); if (dataVersion < 0) { return OptionalInt.empty(); } @@ -104,112 +84,85 @@ public class SpongeSchematicV3Reader extends NBTSchematicReader { } } - private CompoundTag getBaseTag() throws IOException { - NamedTag rootTag = inputStream.readNamedTag(); - CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); - // Nested inside the root tag - return requireTag(schematicTag.getValue(), "Schematic", CompoundTag.class); + private LinCompoundTag getBaseTag() throws IOException { + return LinRootEntry.readFrom(rootStream).value().getTag("Schematic", LinTagType.compoundTag()); } - private Clipboard readVersion3(CompoundTag schematicTag, VersionedDataFixer fixer) throws IOException { - Map schematic = schematicTag.getValue(); + private Clipboard readVersion3(LinCompoundTag schematicTag, VersionedDataFixer fixer) throws IOException { + int width = schematicTag.getTag("Width", LinTagType.shortTag()).valueAsShort() & 0xFFFF; + int height = schematicTag.getTag("Height", LinTagType.shortTag()).valueAsShort() & 0xFFFF; + int length = schematicTag.getTag("Length", LinTagType.shortTag()).valueAsShort() & 0xFFFF; - int width = requireTag(schematic, "Width", ShortTag.class).getValue() & 0xFFFF; - int height = requireTag(schematic, "Height", ShortTag.class).getValue() & 0xFFFF; - int length = requireTag(schematic, "Length", ShortTag.class).getValue() & 0xFFFF; - - BlockVector3 offset = ReaderUtil.decodeBlockVector3( - SchematicNbtUtil.getTag(schematic, "Offset", IntArrayTag.class) - ); + BlockVector3 offset = ReaderUtil.decodeBlockVector3(schematicTag.findTag("Offset", LinTagType.intArrayTag())); BlockVector3 origin = BlockVector3.ZERO; - CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class); - if (metadataTag != null && metadataTag.containsKey("WorldEdit")) { - // We appear to have WorldEdit Metadata - Map worldedit = - requireTag(metadataTag.getValue(), "WorldEdit", CompoundTag.class).getValue(); - origin = ReaderUtil.decodeBlockVector3( - SchematicNbtUtil.getTag(worldedit, "Origin", IntArrayTag.class) - ); + LinCompoundTag metadataTag = schematicTag.findTag("Metadata", LinTagType.compoundTag()); + if (metadataTag != null) { + LinCompoundTag worldeditMeta = metadataTag.findTag("WorldEdit", LinTagType.compoundTag()); + if (worldeditMeta != null) { + origin = ReaderUtil.decodeBlockVector3(worldeditMeta.findTag("Origin", LinTagType.intArrayTag())); + } } BlockVector3 min = offset.add(origin); - BlockArrayClipboard clipboard = new BlockArrayClipboard( - new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector3.ONE)) - ); + BlockArrayClipboard clipboard = new BlockArrayClipboard(new CuboidRegion( + min, + min.add(width, height, length).subtract(BlockVector3.ONE) + )); clipboard.setOrigin(origin); - decodeBlocksIntoClipboard(fixer, schematic, clipboard); + decodeBlocksIntoClipboard(fixer, schematicTag, clipboard); - CompoundTag biomeContainer = getTag(schematic, "Biomes", CompoundTag.class); + LinCompoundTag biomeContainer = schematicTag.findTag("Biomes", LinTagType.compoundTag()); if (biomeContainer != null) { - readBiomes3(clipboard, biomeContainer.getValue(), fixer); + readBiomes3(clipboard, biomeContainer, fixer); } - ListTag entities = getTag(schematic, "Entities", ListTag.class); + LinListTag entities = schematicTag.findListTag("Entities", LinTagType.compoundTag()); if (entities != null) { - ReaderUtil.readEntities(clipboard, entities.getValue(), fixer, true); + ReaderUtil.readEntities(clipboard, entities.value(), fixer, true); } return clipboard; } - private void decodeBlocksIntoClipboard(VersionedDataFixer fixer, Map schematic, - BlockArrayClipboard clipboard) throws IOException { - Map blockContainer = requireTag(schematic, "Blocks", CompoundTag.class).getValue(); + private void decodeBlocksIntoClipboard( + VersionedDataFixer fixer, LinCompoundTag schematic, BlockArrayClipboard clipboard + ) throws IOException { + LinCompoundTag blockContainer = schematic.getTag("Blocks", LinTagType.compoundTag()); - Map paletteObject = requireTag(blockContainer, "Palette", CompoundTag.class).getValue(); - Map palette = ReaderUtil.decodePalette( - paletteObject, fixer - ); + LinCompoundTag paletteObject = blockContainer.getTag("Palette", LinTagType.compoundTag()); + Map palette = ReaderUtil.decodePalette(paletteObject, fixer); - byte[] blocks = requireTag(blockContainer, "Data", ByteArrayTag.class).getValue(); - ListTag tileEntities = getTag(blockContainer, "BlockEntities", ListTag.class); + byte[] blocks = blockContainer.getTag("Data", LinTagType.byteArrayTag()).value(); + LinListTag blockEntities = blockContainer.getListTag("BlockEntities", LinTagType.compoundTag()); - ReaderUtil.initializeClipboardFromBlocks( - clipboard, palette, blocks, tileEntities, fixer, true - ); + ReaderUtil.initializeClipboardFromBlocks(clipboard, palette, blocks, blockEntities, fixer, true); } - private void readBiomes3(BlockArrayClipboard clipboard, Map biomeContainer, - VersionedDataFixer fixer) throws IOException { - CompoundTag paletteTag = requireTag(biomeContainer, "Palette", CompoundTag.class); + private void readBiomes3( + BlockArrayClipboard clipboard, LinCompoundTag biomeContainer, VersionedDataFixer fixer + ) throws IOException { + LinCompoundTag paletteTag = biomeContainer.getTag("Palette", LinTagType.compoundTag()); - Map palette = new HashMap<>(); - - for (Entry palettePart : paletteTag.getValue().entrySet()) { - String key = palettePart.getKey(); - key = fixer.fixUp(DataFixer.FixTypes.BIOME, key); - BiomeType biome = BiomeTypes.get(key); - if (biome == null) { - LOGGER.warn("Unknown biome type `" + key + "` in palette." - + " Are you missing a mod or using a schematic made in a newer version of Minecraft?"); - } - Tag idTag = palettePart.getValue(); - if (!(idTag instanceof IntTag)) { - throw new IOException("Biome mapped to non-Int tag."); - } - palette.put(((IntTag) idTag).getValue(), biome); - } + Int2ObjectMap palette = ReaderUtil.readBiomePalette(fixer, paletteTag, LOGGER); int width = clipboard.getRegion().getWidth(); int length = clipboard.getRegion().getLength(); - byte[] biomes = requireTag(biomeContainer, "Data", ByteArrayTag.class).getValue(); + byte[] biomes = biomeContainer.getTag("Data", LinTagType.byteArrayTag()).value(); BlockVector3 min = clipboard.getMinimumPoint(); int index = 0; for (VarIntIterator iter = new VarIntIterator(biomes); iter.hasNext(); index++) { int nextBiomeId = iter.nextInt(); BiomeType type = palette.get(nextBiomeId); - BlockVector3 pos = ReaderUtil.decodePositionFromDataIndex( - width, length, index - ); + BlockVector3 pos = ReaderUtil.decodePositionFromDataIndex(width, length, index); clipboard.setBiome(min.add(pos), type); } } @Override public void close() throws IOException { - inputStream.close(); } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Writer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Writer.java index 7407c1036..4030c2f90 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Writer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/SpongeSchematicV3Writer.java @@ -19,19 +19,6 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.sk89q.jnbt.ByteArrayTag; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.CompoundTagBuilder; -import com.sk89q.jnbt.IntArrayTag; -import com.sk89q.jnbt.IntTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.LongTag; -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.extension.platform.Platform; @@ -40,18 +27,20 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.block.BaseBlock; +import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import org.enginehub.linbus.stream.LinBinaryIO; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinRootEntry; +import org.enginehub.linbus.tree.LinTagType; import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import java.util.function.Function; -import static com.google.common.base.Preconditions.checkNotNull; - /** * Writes schematic files using the Sponge Schematic Specification (Version 3). */ @@ -60,15 +49,9 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { private static final int CURRENT_VERSION = 3; private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; - private final NBTOutputStream outputStream; + private final DataOutputStream outputStream; - /** - * Create a new schematic writer. - * - * @param outputStream the output stream to write to - */ - public SpongeSchematicV3Writer(NBTOutputStream outputStream) { - checkNotNull(outputStream); + public SpongeSchematicV3Writer(DataOutputStream outputStream) { this.outputStream = outputStream; } @@ -78,9 +61,8 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { // between upstream and FAWE clipboard.flush(); //FAWE end - // For now always write the latest version. Maybe provide support for earlier if more appear. - outputStream.writeNamedTag("", - new CompoundTag(ImmutableMap.of("Schematic", new CompoundTag(write3(clipboard)))) + LinBinaryIO.write(outputStream, + new LinRootEntry("", LinCompoundTag.builder().put("Schematic", write3(clipboard)).build()) ); } @@ -90,7 +72,7 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { * @param clipboard The clipboard * @return The schematic map */ - private Map write3(Clipboard clipboard) { + private LinCompoundTag write3(Clipboard clipboard) { Region region = clipboard.getRegion(); BlockVector3 origin = clipboard.getOrigin(); BlockVector3 min = region.getMinimumPoint(); @@ -109,43 +91,41 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { throw new IllegalArgumentException("Length of region too large for a .schematic"); } - Map schematic = new HashMap<>(); - schematic.put("Version", new IntTag(CURRENT_VERSION)); - schematic.put("DataVersion", new IntTag( - WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion())); + LinCompoundTag.Builder schematic = LinCompoundTag.builder(); + schematic.putInt("Version", CURRENT_VERSION); + schematic.putInt("DataVersion", + WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion() + ); - Map metadata = new HashMap<>(); - metadata.put("Date", new LongTag(System.currentTimeMillis())); + LinCompoundTag.Builder metadata = LinCompoundTag.builder(); + metadata.putLong("Date", System.currentTimeMillis()); - Map worldEditSection = new HashMap<>(); - worldEditSection.put("Version", new StringTag(WorldEdit.getVersion())); - worldEditSection.put("EditingPlatform", new StringTag(WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getId())); - worldEditSection.put("Origin", new IntArrayTag(new int[] { - origin.getBlockX(), origin.getBlockY(), origin.getBlockZ() - })); + LinCompoundTag.Builder worldEditSection = LinCompoundTag.builder(); + worldEditSection.putString("Version", WorldEdit.getVersion()); + worldEditSection.putString("EditingPlatform", + WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).id() + ); + worldEditSection.putIntArray("Origin", new int[]{origin.x(), origin.y(), origin.z()}); - Map platformsSection = new HashMap<>(); + LinCompoundTag.Builder platformsSection = LinCompoundTag.builder(); for (Platform platform : WorldEdit.getInstance().getPlatformManager().getPlatforms()) { - platformsSection.put(platform.getId(), new CompoundTag(ImmutableMap.of( - "Name", new StringTag(platform.getPlatformName()), - "Version", new StringTag(platform.getPlatformVersion()) - ))); + platformsSection.put(platform.id(), LinCompoundTag + .builder() + .putString("Name", platform.getPlatformName()) + .putString("Version", platform.getPlatformVersion()) + .build()); } - worldEditSection.put("Platforms", new CompoundTag(platformsSection)); + worldEditSection.put("Platforms", platformsSection.build()); - metadata.put("WorldEdit", new CompoundTag(worldEditSection)); + metadata.put("WorldEdit", worldEditSection.build()); - schematic.put("Metadata", new CompoundTag(metadata)); + schematic.put("Metadata", metadata.build()); - schematic.put("Width", new ShortTag((short) width)); - schematic.put("Height", new ShortTag((short) height)); - schematic.put("Length", new ShortTag((short) length)); + schematic.putShort("Width", (short) width); + schematic.putShort("Height", (short) height); + schematic.putShort("Length", (short) length); - schematic.put("Offset", new IntArrayTag(new int[] { - offset.getBlockX(), - offset.getBlockY(), - offset.getBlockZ(), - })); + schematic.putIntArray("Offset", new int[]{offset.x(), offset.y(), offset.z(),}); schematic.put("Blocks", encodeBlocks(clipboard)); @@ -154,22 +134,23 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { } if (!clipboard.getEntities().isEmpty()) { - ListTag value = WriterUtil.encodeEntities(clipboard, true); + LinListTag value = WriterUtil.encodeEntities(clipboard, true); if (value != null) { schematic.put("Entities", value); } } - return schematic; + return schematic.build(); } private static final class PaletteMap { - private final Map contents = new LinkedHashMap<>(); + + private final Object2IntMap contents = new Object2IntLinkedOpenHashMap<>(); private int nextId = 0; public int getId(String key) { - Integer result = contents.get(key); - if (result != null) { + int result = contents.getOrDefault(key, -1); + if (result != -1) { return result; } int newValue = nextId; @@ -178,46 +159,43 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { return newValue; } - public CompoundTag toNbt() { - return new CompoundTag(ImmutableMap.copyOf(Maps.transformValues( - contents, IntTag::new - ))); + public LinCompoundTag toNbt() { + LinCompoundTag.Builder result = LinCompoundTag.builder(); + Object2IntMaps.fastForEach(contents, e -> result.putInt(e.getKey(), e.getIntValue())); + return result.build(); } + } - private CompoundTag encodeBlocks(Clipboard clipboard) { - List blockEntities = new ArrayList<>(); - CompoundTag result = encodePalettedData(clipboard, point -> { + private LinCompoundTag encodeBlocks(Clipboard clipboard) { + LinListTag.Builder blockEntities = LinListTag.builder(LinTagType.compoundTag()); + LinCompoundTag.Builder result = encodePalettedData(clipboard, point -> { BaseBlock block = clipboard.getFullBlock(point); // Also compute block entity side-effect here - if (block.getNbtData() != null) { - CompoundTagBuilder builder = CompoundTagBuilder.create(); + LinCompoundTag nbt = block.getNbt(); + if (nbt != null) { + LinCompoundTag.Builder builder = LinCompoundTag.builder(); builder.putString("Id", block.getNbtId()); BlockVector3 adjustedPos = point.subtract(clipboard.getMinimumPoint()); - builder.putIntArray("Pos", new int[] { - adjustedPos.getBlockX(), - adjustedPos.getBlockY(), - adjustedPos.getBlockZ() - }); - builder.put("Data", block.getNbtData()); + builder.putIntArray("Pos", new int[]{adjustedPos.x(), adjustedPos.y(), adjustedPos.z()}); + builder.put("Data", nbt); blockEntities.add(builder.build()); } return block.toImmutableState().getAsString(); }); - return result.createBuilder() - .put("BlockEntities", new ListTag(CompoundTag.class, blockEntities)) - .build(); + return result.put("BlockEntities", blockEntities.build()).build(); } - private CompoundTag encodeBiomes(Clipboard clipboard) { - return encodePalettedData(clipboard, point -> clipboard.getBiome(point).getId()); + private LinCompoundTag encodeBiomes(Clipboard clipboard) { + return encodePalettedData(clipboard, point -> clipboard.getBiome(point).id()).build(); } - private CompoundTag encodePalettedData(Clipboard clipboard, - Function keyFunction) { + private LinCompoundTag.Builder encodePalettedData( + Clipboard clipboard, Function keyFunction + ) { BlockVector3 min = clipboard.getMinimumPoint(); int width = clipboard.getRegion().getWidth(); int height = clipboard.getRegion().getHeight(); @@ -244,14 +222,12 @@ public class SpongeSchematicV3Writer implements ClipboardWriter { } } - return new CompoundTag(ImmutableMap.of( - "Palette", paletteMap.toNbt(), - "Data", new ByteArrayTag(buffer.toByteArray()) - )); + return LinCompoundTag.builder().put("Palette", paletteMap.toNbt()).putByteArray("Data", buffer.toByteArray()); } @Override public void close() throws IOException { outputStream.close(); } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/WriterUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/WriterUtil.java index 1e13569b6..17ff5004d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/WriterUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/sponge/WriterUtil.java @@ -19,69 +19,73 @@ package com.sk89q.worldedit.extent.clipboard.io.sponge; -import com.google.common.collect.ImmutableList; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.CompoundTagBuilder; -import com.sk89q.jnbt.DoubleTag; -import com.sk89q.jnbt.FloatTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Location; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinDoubleTag; +import org.enginehub.linbus.tree.LinFloatTag; +import org.enginehub.linbus.tree.LinListTag; +import org.enginehub.linbus.tree.LinTagType; class WriterUtil { - static ListTag encodeEntities(Clipboard clipboard, boolean positionIsRelative) { - List entities = clipboard.getEntities().stream().map(e -> { - BaseEntity state = e.getState(); - if (state == null) { - return null; - } - CompoundTagBuilder fullTagBuilder = CompoundTagBuilder.create(); - CompoundTagBuilder dataTagBuilder = CompoundTagBuilder.create(); - CompoundTag rawData = state.getNbtData(); - if (rawData != null) { - dataTagBuilder.putAll(rawData.getValue()); - dataTagBuilder.remove("id"); - } - final Location location = e.getLocation(); - Vector3 pos = location.toVector(); - dataTagBuilder.put("Rotation", encodeRotation(location)); - if (positionIsRelative) { - pos = pos.subtract(clipboard.getMinimumPoint().toVector3()); - fullTagBuilder.put("Data", dataTagBuilder.build()); - } else { - fullTagBuilder.putAll(dataTagBuilder.build().getValue()); + static LinListTag encodeEntities(Clipboard clipboard, boolean positionIsRelative) { + LinListTag.Builder entities = LinListTag.builder(LinTagType.compoundTag()); + for (Entity entity : clipboard.getEntities()) { + LinCompoundTag encoded = encodeEntity(clipboard, positionIsRelative, entity); + if (encoded != null) { + entities.add(encoded); } - fullTagBuilder.putString("Id", state.getType().getId()); - fullTagBuilder.put("Pos", encodeVector(pos)); - - return fullTagBuilder.build(); - }).filter(Objects::nonNull).collect(Collectors.toList()); - if (entities.isEmpty()) { + } + var result = entities.build(); + if (result.value().isEmpty()) { return null; } - return new ListTag(CompoundTag.class, entities); + return result; } - static Tag encodeVector(Vector3 vector) { - return new ListTag(DoubleTag.class, ImmutableList.of( - new DoubleTag(vector.getX()), - new DoubleTag(vector.getY()), - new DoubleTag(vector.getZ()) - )); + private static LinCompoundTag encodeEntity(Clipboard clipboard, boolean positionIsRelative, Entity e) { + BaseEntity state = e.getState(); + if (state == null) { + return null; + } + LinCompoundTag.Builder fullTagBuilder = LinCompoundTag.builder(); + LinCompoundTag.Builder dataTagBuilder = LinCompoundTag.builder(); + LinCompoundTag rawData = state.getNbt(); + if (rawData != null) { + dataTagBuilder.putAll(rawData.value()); + dataTagBuilder.remove("id"); + } + final Location location = e.getLocation(); + Vector3 pos = location.toVector(); + dataTagBuilder.put("Rotation", encodeRotation(location)); + if (positionIsRelative) { + pos = pos.subtract(clipboard.getMinimumPoint().toVector3()); + + fullTagBuilder.put("Data", dataTagBuilder.build()); + } else { + fullTagBuilder.putAll(dataTagBuilder.build().value()); + } + fullTagBuilder.putString("Id", state.getType().id()); + fullTagBuilder.put("Pos", encodeVector(pos)); + + return fullTagBuilder.build(); } - static Tag encodeRotation(Location location) { - return new ListTag(FloatTag.class, ImmutableList.of( - new FloatTag(location.getYaw()), - new FloatTag(location.getPitch()) - )); + static LinListTag encodeVector(Vector3 vector) { + return LinListTag.builder(LinTagType.doubleTag()).add(LinDoubleTag.of(vector.x())).add(LinDoubleTag.of(vector.y())).add( + LinDoubleTag.of(vector.z())).build(); } + + static LinListTag encodeRotation(Location location) { + return LinListTag + .builder(LinTagType.floatTag()) + .add(LinFloatTag.of(location.getYaw())) + .add(LinFloatTag.of(location.getPitch())) + .build(); + } + }