Skip to content

Commit

Permalink
Add Sponge Schematic V3 support
Browse files Browse the repository at this point in the history
  • Loading branch information
sakura-ryoko committed Jun 8, 2024
1 parent f85586a commit 7b8b3d4
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 46 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ author = masa
mod_file_name = litematica-fabric

# Current mod version
mod_version = 0.17.999-sakura.9
mod_version = 0.17.999-sakura.10

# Required malilib version
malilib_version = 0.18.999-sakura.5
Expand Down
203 changes: 158 additions & 45 deletions src/main/java/fi/dy/masa/litematica/schematic/LitematicaSchematic.java
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,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 @@ -1290,6 +1291,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 @@ -1336,38 +1359,84 @@ 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
{
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
Map<BlockPos, NbtCompound> tileEntities = this.readSpongeBlockEntitiesFromTag(blocksTag, spongeVersion);
tileEntities = this.convertTileEntities_to_1_20_5(tileEntities, minecraftDataVersion);
this.tileEntities.put(schematicName, tileEntities);
}
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 @@ -1385,39 +1454,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");

if (spongeVersion >= 3)
{
NbtCompound entityData = entityEntry.getCompound("Data");

entities.add(new EntityInfo(pos, entityData));
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 @@ -1426,47 +1515,71 @@ 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;
}

Vec3i size = readSizeFromTagSponge(tag);

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

// Can't really use the Data Fixer for the Block State Palette in this format,
// so we're just going to ignore it, as long as we fix the Tile/Entities.
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;
}

Map<BlockPos, NbtCompound> tileEntities = this.readSpongeBlockEntitiesFromTag(tag);
tileEntities = this.convertTileEntities_to_1_20_5(tileEntities, minecraftDataVersion);
this.tileEntities.put(name, tileEntities);
if (spongeVersion < 3)
{
Map<BlockPos, NbtCompound> tileEntities = this.readSpongeBlockEntitiesFromTag(tag, spongeVersion);
tileEntities = this.convertTileEntities_to_1_20_5(tileEntities, minecraftDataVersion);
this.tileEntities.put(name, tileEntities);
}

List<EntityInfo> entities = this.readSpongeEntitiesFromTag(tag, offset, spongeVersion);
entities = this.convertSpongeEntities_to_1_20_5(entities, minecraftDataVersion);
this.entities.put(name, entities);

List<LitematicaSchematic.EntityInfo> enities = this.readSpongeEntitiesFromTag(tag, offset);
enities = this.convertSpongeEntities_to_1_20_5(enities, minecraftDataVersion);
this.entities.put(name, enities);
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 Expand Up @@ -1792,7 +1905,7 @@ private NbtList convertEntities_to_1_20_5(NbtList oldEntitiesList, int minecraft
return oldEntitiesList;
}

private List<LitematicaSchematic.EntityInfo> convertSpongeEntities_to_1_20_5(List<LitematicaSchematic.EntityInfo> oldEntitiesList, int minecraftDataVersion)
private List<EntityInfo> convertSpongeEntities_to_1_20_5(List<EntityInfo> oldEntitiesList, int minecraftDataVersion)
{
if (minecraftDataVersion < Configs.Generic.DATAFIXER_DEFAULT_SCHEMA.getIntegerValue())
{
Expand All @@ -1801,7 +1914,7 @@ private List<LitematicaSchematic.EntityInfo> convertSpongeEntities_to_1_20_5(Lis

if (minecraftDataVersion < LitematicaSchematic.MINECRAFT_DATA_VERSION)
{
List<LitematicaSchematic.EntityInfo> newEntitiesList = new ArrayList<>();
List<EntityInfo> newEntitiesList = new ArrayList<>();

Litematica.logger.info("SpongeSchematic: executing Vanilla DataFixer for Entities DataVersion {} -> {}", minecraftDataVersion, LitematicaSchematic.MINECRAFT_DATA_VERSION);

Expand Down Expand Up @@ -1871,7 +1984,7 @@ private <T> Map<BlockPos, OrderedTick<T>> readPendingTicksFromNBT(NbtList tagLis
// Don't crash on invalid ResourceLocation in 1.13+
try
{
target = registry.get(new Identifier(tag.getString(tagName)));
target = registry.get(Identifier.tryParse(tag.getString(tagName)));

if (target == null || target == emptyValue)
{
Expand Down

0 comments on commit 7b8b3d4

Please sign in to comment.