game load wip

This commit is contained in:
minjaesong
2021-09-03 17:54:10 +09:00
parent 42ea79bcc2
commit 57e4b83649
27 changed files with 542 additions and 462 deletions

View File

@@ -1,103 +1,5 @@
package net.torvald.terrarum.serialise
import com.badlogic.gdx.Gdx
import net.torvald.random.HQRNG
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameitem.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.*
import net.torvald.util.SortedArrayList
import java.io.File
import java.nio.charset.Charset
import kotlin.math.roundToInt
internal class RNGPool() {
private val RNG = HQRNG()
private val used = SortedArrayList<Int>()
init {
for (i in 0 until 32767) {
used.add(i)
}
}
fun next(): Int {
var n = RNG.nextLong().ushr(32).toInt()
while (used.contains(n)) {
n = RNG.nextLong().ushr(32).toInt()
}
used.add(n)
return n
}
}
/**
* Created by minjaesong on 2018-10-03.
*/
object SavegameWriter {
// TODO create temporary files (worldinfo), create JSON files on RAM, pack those into TEVd as per Savegame container.txt
private val rngPool = RNGPool()
private val charset = Charset.forName("UTF-8")
private lateinit var playerName: String
operator fun invoke(pnameOverride: String? = null): Boolean {
playerName = pnameOverride ?: "${Terrarum.ingame!!.actorGamer.actorValue[AVKey.NAME]}"
if (playerName.isEmpty()) playerName = "Test subject ${Math.random().times(0x7FFFFFFF).roundToInt()}"
try {
val diskImage = generateNewDiskImage()
val outFile = File("${AppLoader.defaultSaveDir}/$playerName")
VDUtil.dumpToRealMachine(diskImage, outFile)
return true
}
catch (e: Throwable) {
e.printStackTrace()
}
return false
}
fun generateNewDiskImage(): VirtualDisk {
val creationDate = System.currentTimeMillis() / 1000L
val ingame = Terrarum.ingame!!
val gameworld = ingame.world
val player = ingame.actorGamer
val disk = VDUtil.createNewDisk(0x7FFFFFFFFFFFFFFFL, "Tesv-$playerName", charset)
val ROOT = disk.root.entryID
// serialise current world (stage)
// items
/*ItemCodex.dynamicItemDescription.forEach { dynamicID, item ->
VDUtil.registerFile(disk, DiskEntry(
rngPool.next(), ROOT,
dynamicID.toByteArray(charset),
creationDate, creationDate,
EntryFile(serialiseItem(item))
))
}*/
System.gc()
return disk
}
fun modifyExistingSave(savefile: File): VirtualDisk {
TODO()
}
}
fun Int.toLittle() = byteArrayOf(
this.and(0xFF).toByte(),
this.ushr(8).and(0xFF).toByte(),
@@ -171,4 +73,4 @@ 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)

View File

@@ -10,6 +10,7 @@ import net.torvald.terrarum.gameworld.WorldTime
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 net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64Reader
import net.torvald.terrarum.tail
import net.torvald.terrarum.utils.*
import org.apache.commons.codec.digest.DigestUtils
@@ -47,7 +48,6 @@ object Common {
init {
// BigInteger
jsoner.setSerializer(BigInteger::class.java, object : Json.Serializer<BigInteger> {
override fun write(json: Json, obj: BigInteger?, knownType: Class<*>?) {
json.writeValue(obj?.toString())
}
@@ -56,9 +56,18 @@ object Common {
return BigInteger(jsonData.asString())
}
})
// ZipCodedStr
jsoner.setSerializer(ZipCodedStr::class.java, object : Json.Serializer<ZipCodedStr> {
override fun write(json: Json, obj: ZipCodedStr, knownType: Class<*>?) {
json.writeValue(zipStrAndEnascii(obj.doc))
}
override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): ZipCodedStr {
return ZipCodedStr(unasciiAndUnzipStr(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) }
@@ -66,9 +75,6 @@ object Common {
val layer = LayerInfo(hash, blockLayerToStr(obj), obj.width, obj.height)
// printdbg(this, "pre: ${(0L..1023L).map { obj.ptr[it].tostr() }.joinToString(" ")}")
json.writeValue(layer)
}
@@ -270,4 +276,16 @@ object Common {
return unzipdBytes
}
/**
* @param [s] a String
* @return UTF-8 encoded [s] which are GZip'd then Ascii85-encoded
*/
fun zipStrAndEnascii(s: String): String {
return Common.bytesToZipdStr(s.toByteArray(Common.CHARSET).iterator())
}
fun unasciiAndUnzipStr(s: String): String {
return ByteArray64Reader(strToBytes(StringReader(s)), CHARSET).readText()
}
}

View File

@@ -1,60 +0,0 @@
package net.torvald.terrarum.serialise
import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.terrarum.NoSuchActorWithIDException
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import java.io.InputStream
import java.io.Reader
/**
* Actor's JSON representation is expected to have "class" property on the root object, such as:
* ```
* "class":"net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer"
* ```
*
* Created by minjaesong on 2021-08-27.
*/
class ReadActor(val ingame: TerrarumIngame) {
open fun invoke(worldDataStream: InputStream) {
postRead(Common.jsoner.fromJson(null, worldDataStream))
}
open fun invoke(worldDataStream: Reader) {
postRead(Common.jsoner.fromJson(null, worldDataStream))
}
private fun postRead(actor: Actor) {
// filling in Transients
actor.actorValue.actor = actor
if (actor is Pocketed)
actor.inventory.actor = actor
if (actor is ActorWithBody) {
actor.sprite = SpriteAnimation(actor)
if (actor is HasAssembledSprite) {
if (actor.animDescPathGlow != null) actor.spriteGlow = SpriteAnimation(actor)
actor.reassembleSprite(actor.sprite!!, actor.spriteGlow)
}
}
// replace existing player
val oldPlayerID = ingame.actorNowPlaying?.referenceID
try {
ingame.forceRemoveActor(ingame.getActorByID(actor.referenceID))
}
catch (e: NoSuchActorWithIDException) { /* no actor to delete, you may proceed */ }
ingame.addNewActor(actor)
if (actor.referenceID == oldPlayerID)
ingame.actorNowPlaying = actor as ActorHumanoid
}
}

View File

@@ -1,29 +0,0 @@
package net.torvald.terrarum.serialise
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import java.io.InputStream
import java.io.Reader
/**
* Created by minjaesong on 2021-08-25.
*/
open class ReadWorld(val ingame: TerrarumIngame) {
open fun invoke(worldDataStream: InputStream) {
postRead(Common.jsoner.fromJson(GameWorld::class.java, worldDataStream))
}
open fun invoke(worldDataStream: Reader) {
postRead(Common.jsoner.fromJson(GameWorld::class.java, worldDataStream))
}
private fun postRead(world: GameWorld) {
world.postLoad()
ingame.world = world
}
}

View File

@@ -1,33 +0,0 @@
package net.torvald.terrarum.serialise
import net.torvald.terrarum.AppLoader
import java.io.File
import java.io.FileInputStream
object SavegameLedger {
private val SAVE_DIRECTORY = File(AppLoader.defaultSaveDir)
fun hasSavegameDirectory() = SAVE_DIRECTORY.exists() && SAVE_DIRECTORY.isDirectory
private fun peekFewBytes(file: File, length: Int): ByteArray {
val buffer = ByteArray(length)
val `is` = FileInputStream(file)
if (`is`.read(buffer) != buffer.size) {
throw InternalError()
}
`is`.close()
return buffer
}
private val MAGIC_TEVD = "TEVd".toByteArray()
fun getSavefileList(): List<File>? {
return if (!hasSavegameDirectory()) null
else SAVE_DIRECTORY.listFiles().filter { it.isFile && peekFewBytes(it, 4) contentEquals MAGIC_TEVD }
}
fun getSavefileCount() = getSavefileList()?.count() ?: 0
}

View File

@@ -1,10 +0,0 @@
package net.torvald.terrarum.serialise
/**
* Created by minjaesong on 2018-10-03.
*/
object SavegameReader {
// TODO read TEVd, load necessary shits
}

View File

@@ -1,8 +1,16 @@
package net.torvald.terrarum.serialise
import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.terrarum.NoSuchActorWithIDException
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64Writer
import java.io.Reader
/**
* Created by minjaesong on 2021-08-24.
@@ -23,4 +31,54 @@ object WriteActor {
return baw.toByteArray64()
}
}
/**
* Actor's JSON representation is expected to have "class" property on the root object, such as:
* ```
* "class":"net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer"
* ```
*
* Created by minjaesong on 2021-08-27.
*/
object ReadActor {
fun readActorOnly(worldDataStream: Reader): Actor =
Common.jsoner.fromJson(null, worldDataStream)
operator fun invoke(ingame: TerrarumIngame, worldDataStream: Reader): Actor =
postRead(ingame, readActorOnly(worldDataStream))
private fun postRead(ingame: TerrarumIngame, actor: Actor): Actor {
// filling in Transients
actor.actorValue.actor = actor
if (actor is Pocketed)
actor.inventory.actor = actor
if (actor is ActorWithBody) {
actor.sprite = SpriteAnimation(actor)
if (actor is HasAssembledSprite) {
if (actor.animDescPathGlow != null) actor.spriteGlow = SpriteAnimation(actor)
actor.reassembleSprite(actor.sprite!!, actor.spriteGlow)
}
}
// replace existing player
val oldPlayerID = ingame.actorNowPlaying?.referenceID
try {
ingame.forceRemoveActor(ingame.getActorByID(actor.referenceID))
}
catch (e: NoSuchActorWithIDException) { /* no actor to delete, you may proceed */ }
ingame.addNewActor(actor)
if (actor.referenceID == oldPlayerID)
ingame.actorNowPlaying = actor as ActorHumanoid
return actor
}
}

View File

@@ -6,17 +6,21 @@ import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64Reader
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.EntryFile
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VirtualDisk
import net.torvald.terrarum.utils.MetaModuleCSVPair
import net.torvald.terrarum.utils.ZipCodedStr
import net.torvald.terrarum.weather.WeatherMixer
import java.io.StringReader
/**
* Created by minjaesong on 2021-08-23.
*/
open class WriteMeta(val ingame: TerrarumIngame) {
object WriteMeta {
open fun invoke(): String {
operator fun invoke(ingame: TerrarumIngame, currentPlayTime_t: Long): String {
val world = ingame.world
val meta = WorldMeta(
genver = Common.GENVER,
savename = world.worldName,
@@ -28,34 +32,26 @@ open class WriteMeta(val ingame: TerrarumIngame) {
playerid = ingame.actorGamer.referenceID,
creation_t = world.creationTime,
lastplay_t = world.lastPlayTime,
playtime_t = world.totalPlayTime,
blocks = StringBuilder().let {
ModMgr.getFilesFromEveryMod("blocks/blocks.csv").forEach { (modname, file) ->
it.append(modnameToOrnamentalHeader(modname))
it.append(file.readText())
}
zipStrAndEnascii(it.toString())
playtime_t = world.totalPlayTime + currentPlayTime_t,
blocks = ModMgr.getFilesFromEveryMod("blocks/blocks.csv").fold(MetaModuleCSVPair()) {
map, (modname, file) ->
map[modname] = ZipCodedStr(file.readText ())
/*return*/map
},
items = StringBuilder().let {
ModMgr.getFilesFromEveryMod("items/itemid.csv").forEach { (modname, file) ->
it.append(modnameToOrnamentalHeader(modname))
it.append(file.readText())
}
zipStrAndEnascii(it.toString())
items = ModMgr.getFilesFromEveryMod("items/itemid.csv").fold(MetaModuleCSVPair()) {
map, (modname, file) ->
map[modname] = ZipCodedStr(file.readText ())
/*return*/map
},
wires = StringBuilder().let {
ModMgr.getFilesFromEveryMod("wires/wires.csv").forEach { (modname, file) ->
it.append(modnameToOrnamentalHeader(modname))
it.append(file.readText())
}
zipStrAndEnascii(it.toString())
wires = ModMgr.getFilesFromEveryMod("wires/wires.csv").fold(MetaModuleCSVPair()) {
map, (modname, file) ->
map[modname] = ZipCodedStr(file.readText ())
/*return*/map
},
materials = StringBuilder().let {
ModMgr.getFilesFromEveryMod("materials/materials.csv").forEach { (modname, file) ->
it.append(modnameToOrnamentalHeader(modname))
it.append(file.readText())
}
zipStrAndEnascii(it.toString())
materials = ModMgr.getFilesFromEveryMod("materials/materials.csv").fold(MetaModuleCSVPair()) {
map, (modname, file) ->
map[modname] = ZipCodedStr(file.readText ())
/*return*/map
},
loadorder = ModMgr.loadOrder.toTypedArray(),
worlds = ingame.gameworldIndices.toTypedArray()
@@ -64,9 +60,9 @@ open class WriteMeta(val ingame: TerrarumIngame) {
return Common.jsoner.toJson(meta)
}
fun encodeToByteArray64(): ByteArray64 {
fun encodeToByteArray64(ingame: TerrarumIngame, currentPlayTime_t: Long): ByteArray64 {
val ba = ByteArray64()
this.invoke().toByteArray(Common.CHARSET).forEach { ba.add(it) }
this.invoke(ingame, currentPlayTime_t).toByteArray(Common.CHARSET).forEach { ba.add(it) }
return ba
}
@@ -82,10 +78,10 @@ open class WriteMeta(val ingame: TerrarumIngame) {
val creation_t: Long = 0,
val lastplay_t: Long = 0,
val playtime_t: Long = 0,
val blocks: String = "",
val items: String = "",
val wires: String = "",
val materials: String = "",
val blocks: MetaModuleCSVPair = MetaModuleCSVPair(),
val items: MetaModuleCSVPair = MetaModuleCSVPair(),
val wires: MetaModuleCSVPair = MetaModuleCSVPair(),
val materials: MetaModuleCSVPair = MetaModuleCSVPair(),
val loadorder: Array<String> = arrayOf(), // do not use list; Could not instantiate instance of class: java.util.Collections$SingletonList
val worlds: Array<Int> = arrayOf() // do not use list; Could not instantiate instance of class: java.util.Collections$SingletonList
) {
@@ -94,23 +90,22 @@ open class WriteMeta(val ingame: TerrarumIngame) {
}
}
companion object {
private fun modnameToOrnamentalHeader(s: String) =
"\n\n${"#".repeat(16 + s.length)}\n" +
"## module: $s ##\n" +
"${"#".repeat(16 + s.length)}\n\n"
/**
* @param [s] a String
* @return UTF-8 encoded [s] which are GZip'd then Ascii85-encoded
*/
fun zipStrAndEnascii(s: String): String {
return Common.bytesToZipdStr(s.toByteArray(Common.CHARSET).iterator())
}
fun unasciiAndUnzipStr(s: String): String {
return ByteArray64Reader(Common.strToBytes(StringReader(s)), Common.CHARSET).readText()
}
}
private fun modnameToOrnamentalHeader(s: String) =
"\n\n${"#".repeat(16 + s.length)}\n" +
"## module: $s ##\n" +
"${"#".repeat(16 + s.length)}\n\n"
}
/**
* Created by minjaesong on 2021-09-03.
*/
object ReadMeta {
operator fun invoke(savefile: VirtualDisk): WriteMeta.WorldMeta {
val metaFile = savefile.entries[-1]!!
val metaReader = ByteArray64Reader((metaFile.contents as EntryFile).getContent(), Common.CHARSET)
return Common.jsoner.fromJson(WriteMeta.WorldMeta::class.java, metaReader)
}
}

View File

@@ -0,0 +1,98 @@
package net.torvald.terrarum.serialise
import net.torvald.terrarum.*
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.BlockMarkerActor
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.*
import net.torvald.terrarum.serialise.WriteWorld.actorAcceptable
import java.io.File
/**
* It's your responsibility to create a new VirtualDisk if your save is new, and create a backup for modifying existing save.
*
* Created by minjaesong on 2021-09-03.
*/
object WriteSavegame {
/**
* Will happily overwrite existing entry
*/
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
disk.entries[file.entryID] = file
file.parentEntryID = 0
val dir = VDUtil.getAsDirectory(disk, 0)
if (!dir.contains(file.entryID)) dir.add(file.entryID)
}
operator fun invoke(disk: VirtualDisk, outFile: File, ingame: TerrarumIngame) {
val creation_t = ingame.world.creationTime
val time_t = AppLoader.getTIME_T()
val currentPlayTime_t = time_t - ingame.loadedTime_t
// Write Meta //
val metaContent = EntryFile(WriteMeta.encodeToByteArray64(ingame, currentPlayTime_t))
val meta = DiskEntry(-1, 0, "savegame".toByteArray(), creation_t, time_t, metaContent)
addFile(disk, meta)
// Write World //
val worldNum = ingame.world.worldIndex
val worldContent = EntryFile(WriteWorld.encodeToByteArray64(ingame))
val world = DiskEntry(worldNum, 0, "world${worldNum}".toByteArray(), creation_t, time_t, worldContent)
addFile(disk, world)
// Write Actors //
listOf(ingame.actorContainerActive, ingame.actorContainerInactive).forEach { actors ->
actors.forEach {
if (actorAcceptable(it)) {
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
val actor = DiskEntry(it.referenceID, 0, "actor${it.referenceID}".toByteArray(), creation_t, time_t, actorContent)
addFile(disk, actor)
}
}
}
disk.capacity = 0
VDUtil.dumpToRealMachine(disk, outFile)
}
}
/**
* Load and setup the game for the first load.
*
* To load additional actors/worlds, use ReadActor/ReadWorld.
*
* Created by minjaesong on 2021-09-03.
*/
object LoadSavegame {
operator fun invoke(disk: VirtualDisk) {
val meta = ReadMeta(disk)
val player = ReadActor.readActorOnly(
ByteArray64Reader(VDUtil.getAsNormalFile(disk, 9545698).getContent(), Common.CHARSET)
) as IngamePlayer
val world = ReadWorld.readWorldOnly(
ByteArray64Reader(VDUtil.getAsNormalFile(disk, player.worldCurrentlyPlaying).getContent(), Common.CHARSET)
)
val actors = world.actors.map {
ReadActor.readActorOnly(ByteArray64Reader(VDUtil.getAsNormalFile(disk, it).getContent(), Common.CHARSET))
}
val ingame = TerrarumIngame(AppLoader.batch)
val worldParam = meta
ingame.world = world
ingame.gameLoadInfoPayload = worldParam
ingame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM
ingame.savegameArchive = disk
actors.forEach { ingame.addNewActor(it) }
ingame.actorNowPlaying = player
Terrarum.setCurrentIngameInstance(ingame)
val loadScreen = SanicLoadScreen
AppLoader.setLoadScreen(loadScreen)
}
}

View File

@@ -1,32 +1,69 @@
package net.torvald.terrarum.serialise
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ReferencingRanges
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.BlockMarkerActor
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64Writer
import java.io.Reader
/**
* Created by minjaesong on 2021-08-23.
*/
open class WriteWorld(val ingame: TerrarumIngame) {
object WriteWorld {
open fun invoke(): String {
val world = ingame.world
world.genver = Common.GENVER
world.comp = Common.COMP_GZIP
return Common.jsoner.toJson(world)
fun actorAcceptable(actor: Actor): Boolean {
return actor.referenceID !in ReferencingRanges.ACTORS_WIRES &&
actor.referenceID !in ReferencingRanges.ACTORS_WIRES_HELPER &&
actor != (CommonResourcePool.get("blockmarking_actor") as BlockMarkerActor)
}
fun encodeToByteArray64(): ByteArray64 {
private fun preWrite(ingame: TerrarumIngame): GameWorld {
val world = ingame.world
world.genver = Common.GENVER
world.comp = Common.COMP_GZIP
ingame.actorContainerActive.filter { actorAcceptable(it) }.forEach { world.actors.add(it.referenceID) }
ingame.actorContainerInactive.filter { actorAcceptable(it) }.forEach { world.actors.add(it.referenceID) }
return world
}
operator fun invoke(ingame: TerrarumIngame): String {
return Common.jsoner.toJson(preWrite(ingame))
}
fun encodeToByteArray64(ingame: TerrarumIngame): ByteArray64 {
val baw = ByteArray64Writer(Common.CHARSET)
Common.jsoner.toJson(world, baw)
Common.jsoner.toJson(preWrite(ingame), baw)
baw.flush(); baw.close()
return baw.toByteArray64()
}
}
/**
* Created by minjaesong on 2021-08-25.
*/
object ReadWorld {
fun readWorldOnly(worldDataStream: Reader): GameWorld =
Common.jsoner.fromJson(GameWorld::class.java, worldDataStream)
operator fun invoke(ingame: TerrarumIngame, worldDataStream: Reader): GameWorld =
postRead(ingame, readWorldOnly(worldDataStream))
private fun postRead(ingame: TerrarumIngame, world: GameWorld): GameWorld {
world.postLoad()
ingame.world = world
return world
}
}