Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sponge Schematic V3 (.schem) IMPORT support #850

Open
wants to merge 5 commits into
base: pre-rewrite/fabric/1.20.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
before_install:
- sdk install java 21.0.2-tem
- sdk use java 21.0.2-tem
185 changes: 153 additions & 32 deletions src/main/java/fi/dy/masa/litematica/schematic/LitematicaSchematic.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public class LitematicaSchematic
{
public static final String FILE_EXTENSION = ".litematic";
public static final int SCHEMATIC_VERSION_1_13_2 = 5;
public static final int MINECRAFT_DATA_VERSION_1_12 = 1139; // MC 1.12
public static final int MINECRAFT_DATA_VERSION_1_13_2 = 1631; // MC 1.13.2

public static final int MINECRAFT_DATA_VERSION = SharedConstants.getGameVersion().getSaveVersion().getId();
Expand Down Expand Up @@ -1298,6 +1299,7 @@ protected boolean readPaletteFromLitematicaFormatTag(NbtList tagList, ILitematic

public static boolean isValidSpongeSchematic(NbtCompound tag)
{
// v2 Sponge Schematic
if (tag.contains("Width", Constants.NBT.TAG_ANY_NUMERIC) &&
tag.contains("Height", Constants.NBT.TAG_ANY_NUMERIC) &&
tag.contains("Length", Constants.NBT.TAG_ANY_NUMERIC) &&
Expand All @@ -1311,6 +1313,28 @@ public static boolean isValidSpongeSchematic(NbtCompound tag)
return false;
}

public static boolean isValidSpongeSchematicv3(NbtCompound tag)
{
// v3 Sponge Schematic
if (tag.contains("Schematic", Constants.NBT.TAG_COMPOUND))
{
NbtCompound nbtV3 = tag.getCompound("Schematic");

if (nbtV3.contains("Width", Constants.NBT.TAG_ANY_NUMERIC) &&
nbtV3.contains("Height", Constants.NBT.TAG_ANY_NUMERIC) &&
nbtV3.contains("Length", Constants.NBT.TAG_ANY_NUMERIC) &&
nbtV3.contains("Version", Constants.NBT.TAG_INT) &&
nbtV3.getInt("Version") >= 3 &&
nbtV3.contains("Blocks") &&
nbtV3.contains("DataVersion"))
{
return isSizeValid(readSizeFromTagSponge(nbtV3));
}
}

return false;
}

public static Vec3i readSizeFromTagSponge(NbtCompound tag)
{
return new Vec3i(tag.getInt("Width"), tag.getInt("Height"), tag.getInt("Length"));
Expand Down Expand Up @@ -1357,38 +1381,88 @@ protected boolean readSpongePaletteFromTag(NbtCompound tag, ILitematicaBlockStat
return palette.setMapping(list);
}

protected boolean readSpongeBlocksFromTag(NbtCompound tag, String schematicName, Vec3i size)
protected boolean readSpongeBlocksFromTag(NbtCompound tag, String schematicName, Vec3i size, int minecraftDataVersion, int spongeVersion)
{
if (tag.contains("Palette", Constants.NBT.TAG_COMPOUND) &&
tag.contains("BlockData", Constants.NBT.TAG_BYTE_ARRAY))
NbtCompound blocksTag = new NbtCompound();
NbtCompound paletteTag;
byte[] blockData;
int paletteSize;

if (spongeVersion >= 3 && tag.contains("Blocks"))
{
NbtCompound paletteTag = tag.getCompound("Palette");
byte[] blockData = tag.getByteArray("BlockData");
int paletteSize = paletteTag.getKeys().size();
LitematicaBlockStateContainer container = LitematicaBlockStateContainer.createContainer(paletteSize, blockData, size);
blocksTag = tag.getCompound("Blocks");

if (container == null)
if (blocksTag.contains("Palette", Constants.NBT.TAG_COMPOUND) &&
blocksTag.contains("Data", Constants.NBT.TAG_BYTE_ARRAY) &&
blocksTag.contains("BlockEntities", Constants.NBT.TAG_LIST))
{
paletteTag = blocksTag.getCompound("Palette");
blockData = blocksTag.getByteArray("Data");
paletteSize = paletteTag.getKeys().size();
}
else
{
String msg = "Failed to read blocks from Sponge schematic";
InfoUtils.showGuiOrInGameMessage(MessageType.ERROR, msg);
Litematica.logger.error(msg);
return false;
}
}
else
{
if (tag.contains("Palette", Constants.NBT.TAG_COMPOUND) &&
tag.contains("BlockData", Constants.NBT.TAG_BYTE_ARRAY))
{
paletteTag = tag.getCompound("Palette");
blockData = tag.getByteArray("BlockData");
paletteSize = paletteTag.getKeys().size();
}
else
{
String msg = "Failed to read blocks from Sponge schematic";
InfoUtils.showGuiOrInGameMessage(MessageType.ERROR, msg);
Litematica.logger.error(msg);
return false;
}
}

this.blockContainers.put(schematicName, container);
LitematicaBlockStateContainer container = LitematicaBlockStateContainer.createContainer(paletteSize, blockData, size);

return this.readSpongePaletteFromTag(paletteTag, container.getPalette());
if (container == null)
{
String msg = "Failed to read blocks from Sponge schematic";
InfoUtils.showGuiOrInGameMessage(MessageType.ERROR, msg);
Litematica.logger.error(msg);
return false;
}

return false;
this.blockContainers.put(schematicName, container);

if (this.readSpongePaletteFromTag(paletteTag, container.getPalette()) == false)
{
return false;
}

if (spongeVersion >= 3)
{
if (blocksTag.isEmpty() == false)
{
// tileEntities list moved to "Blocks" tag for V3
this.tileEntities.put(schematicName, this.readSpongeBlockEntitiesFromTag(blocksTag, spongeVersion));
}
else
{
return false;
}
}

return true;
}

protected Map<BlockPos, NbtCompound> readSpongeBlockEntitiesFromTag(NbtCompound tag)
protected Map<BlockPos, NbtCompound> readSpongeBlockEntitiesFromTag(NbtCompound tag, int spongeVersion)
{
Map<BlockPos, NbtCompound> blockEntities = new HashMap<>();

int version = tag.getInt("Version");
String tagName = version == 1 ? "TileEntities" : "BlockEntities";
String tagName = spongeVersion == 1 ? "TileEntities" : "BlockEntities";
NbtList tagList = tag.getList(tagName, Constants.NBT.TAG_COMPOUND);

final int size = tagList.size();
Expand All @@ -1406,38 +1480,59 @@ protected Map<BlockPos, NbtCompound> readSpongeBlockEntitiesFromTag(NbtCompound
beTag.remove("Id");
beTag.remove("Pos");

if (version == 1)
if (spongeVersion == 1)
{
beTag.remove("ContentVersion");
}

blockEntities.put(pos, beTag);
if (spongeVersion >= 3)
{
NbtCompound beData = beTag.getCompound("Data");
blockEntities.put(pos, beData);
}
else
{
blockEntities.put(pos, beTag);
}
}
}

return blockEntities;
}

protected List<EntityInfo> readSpongeEntitiesFromTag(NbtCompound tag, Vec3i offset)
protected List<EntityInfo> readSpongeEntitiesFromTag(NbtCompound tag, Vec3i offset, int spongeVersion)
{
List<EntityInfo> entities = new ArrayList<>();
NbtList tagList = tag.getList("Entities", Constants.NBT.TAG_COMPOUND);
final int size = tagList.size();

for (int i = 0; i < size; ++i)
{
NbtCompound entityData = tagList.getCompound(i);
Vec3d pos = NbtUtils.readVec3dFromListTag(entityData);
NbtCompound entityEntry = tagList.getCompound(i);
Vec3d pos = NbtUtils.readVec3dFromListTag(entityEntry);

if (pos != null && entityData.isEmpty() == false)
if (pos != null && entityEntry.isEmpty() == false)
{
pos = new Vec3d(pos.x - offset.getX(), pos.y - offset.getY(), pos.z - offset.getZ());
entityData.putString("id", entityData.getString("Id"));
entityEntry.putString("id", entityEntry.getString("Id"));

// Remove the Sponge tags from the data that is kept in memory
entityData.remove("Id");
entityEntry.remove("Id");

entities.add(new EntityInfo(pos, entityData));
if (spongeVersion >= 3)
{
NbtCompound entityData = entityEntry.getCompound("Data");

if (entityData.contains("id", Constants.NBT.TAG_STRING) == false)
{
entityData.putString("id", entityEntry.getString("id"));
}
entities.add(new EntityInfo(pos, entityData));
}
else
{
pos = new Vec3d(pos.x - offset.getX(), pos.y - offset.getY(), pos.z - offset.getZ());
entities.add(new EntityInfo(pos, entityEntry));
}
}
}

Expand All @@ -1446,38 +1541,64 @@ protected List<EntityInfo> readSpongeEntitiesFromTag(NbtCompound tag, Vec3i offs

public boolean readFromSpongeSchematic(String name, NbtCompound tag)
{
if (isValidSpongeSchematic(tag) == false)
if (isValidSpongeSchematicv3(tag))
{
// Probably not the "best" solution, but it works
NbtCompound spongeTag = tag.getCompound("Schematic");
tag.remove("Schematic");
tag.copyFrom(spongeTag);
}
else if (isValidSpongeSchematic(tag) == false)
{
return false;
}

final int spongeVersion = tag.contains("Version") ? tag.getInt("Version") : -1;
final int minecraftDataVersion = tag.contains("DataVersion") ? tag.getInt("DataVersion") : MINECRAFT_DATA_VERSION_1_12;
Vec3i size = readSizeFromTagSponge(tag);

if (this.readSpongeBlocksFromTag(tag, name, size) == false)
if (this.readSpongeBlocksFromTag(tag, name, size, minecraftDataVersion, spongeVersion) == false)
{
return false;
}

Vec3i offset = NbtUtils.readVec3iFromIntArray(tag, "Offset");

if (offset == null)
{
offset = Vec3i.ZERO;
}

if (spongeVersion < 3)
{
this.tileEntities.put(name, this.readSpongeBlockEntitiesFromTag(tag, spongeVersion));
}
this.entities.put(name, this.readSpongeEntitiesFromTag(tag, offset, spongeVersion));

this.tileEntities.put(name, this.readSpongeBlockEntitiesFromTag(tag));
this.entities.put(name, this.readSpongeEntitiesFromTag(tag, offset));
if (tag.contains("Metadata", Constants.NBT.TAG_COMPOUND))
{
NbtCompound metadata = tag.getCompound("Metadata");

this.metadata.setName(metadata.contains("Name", Constants.NBT.TAG_STRING) ? metadata.getString("Name") : name);
this.metadata.setAuthor(metadata.contains("Author", Constants.NBT.TAG_STRING) ? metadata.getString("Author") : "unknown");
this.metadata.setTimeCreated(metadata.contains("Date", Constants.NBT.TAG_LONG) ? metadata.getLong("Date") : System.currentTimeMillis());
}
else
{
this.metadata.setAuthor("unknown");
this.metadata.setName(name);
this.metadata.setTimeCreated(System.currentTimeMillis());
}
if (tag.contains("author", Constants.NBT.TAG_STRING))
{
this.getMetadata().setAuthor(tag.getString("author"));
this.metadata.setAuthor(tag.getString("author"));
}

this.subRegionPositions.put(name, BlockPos.ORIGIN);
this.subRegionSizes.put(name, new BlockPos(size));
this.metadata.setName(name);
this.metadata.setRegionCount(1);
this.metadata.setTotalVolume(size.getX() * size.getY() * size.getZ());
this.metadata.setEnclosingSize(size);
this.metadata.setTimeCreated(System.currentTimeMillis());
this.metadata.setTimeModified(this.metadata.getTimeCreated());
this.metadata.setTotalBlocks(this.totalBlocksReadFromWorld);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@
public class SchematicConversionFixers
{
private static final BooleanProperty[] HORIZONTAL_CONNECTING_BLOCK_PROPS = new BooleanProperty[] { null, null, HorizontalConnectingBlock.NORTH, HorizontalConnectingBlock.SOUTH, HorizontalConnectingBlock.WEST, HorizontalConnectingBlock.EAST };
private static final BlockState REDSTONE_WIRE_DOT = Blocks.REDSTONE_WIRE.getDefaultState();
private static final BlockState REDSTONE_WIRE_DOT_OLD = Blocks.REDSTONE_WIRE.getDefaultState();
private static final BlockState REDSTONE_WIRE_DOT = Blocks.REDSTONE_WIRE.getDefaultState()
.with(RedstoneWireBlock.POWER, 0)
.with(RedstoneWireBlock.WIRE_CONNECTION_NORTH, WireConnection.NONE)
.with(RedstoneWireBlock.WIRE_CONNECTION_EAST, WireConnection.NONE)
.with(RedstoneWireBlock.WIRE_CONNECTION_SOUTH, WireConnection.NONE)
.with(RedstoneWireBlock.WIRE_CONNECTION_WEST, WireConnection.NONE);
private static final BlockState REDSTONE_WIRE_CROSS = Blocks.REDSTONE_WIRE.getDefaultState()
.with(RedstoneWireBlock.WIRE_CONNECTION_NORTH, WireConnection.SIDE)
.with(RedstoneWireBlock.WIRE_CONNECTION_EAST, WireConnection.SIDE)
Expand Down Expand Up @@ -342,7 +348,7 @@ public class SchematicConversionFixers
state = ((IMixinRedstoneWireBlock) wire).litematicaGetPlacementState(reader, state, pos);

// Turn all old dots into crosses, while keeping the power level
if (state.with(RedstoneWireBlock.POWER, 0) == REDSTONE_WIRE_DOT)
if (state.equals(REDSTONE_WIRE_DOT) == false && state.with(RedstoneWireBlock.POWER, 0) == REDSTONE_WIRE_DOT_OLD)
{
state = REDSTONE_WIRE_CROSS.with(RedstoneWireBlock.POWER, state.get(RedstoneWireBlock.POWER));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.decoration.AbstractDecorationEntity;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.entity.decoration.ItemFrameEntity;
import net.minecraft.entity.decoration.painting.PaintingEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.inventory.Inventory;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
Expand All @@ -36,6 +39,7 @@
import fi.dy.masa.litematica.schematic.container.LitematicaBlockStateContainer;
import fi.dy.masa.litematica.schematic.placement.SchematicPlacement;
import fi.dy.masa.litematica.schematic.placement.SubRegionPlacement;
import fi.dy.masa.malilib.util.Constants;
import fi.dy.masa.malilib.util.IntBoundingBox;
import fi.dy.masa.malilib.util.NBTUtils;

Expand Down Expand Up @@ -385,6 +389,7 @@ public static void placeEntitiesToWorldWithinChunk(World world, ChunkPos chunkPo
double x = pos.x + offX;
double y = pos.y + offY;
double z = pos.z + offZ;
float[] origRot = new float[2];

if (x >= minX && x < maxX && z >= minZ && z < maxZ)
{
Expand Down Expand Up @@ -412,6 +417,10 @@ public static void placeEntitiesToWorldWithinChunk(World world, ChunkPos chunkPo
tag.putInt("TileZ", (int) p.z);
}

NbtList rotation = tag.getList("Rotation", Constants.NBT.TAG_FLOAT);
origRot[0] = rotation.getFloat(0);
origRot[1] = rotation.getFloat(1);

Entity entity = EntityUtils.createEntityAndPassengersFromNBT(tag, world);

if (entity != null)
Expand Down Expand Up @@ -448,6 +457,14 @@ public static void placeEntitiesToWorldWithinChunk(World world, ChunkPos chunkPo

entity.setPosition(x, y, z);
}
if (entity instanceof ItemFrameEntity frameEntity)
{
if (frameEntity.getYaw() != origRot[0] && (frameEntity.getPitch() == 90.0F || frameEntity.getPitch() == -90.0F))
{
// Fix Yaw only if Pitch is +/- 90.0F (Floor, Ceiling mounted)
frameEntity.setYaw(origRot[0]);
}
}

EntityUtils.spawnEntityAndPassengersInWorld(entity, world);

Expand Down