Fix: schematic encoder with help from chatgpt
This commit is contained in:
1 parent
92fe6f902f
commit
f76c975299
2 files changed
+72
-44
No files matched your search
@@ -5,6 +5,7 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.SneakyThrows;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import org.apache.commons.compress.utils.Lists;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -63,7 +64,7 @@ public class LitematicConverter implements AutoCloseable {
|
||||
worldEditTag.put("BlockEntities", convertToWETileEntities(tileEntities));
|
||||
worldEditTag.putInt("Version", 2);
|
||||
worldEditTag.putIntArray("Offset", new int[3]);
|
||||
worldEditTag.putByteArray("BlockData", convertToWEBlocks(Math.abs(size.x * size.y * size.z), region));
|
||||
worldEditTag.putByteArray("BlockData", convertToWEBlocks(size, region));
|
||||
var schematicsRoot = new NbtCompound();
|
||||
schematicsRoot.put("Schematic", worldEditTag);
|
||||
return schematicsRoot;
|
||||
@@ -129,52 +130,32 @@ public class LitematicConverter implements AutoCloseable {
|
||||
return nbt;
|
||||
}
|
||||
|
||||
protected byte[] convertToWEBlocks(int totalBlocks, NbtCompound region) {
|
||||
var buf = Unpooled.buffer();
|
||||
protected byte[] convertToWEBlocks(SizeTuple size, NbtCompound region) {
|
||||
var blockCount = Math.abs(size.x * size.y * size.z);
|
||||
var blockStates = region.getLongArray("BlockStates");
|
||||
|
||||
int bitsPerBlock = region.getList("BlockStatePalette", NbtElement.COMPOUND_TYPE).size();
|
||||
bitsPerBlock = Math.max(2, Integer.SIZE - Integer.numberOfLeadingZeros(bitsPerBlock - 1));
|
||||
int maxEntryValue = (1 << bitsPerBlock) - 1;
|
||||
var buffer = new PacketByteBuf(Unpooled.buffer());
|
||||
for (int index = 0; index < blockCount; index++) {
|
||||
int startBit = index * bitsPerBlock;
|
||||
int startLongIndex = startBit / 64;
|
||||
int startBitOffset = startBit % 64;
|
||||
int endBit = startBit + bitsPerBlock - 1;
|
||||
int endLongIndex = endBit / 64;
|
||||
|
||||
int bitCounts = 0, blockIterated = 0;
|
||||
long bitMask, bits = 0;
|
||||
for (long blockState : blockStates) {
|
||||
int remainingBits = bitCounts + 64;
|
||||
if (bitCounts != 0) {
|
||||
bitMask = (1 << (bitsPerBlock - bitCounts)) - 1;
|
||||
long newBits = (blockState & bitMask) << bitCounts;
|
||||
bits = bits | newBits;
|
||||
blockState = blockState >>> (bitsPerBlock - bitCounts);
|
||||
remainingBits -= bitsPerBlock;
|
||||
writeBlocks(buf, (short) bits);
|
||||
blockIterated++;
|
||||
}
|
||||
|
||||
bitMask = (1 << bitsPerBlock) - 1;
|
||||
while (remainingBits >= bitsPerBlock) {
|
||||
bits = blockState & bitMask;
|
||||
blockState = blockState >>> bitsPerBlock;
|
||||
remainingBits -= bitsPerBlock;
|
||||
if (blockIterated >= totalBlocks) break;
|
||||
writeBlocks(buf, (short) bits);
|
||||
blockIterated++;
|
||||
}
|
||||
bits = blockState;
|
||||
bitCounts = remainingBits;
|
||||
}
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
protected static int writeBlocks(ByteBuf buf, short block) {
|
||||
int b = block >>> 7;
|
||||
if (b == 0) {
|
||||
buf.writeByte(block);
|
||||
return 1;
|
||||
int value;
|
||||
if (startLongIndex == endLongIndex) {
|
||||
value = (int) ((blockStates[startLongIndex] >>> startBitOffset) & maxEntryValue);
|
||||
} else {
|
||||
buf.writeByte(block | 128);
|
||||
buf.writeByte(block);
|
||||
return 2;
|
||||
int bitsInFirstPart = 64 - startBitOffset; // 第一个 long 提取的位数
|
||||
long firstPart = blockStates[startLongIndex] >>> startBitOffset;
|
||||
long secondPart = blockStates[endLongIndex] & ((1L << (bitsPerBlock - bitsInFirstPart)) - 1);
|
||||
value = (int) ((firstPart | (secondPart << bitsInFirstPart)) & maxEntryValue);
|
||||
}
|
||||
buffer.writeVarInt(value);
|
||||
}
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,7 +163,7 @@ public class LitematicConverter implements AutoCloseable {
|
||||
input.close();
|
||||
}
|
||||
|
||||
record SizeTuple(
|
||||
protected record SizeTuple(
|
||||
int x, int y, int z
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package io.ib67.sfcraft.util.litematic;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.nbt.NbtSizeTracker;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@Log4j2
|
||||
public class LitematicConverterV3 extends LitematicConverter {
|
||||
public LitematicConverterV3(InputStream input, NbtSizeTracker sizeTracker) {
|
||||
super(input, sizeTracker);
|
||||
@@ -34,15 +41,55 @@ public class LitematicConverterV3 extends LitematicConverter {
|
||||
|
||||
var blocksNbt = new NbtCompound();
|
||||
blocksNbt.put("Palette", wePalette);
|
||||
blocksNbt.putByteArray("Data", convertToWEBlocks(Math.abs(size.x() * size.y() * size.z()), region));
|
||||
var weBlockData = convertToWEBlocks(size, region);
|
||||
blocksNbt.putByteArray("Data", weBlockData);
|
||||
blocksNbt.put("BlockEntities", convertToWETileEntities(tileEntities));
|
||||
|
||||
validateData(wePalette, weBlockData, Math.abs(size.x()), Math.abs(size.z()));
|
||||
schematicsTag.put("Blocks", blocksNbt);
|
||||
var schematicsRoot = new NbtCompound();
|
||||
schematicsRoot.put("Schematic", schematicsTag);
|
||||
return schematicsRoot;
|
||||
}
|
||||
|
||||
private void validateData(
|
||||
NbtCompound wePalette,
|
||||
byte[] weBlockData,
|
||||
int width,
|
||||
int length
|
||||
) {
|
||||
// find invalid palette ids
|
||||
var buf = new PacketByteBuf(Unpooled.wrappedBuffer(weBlockData));
|
||||
var paletteIds = new Int2ObjectOpenHashMap<>();
|
||||
var touchedIds = new IntOpenHashSet();
|
||||
for (String key : wePalette.getKeys()) {
|
||||
paletteIds.put(wePalette.getInt(key), key);
|
||||
}
|
||||
int counter = 0;
|
||||
try {
|
||||
while (buf.isReadable()) {
|
||||
counter++;
|
||||
var id = buf.readVarInt();
|
||||
if (!paletteIds.containsKey(id)) {
|
||||
log.error("Cannot find id {} at {}th block in palette, pos: {}", id, counter, decodePositionFromDataIndex(width, length, counter));
|
||||
} else {
|
||||
touchedIds.add(id);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error validing schematic", e);
|
||||
}
|
||||
paletteIds.keySet().intStream().filter(it->!touchedIds.contains(it))
|
||||
.forEach(it-> log.error("Unused palette id: {}, blockState: {}", it, paletteIds.get(it)));
|
||||
}
|
||||
private static Vec3i decodePositionFromDataIndex(int width, int length, int index) {
|
||||
// index = (y * width * length) + (z * width) + x
|
||||
int y = index / (width * length);
|
||||
int remainder = index - (y * width * length);
|
||||
int z = remainder / width;
|
||||
int x = remainder - z * width;
|
||||
return new Vec3i(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NbtElement convertToWeMeta(SizeTuple size, NbtCompound region) {
|
||||
var pos = (NbtCompound) region.get("Position");
|
||||
|
||||
Reference in new issue
Block a user