From 95ed2a8a855cdec10a76b44a32c9f431fd253f3d Mon Sep 17 00:00:00 2001 From: iceBear67 Date: Mon, 6 Jan 2025 21:55:46 +0800 Subject: [PATCH] Add support for schematics v3 output <3 --- .../sfcraft/module/supervisor/WebModule.java | 13 +--- .../{ => litematic}/LitematicConverter.java | 34 +++++------ .../util/litematic/LitematicConverterV3.java | 61 +++++++++++++++++++ 3 files changed, 81 insertions(+), 27 deletions(-) rename src/main/java/io/ib67/sfcraft/util/{ => litematic}/LitematicConverter.java (86%) create mode 100644 src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverterV3.java diff --git a/src/main/java/io/ib67/sfcraft/module/supervisor/WebModule.java b/src/main/java/io/ib67/sfcraft/module/supervisor/WebModule.java index e07a8e9..be3ca5e 100644 --- a/src/main/java/io/ib67/sfcraft/module/supervisor/WebModule.java +++ b/src/main/java/io/ib67/sfcraft/module/supervisor/WebModule.java @@ -5,27 +5,20 @@ import io.ib67.sfcraft.ServerModule; import io.ib67.sfcraft.config.SFConfig; import io.ib67.sfcraft.module.SignatureService; import io.ib67.sfcraft.util.Helper; -import io.ib67.sfcraft.util.LitematicConverter; +import io.ib67.sfcraft.util.litematic.LitematicConverter; +import io.ib67.sfcraft.util.litematic.LitematicConverterV3; import io.javalin.Javalin; import io.javalin.http.Context; import io.javalin.http.UploadedFile; -import io.netty.buffer.Unpooled; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; -import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtSizeTracker; import org.jetbrains.annotations.NotNull; -import java.io.DataOutputStream; -import java.io.FileOutputStream; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Base64; import java.util.List; -import java.util.concurrent.ForkJoinPool; -import java.util.regex.Pattern; @Log4j2 public class WebModule extends ServerModule { @@ -87,7 +80,7 @@ public class WebModule extends ServerModule { log.info("Saved " + fileName + " as a schematic."); } else if (fileName.endsWith(".litematic")) { log.error("Handling new {}", fileName); - var converter = new LitematicConverter( + var converter = new LitematicConverterV3( file.content(), new NbtSizeTracker(config.maxSchematicSize, 16) ); diff --git a/src/main/java/io/ib67/sfcraft/util/LitematicConverter.java b/src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverter.java similarity index 86% rename from src/main/java/io/ib67/sfcraft/util/LitematicConverter.java rename to src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverter.java index b43de6a..b5e4535 100644 --- a/src/main/java/io/ib67/sfcraft/util/LitematicConverter.java +++ b/src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverter.java @@ -1,4 +1,4 @@ -package io.ib67.sfcraft.util; +package io.ib67.sfcraft.util.litematic; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -6,8 +6,6 @@ import lombok.SneakyThrows; import net.minecraft.nbt.*; import java.io.InputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; import java.util.ArrayList; import java.util.function.BiConsumer; import java.util.stream.Collectors; @@ -17,8 +15,8 @@ import java.util.stream.Collectors; * Uses {@link NbtCompound} from Minecraft. */ public class LitematicConverter implements AutoCloseable { - private final InputStream input; - private final NbtSizeTracker sizeTracker; + protected final InputStream input; + protected final NbtSizeTracker sizeTracker; public LitematicConverter(InputStream input, NbtSizeTracker sizeTracker) { this.input = input; @@ -41,7 +39,7 @@ public class LitematicConverter implements AutoCloseable { } } - private NbtCompound convertRegionToSchematic(int dataVersion, NbtCompound region) { + protected NbtCompound convertRegionToSchematic(int dataVersion, NbtCompound region) { var worldEditTag = new NbtCompound(); // prepare metadata @@ -69,7 +67,7 @@ public class LitematicConverter implements AutoCloseable { return schematicsRoot; } - private SizeTuple readSizeTuple(NbtCompound region) { + protected SizeTuple readSizeTuple(NbtCompound region) { var size = (NbtCompound) region.get("Size"); var sizeX = size.getInt("x"); var sizeY = size.getInt("y"); @@ -77,27 +75,29 @@ public class LitematicConverter implements AutoCloseable { return new SizeTuple(sizeX, sizeY, sizeZ); } - private NbtList convertToWETileEntities(NbtList tileEntities) { + protected NbtList convertToWETileEntities(NbtList tileEntities) { var weTEs = new NbtList(); for (NbtElement _tileEntity : tileEntities) { + var weTE = new NbtCompound(); var tE = (NbtCompound) _tileEntity; - tE.putIntArray("Pos", new int[]{ + weTE.putIntArray("Pos", new int[]{ tE.getInt("x"), tE.getInt("y"), tE.getInt("z") }); - tE.putString("Id", tE.getString("id")); + weTE.putString("Id", tE.getString("id")); // other properties tE.remove("x"); tE.remove("y"); tE.remove("z"); tE.remove("id"); - weTEs.add(tE); + weTE.put("Data",tE); + weTEs.add(weTE); } return weTEs; } - private NbtCompound convertToWEPalette(NbtList paletteNbt) { + protected NbtCompound convertToWEPalette(NbtList paletteNbt) { var wePalette = new NbtCompound(); for (int i = 0; i < paletteNbt.size(); i++) { var entry = (NbtCompound) paletteNbt.get(i); @@ -117,7 +117,7 @@ public class LitematicConverter implements AutoCloseable { return wePalette; } - private NbtElement convertToWeMeta(SizeTuple size, NbtCompound region) { + protected NbtElement convertToWeMeta(SizeTuple size, NbtCompound region) { var pos = (NbtCompound) region.get("Position"); var nbt = new NbtCompound(); nbt.putInt("WEOffsetX", pos.getInt("x") + (size.x < 0 ? size.x + 1 : 0)); @@ -126,7 +126,7 @@ public class LitematicConverter implements AutoCloseable { return nbt; } - private byte[] convertToWEBlocks(int totalBlocks, NbtCompound region) { + protected byte[] convertToWEBlocks(int totalBlocks, NbtCompound region) { var buf = Unpooled.buffer(); var blockStates = region.getLongArray("BlockStates"); @@ -148,11 +148,11 @@ public class LitematicConverter implements AutoCloseable { } bitMask = (1 << bitsPerBlock) - 1; - while(remainingBits >= bitsPerBlock){ + while (remainingBits >= bitsPerBlock) { bits = blockState & bitMask; blockState = blockState >>> bitsPerBlock; remainingBits -= bitsPerBlock; - if(blockIterated >= totalBlocks) break; + if (blockIterated >= totalBlocks) break; writeBlocks(buf, (short) bits); blockIterated++; } @@ -162,7 +162,7 @@ public class LitematicConverter implements AutoCloseable { return buf.array(); } - private static int writeBlocks(ByteBuf buf, short block) { + protected static int writeBlocks(ByteBuf buf, short block) { int b = block >>> 7; if (b == 0) { buf.writeByte(block); diff --git a/src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverterV3.java b/src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverterV3.java new file mode 100644 index 0000000..e26b5cb --- /dev/null +++ b/src/main/java/io/ib67/sfcraft/util/litematic/LitematicConverterV3.java @@ -0,0 +1,61 @@ +package io.ib67.sfcraft.util.litematic; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtSizeTracker; + +import java.io.InputStream; + +public class LitematicConverterV3 extends LitematicConverter { + public LitematicConverterV3(InputStream input, NbtSizeTracker sizeTracker) { + super(input, sizeTracker); + } + + @Override + protected NbtCompound convertRegionToSchematic(int dataVersion, NbtCompound region) { + var schematicsTag = new NbtCompound(); + + // prepare metadata + var size = readSizeTuple(region); + schematicsTag.put("Metadata", convertToWeMeta(size, region)); + schematicsTag.putInt("DataVersion", dataVersion); + schematicsTag.putInt("Version", 3); + schematicsTag.putIntArray("Offset", new int[3]); + + // dimensional data. + schematicsTag.putShort("Height", (short) Math.abs(size.y())); + schematicsTag.putShort("Length", (short) Math.abs(size.z())); + schematicsTag.putShort("Width", (short) Math.abs(size.x())); + + // block & tile entities + var paletteNbt = region.getList("BlockStatePalette", NbtElement.COMPOUND_TYPE); + var wePalette = convertToWEPalette(paletteNbt); + var tileEntities = region.getList("TileEntities", NbtElement.COMPOUND_TYPE); + + var blocksNbt = new NbtCompound(); + blocksNbt.put("Palette", wePalette); + blocksNbt.putByteArray("Data", convertToWEBlocks(Math.abs(size.x() * size.y() * size.z()), region)); + blocksNbt.put("BlockEntities", convertToWETileEntities(tileEntities)); + + schematicsTag.put("Blocks", blocksNbt); + var schematicsRoot = new NbtCompound(); + schematicsRoot.put("Schematic", schematicsTag); + return schematicsRoot; + } + + @Override + protected NbtElement convertToWeMeta(SizeTuple size, NbtCompound region) { + var pos = (NbtCompound) region.get("Position"); + var worldEditNbt = new NbtCompound(); + worldEditNbt.putIntArray("Origin", new int[]{ + pos.getInt("x") + (size.x() < 0 ? size.x() + 1 : 0), + pos.getInt("y") + (size.y() < 0 ? size.y() + 1 : 0), + pos.getInt("z") + (size.z() < 0 ? size.z() + 1 : 0) + }); + var Metadata = new NbtCompound(); + Metadata.put("WorldEdit", worldEditNbt); + Metadata.putLong("Date", System.currentTimeMillis()); + + return Metadata; + } +}