mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
game load wip
This commit is contained in:
@@ -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>) {}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()]!!
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package net.torvald.terrarum.serialise
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2018-10-03.
|
||||
*/
|
||||
object SavegameReader {
|
||||
|
||||
// TODO read TEVd, load necessary shits
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
98
src/net/torvald/terrarum/serialise/WriteSavegame.kt
Normal file
98
src/net/torvald/terrarum/serialise/WriteSavegame.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user