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

@@ -10,6 +10,7 @@ import net.torvald.terrarum.gameitem.ItemID
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VirtualDisk
import net.torvald.terrarum.ui.ConsoleWindow
import net.torvald.util.SortedArrayList
import java.util.concurrent.locks.Lock
@@ -20,6 +21,9 @@ import java.util.concurrent.locks.Lock
*/
open class IngameInstance(val batch: SpriteBatch) : Screen {
lateinit var savegameArchive: VirtualDisk
internal set
var screenZoom = 1.0f
val ZOOM_MAXIMUM = 4.0f
val ZOOM_MINIMUM = 1.0f
@@ -81,6 +85,9 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
val wallChangeQueue = ArrayList<BlockChangeQueueItem>()
val wireChangeQueue = ArrayList<BlockChangeQueueItem>() // if 'old' is set and 'new' is blank, it's a wire cutter
var loadedTime_t = AppLoader.getTIME_T()
protected set
override fun hide() {
}
@@ -119,6 +126,8 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
printdbg(this, "dispose called by")
printStackTrace(this)
actorContainerActive.forEach { it.dispose() }
actorContainerInactive.forEach { it.dispose() }
world.dispose()
}
@@ -275,6 +284,7 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
data class BlockChangeQueueItem(val old: ItemID, val new: ItemID, val posX: Int, val posY: Int)
open fun sendNotification(messages: Array<String>) {}

View File

@@ -14,6 +14,7 @@ import net.torvald.terrarum.utils.CSVFetcher
import net.torvald.terrarum.utils.JsonFetcher
import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVParser
import org.apache.commons.csv.CSVRecord
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
@@ -250,7 +251,20 @@ object ModMgr {
val itemPath = "items/"
@JvmStatic operator fun invoke(module: String) {
val csv = CSVFetcher.readFromModule(module, itemPath + "itemid.csv")
register(module, CSVFetcher.readFromModule(module, itemPath + "itemid.csv"))
}
fun fromCSV(module: String, csvString: String) {
val csvParser = org.apache.commons.csv.CSVParser.parse(
csvString,
CSVFetcher.terrarumCSVFormat
)
val csvRecordList = csvParser.records
csvParser.close()
register(module, csvRecordList)
}
private fun register(module: String, csv: List<CSVRecord>) {
csv.forEach {
val className: String = it["classname"].toString()
val internalID: Int = it["id"].toInt()

View File

@@ -46,49 +46,70 @@ object BlockCodex {
*/
val virtualToTile = HashMap<ItemID, ItemID>()
fun clear() {
blockProps.clear()
dynamicLights.clear()
highestNumber = -1
virtualTileCursor = 1
tileToVirtual.clear()
virtualToTile.clear()
}
/**
* Later entry (possible from other modules) will replace older ones
*/
operator fun invoke(module: String, path: String) {
AppLoader.printmsg(this, "Building block properties table")
try {
val records = CSVFetcher.readFromModule(module, path)
AppLoader.printmsg(this, "Building block properties table")
records.forEach {
/*if (it.intVal("id") == -1) {
setProp(nullProp, it)
}
else {
setProp(blockProps[it.intVal("id")], it)
}*/
setProp(module, it.intVal("id"), it)
val tileId = "$module:${it.intVal("id")}"
// register tiles with dynamic light
if ((blockProps[tileId]?.dynamicLuminosityFunction ?: 0) != 0) {
dynamicLights.add(tileId)
// add virtual props for dynamic lights
val virtualChunk = ArrayList<ItemID>()
repeat(DYNAMIC_RANDOM_CASES) { _ ->
val virtualID = "$PREFIX_VIRTUALTILE:$virtualTileCursor"
virtualToTile[virtualID] = tileId
virtualChunk.add(virtualID)
setProp(PREFIX_VIRTUALTILE, virtualTileCursor, it)
printdbg(this, "Block ID $tileId -> Virtual ID $virtualID, baseLum: ${blockProps[virtualID]?.baseLumCol}")
virtualTileCursor += 1
}
tileToVirtual[tileId] = virtualChunk.sorted().toList()
}
}
register(module, CSVFetcher.readFromModule(module, path))
}
catch (e: IOException) {
e.printStackTrace()
catch (e: IOException) { e.printStackTrace() }
}
fun fromCSV(module: String, csvString: String) {
AppLoader.printmsg(this, "Building wire properties table for module $module")
val csvParser = org.apache.commons.csv.CSVParser.parse(
csvString,
CSVFetcher.terrarumCSVFormat
)
val csvRecordList = csvParser.records
csvParser.close()
register(module, csvRecordList)
}
private fun register(module: String, records: List<CSVRecord>) {
records.forEach {
/*if (it.intVal("id") == -1) {
setProp(nullProp, it)
}
else {
setProp(blockProps[it.intVal("id")], it)
}*/
setProp(module, it.intVal("id"), it)
val tileId = "$module:${it.intVal("id")}"
// register tiles with dynamic light
if ((blockProps[tileId]?.dynamicLuminosityFunction ?: 0) != 0) {
dynamicLights.add(tileId)
// add virtual props for dynamic lights
val virtualChunk = ArrayList<ItemID>()
repeat(DYNAMIC_RANDOM_CASES) { _ ->
val virtualID = "$PREFIX_VIRTUALTILE:$virtualTileCursor"
virtualToTile[virtualID] = tileId
virtualChunk.add(virtualID)
setProp(PREFIX_VIRTUALTILE, virtualTileCursor, it)
printdbg(this, "Block ID $tileId -> Virtual ID $virtualID, baseLum: ${blockProps[virtualID]?.baseLumCol}")
virtualTileCursor += 1
}
tileToVirtual[tileId] = virtualChunk.sorted().toList()
}
}
}

View File

@@ -21,6 +21,10 @@ object WireCodex {
private val nullProp = WireProp()
fun clear() {
wireProps.clear()
}
/**
* `wire.csv` and texture for all wires are expected to be found in the given path.
*
@@ -29,30 +33,41 @@ object WireCodex {
*/
operator fun invoke(module: String, path: String) {
AppLoader.printmsg(this, "Building wire properties table for module $module")
try {
val records = CSVFetcher.readFromModule(module, path + "wires.csv")
records.forEach {
WireCodex.setProp(module, it.intVal("id"), it)
}
AppLoader.printmsg(this, "Registering wire textures into the resource pool")
wireProps.keys.forEach { id ->
val wireid = id.split(':').last().toInt()
CommonResourcePool.addToLoadingList(id) {
val t = TextureRegionPack(ModMgr.getPath(module, "$path$wireid.tga"), TILE_SIZE, TILE_SIZE)
/*return*/t
}
}
CommonResourcePool.loadAll()
register(module, path, CSVFetcher.readFromModule(module, path + "wires.csv"))
}
catch (e: IOException) {
e.printStackTrace()
catch (e: IOException) { e.printStackTrace() }
}
fun fromCSV(module: String, path: String, csvString: String) {
AppLoader.printmsg(this, "Building wire properties table for module $module")
val csvParser = org.apache.commons.csv.CSVParser.parse(
csvString,
CSVFetcher.terrarumCSVFormat
)
val csvRecordList = csvParser.records
csvParser.close()
register(module, path, csvRecordList)
}
private fun register(module: String, path: String, records: List<CSVRecord>) {
records.forEach {
WireCodex.setProp(module, it.intVal("id"), it)
}
AppLoader.printmsg(this, "Registering wire textures into the resource pool")
wireProps.keys.forEach { id ->
val wireid = id.split(':').last().toInt()
CommonResourcePool.addToLoadingList(id) {
val t = TextureRegionPack(ModMgr.getPath(module, "$path$wireid.tga"), TILE_SIZE, TILE_SIZE)
/*return*/t
}
}
CommonResourcePool.loadAll()
}
fun getAll() = WireCodex.wireProps.values

View File

@@ -110,8 +110,10 @@ class SavegameCracker(
it.call(this, args)
}
catch (e: Throwable) {
printerrln("An error occured:")
e.printStackTrace(stderr)
val error = e.cause ?: e
printerrln("Error -- ${error}")
error.printStackTrace(stderr)
printerrln("Error -- ${error}")
}
}
}
@@ -128,6 +130,13 @@ class SavegameCracker(
return null
}
private fun String.padEnd(len: Int, padfun: (Int) -> Char): String {
val sb = StringBuilder()
for (i in 0 until len - this.length)
sb.append(padfun(i))
return this + sb.toString()
}
@Command("Loads a disk archive", "path-to-file")
fun load(args: List<String>) {
file = File(args[1])
@@ -142,12 +151,12 @@ class SavegameCracker(
if (i != 0)
println(
ccNoun + i.toString(10).padStart(11, ' ') + " " +
ccNoun2 + (entry.filename.toCanonicalString(charset) + " " + cc0).padEnd(18, '.') + " " +
ccConst + entry.contents.getSizePure() + " bytes"
ccNoun2 + (entry.filename.toCanonicalString(charset) + cc0).padEnd(18) { if (it == 0) ' ' else '.' } +
ccConst + " " + entry.contents.getSizePure() + " bytes"
)
}
val entryCount = it.entries.size - 1
println("${cc0}$entryCount ${if (entryCount != 1) "Entries" else "Entry"}, total ${it.usedBytes}/${it.capacity} bytes")
println("${cc0}$entryCount entries, total ${it.usedBytes} bytes")
}
}

View File

@@ -8,6 +8,7 @@ import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.WireActor
import net.torvald.terrarum.gameitem.ItemID
import net.torvald.terrarum.realestate.LandUtil
@@ -42,9 +43,6 @@ class GameWorld() : Disposable {
var totalPlayTime: Long = 0
internal set
/** Used to calculate play time */
@Transient open val loadTime: Long = System.currentTimeMillis() / 1000L
//layers
lateinit var layerWall: BlockLayer
lateinit var layerTerrain: BlockLayer
@@ -106,6 +104,8 @@ class GameWorld() : Disposable {
internal var genver = -1
internal var comp = -1
internal val actors = ArrayList<ActorID>() // only filled up on save and load; DO NOT USE THIS
/**
* Create new world
*/

View File

@@ -32,6 +32,12 @@ object ItemCodex {
val ACTORID_MIN = ReferencingRanges.ACTORS.first
fun clear() {
itemCodex.clear()
dynamicItemDescription.clear()
dynamicToStaticTable.clear()
}
private val itemImagePlaceholder: TextureRegion
get() = CommonResourcePool.getAsTextureRegion("itemplaceholder_24") // copper pickaxe

View File

@@ -1,9 +1,11 @@
package net.torvald.terrarum.itemproperties
import net.torvald.terrarum.AppLoader.printmsg
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.blockproperties.floatVal
import net.torvald.terrarum.blockproperties.intVal
import net.torvald.terrarum.utils.CSVFetcher
import org.apache.commons.csv.CSVRecord
import java.io.IOException
/**
@@ -32,30 +34,37 @@ object MaterialCodex {
private val nullMaterial = Material()
operator fun invoke(module: String, path: String) {
try {
val records = CSVFetcher.readFromModule(module, path)
register(CSVFetcher.readFromModule(module, path))
}
printmsg(this, "Building materials table")
fun fromCSV(module: String, csvString: String) {
val csvParser = org.apache.commons.csv.CSVParser.parse(
csvString,
CSVFetcher.terrarumCSVFormat
)
val csvRecordList = csvParser.records
csvParser.close()
register(csvRecordList)
}
records.forEach {
val prop = Material()
prop.strength = it.intVal("tens")
prop.density = it.intVal("dsty")
prop.forceMod = it.intVal("fmod")
prop.enduranceMod = it.floatVal("endurance")
prop.thermalConductivity = it.floatVal("tcond")
prop.identifier = it.get("idst").toUpperCase()
private fun register(records: List<CSVRecord>) {
records.forEach {
val prop = Material()
prop.strength = it.intVal("tens")
prop.density = it.intVal("dsty")
prop.forceMod = it.intVal("fmod")
prop.enduranceMod = it.floatVal("endurance")
prop.thermalConductivity = it.floatVal("tcond")
prop.identifier = it.get("idst").toUpperCase()
materialProps[prop.identifier] = prop
materialProps[prop.identifier] = prop
printmsg(this, "${prop.identifier}\t${prop.strength}\t${prop.density}\t${prop.forceMod}\t${prop.enduranceMod}")
}
}
catch (e: IOException) {
e.printStackTrace()
printmsg(this, "${prop.identifier}\t${prop.strength}\t${prop.density}\t${prop.forceMod}\t${prop.enduranceMod}")
}
}
fun clear() = materialProps.clear()
operator fun get(identifier: String) = try {
materialProps[identifier.toUpperCase()]!!
}

View File

@@ -9,6 +9,7 @@ import net.torvald.terrarum.*
import net.torvald.terrarum.AppLoader.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.BlockPropUtil
import net.torvald.terrarum.blockproperties.WireCodex
import net.torvald.terrarum.blockstats.BlockStats
@@ -23,17 +24,24 @@ import net.torvald.terrarum.gameitem.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.console.AVTracker
import net.torvald.terrarum.console.ActorsList
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameparticles.ParticleBase
import net.torvald.terrarum.gameactors.WireActor
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.*
import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver
import net.torvald.terrarum.gameworld.WorldSimulator
import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.modulebasegame.gameworld.GameEconomy
import net.torvald.terrarum.modulebasegame.ui.*
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.weather.WeatherMixer
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VDUtil
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VirtualDisk
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.WriteMeta
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
@@ -221,7 +229,7 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
when (gameLoadMode) {
GameLoadMode.CREATE_NEW -> enterCreateNewWorld(gameLoadInfoPayload as NewWorldParameters)
GameLoadMode.LOAD_FROM -> enterLoadFromSave(gameLoadInfoPayload as GameSaveData)
GameLoadMode.LOAD_FROM -> enterLoadFromSave(gameLoadInfoPayload as WriteMeta.WorldMeta)
}
IngameRenderer.setRenderedWorld(world)
@@ -230,16 +238,6 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
super.show() // gameInitialised = true
}
data class GameSaveData(
val world: GameWorld,
val historicalFigureIDBucket: ArrayList<Int>,
val realGamePlayer: IngamePlayer,
val rogueS0: Long,
val rogueS1: Long,
val weatherS0: Long,
val weatherS1: Long
)
data class NewWorldParameters(
val width: Int,
val height: Int,
@@ -259,12 +257,30 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
/**
* Init instance by loading saved world
*/
private fun enterLoadFromSave(gameSaveData: GameSaveData) {
private fun enterLoadFromSave(meta: WriteMeta.WorldMeta) {
if (gameInitialised) {
printdbg(this, "loaded successfully.")
}
else {
TODO()
RoguelikeRandomiser.loadFromSave(meta.randseed0, meta.randseed1)
WeatherMixer.loadFromSave(meta.weatseed0, meta.weatseed1)
// Load BlockCodex //
BlockCodex.clear()
meta.blocks.forEach { module, csv -> BlockCodex.fromCSV(module, csv.doc) }
// Load WireCodex //
WireCodex.clear()
meta.wires.forEach { module, csv -> WireCodex.fromCSV(module, ModMgr.getPath(module, "wires/"), csv.doc) }
// Load ItemCodex //
ItemCodex.clear()
meta.items.forEach { module, csv -> ModMgr.GameItemLoader.fromCSV(module, csv.doc) }
// TODO registerNewDynamicItem
// Load MaterialCodex //
MaterialCodex.clear()
meta.materials.forEach { module, csv -> MaterialCodex.fromCSV(module, csv.doc) }
}
}
@@ -284,7 +300,7 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
// init map as chosen size
val timeNow = System.currentTimeMillis() / 1000
val timeNow = AppLoader.getTIME_T()
world = GameWorld(1, worldParams.width, worldParams.height, timeNow, timeNow, 0) // new game, so the creation time is right now
gameworldIndices.add(world.worldIndex)
world.extraFields["basegame.economy"] = GameEconomy()
@@ -307,10 +323,17 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
/** Load rest of the game with GL context */
fun postInit() {
//setTheRealGamerFirstTime(PlayerBuilderSigrid())
setTheRealGamerFirstTime(PlayerBuilderTestSubject1())
if (actorNowPlaying == null) {
//setTheRealGamerFirstTime(PlayerBuilderSigrid())
setTheRealGamerFirstTime(PlayerBuilderTestSubject1())
// setTheRealGamerFirstTime(PlayerBuilderWerebeastTest())
savegameArchive = VDUtil.createNewDisk(
1L shl 60,
actorNowPlaying!!.actorValue.getAsString(AVKey.NAME) ?: "Player ${AppLoader.getTIME_T()}",
Common.CHARSET
)
}
MegaRainGovernor // invoke MegaRain Governor

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame.console
import com.badlogic.gdx.utils.Json
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.Terrarum.ingame
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.TerrarumIngame
@@ -19,7 +20,8 @@ import java.io.IOException
object ExportMeta : ConsoleCommand {
override fun execute(args: Array<String>) {
try {
val str = WriteMeta(Terrarum.ingame!! as TerrarumIngame).invoke()
val currentPlayTime_t = AppLoader.getTIME_T() - ingame!!.loadedTime_t
val str = WriteMeta(ingame!! as TerrarumIngame, currentPlayTime_t)
val writer = java.io.FileWriter(AppLoader.defaultDir + "/Exports/savegame.json", false)
writer.write(str)
writer.close()
@@ -40,7 +42,7 @@ object ExportWorld : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
try {
val str = WriteWorld(Terrarum.ingame!! as TerrarumIngame).invoke()
val str = WriteWorld(ingame!! as TerrarumIngame)
val writer = java.io.FileWriter(AppLoader.defaultDir + "/Exports/${args[1]}.json", false)
writer.write(str)
writer.close()

View File

@@ -18,7 +18,7 @@ object ImportWorld : ConsoleCommand {
if (args.size == 2) {
try {
val reader = java.io.FileReader(AppLoader.defaultDir + "/Exports/${args[1]}.json")
ReadWorld(Terrarum.ingame!! as TerrarumIngame).invoke(reader)
ReadWorld(Terrarum.ingame!! as TerrarumIngame, reader)
Echo("Importworld: imported a world from ${args[1]}.json")
}
catch (e: IOException) {
@@ -41,7 +41,7 @@ object ImportActor : ConsoleCommand {
if (args.size == 2) {
try {
val reader = java.io.FileReader(AppLoader.defaultDir + "/Exports/${args[1]}.json")
ReadActor(Terrarum.ingame!! as TerrarumIngame).invoke(reader)
ReadActor(Terrarum.ingame!! as TerrarumIngame, reader)
Echo("Importactor: imported an actor from ${args[1]}.json")
}
catch (e: IOException) {

View File

@@ -23,17 +23,9 @@ object Load : ConsoleCommand {
val charset = Common.CHARSET
val file = File(AppLoader.defaultDir + "/Exports/${args[1]}")
val disk = VDUtil.readDiskArchive(file, charset = charset)
val meta = ReadMeta(disk)
val metaFile = disk.entries[-1]!!
val metaReader = ByteArray64Reader((metaFile.contents as EntryFile).getContent(), Common.CHARSET)
val meta = Common.jsoner.fromJson(WriteMeta.WorldMeta::class.java, metaReader)
WriteMeta.WorldMeta::class.declaredMemberProperties.forEach {
println("${it.name} = ${it.get(meta)}")
}
println(WriteMeta.unasciiAndUnzipStr(meta.blocks))
meta.blocks.forEach { s, str -> println("Module $s\n"); println(str.doc) }
println(meta.loadorder.joinToString())
}
catch (e: IOException) {

View File

@@ -11,10 +11,7 @@ import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.BlockMarkerActor
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.*
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.WriteActor
import net.torvald.terrarum.serialise.WriteMeta
import net.torvald.terrarum.serialise.WriteWorld
import net.torvald.terrarum.serialise.*
import java.io.File
import java.io.IOException
@@ -40,33 +37,11 @@ object Save : ConsoleCommand {
try {
val ingame = Terrarum.ingame!! as TerrarumIngame
val savename = args[1].trim()
val creation_t = VDUtil.currentUnixtime
val time_t = VDUtil.currentUnixtime
val disk = VDUtil.createNewDisk(1L shl 60, savename, Common.CHARSET)
val file = File(AppLoader.defaultDir + "/Exports/${args[1]}")
// NOTE: don't bother with the entryID of DiskEntries; it will be overwritten anyway
val metaContent = EntryFile(WriteMeta(ingame).encodeToByteArray64())
val meta = DiskEntry(-1, 0, "savegame".toByteArray(), creation_t, time_t, metaContent)
addFile(disk, meta)
val worldContent = EntryFile(WriteWorld(ingame).encodeToByteArray64())
val world = DiskEntry(ingame.world.worldIndex, 0, "world${ingame.world.worldIndex}".toByteArray(), creation_t, time_t, worldContent)
addFile(disk, world)
listOf(ingame.actorContainerActive, ingame.actorContainerInactive).forEach { actors ->
actors.forEach {
if (acceptable(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, File(AppLoader.defaultDir + "/Exports/${args[1]}"))
WriteSavegame(disk, file, ingame)
}
catch (e: IOException) {
Echo("Save: IOException raised.")

View File

@@ -1,23 +0,0 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.console.EchoError
import net.torvald.terrarum.serialise.SavegameWriter
/**
* Created by minjaesong on 2019-02-22.
*/
internal object SavegameWriterTest: ConsoleCommand {
override fun execute(args: Array<String>) {
val r = SavegameWriter.invoke(args.getOrNull(1))
if (!r) {
EchoError("Saving failed")
}
}
override fun printUsage() {
Echo("savetest [optional out name}")
}
}

View File

@@ -15,6 +15,7 @@ class IngamePlayer : ActorHumanoid, HasAssembledSprite {
override var animDescPath = "invalid"
override var animDescPathGlow: String? = null
internal var worldCurrentlyPlaying = 0 // only filled up on save and load; DO NOT USE THIS
private constructor()
@@ -33,6 +34,7 @@ class IngamePlayer : ActorHumanoid, HasAssembledSprite {
referenceID = Terrarum.PLAYER_REF_ID // forcibly set ID
density = BASE_DENSITY
collisionType = COLLISION_KINEMATIC
worldCurrentlyPlaying = Terrarum.ingame?.world?.worldIndex ?: 0
}
}

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
}
}

View File

@@ -1,10 +1,15 @@
package net.torvald.terrarum.utils
import com.badlogic.gdx.utils.Json
import com.badlogic.gdx.utils.JsonValue
import net.torvald.terrarum.gameitem.ItemID
import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.ByteArray64Reader
import net.torvald.terrarum.serialise.Common
import java.io.StringReader
/**
* Created by minjaesong on 2021-08-26.
@@ -16,3 +21,14 @@ class WiringGraphMap: HashMap<ItemID, GameWorld.WiringSimCell>()
class HashedFluidType: HashMap<BlockAddress, FluidType>()
class HashedWirings: HashMap<BlockAddress, GameWorld.WiringNode>()
class HashedWiringGraph: HashMap<BlockAddress, WiringGraphMap>()
class MetaModuleCSVPair: HashMap<String, ZipCodedStr>()
/**
* @param doc plaintext
*
* Note: the content of the class is only encoded on serialisation; when the class is deserialised, this
* class always holds plaintext.
*/
@JvmInline value class ZipCodedStr(val doc: String = "") {
override fun toString() = doc
}

View File

@@ -1,4 +1,37 @@
Following code is an example savegame JSON files.
## Savegame Structure
- The Savegame is a TerranVirtualDisk archive that stores multiple files in the disk's root directory
- Savegame stores metadata, Worlds and Actors in the game
- A player gets one unique Savegame
- A player can have Multiple worlds
- Worlds are identified using integer ranged 1 through 32767 (inclusive)
- Actor ID is unique within the scope of the Savegame
- A World stores list of Actor IDs that resides in the world
### File Structure
Each file on the Savegame has following convention:
|Type|Filename|ID|
|--|--|--|
|Metadata|savegame|-1|
|Worlds|world$n ($n is a world index)|$n|
|Actors|actor$n ($n is an Actor ID)|$n|
### Solving Problems
#### How do I determine which world to read in?
Load the player (always has the entry ID of 9545698) and the property "worldCurrentlyPlaying" should
contain an integer that is a world index. Only the actors that are instance of IngamePlayer will have
the property.
### Save File Examples
Following code is an example Savegame JSON files.
#### savegame.json
```