@@ -19,6 +19,7 @@ package com.lambda.util.extension
1919
2020import com.lambda.Lambda.mc
2121import com.lambda.util.VarIntIterator
22+ import com.lambda.util.math.MathUtils.logCap
2223import com.lambda.util.world.FastVector
2324import com.lambda.util.world.fastVectorOf
2425import com.lambda.util.world.x
@@ -31,6 +32,7 @@ import net.minecraft.nbt.NbtList
3132import net.minecraft.registry.RegistryEntryLookup
3233import net.minecraft.structure.StructureTemplate
3334import kotlin.experimental.and
35+ import kotlin.math.abs
3436
3537private fun positionFromIndex (width : Int , length : Int , index : Int ): FastVector {
3638 val y = index / (width * length)
@@ -49,8 +51,11 @@ fun StructureTemplate.readNbtOrException(
4951fun StructureTemplate.readSpongeOrException (
5052 lookup : RegistryEntryLookup <Block >,
5153 nbt : NbtCompound ,
52- ): Throwable ? = when (nbt.getInt(" Version" )) {
53- 1 , 2 , 3 -> readSpongeV1OrException(lookup, nbt)
54+ ): Throwable ? = when (nbt.getInt(" Version" ) +
55+ nbt.getCompound(" Schematic" ).getInt(" Version" ))
56+ {
57+ 1 , 2 -> readSpongeV1OrException(lookup, nbt)
58+ 3 -> readSpongeV3OrException(lookup, nbt)
5459 else -> IllegalStateException (" Invalid sponge schematic version" )
5560}
5661
@@ -86,12 +91,8 @@ private fun StructureTemplate.readSpongeV1OrException(
8691 // ?.takeIf { 274945015809L times 16 < it } ?: 0L
8792
8893 val palette = nbt.getCompound(" Palette" )
89-
90- val paletteMax = nbt.getInt(" PaletteMax" )
9194 val newPalette = NbtList ()
9295
93- if (palette.size != paletteMax) return IllegalStateException (" Block palette size does not match the provided size (corrupted?)" )
94-
9596 palette.keys
9697 .sortedBy { palette.getInt(it) }
9798 .forEach { key ->
@@ -143,16 +144,89 @@ private fun StructureTemplate.readSpongeV3OrException(
143144 lookup : RegistryEntryLookup <Block >,
144145 nbt : NbtCompound ,
145146): Throwable ? {
146- // Third revision
147- // - 3D Biome support
148- // - Rename Palette to BlockPalette
149- // - Wordsmithing varint and palette usages
150- nbt.put(" Palette" , nbt.getCompound(" BlockPalette" ))
147+ val schematic = nbt.getCompound(" Schematic" )
148+ val blocks = schematic.getCompound(" Blocks" )
149+
150+ schematic.put(" Palette" , blocks.getCompound(" Palette" ))
151+ schematic.putByteArray(" BlockData" , blocks.getByteArray(" Data" ))
152+
153+ nbt.clear()
154+ nbt.copyFrom(schematic)
151155
152156 return readSpongeV1OrException(lookup, nbt)
153157}
154158
155159fun StructureTemplate.readLitematicaOrException (
156160 lookup : RegistryEntryLookup <Block >,
157161 nbt : NbtCompound ,
158- ): Throwable = NotImplementedError (" Litematica is not supported, you can help by contributing to the project" )
162+ ): Throwable ? {
163+ val version = nbt.getInt(" MinecraftDataVersion" )
164+
165+ val metadata = nbt.getCompound(" Metadata" )
166+ val author = metadata.getString(" Author" )
167+
168+ val dimension = metadata.getVector(" EnclosingSize" )
169+
170+ val newPalette = NbtList ()
171+ val newBlocks = NbtList ()
172+
173+ val regions = nbt.getCompound(" Regions" )
174+ regions.keys.map { regions.getCompound(it) }
175+ .forEach {
176+ val position = it.getVector(" Position" )
177+ val size = it.getVector(" Size" )
178+
179+ val xSizeAbs = abs(size.x)
180+ val ySizeAbs = abs(size.y)
181+ val zSizeAbs = abs(size.z)
182+
183+ if (size.x < 0 ) position.x % = size.x + 1
184+ if (size.y < 0 ) position.y % = size.y + 1
185+ if (size.z < 0 ) position.z % = size.z + 1
186+
187+ // The litematic's block state palette is the same as nbt
188+ newPalette.addAll(it.getList(" BlockStatePalette" , 10 ))
189+
190+ val palette = it.getLongArray(" BlockStates" )
191+ val bits = palette.size.logCap(2 )
192+ val maxEntryValue = (1 shl bits) - 1L
193+
194+ for (x in 0 until xSizeAbs) {
195+ for (y in 0 until ySizeAbs) {
196+ for (z in 0 until zSizeAbs) {
197+ val index = (y * xSizeAbs * zSizeAbs) + z * xSizeAbs + x
198+
199+ val startOffset = index * bits
200+ val startArrIndex = startOffset / 64
201+ val endArrIndex = ((index + 1 ) * bits - 1 ) / 64
202+ val startBitOffset = startOffset % 64
203+
204+
205+ val stateId = if (startArrIndex == endArrIndex) {
206+ palette[startArrIndex] ushr startBitOffset and maxEntryValue
207+ } else {
208+ (palette[startArrIndex] ushr startBitOffset or palette[endArrIndex] shl (64 - startBitOffset)) and maxEntryValue
209+ }
210+
211+ newBlocks.add(NbtCompound ().apply {
212+ putIntList(" pos" , x, y, z)
213+ putInt(" state" , stateId.toInt())
214+ })
215+ }
216+ }
217+ }
218+ }
219+
220+ // Construct a structure compatible nbt compound
221+ nbt.putInt(" DataVersion" , version)
222+ nbt.putIntList(" size" , dimension.x, dimension.y, dimension.z)
223+ nbt.put(" palette" , newPalette)
224+ nbt.put(" blocks" , newBlocks)
225+ nbt.putString(" author" , author)
226+
227+ // Fix the data for future versions
228+ DataFixTypes .STRUCTURE .update(mc.dataFixer, nbt, version)
229+
230+ // Use the StructureTemplate NBT read utils in order to construct the template
231+ return readNbtOrException(lookup, nbt)
232+ }
0 commit comments