diff --git a/src/net/torvald/terrarum/console/CommandDict.kt b/src/net/torvald/terrarum/console/CommandDict.kt index 6a4acbe45..127cf7260 100644 --- a/src/net/torvald/terrarum/console/CommandDict.kt +++ b/src/net/torvald/terrarum/console/CommandDict.kt @@ -60,6 +60,7 @@ object CommandDict { "savetest" to SavegameWriterTest, /* !! */"exportmeta" to ExportMeta, + /* !! */"exportworld" to ExportWorld, /* !! */"importlayer" to ImportLayerData, /* !! */"exportfborgb" to ExportRendererFboRGB ) diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index dd865fcde..87a921215 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -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 - @TEMzPayload("TdMG", TEMzPayload.INT48_FLOAT_PAIR) val terrainDamages: HashMap - @TEMzPayload("FlTP", TEMzPayload.INT48_SHORT_PAIR) val fluidTypes: HashMap - @TEMzPayload("FlFL", TEMzPayload.INT48_FLOAT_PAIR) val fluidFills: HashMap /** * Single block can have multiple conduits, different types of conduits are stored separately. */ - @TEMzPayload("WiNt", TEMzPayload.EXTERNAL_JSON) private val wirings: HashMap private val wiringGraph = HashMap>() @@ -106,7 +106,6 @@ open class GameWorld : Disposable { ) - @TEMzPayload("TMaP", TEMzPayload.EXTERNAL_JSON) val tileNumberToNameMap: HashMap // does not go to the savefile val tileNameToNumberMap: HashMap @@ -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( + * """".": ${Json(JsonWriter.OutputType.json).toJson()}""" + * ) + * ``` + */ + open fun getJsonFields(): List { + 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() +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/console/ExportMeta.kt b/src/net/torvald/terrarum/modulebasegame/console/ExportMeta.kt index 8f238bc23..035715a8e 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ExportMeta.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ExportMeta.kt @@ -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) { + 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") + } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt b/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt index 7b2280eb5..e61277bec 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameworld/GameWorldExtension.kt @@ -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 { + return super.getJsonFields() + arrayListOf( + """"basegame.economy": ${Json(JsonWriter.OutputType.json).toJson(economy)}""" + ) + } + } \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/WriteMeta.kt b/src/net/torvald/terrarum/serialise/WriteMeta.kt index c9afa9caf..d0217841c 100644 --- a/src/net/torvald/terrarum/serialise/WriteMeta.kt +++ b/src/net/torvald/terrarum/serialise/WriteMeta.kt @@ -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() } \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/WriteWorld.kt b/src/net/torvald/terrarum/serialise/WriteWorld.kt index d6ea81bae..6674a409b 100644 --- a/src/net/torvald/terrarum/serialise/WriteWorld.kt +++ b/src/net/torvald/terrarum/serialise/WriteWorld.kt @@ -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() - } - -} \ No newline at end of file +} diff --git a/work_files/DataFormats/just-json-it-saveformat.md b/work_files/DataFormats/just-json-it-saveformat.md index 87133ea0a..e0385c14b 100644 --- a/work_files/DataFormats/just-json-it-saveformat.md +++ b/work_files/DataFormats/just-json-it-saveformat.md @@ -40,37 +40,38 @@ File is named as `"world"+world_index+".json"` genver: 4, /* generator version in integer */ time_t: , terr: { - s: 33554432, h: "a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11", b: }, wall: { - s: 33554432, h: , b: }, tdmg: { - s: 8795, h: , b: }, wdmg: { - s: 2, h: , b: }, - flui: { - s: 15734 + flut: { h: , - b: + b: + }, + fluf: { + h: , + b: }, wire: { - s: 2, h: , - b: + b: + }, + wirg: { + h: , + b: }, tmap: { - s: 4316, h: , b: }