mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-13 03:54:06 +09:00
using gdx's preferred way to generate world json
This commit is contained in:
@@ -56,7 +56,7 @@ open class GameWorld : Disposable {
|
|||||||
internal set
|
internal set
|
||||||
|
|
||||||
/** Used to calculate play time */
|
/** Used to calculate play time */
|
||||||
open val loadTime: Long = System.currentTimeMillis() / 1000L
|
@Transient open val loadTime: Long = System.currentTimeMillis() / 1000L
|
||||||
|
|
||||||
//layers
|
//layers
|
||||||
val layerWall: BlockLayer
|
val layerWall: BlockLayer
|
||||||
@@ -82,8 +82,8 @@ open class GameWorld : Disposable {
|
|||||||
private val wirings: HashMap<BlockAddress, WiringNode>
|
private val wirings: HashMap<BlockAddress, WiringNode>
|
||||||
|
|
||||||
private val wiringGraph = HashMap<BlockAddress, HashMap<ItemID, WiringSimCell>>()
|
private val wiringGraph = HashMap<BlockAddress, HashMap<ItemID, WiringSimCell>>()
|
||||||
private val WIRE_POS_MAP = intArrayOf(1,2,4,8)
|
@Transient private val WIRE_POS_MAP = intArrayOf(1,2,4,8)
|
||||||
private val WIRE_ANTIPOS_MAP = intArrayOf(4,8,1,2)
|
@Transient private val WIRE_ANTIPOS_MAP = intArrayOf(4,8,1,2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised.
|
* Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised.
|
||||||
@@ -102,7 +102,7 @@ open class GameWorld : Disposable {
|
|||||||
open var generatorSeed: Long = 0
|
open var generatorSeed: Long = 0
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
var disposed = false
|
@Transient var disposed = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val worldTime: WorldTime = WorldTime( // Year EPOCH (125), Month 1, Day 1 is implied
|
val worldTime: WorldTime = WorldTime( // Year EPOCH (125), Month 1, Day 1 is implied
|
||||||
@@ -113,7 +113,7 @@ open class GameWorld : Disposable {
|
|||||||
|
|
||||||
val tileNumberToNameMap: HashMap<Int, ItemID>
|
val tileNumberToNameMap: HashMap<Int, ItemID>
|
||||||
// does not go to the savefile
|
// does not go to the savefile
|
||||||
val tileNameToNumberMap: HashMap<ItemID, Int>
|
@Transient val tileNameToNumberMap: HashMap<ItemID, Int>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new world
|
* Create new world
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ class GameWorldExtension : GameWorld {
|
|||||||
//internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) : super(worldIndex, layerData, creationTIME_T, lastPlayTIME_T, totalPlayTime)
|
//internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) : super(worldIndex, layerData, creationTIME_T, lastPlayTIME_T, totalPlayTime)
|
||||||
|
|
||||||
|
|
||||||
val economy = GameEconomy()
|
var economy = GameEconomy()
|
||||||
|
internal set
|
||||||
|
|
||||||
// delegated properties //
|
// delegated properties //
|
||||||
/*val layerWall: MapLayer; get() = baseworld.layerWall
|
/*val layerWall: MapLayer; get() = baseworld.layerWall
|
||||||
|
|||||||
28
src/net/torvald/terrarum/serialise/ReadWorld.kt
Normal file
28
src/net/torvald/terrarum/serialise/ReadWorld.kt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package net.torvald.terrarum.serialise
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Json
|
||||||
|
import com.badlogic.gdx.utils.JsonValue
|
||||||
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameworld.GameEconomy
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2021-08-25.
|
||||||
|
*/
|
||||||
|
open class ReadWorld(val ingame: TerrarumIngame) {
|
||||||
|
|
||||||
|
open fun invoke(worldIndex: Int, metadata: JsonValue, worlddata: JsonValue) {
|
||||||
|
val json = Json()
|
||||||
|
val world = GameWorldExtension(
|
||||||
|
worldIndex,
|
||||||
|
worlddata.getInt("width"),
|
||||||
|
worlddata.getInt("height"),
|
||||||
|
metadata.getLong("creation_t"),
|
||||||
|
metadata.getLong("lastplay_t"),
|
||||||
|
metadata.getInt("playtime_t")
|
||||||
|
)
|
||||||
|
|
||||||
|
//world.economy = json.fromJson(GameEconomy::class.java, worlddata.get("basegame.economy").)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,16 +1,29 @@
|
|||||||
package net.torvald.terrarum.serialise
|
package net.torvald.terrarum.serialise
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Json
|
||||||
|
import com.badlogic.gdx.utils.JsonReader
|
||||||
|
import com.badlogic.gdx.utils.JsonValue
|
||||||
|
import com.badlogic.gdx.utils.JsonWriter
|
||||||
|
import net.torvald.terrarum.gameworld.BlockLayer
|
||||||
|
import net.torvald.terrarum.gameworld.WorldTime
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
|
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
|
||||||
|
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64GrowableOutputStream
|
||||||
|
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64InputStream
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
import java.util.zip.GZIPOutputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2021-08-23.
|
* Created by minjaesong on 2021-08-23.
|
||||||
*/
|
*/
|
||||||
class WriteWorld(val ingame: TerrarumIngame) {
|
open class WriteWorld(val ingame: TerrarumIngame) {
|
||||||
|
|
||||||
open fun invoke(): String {
|
open fun invoke(): String {
|
||||||
val world = ingame.world
|
val world = ingame.world
|
||||||
return "{${world.getJsonFields().joinToString(",\n")}}"
|
//return "{${world.getJsonFields().joinToString(",\n")}}"
|
||||||
|
return jsoner.toJson(world)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeToByteArray64(): ByteArray64 {
|
fun encodeToByteArray64(): ByteArray64 {
|
||||||
@@ -27,4 +40,148 @@ class WriteWorld(val ingame: TerrarumIngame) {
|
|||||||
ba.add('}'.code.toByte())
|
ba.add('}'.code.toByte())
|
||||||
return ba
|
return ba
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** dispose of the `offendingObject` after rejection! */
|
||||||
|
class BlockLayerHashMismatchError(val offendingObject: BlockLayer) : Error()
|
||||||
|
|
||||||
|
private fun Byte.tostr() = this.toInt().and(255).toString(16).padStart(2,'0')
|
||||||
|
private val digester = DigestUtils.getSha256Digest()
|
||||||
|
|
||||||
|
val jsoner = Json(JsonWriter.OutputType.json)
|
||||||
|
|
||||||
|
// install custom (de)serialiser
|
||||||
|
init {
|
||||||
|
// BigInteger
|
||||||
|
jsoner.setSerializer(BigInteger::class.java, object : Json.Serializer<BigInteger> {
|
||||||
|
|
||||||
|
override fun write(json: Json, obj: BigInteger?, knownType: Class<*>?) {
|
||||||
|
json.writeValue(obj?.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): BigInteger {
|
||||||
|
return BigInteger(jsonData.asString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// BlockLayer
|
||||||
|
jsoner.setSerializer(BlockLayer::class.java, object : Json.Serializer<BlockLayer> {
|
||||||
|
|
||||||
|
override fun write(json: Json, obj: BlockLayer, knownType: Class<*>?) {
|
||||||
|
digester.reset()
|
||||||
|
obj.bytesIterator().forEachRemaining { digester.update(it) }
|
||||||
|
val hash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() }
|
||||||
|
|
||||||
|
val layer = LayerInfo(hash, blockLayerToStr(obj), obj.width, obj.height)
|
||||||
|
json.writeValue(layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(json: Json, jsonData: JsonValue, type: Class<*>): BlockLayer {
|
||||||
|
// full auto
|
||||||
|
//return strToBlockLayer(json.fromJson(type, jsonData.toJson(JsonWriter.OutputType.minimal)) as LayerInfo)
|
||||||
|
|
||||||
|
// full manual
|
||||||
|
return strToBlockLayer(LayerInfo(
|
||||||
|
jsonData.getString("h"),
|
||||||
|
jsonData.getString("b"),
|
||||||
|
jsonData.getInt("x"),
|
||||||
|
jsonData.getInt("y")
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// WorldTime
|
||||||
|
jsoner.setSerializer(WorldTime::class.java, object : Json.Serializer<WorldTime> {
|
||||||
|
override fun write(json: Json, obj: WorldTime, knownType: Class<*>?) {
|
||||||
|
json.writeValue(obj.TIME_T)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): WorldTime {
|
||||||
|
return WorldTime(jsonData.asLong())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class LayerInfo(val h: String, val b: String, val x: Int, val y: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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)
|
||||||
|
|
||||||
|
// zip
|
||||||
|
b.bytesIterator().forEachRemaining {
|
||||||
|
zo.write(it.toInt())
|
||||||
|
}
|
||||||
|
zo.flush(); zo.close()
|
||||||
|
|
||||||
|
// enascii
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun strToBlockLayer(layerInfo: LayerInfo): BlockLayer {
|
||||||
|
val layer = BlockLayer(layerInfo.x, layerInfo.y)
|
||||||
|
val unasciidBytes = ByteArray64()
|
||||||
|
val unzipdBytes = ByteArray64()
|
||||||
|
|
||||||
|
// unascii
|
||||||
|
var bai = 0
|
||||||
|
val buf = CharArray(5) { Ascii85.PAD_CHAR }
|
||||||
|
layerInfo.b.forEach {
|
||||||
|
if (bai > 0 && bai % 5 == 0) {
|
||||||
|
Ascii85.decode(buf[0], buf[1], buf[2], buf[3], buf[4]).forEach { unasciidBytes.add(it) }
|
||||||
|
buf.fill(Ascii85.PAD_CHAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[bai % 5] = it
|
||||||
|
|
||||||
|
bai += 1
|
||||||
|
}; Ascii85.decode(buf[0], buf[1], buf[2], buf[3], buf[4]).forEach { unasciidBytes.add(it) }
|
||||||
|
|
||||||
|
// unzip
|
||||||
|
val zi = GZIPInputStream(ByteArray64InputStream(unasciidBytes))
|
||||||
|
while (true) {
|
||||||
|
val byte = zi.read()
|
||||||
|
if (byte == -1) break
|
||||||
|
unzipdBytes.add(byte.toByte())
|
||||||
|
}
|
||||||
|
zi.close()
|
||||||
|
|
||||||
|
// write to blocklayer and the digester
|
||||||
|
digester.reset()
|
||||||
|
var writeCursor = 0L
|
||||||
|
unzipdBytes.forEach {
|
||||||
|
if (writeCursor < layer.ptr.size) {
|
||||||
|
layer.ptr[writeCursor] = it
|
||||||
|
digester.update(it)
|
||||||
|
writeCursor += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check hash
|
||||||
|
val hash = StringBuilder().let { sb -> digester.digest().forEach { sb.append(it.tostr()) }; sb.toString() }
|
||||||
|
|
||||||
|
if (hash != layerInfo.h) {
|
||||||
|
throw BlockLayerHashMismatchError(layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,59 +27,56 @@ Following code is an example savegame JSON files.
|
|||||||
|
|
||||||
#### world1.json
|
#### world1.json
|
||||||
|
|
||||||
File is named as `"world"+world_index+".json"`
|
File is named as `"world"+world_index+".json"`.
|
||||||
|
The fields are auto-generated by GDX's JSON serialiser.
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
worldname: "New World",
|
worldName: "New World",
|
||||||
comp: <null, "gzip">,
|
worldIndex: 1,
|
||||||
width: 8192,
|
width: 8192,
|
||||||
height: 2048,
|
height: 2048,
|
||||||
spawnx: 4096,
|
spawnX: 4096,
|
||||||
spawny: 248,
|
spawnY: 248,
|
||||||
genver: 4, /* generator version in integer */
|
creationTime: 1629857065,
|
||||||
time_t: <in-game TIME_T of this world>,
|
lastPlayTime: 1629857065,
|
||||||
terr: {
|
totalPlayTime: 0,
|
||||||
|
layerTerrain: {
|
||||||
h: "a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11",
|
h: "a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11",
|
||||||
b: <Ascii85-encoded gzipped terrain layerdata>
|
b: <Ascii85-encoded gzipped terrain layerdata>,
|
||||||
|
x: 8192,
|
||||||
|
y: 2048
|
||||||
},
|
},
|
||||||
wall: {
|
layerWall: {
|
||||||
h: <SHA-256 hash of 'b'>,
|
h: <SHA-256 hash of 'b'>,
|
||||||
b: <Ascii85-encoded gzipped wall layerdata>
|
b: <Ascii85-encoded gzipped wall layerdata>,
|
||||||
|
x: 8192,
|
||||||
|
y: 2048
|
||||||
},
|
},
|
||||||
tdmg: {
|
wallDamages:{},
|
||||||
h: <SHA-256 hash of 'b'>,
|
terrainDamages: {},
|
||||||
b: <Ascii85-encoded gzipped terrain damage in JSON>
|
fluidTypes: {}
|
||||||
|
fluidFills: {},
|
||||||
|
wirings: {},
|
||||||
|
wiringGraph: {},
|
||||||
|
gravitation: {y:9.8}
|
||||||
|
globalLight: {
|
||||||
|
r:0.8826928,
|
||||||
|
g:0.8901961,
|
||||||
|
b:0.9055425,
|
||||||
|
a:0.93691504
|
||||||
},
|
},
|
||||||
wdmg: {
|
averageTemperature: 288,
|
||||||
h: <SHA-256 hash of 'b'>,
|
generatorSeed: 0,
|
||||||
b: <Ascii85-encoded gzipped wall damage in JSON>
|
worldTime: 27874,
|
||||||
},
|
tileNumberToNameMap: {}
|
||||||
flut: {
|
|
||||||
h: <SHA-256 hash of 'b'>,
|
|
||||||
b: <Ascii85-encoded gzipped fluidTypes in JSON>
|
|
||||||
},
|
|
||||||
fluf: {
|
|
||||||
h: <SHA-256 hash of 'b'>,
|
|
||||||
b: <Ascii85-encoded gzipped fluidFills in JSON>
|
|
||||||
},
|
|
||||||
wire: {
|
|
||||||
h: <SHA-256 hash of 'b'>,
|
|
||||||
b: <Ascii85-encoded gzipped wirings in JSON>
|
|
||||||
},
|
|
||||||
wirg: {
|
|
||||||
h: <SHA-256 hash of 'b'>,
|
|
||||||
b: <Ascii85-encoded gzipped wiringGraph in JSON>
|
|
||||||
},
|
|
||||||
tmap: {
|
|
||||||
h: <SHA-256 hash of 'b'>,
|
|
||||||
b: <Ascii85-encoded gzipped tilenumber-to-tilename map in JSON>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### actors.json
|
#### actors.json
|
||||||
|
|
||||||
|
The fields are auto-generated by GDX's JSON serialiser.
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
<actor id>: { actor serialised in JSON },
|
<actor id>: { actor serialised in JSON },
|
||||||
|
|||||||
Reference in New Issue
Block a user