mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
serialiser for world
This commit is contained in:
@@ -60,6 +60,7 @@ object CommandDict {
|
||||
"savetest" to SavegameWriterTest,
|
||||
|
||||
/* !! */"exportmeta" to ExportMeta,
|
||||
/* !! */"exportworld" to ExportWorld,
|
||||
/* !! */"importlayer" to ImportLayerData,
|
||||
/* !! */"exportfborgb" to ExportRendererFboRGB
|
||||
)
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
package net.torvald.terrarum.gameworld
|
||||
|
||||
import com.badlogic.gdx.utils.Disposable
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import com.badlogic.gdx.utils.JsonValue
|
||||
import com.badlogic.gdx.utils.JsonWriter
|
||||
import net.torvald.gdx.graphics.Cvec
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.AppLoader.printdbg
|
||||
@@ -10,9 +13,13 @@ import net.torvald.terrarum.blockproperties.BlockCodex
|
||||
import net.torvald.terrarum.blockproperties.Fluid
|
||||
import net.torvald.terrarum.gameactors.WireActor
|
||||
import net.torvald.terrarum.gameitem.ItemID
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.serialise.Ascii85
|
||||
import net.torvald.util.SortedArrayList
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
import org.dyn4j.geometry.Vector2
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import kotlin.experimental.and
|
||||
import kotlin.experimental.or
|
||||
import kotlin.math.absoluteValue
|
||||
@@ -47,9 +54,7 @@ open class GameWorld : Disposable {
|
||||
open val loadTime: Long = System.currentTimeMillis() / 1000L
|
||||
|
||||
//layers
|
||||
@TEMzPayload("WALL", TEMzPayload.INT16_LITTLE)
|
||||
val layerWall: BlockLayer
|
||||
@TEMzPayload("TERR", TEMzPayload.INT16_LITTLE)
|
||||
val layerTerrain: BlockLayer
|
||||
//val layerWire: MapLayer
|
||||
|
||||
@@ -61,19 +66,14 @@ open class GameWorld : Disposable {
|
||||
/** Tilewise spawn point */
|
||||
open var spawnY: Int
|
||||
|
||||
@TEMzPayload("WdMG", TEMzPayload.INT48_FLOAT_PAIR)
|
||||
val wallDamages: HashMap<BlockAddress, Float>
|
||||
@TEMzPayload("TdMG", TEMzPayload.INT48_FLOAT_PAIR)
|
||||
val terrainDamages: HashMap<BlockAddress, Float>
|
||||
@TEMzPayload("FlTP", TEMzPayload.INT48_SHORT_PAIR)
|
||||
val fluidTypes: HashMap<BlockAddress, FluidType>
|
||||
@TEMzPayload("FlFL", TEMzPayload.INT48_FLOAT_PAIR)
|
||||
val fluidFills: HashMap<BlockAddress, Float>
|
||||
|
||||
/**
|
||||
* Single block can have multiple conduits, different types of conduits are stored separately.
|
||||
*/
|
||||
@TEMzPayload("WiNt", TEMzPayload.EXTERNAL_JSON)
|
||||
private val wirings: HashMap<BlockAddress, WiringNode>
|
||||
|
||||
private val wiringGraph = HashMap<BlockAddress, HashMap<ItemID, WiringSimCell>>()
|
||||
@@ -106,7 +106,6 @@ open class GameWorld : Disposable {
|
||||
)
|
||||
|
||||
|
||||
@TEMzPayload("TMaP", TEMzPayload.EXTERNAL_JSON)
|
||||
val tileNumberToNameMap: HashMap<Int, ItemID>
|
||||
// does not go to the savefile
|
||||
val tileNameToNumberMap: HashMap<ItemID, Int>
|
||||
@@ -672,6 +671,67 @@ open class GameWorld : Disposable {
|
||||
open fun updateWorldTime(delta: Float) {
|
||||
worldTime.update(delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lines that are part of the entire JSON
|
||||
*
|
||||
* To extend this function, you can code something like this:
|
||||
* ```
|
||||
* return super.getJsonFields() + arrayListOf(
|
||||
* """"<myModuleName>.<myNewObject>": ${Json(JsonWriter.OutputType.json).toJson(<myNewObject>)}"""
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
open fun getJsonFields(): List<String> {
|
||||
fun Byte.tostr() = this.toInt().and(255).toString(16).padStart(2,'0')
|
||||
|
||||
val tdmgstr = Json(JsonWriter.OutputType.json).toJson(terrainDamages)
|
||||
val wdmgstr = Json(JsonWriter.OutputType.json).toJson(wallDamages)
|
||||
val flutstr = Json(JsonWriter.OutputType.json).toJson(fluidTypes)
|
||||
val flufstr = Json(JsonWriter.OutputType.json).toJson(fluidFills)
|
||||
val wirestr = Json(JsonWriter.OutputType.json).toJson(wirings)
|
||||
val wirgstr = Json(JsonWriter.OutputType.json).toJson(wiringGraph)
|
||||
|
||||
val digester = DigestUtils.getSha256Digest()
|
||||
|
||||
layerTerrain.bytesIterator().forEachRemaining { digester.update(it) }
|
||||
val terrhash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() }
|
||||
layerWall.bytesIterator().forEachRemaining { digester.update(it) }
|
||||
val wallhash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() }
|
||||
|
||||
return arrayListOf(
|
||||
""""worldname": "$worldName"""",
|
||||
""""comp": 1""",
|
||||
""""width": $width""",
|
||||
""""height": $height""",
|
||||
""""genver": 4""",
|
||||
""""time_t": ${worldTime.TIME_T}""",
|
||||
""""terr": {
|
||||
|"h": "$terrhash",
|
||||
|"b": "${blockLayerToStr(layerTerrain)}"}""".trimMargin(),
|
||||
""""wall": {
|
||||
|"h": "$wallhash",
|
||||
|"b": "${blockLayerToStr(layerWall)}"}""".trimMargin(),
|
||||
""""tdmg": {
|
||||
|"h": "${StringBuilder().let { sb -> digester.digest(tdmgstr.toByteArray()).forEach { sb.append(it.tostr()) }; sb.toString() }}",
|
||||
|"b": $tdmgstr}""".trimMargin(),
|
||||
""""wdmg": {
|
||||
|"h": "${StringBuilder().let { sb -> digester.digest(wdmgstr.toByteArray()).forEach { sb.append(it.tostr()) }; sb.toString() }}",
|
||||
|"b": $wdmgstr}""".trimMargin(),
|
||||
""""flut": {
|
||||
|"h": "${StringBuilder().let { sb -> digester.digest(flutstr.toByteArray()).forEach { sb.append(it.tostr()) }; sb.toString() }}",
|
||||
|"b": $flutstr}""".trimMargin(),
|
||||
""""fluf": {
|
||||
|"h": "${StringBuilder().let { sb -> digester.digest(flufstr.toByteArray()).forEach { sb.append(it.tostr()) }; sb.toString() }}",
|
||||
|"b": $flufstr}""".trimMargin(),
|
||||
""""wire": {
|
||||
|"h": "${StringBuilder().let { sb -> digester.digest(wirestr.toByteArray()).forEach { sb.append(it.tostr()) }; sb.toString() }}",
|
||||
|"b": $wirestr}""".trimMargin(),
|
||||
""""wirg": {
|
||||
|"h": "${StringBuilder().let { sb -> digester.digest(wirgstr.toByteArray()).forEach { sb.append(it.tostr()) }; sb.toString() }}",
|
||||
|"b": $wirgstr}""".trimMargin()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
infix fun Int.fmod(other: Int) = Math.floorMod(this, other)
|
||||
@@ -683,21 +743,34 @@ inline class FluidType(val value: Int) {
|
||||
fun abs() = this.value.absoluteValue
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param payloadName Payload name defined in Map Data Format.txt
|
||||
* * 4 Letters: regular payload
|
||||
* * 3 Letters: only valid for arrays with 16 elements, names are auto-generated by appending '0'..'9'+'a'..'f'. E.g.: 'CfL' turns into 'CfL0', 'CfL1' ... 'CfLe', 'CfLf'
|
||||
*
|
||||
* @param arg 0 for 8 MSBs of Terrain/Wall layer, 1 for 4 LSBs of Terrain/Wall layer, 2 for Int48-Float pair, 3 for Int48-Short pair, 4 for Int48-Int pair
|
||||
* @param b a BlockLayer
|
||||
* @return Bytes in [b] which are GZip'd then Ascii85-encoded
|
||||
*/
|
||||
annotation class TEMzPayload(val payloadName: String, val arg: Int) {
|
||||
companion object {
|
||||
const val EXTERNAL_JAVAPROPERTIES = -3
|
||||
const val EXTERNAL_CSV = -2
|
||||
const val EXTERNAL_JSON = -1
|
||||
const val INT16_LITTLE = 1
|
||||
const val INT48_FLOAT_PAIR = 2
|
||||
const val INT48_SHORT_PAIR = 3
|
||||
const val INT48_INT_PAIR = 4
|
||||
fun blockLayerToStr(b: BlockLayer): String {
|
||||
val sb = StringBuilder()
|
||||
val bo = ByteArray64GrowableOutputStream()
|
||||
val zo = GZIPOutputStream(bo)
|
||||
|
||||
b.bytesIterator().forEachRemaining {
|
||||
zo.write(it.toInt())
|
||||
}
|
||||
}
|
||||
zo.flush(); zo.close()
|
||||
|
||||
val ba = bo.toByteArray64()
|
||||
var bai = 0
|
||||
val buf = IntArray(4) { Ascii85.PAD_BYTE }
|
||||
ba.forEach {
|
||||
if (bai > 0 && bai % 4 == 0) {
|
||||
sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
buf.fill(Ascii85.PAD_BYTE)
|
||||
}
|
||||
|
||||
buf[bai % 4] = it.toInt() and 255
|
||||
|
||||
bai += 1
|
||||
}; sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import net.torvald.terrarum.console.ConsoleCommand
|
||||
import net.torvald.terrarum.console.Echo
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.serialise.WriteMeta
|
||||
import net.torvald.terrarum.serialise.WriteWorld
|
||||
import net.torvald.terrarum.utils.JsonWriter
|
||||
import java.io.IOException
|
||||
|
||||
@@ -31,4 +32,25 @@ object ExportMeta : ConsoleCommand {
|
||||
override fun printUsage() {
|
||||
Echo("Usage: Exportmeta")
|
||||
}
|
||||
}
|
||||
|
||||
object ExportWorld : ConsoleCommand {
|
||||
override fun execute(args: Array<String>) {
|
||||
try {
|
||||
val world = Terrarum.ingame!!.world
|
||||
val str = WriteWorld(Terrarum.ingame!! as TerrarumIngame).invoke()
|
||||
val writer = java.io.FileWriter(AppLoader.defaultDir + "/Exports/world${world.worldIndex}.json", false)
|
||||
writer.write(str)
|
||||
writer.close()
|
||||
Echo("Exportworld: exported to world${world.worldIndex}.json")
|
||||
}
|
||||
catch (e: IOException) {
|
||||
Echo("Exportworld: IOException raised.")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun printUsage() {
|
||||
Echo("Usage: Exportworld")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.torvald.terrarum.modulebasegame.gameworld
|
||||
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import com.badlogic.gdx.utils.JsonWriter
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.gameworld.WorldTime
|
||||
|
||||
@@ -37,4 +39,10 @@ class GameWorldExtension : GameWorld {
|
||||
init {
|
||||
}
|
||||
|
||||
override fun getJsonFields(): List<String> {
|
||||
return super.getJsonFields() + arrayListOf(
|
||||
""""basegame.economy": ${Json(JsonWriter.OutputType.json).toJson(economy)}"""
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,35 +65,35 @@ open class WriteMeta(val ingame: TerrarumIngame) {
|
||||
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b a ByteArray
|
||||
* @return Bytes in [b] which are GZip'd then Ascii85-encoded
|
||||
*/
|
||||
private fun bytesToZipdStr(b: ByteArray): String {
|
||||
val sb = StringBuilder()
|
||||
val bo = ByteArray64GrowableOutputStream()
|
||||
val zo = GZIPOutputStream(bo)
|
||||
/**
|
||||
* @param b a ByteArray
|
||||
* @return Bytes in [b] which are GZip'd then Ascii85-encoded
|
||||
*/
|
||||
fun bytesToZipdStr(b: ByteArray): String {
|
||||
val sb = StringBuilder()
|
||||
val bo = ByteArray64GrowableOutputStream()
|
||||
val zo = GZIPOutputStream(bo)
|
||||
|
||||
b.forEach {
|
||||
zo.write(it.toInt())
|
||||
}
|
||||
zo.flush(); zo.close()
|
||||
|
||||
val ba = bo.toByteArray64()
|
||||
var bai = 0
|
||||
val buf = IntArray(4) { Ascii85.PAD_BYTE }
|
||||
ba.forEach {
|
||||
if (bai > 0 && bai % 4 == 0) {
|
||||
sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
buf.fill(Ascii85.PAD_BYTE)
|
||||
}
|
||||
|
||||
buf[bai % 4] = it.toInt() and 255
|
||||
|
||||
bai += 1
|
||||
}; sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
|
||||
return sb.toString()
|
||||
b.forEach {
|
||||
zo.write(it.toInt())
|
||||
}
|
||||
zo.flush(); zo.close()
|
||||
|
||||
val ba = bo.toByteArray64()
|
||||
var bai = 0
|
||||
val buf = IntArray(4) { Ascii85.PAD_BYTE }
|
||||
ba.forEach {
|
||||
if (bai > 0 && bai % 4 == 0) {
|
||||
sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
buf.fill(Ascii85.PAD_BYTE)
|
||||
}
|
||||
|
||||
buf[bai % 4] = it.toInt() and 255
|
||||
|
||||
bai += 1
|
||||
}; sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
@@ -1,49 +1,21 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import com.badlogic.gdx.utils.JsonWriter
|
||||
import net.torvald.terrarum.gameworld.BlockLayer
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2021-08-23.
|
||||
*/
|
||||
class WriteWorld {
|
||||
class WriteWorld(val ingame: TerrarumIngame) {
|
||||
|
||||
open fun invoke(): String {
|
||||
|
||||
|
||||
return ""
|
||||
val world = ingame.world
|
||||
return "{${world.getJsonFields().joinToString(",\n")}}"
|
||||
}
|
||||
|
||||
/**
|
||||
* @param b a BlockLayer
|
||||
* @return Bytes in [b] which are GZip'd then Ascii85-encoded
|
||||
*/
|
||||
private fun blockLayerToStr(b: BlockLayer): String {
|
||||
val sb = StringBuilder()
|
||||
val bo = ByteArray64GrowableOutputStream()
|
||||
val zo = GZIPOutputStream(bo)
|
||||
|
||||
b.bytesIterator().forEachRemaining {
|
||||
zo.write(it.toInt())
|
||||
}
|
||||
zo.flush(); zo.close()
|
||||
|
||||
val ba = bo.toByteArray64()
|
||||
var bai = 0
|
||||
val buf = IntArray(4) { Ascii85.PAD_BYTE }
|
||||
ba.forEach {
|
||||
if (bai > 0 && bai % 4 == 0) {
|
||||
sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
buf.fill(Ascii85.PAD_BYTE)
|
||||
}
|
||||
|
||||
buf[bai % 4] = it.toInt() and 255
|
||||
|
||||
bai += 1
|
||||
}; sb.append(Ascii85.encode(buf[0], buf[1], buf[2], buf[3]))
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,37 +40,38 @@ File is named as `"world"+world_index+".json"`
|
||||
genver: 4, /* generator version in integer */
|
||||
time_t: <in-game TIME_T of this world>,
|
||||
terr: {
|
||||
s: 33554432,
|
||||
h: "a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11",
|
||||
b: <Ascii85-encoded gzipped terrain layerdata>
|
||||
},
|
||||
wall: {
|
||||
s: 33554432,
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped wall layerdata>
|
||||
},
|
||||
tdmg: {
|
||||
s: 8795,
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped terrain damage in JSON>
|
||||
},
|
||||
wdmg: {
|
||||
s: 2,
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped wall damage in JSON>
|
||||
},
|
||||
flui: {
|
||||
s: 15734
|
||||
flut: {
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped fluids in JSON>
|
||||
b: <Ascii85-encoded gzipped fluidTypes in JSON>
|
||||
},
|
||||
fluf: {
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped fluidFills in JSON>
|
||||
},
|
||||
wire: {
|
||||
s: 2,
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped wiring nodes in JSON>
|
||||
b: <Ascii85-encoded gzipped wirings in JSON>
|
||||
},
|
||||
wirg: {
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped wiringGraph in JSON>
|
||||
},
|
||||
tmap: {
|
||||
s: 4316,
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped tilenumber-to-tilename map in JSON>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user