mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
map data format adds world generator version and fluids
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -123,6 +121,16 @@ fun Int.toLittle() = byteArrayOf(
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
this.ushr(24).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts int as 2-byte array, discarding the sign.*/
|
||||
fun Int.toULittleShort() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte()
|
||||
)
|
||||
/** Converts int as 2-byte array, preserving the sign. In other words, it converts int to short. */
|
||||
fun Int.toLittleShort() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.shr(8).and(0xFF).toByte()
|
||||
)
|
||||
fun Long.toLittle() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
@@ -133,7 +141,8 @@ fun Long.toLittle() = byteArrayOf(
|
||||
this.ushr(48).and(0xFF).toByte(),
|
||||
this.ushr(56).and(0xFF).toByte()
|
||||
)
|
||||
fun Long.toLittle48() = byteArrayOf(
|
||||
/** Converts long as 6-byte array, discarding the sign. */
|
||||
fun Long.toULittle48() = byteArrayOf(
|
||||
this.and(0xFF).toByte(),
|
||||
this.ushr(8).and(0xFF).toByte(),
|
||||
this.ushr(16).and(0xFF).toByte(),
|
||||
@@ -150,6 +159,14 @@ fun ByteArray.toLittleInt() =
|
||||
this[1].toUint().shl(8) or
|
||||
this[2].toUint().shl(16) or
|
||||
this[3].toUint().shl(24)
|
||||
fun ByteArray.toULittleShort() =
|
||||
if (this.size != 4) throw Error("Array not in size of 2")
|
||||
else this[0].toUint() or
|
||||
this[1].toUint().shl(8)
|
||||
fun ByteArray.toLittleShort() =
|
||||
if (this.size != 4) throw Error("Array not in size of 2")
|
||||
else this[0].toUint() or
|
||||
this[1].toInt().shl(8)
|
||||
fun ByteArray.toLittleLong() =
|
||||
if (this.size != 8) throw Error("Array not in size of 8")
|
||||
else this[0].toUlong() or
|
||||
@@ -171,4 +188,6 @@ fun ByteArray.toLittleInt48() =
|
||||
fun ByteArray.toLittleFloat() = java.lang.Float.intBitsToFloat(this.toLittleInt())
|
||||
|
||||
fun Byte.toUlong() = java.lang.Byte.toUnsignedLong(this)
|
||||
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
|
||||
fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this)
|
||||
|
||||
const val WORLD_GENERATOR_VERSION = 1
|
||||
|
||||
@@ -6,13 +6,12 @@ import net.torvald.terrarum.gameworld.BlockAddress
|
||||
import net.torvald.terrarum.gameworld.FluidType
|
||||
import net.torvald.terrarum.gameworld.MapLayer
|
||||
import net.torvald.terrarum.gameworld.PairedMapLayer
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.toHex
|
||||
import java.io.*
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import kotlin.IllegalArgumentException
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
@@ -45,6 +44,7 @@ internal object ReadLayerDataLzma {
|
||||
val layerCount = inputStream.read(1)[0].toUint()
|
||||
val payloadCount = inputStream.read(1)[0].toUint()
|
||||
val compression = inputStream.read(1)[0].toUint()
|
||||
val generatorVer = inputStream.read(2).toULittleShort()
|
||||
val width = inputStream.read(4).toLittleInt()
|
||||
val height = inputStream.read(4).toLittleInt()
|
||||
val spawnAddress = inputStream.read(6).toLittleInt48()
|
||||
@@ -55,6 +55,7 @@ internal object ReadLayerDataLzma {
|
||||
printdbg(this, "Layers count: $layerCount")
|
||||
printdbg(this, "Payloads count: $payloadCount")
|
||||
printdbg(this, "Compression: $compression")
|
||||
printdbg(this, "World generator version: $generatorVer")
|
||||
printdbg(this, "Dimension: ${width}x$height")
|
||||
|
||||
// read payloads
|
||||
|
||||
@@ -4,14 +4,16 @@ import net.torvald.terrarum.AppLoader.printdbg
|
||||
import net.torvald.terrarum.gameworld.BlockAddress
|
||||
import net.torvald.terrarum.gameworld.MapLayer
|
||||
import net.torvald.terrarum.gameworld.PairedMapLayer
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.toHex
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import java.util.zip.Inflater
|
||||
import kotlin.IllegalArgumentException
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
@@ -44,6 +46,7 @@ internal object ReadLayerDataZip {
|
||||
val layerCount = inputStream.read(1)[0].toUint()
|
||||
val payloadCount = inputStream.read(1)[0].toUint()
|
||||
val compression = inputStream.read(1)[0].toUint()
|
||||
val generatorVer = inputStream.read(2).toULittleShort()
|
||||
val width = inputStream.read(4).toLittleInt()
|
||||
val height = inputStream.read(4).toLittleInt()
|
||||
val spawnAddress = inputStream.read(6).toLittleInt48()
|
||||
@@ -54,6 +57,7 @@ internal object ReadLayerDataZip {
|
||||
printdbg(this, "Layers count: $layerCount")
|
||||
printdbg(this, "Payloads count: $payloadCount")
|
||||
printdbg(this, "Compression: $compression")
|
||||
printdbg(this, "World generator version: $generatorVer")
|
||||
printdbg(this, "Dimension: ${width}x$height")
|
||||
|
||||
// read payloads
|
||||
|
||||
@@ -88,3 +88,5 @@ internal object WriteLayerData {
|
||||
|
||||
|
||||
}
|
||||
|
||||
const val WORLD_WRITER_FORMAT_VERSION = 3
|
||||
@@ -2,12 +2,9 @@ package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.utils.compression.Lzma
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.console.EchoError
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import java.io.*
|
||||
import java.nio.charset.Charset
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
|
||||
/**
|
||||
@@ -33,10 +30,11 @@ internal object WriteLayerDataLzma {
|
||||
val LAYERS_FILENAME = "world"
|
||||
|
||||
val MAGIC = byteArrayOf(0x54, 0x45, 0x4D, 0x7A)
|
||||
val VERSION_NUMBER = 3.toByte()
|
||||
val VERSION_NUMBER = WORLD_WRITER_FORMAT_VERSION.toByte()
|
||||
val NUMBER_OF_LAYERS = 3.toByte()
|
||||
val NUMBER_OF_PAYLOADS = 5.toByte()
|
||||
val COMPRESSION_ALGORITHM = 2.toByte()
|
||||
val GENERATOR_VERSION = WORLD_GENERATOR_VERSION.toULittleShort()
|
||||
val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64)
|
||||
val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1)
|
||||
val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2)
|
||||
@@ -77,7 +75,7 @@ internal object WriteLayerDataLzma {
|
||||
fun wb(byte: Byte) { outputStream.write(byte.toInt()) }
|
||||
//fun wb(byte: Int) { outputStream.write(byte) }
|
||||
fun wi32(int: Int) { wb(int.toLittle()) }
|
||||
fun wi48(long: Long) { wb(long.toLittle48()) }
|
||||
fun wi48(long: Long) { wb(long.toULittle48()) }
|
||||
fun wi64(long: Long) { wb(long.toLittle()) }
|
||||
fun wf32(float: Float) { wi32(float.toRawBits()) }
|
||||
|
||||
@@ -88,7 +86,7 @@ internal object WriteLayerDataLzma {
|
||||
|
||||
|
||||
// all the necessary headers
|
||||
wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM)
|
||||
wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM); wb(GENERATOR_VERSION)
|
||||
|
||||
// world width, height, and spawn point
|
||||
wi32(world.width); wi32(world.height)
|
||||
@@ -122,11 +120,11 @@ internal object WriteLayerDataLzma {
|
||||
|
||||
// TdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("TdMG".toByteArray())
|
||||
wi48(world.terrainDamages.size.toLong())
|
||||
wi48(world.terrainDamages.size * 10L)
|
||||
|
||||
|
||||
world.terrainDamages.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toLittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream)
|
||||
}
|
||||
|
||||
@@ -134,16 +132,42 @@ internal object WriteLayerDataLzma {
|
||||
|
||||
// WdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("WdMG".toByteArray())
|
||||
wi48(world.wallDamages.size.toLong())
|
||||
wi48(world.wallDamages.size * 10L)
|
||||
|
||||
|
||||
world.wallDamages.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toLittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlTP payload
|
||||
wb(PAYLOAD_HEADER); wb("FlTP".toByteArray())
|
||||
wi48(world.fluidTypes.size * 8L)
|
||||
|
||||
|
||||
world.fluidTypes.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.value.toLittleShort()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlFL payload
|
||||
wb(PAYLOAD_HEADER); wb("FlFL".toByteArray())
|
||||
wi48(world.fluidFills.size * 10L)
|
||||
|
||||
|
||||
world.fluidFills.forEach { t, u ->
|
||||
Lzma.compress(ByteArrayInputStream(t.toULittle48()), outputStream)
|
||||
Lzma.compress(ByteArrayInputStream(u.toRawBits().toLittle()), outputStream)
|
||||
}
|
||||
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
|
||||
|
||||
// write footer
|
||||
wb(FILE_FOOTER)
|
||||
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.console.EchoError
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.nio.charset.Charset
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
/**
|
||||
* This object only writes a file named 'worldinfo1'.
|
||||
@@ -39,10 +35,11 @@ internal object WriteLayerDataZip {
|
||||
val LAYERS_FILENAME = "world"
|
||||
|
||||
val MAGIC = byteArrayOf(0x54, 0x45, 0x4D, 0x7A)
|
||||
val VERSION_NUMBER = 3.toByte()
|
||||
val VERSION_NUMBER = WORLD_WRITER_FORMAT_VERSION.toByte()
|
||||
val NUMBER_OF_LAYERS = 3.toByte()
|
||||
val NUMBER_OF_PAYLOADS = 5.toByte()
|
||||
val COMPRESSION_ALGORITHM = 1.toByte()
|
||||
val GENERATOR_VERSION = WORLD_GENERATOR_VERSION.toULittleShort()
|
||||
val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64)
|
||||
val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1)
|
||||
val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2)
|
||||
@@ -83,7 +80,7 @@ internal object WriteLayerDataZip {
|
||||
fun wb(byte: Byte) { outputStream.write(byte.toInt()) }
|
||||
//fun wb(byte: Int) { outputStream.write(byte) }
|
||||
fun wi32(int: Int) { wb(int.toLittle()) }
|
||||
fun wi48(long: Long) { wb(long.toLittle48()) }
|
||||
fun wi48(long: Long) { wb(long.toULittle48()) }
|
||||
fun wi64(long: Long) { wb(long.toLittle()) }
|
||||
fun wf32(float: Float) { wi32(float.toRawBits()) }
|
||||
|
||||
@@ -94,7 +91,7 @@ internal object WriteLayerDataZip {
|
||||
|
||||
|
||||
// all the necessary headers
|
||||
wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM)
|
||||
wb(MAGIC); wb(VERSION_NUMBER); wb(NUMBER_OF_LAYERS); wb(NUMBER_OF_PAYLOADS); wb(COMPRESSION_ALGORITHM); wb(GENERATOR_VERSION)
|
||||
|
||||
// world width, height, and spawn point
|
||||
wi32(world.width); wi32(world.height)
|
||||
@@ -134,12 +131,12 @@ internal object WriteLayerDataZip {
|
||||
|
||||
// TdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("TdMG".toByteArray())
|
||||
wi48(world.terrainDamages.size.toLong())
|
||||
wi48(world.terrainDamages.size * 10L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION), true)
|
||||
|
||||
world.terrainDamages.forEach { t, u ->
|
||||
deflater.write(t.toLittle48())
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.toRawBits().toLittle())
|
||||
}
|
||||
|
||||
@@ -148,18 +145,49 @@ internal object WriteLayerDataZip {
|
||||
|
||||
// WdMG payload
|
||||
wb(PAYLOAD_HEADER); wb("WdMG".toByteArray())
|
||||
wi48(world.wallDamages.size.toLong())
|
||||
wi48(world.wallDamages.size * 10L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION), true)
|
||||
|
||||
world.wallDamages.forEach { t, u ->
|
||||
deflater.write(t.toLittle48())
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.toRawBits().toLittle())
|
||||
}
|
||||
|
||||
deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlTP payload
|
||||
wb(PAYLOAD_HEADER); wb("FlTP".toByteArray())
|
||||
wi48(world.fluidTypes.size * 8L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION), true)
|
||||
|
||||
world.fluidTypes.forEach { t, u ->
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.value.toLittleShort())
|
||||
}
|
||||
|
||||
deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
// FlFL payload
|
||||
wb(PAYLOAD_HEADER); wb("FlFL".toByteArray())
|
||||
wi48(world.fluidFills.size * 10L)
|
||||
|
||||
deflater = DeflaterOutputStream(outputStream, Deflater(Deflater.BEST_COMPRESSION), true)
|
||||
|
||||
world.fluidFills.forEach { t, u ->
|
||||
deflater.write(t.toULittle48())
|
||||
deflater.write(u.toRawBits().toLittle())
|
||||
}
|
||||
|
||||
deflater.finish()
|
||||
wb(PAYLOAD_FOOTER)
|
||||
|
||||
|
||||
|
||||
|
||||
// write footer
|
||||
wb(FILE_FOOTER)
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import net.torvald.terrarum.AppLoader
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.PlayerBuilder
|
||||
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
|
||||
import net.torvald.terrarum.modulebasegame.weather.WeatherMixer
|
||||
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
|
||||
@@ -101,11 +99,11 @@ object WriteWorldInfo {
|
||||
metaOut.write((world as GameWorldExtension).time.TIME_T.toLittle())
|
||||
|
||||
// creation time (real world time)
|
||||
metaOut.write(world.creationTime.toLittle48())
|
||||
metaOut.write(world.creationTime.toULittle48())
|
||||
|
||||
// time at save (real world time)
|
||||
val timeNow = System.currentTimeMillis() / 1000L
|
||||
metaOut.write(timeNow.toLittle48())
|
||||
metaOut.write(timeNow.toULittle48())
|
||||
|
||||
// get playtime and save it
|
||||
val timeToAdd = (timeNow - world.loadTime).toInt()
|
||||
|
||||
@@ -8,7 +8,7 @@ Ord Hex Description
|
||||
02 4D M
|
||||
03 7A z # 'z' because it's compressed
|
||||
|
||||
04 03 Version revision number (unreleased numbers also count)
|
||||
04 03 Version revision number of this format (unreleased numbers also count)
|
||||
|
||||
05 03 Number of layers, NOT the number of payload
|
||||
|
||||
@@ -17,22 +17,25 @@ Ord Hex Description
|
||||
07 01 Compression algorithm, 0 for none, 1 for DEFLATE, 2 for LZMA, otherwise undefined (maybe LZMA2 for the future?)
|
||||
Value of 01 (DEFLATE) is recommended for its faster compression
|
||||
|
||||
08 World width
|
||||
09 World width
|
||||
08 World generator version. If the generator adds new feature (e.g. new ores, new buildings)
|
||||
09 this number must be incremented by one.
|
||||
|
||||
0A World width
|
||||
0B World width
|
||||
0C World width
|
||||
0D World width
|
||||
|
||||
0C World height
|
||||
0D World height
|
||||
0E World height
|
||||
0F World height
|
||||
10 World height
|
||||
11 World height
|
||||
|
||||
10 Default spawn coord in Absolute Tile Number
|
||||
11 Default spawn coord in Absolute Tile Number
|
||||
12 Default spawn coord in Absolute Tile Number
|
||||
13 Default spawn coord in Absolute Tile Number
|
||||
14 Default spawn coord in Absolute Tile Number
|
||||
15 Default spawn coord in Absolute Tile Number
|
||||
16 Default spawn coord in Absolute Tile Number
|
||||
17 Default spawn coord in Absolute Tile Number
|
||||
|
||||
# Payload
|
||||
#
|
||||
@@ -62,6 +65,13 @@ Payload "TdMG" -- world terrain damage data, array of: (Int48 tileAddress, Float
|
||||
Payload "WdMG" -- world walls damage data, array of: (Int48 tileAddress, Float32 damage)
|
||||
Uncompressed size will be arbitrary (multiple of tens)
|
||||
|
||||
Payload "FlTP" -- world fluid types, array of: (Int48 tileAddress, Signed Int16 type)
|
||||
Uncompressed size will be arbitrary (multiple of eights)
|
||||
|
||||
Payload "FlFL" -- world fluid fills, array of: (Int48 tileAddress, Float32 amount)
|
||||
Uncompressed size will be arbitrary (multiple of tens)
|
||||
If the 'amount' < 0.0001f (WorldSimulator.FLUID_MIN_MASS), the entry must be discarded
|
||||
|
||||
EOF 45 E
|
||||
EOF 6E n
|
||||
EOF 64 d
|
||||
|
||||
Reference in New Issue
Block a user