new load wip

This commit is contained in:
minjaesong
2021-10-11 17:46:46 +09:00
parent 198237dee2
commit ef603ec5d5
26 changed files with 250 additions and 285 deletions

View File

@@ -7,7 +7,7 @@ The main game directory is composed of following directories:
```
.Terrarum
+ Players
- "${PlayerName}-${UUID}", TVDA {
- "${PlayerName}-${UUID}", TVDA {
[-1] player JSON,
[-2] spritedef,
[-3] optional spritedef-glow,
@@ -30,6 +30,8 @@ The main game directory is composed of following directories:
(TEVD stands for Terrarum Virtual Disk spec version 3, TVDA stands for spec version 254; both have MAGIC header of `TEVd`)
Do not rely on filename to look for a world; players can change the filename
## Handling The Player Data
Some of the "player assets" are stored to the world, such assets include:
@@ -48,6 +50,8 @@ If the World has the Actorvalue, World's value will be used; otherwise use incom
Multiplayer world will initialise Actorvalue pool using incoming value -- or they may choose to use
their own Actorvalue called "gamerules" to either implement their own "gamemode" or prevent cheating)
For Singleplayer, only the xy-position is saved to the world for later load.
Worlds must overwrite new Actor's position to make them spawn in right place.
### Remarks

View File

@@ -55,13 +55,6 @@ public class App implements ApplicationListener {
public static final String GAME_NAME = TerrarumAppConfiguration.GAME_NAME;
// is this jvm good?
static {
if (System.getProperty("sun.arch.data.model") == null || System.getProperty("sun.arch.data.model").equals("unknown")) {
System.err.println("Error: Your JVM is not supported by the application.\nPlease install the desired version.");
System.exit(1);
}
}
public static final int VERSION_RAW = TerrarumAppConfiguration.VERSION_RAW;
@@ -195,7 +188,8 @@ public class App implements ApplicationListener {
/**
* Sorted by the lastplaytime, in reverse order (index 0 is the most recent game played)
*/
public static ArrayList<DiskSkimmer> savegames = new ArrayList<>();
public static TreeMap<UUID, DiskSkimmer> savegameWorlds = new TreeMap<>();
public static TreeMap<UUID, DiskSkimmer> savegamePlayers = new TreeMap<>();
public static void updateListOfSavegames() {
AppUpdateListOfSavegames();
@@ -711,6 +705,7 @@ public class App implements ApplicationListener {
//MinimapComposer.INSTANCE.dispose();
//FloatDrawer.INSTANCE.dispose();
ThreadExecutor.INSTANCE.killAll();
ditherPattern.dispose();
shaderBayerSkyboxFill.dispose();

View File

@@ -30,7 +30,7 @@ import java.util.concurrent.locks.Lock
* Although the game (as product) can have infinitely many stages/planets/etc., those stages must be manually managed by YOU;
* this instance only stores the stage that is currently being used.
*/
open class IngameInstance(val batch: SpriteBatch) : Screen {
open class IngameInstance(val batch: SpriteBatch, val isMultiplayer: Boolean = false) : Screen {
open protected val actorMBRConverter = object : MBRConverter<ActorWithBody> {
override fun getDimensions(): Int = 2
@@ -127,10 +127,6 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
val modifiedChunks = Array(16) { TreeSet<Int>() }
internal var creationTime = App.getTIME_T() // cumulative value for the savegame
internal var lastPlayTime = App.getTIME_T() // cumulative value for the savegame
internal var totalPlayTime = 0L // cumulative value for the savegame
var loadedTime_t = App.getTIME_T()
protected set

View File

@@ -10,25 +10,24 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.utils.Disposable
import com.badlogic.gdx.utils.JsonReader
import com.jme3.math.FastMath
import net.torvald.UnsafeHelper
import net.torvald.gdx.graphics.Cvec
import net.torvald.random.HQRNG
import net.torvald.terrarum.App.*
import net.torvald.terrarum.Terrarum.PLAYER_REF_ID
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.WireCodex
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.faction.FactionCodex
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.serialise.*
import net.torvald.terrarum.tvda.*
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.tvda.ByteArray64Reader
import net.torvald.terrarum.tvda.DiskSkimmer
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.GameFontBase
@@ -36,6 +35,7 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.util.CircularArray
import java.io.File
import java.io.PrintStream
import java.util.*
import kotlin.math.absoluteValue
import kotlin.math.round
@@ -681,8 +681,11 @@ class Codex : KVHashMap() {
}
fun AppUpdateListOfSavegames() {
App.savegames.clear()
File(App.saveDir).listFiles().filter { !it.isDirectory && !it.name.contains('.') }.map { file ->
App.savegamePlayers.clear()
App.savegameWorlds.clear()
// create list of worlds
(File(App.worldsDir).listFiles().filter { !it.isDirectory && !it.name.contains('.') }.map { file ->
try {
DiskSkimmer(file, Common.CHARSET, true)
}
@@ -691,9 +694,14 @@ fun AppUpdateListOfSavegames() {
e.printStackTrace()
null
}
}.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() }.forEach {
App.savegames.add(it!!)
}.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() } as List<DiskSkimmer>).forEach {
// TODO write simple and dumb SAX parser for JSON
val json = JsonReader().parse(ByteArray64Reader(it.getFile(-1L)!!.bytes, Common.CHARSET).readText())
val worldUUID = UUID.fromString(json.get("worldIndex")!!.asString())
App.savegameWorlds[worldUUID] = it
}
// TODO: savegamePlayers
}
/**
@@ -708,7 +716,7 @@ fun checkForSavegameDamage(skimmer: DiskSkimmer): Boolean {
// # check if:
// the world The Player is at actually exists
// all the actors for the world actually exists
// TODO might want SAX parser for JSON
// TODO SAX parser for JSON -- use JsonReader().parse(<String>) for now...
val currentWorld = (player as EntryFile).bytes.let {
(ReadActor.readActorBare(ByteArray64Reader(it, Common.CHARSET)) as? IngamePlayer
?: return true).worldCurrentlyPlaying

View File

@@ -203,7 +203,7 @@ class TitleScreen(batch: SpriteBatch) : IngameInstance(batch) {
uiContainer.add(uiFakeBlurOverlay)
uiRemoCon = UIRemoCon(this, UITitleRemoConYaml(App.savegames.isNotEmpty()))
uiRemoCon = UIRemoCon(this, UITitleRemoConYaml(App.savegamePlayers.isNotEmpty()))
uiRemoCon.setPosition(0, 0)
uiRemoCon.setAsOpen()

View File

@@ -71,6 +71,9 @@ object ThreadExecutor {
allFinished = true
}
fun killAll() {
executor.shutdownNow()
}
}
/**

View File

@@ -59,7 +59,7 @@ class SavegameCracker(
private val cc0 = colourCodes[0]
private val prompt: String
get() = "$ccConst${disk?.getDiskNameString(charset) ?: ""}$cc0% "
get() = "$ccConst${disk?.getDiskName(charset) ?: ""}$cc0% "
private val cmds: HashMap<String, KFunction<*>> = HashMap()
init {

View File

@@ -18,6 +18,12 @@ abstract class Actor : Comparable<Actor>, Runnable {
* @return Reference ID. (16777216-0x7FFF_FFFF)
*/
open var referenceID: ActorID = 0 // in old time this was nullable without initialiser. If you're going to revert to that, add the reason why this should be nullable.
/**
* RenderOrder does not affect ReferenceID "too much" (ID generation will still depend on it, but it's just because of ye olde tradition by now)
*
* IngameRenderer will only look for RenderOrder and won't look for referenceID, so if you want to change the RenderOrder, just modify this field and not the referenceID.
*/
var renderOrder = RenderOrder.MIDDLE
protected constructor()

View File

@@ -23,12 +23,10 @@ typealias BlockAddress = Long
class PhysicalStatus() {
// bottom-center point
var position = Point2d()
// some actorvalues
var scale = 1.0
// actorvalues are copied separately so don't worry about them here
constructor(player: IngamePlayer) : this() {
this.position = Point2d(player.hitbox.canonicalX, player.hitbox.canonicalY)
this.scale = player.avBaseScale
}
}
@@ -50,6 +48,12 @@ open class GameWorld() : Disposable {
var playersLastStatus = PlayersLastStatus() // only gets used when the game saves and loads
/**
* 0,1 - RoguelikeRandomiser
* 2,3 - WeatherMixer
*/
val randSeeds = LongArray(256) // stores 128 128-bit numbers
/** Creation time for this world, NOT the entire savegame */
internal var creationTime: Long = App.getTIME_T()
internal set

View File

@@ -7,7 +7,6 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.EMDASH
import net.torvald.terrarum.*
import net.torvald.terrarum.App.*
import net.torvald.terrarum.Terrarum.PLAYER_REF_ID
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.BlockPropUtil
@@ -24,7 +23,6 @@ import net.torvald.terrarum.gameitem.inInteractableRange
import net.torvald.terrarum.gameparticles.ParticleBase
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldSimulator
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.gameactors.*
import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver
import net.torvald.terrarum.modulebasegame.gameitems.PickaxeCore
@@ -34,9 +32,12 @@ import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.serialise.*
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.LoadSavegame
import net.torvald.terrarum.serialise.ReadActor
import net.torvald.terrarum.serialise.WriteSavegame
import net.torvald.terrarum.tvda.DiskSkimmer
import net.torvald.terrarum.tvda.VDUtil
import net.torvald.terrarum.tvda.VirtualDisk
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UIAutosaveNotifier
import net.torvald.terrarum.ui.UICanvas
@@ -249,15 +250,17 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
}
data class Codices(
val disk: VirtualDisk,
val meta: WriteMeta.WorldMeta,
val disk: DiskSkimmer, // WORLD disk
val world: GameWorld,
// val meta: WriteMeta.WorldMeta,
// val block: BlockCodex,
val item: ItemCodex,
// val item: ItemCodex,
// val wire: WireCodex,
// val material: MaterialCodex,
// val faction: FactionCodex,
val apocryphas: Map<String, Any>,
val actors: List<ActorID>
// val apocryphas: Map<String, Any>,
val actors: List<ActorID>,
val player: IngamePlayer
)
@@ -272,13 +275,13 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
else {
printdbg(this, "Ingame setting things up from the savegame")
RoguelikeRandomiser.loadFromSave(codices.meta.randseed0, codices.meta.randseed1)
WeatherMixer.loadFromSave(codices.meta.weatseed0, codices.meta.weatseed1)
RoguelikeRandomiser.loadFromSave(codices.world.randSeeds[0], codices.world.randSeeds[1])
WeatherMixer.loadFromSave(codices.world.randSeeds[2], codices.world.randSeeds[3])
Terrarum.itemCodex.loadFromSave(codices.item)
Terrarum.apocryphas = HashMap(codices.apocryphas)
// Terrarum.itemCodex.loadFromSave(codices.item)
// Terrarum.apocryphas = HashMap(codices.apocryphas)
savegameNickname = codices.disk.getDiskNameString(Common.CHARSET)
savegameNickname = codices.disk.getDiskName(Common.CHARSET)
}
}
@@ -287,18 +290,36 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
codices.actors.forEach {
try {
val actor = ReadActor(codices.disk, LoadSavegame.getFileReader(codices.disk, it.toLong()))
addNewActor(actor)
if (actor !is IngamePlayer) { // actor list should not contain IngamePlayers (see WriteWorld.preWrite) but just in case...
addNewActor(actor)
}
}
catch (e: NullPointerException) {
System.err.println("Could not read the actor ${it} from the disk")
e.printStackTrace()
if (it == PLAYER_REF_ID) throw e
// throw e // if not player, don't rethrow -- let players play the corrupted world if it loads, they'll be able to cope with their losses even though there will be buncha lone actorblocks lying around...
}
}
// assign new random referenceID for player
codices.player.referenceID = Terrarum.generateUniqueReferenceID(Actor.RenderOrder.MIDDLE)
addNewActor(codices.player)
// overwrite player's props with world's for multiplayer
// see comments on IngamePlayer.unauthorisedPlayerProps to know why this is necessary.
codices.player.backupPlayerProps(isMultiplayer) // backup first!
world.playersLastStatus[codices.player.uuid]?.let { // if nothing was saved, nothing would happen and we still keep the backup, which WriteActor looks for it
codices.player.setPosition(it.physics.position)
if (isMultiplayer) {
codices.player.actorValue = it.actorValue!!
codices.player.inventory = it.inventory!!
}
}
// by doing this, whatever the "possession" the player had will be broken by the game load
actorNowPlaying = getActorByID(Terrarum.PLAYER_REF_ID) as IngamePlayer
actorNowPlaying = codices.player
actorGamer = codices.player
makeSavegameBackupCopy() // don't put it on the postInit() or render(); postInitForNewGame calls this function on the savegamewriter's callback
}
@@ -332,10 +353,11 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
// 1. lighten the IO burden
// 2. cannot sync up the "counter" to determine whether both are finished
uiAutosaveNotifier.setAsOpen()
WriteSavegame.immediate(WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, false, true) {
val saveTime_t = App.getTIME_T()
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, false, true) {
makeSavegameBackupCopy(getPlayerSaveFiledesc(playerSavefileName))
WriteSavegame.immediate(WriteSavegame.SaveMode.WORLD, worldDisk, getWorldSaveFiledesc(worldSavefileName), this, false, true) {
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.WORLD, worldDisk, getWorldSaveFiledesc(worldSavefileName), this, false, true) {
makeSavegameBackupCopy(getWorldSaveFiledesc(worldSavefileName)) // don't put it on the postInit() or render(); must be called using callback
uiAutosaveNotifier.setAsClose()
}

View File

@@ -8,17 +8,16 @@ import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.serialise.WriteActor
import net.torvald.terrarum.serialise.WriteMeta
import net.torvald.terrarum.serialise.WriteWorld
import java.io.IOException
/**
* Created by minjaesong on 2017-07-18.
*/
object ExportMeta : ConsoleCommand {
/*object ExportMeta : ConsoleCommand {
override fun execute(args: Array<String>) {
try {
val str = WriteMeta(ingame!! as TerrarumIngame, App.getTIME_T())
val str = net.torvald.terrarum.serialise.WriteMeta(ingame!! as TerrarumIngame, App.getTIME_T())
val writer = java.io.FileWriter(App.defaultDir + "/Exports/savegame.json", false)
writer.write(str)
writer.close()
@@ -33,7 +32,7 @@ object ExportMeta : ConsoleCommand {
override fun printUsage() {
Echo("Usage: Exportmeta")
}
}
}*/
object ExportWorld : ConsoleCommand {
override fun execute(args: Array<String>) {

View File

@@ -2,13 +2,14 @@ package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Texture
import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.spriteassembler.ADProperties
import net.torvald.spriteassembler.AssembleSheetPixmap
import net.torvald.terrarum.App
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.tvda.SimpleFileSystem
import net.torvald.terrarum.utils.PlayerLastStatus
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import java.util.*
@@ -21,7 +22,11 @@ import java.util.*
class IngamePlayer : ActorHumanoid {
var uuid = UUID.randomUUID(); private set
val creationTime = App.getTIME_T()
var lastPlayTime = App.getTIME_T() // cumulative value for the savegame
var totalPlayTime = 0L // cumulative value for the savegame
val uuid = UUID.randomUUID()
var worldCurrentlyPlaying: UUID = UUID(0L,0L) // only filled up on save and load; DO NOT USE THIS
/** ADL for main sprite. Necessary. */
@@ -50,6 +55,17 @@ class IngamePlayer : ActorHumanoid {
}
/** Copy of some of the player's props before get overwritten by the props saved in the world.
*
* This field is only there for loading multiplayer map on singleplayer instances where the world loader would
* permanently changing player's props into multiplayer world's.
*/
@Transient internal lateinit var unauthorisedPlayerProps: PlayerLastStatus
fun backupPlayerProps(isMultiplayer: Boolean) {
unauthorisedPlayerProps = PlayerLastStatus(this, isMultiplayer)
}
/**

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.utils.JsonReader
import net.torvald.getKeycapConsole
import net.torvald.getKeycapPC
import net.torvald.terrarum.*
@@ -14,8 +15,6 @@ import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.LoadSavegame
import net.torvald.terrarum.serialise.ReadMeta
import net.torvald.terrarum.serialise.WriteMeta
import net.torvald.terrarum.tvda.*
import net.torvald.terrarum.ui.*
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
@@ -26,6 +25,9 @@ import java.util.logging.Level
import java.util.zip.GZIPInputStream
import kotlin.math.roundToInt
val SAVE_CELL_WIDTH = 480
val SAVE_CELL_HEIGHT = 120
/**
* Only works if current screen set by the App is [TitleScreen]
*
@@ -55,13 +57,13 @@ class UILoadDemoSavefiles : UICanvas() {
private val shapeRenderer = ShapeRenderer()
internal val uiWidth = UIItemDemoSaveCells.WIDTH // 480
internal val uiWidth = SAVE_CELL_WIDTH
internal val uiX = (width - uiWidth) / 2
internal val textH = App.fontGame.lineHeight.toInt()
internal val cellGap = 20
internal val cellInterval = cellGap + UIItemDemoSaveCells.HEIGHT
internal val cellInterval = cellGap + SAVE_CELL_HEIGHT
internal val gradAreaHeight = 32
internal val titleTextPosY: Int = App.scr.tvSafeGraphicsHeight + 10
@@ -106,15 +108,15 @@ class UILoadDemoSavefiles : UICanvas() {
Thread {
// read savegames
var savegamesCount = 0
App.savegames.forEach { skimmer ->
App.savegameWorlds.forEach { (uuid, skimmer) ->
val x = uiX + if (App.getConfigBoolean("fx_streamerslayout")) App.scr.chatWidth / 2 else 0
val y = titleTopGradEnd + cellInterval * savegamesCount
try {
addUIitem(UIItemDemoSaveCells(this, x, y, skimmer))
addUIitem(UIItemWorldCells(this, x, y, skimmer))
savegamesCount += 1
}
catch (e: Throwable) {
System.err.println("[UILoadDemoSavefiles] Savefile '${skimmer.diskFile.absolutePath}' cannot be loaded")
System.err.println("[UILoadDemoSavefiles] Error while loading World '${skimmer.diskFile.absolutePath}'")
e.printStackTrace()
}
@@ -290,7 +292,7 @@ class UILoadDemoSavefiles : UICanvas() {
override fun resize(width: Int, height: Int) {
super.resize(width, height)
scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + UIItemDemoSaveCells.HEIGHT)
savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + SAVE_CELL_HEIGHT)
listScroll = 0
scrollTarget = 0
@@ -311,49 +313,66 @@ class UILoadDemoSavefiles : UICanvas() {
class UIItemDemoSaveCells(
class UIItemPlayerCells(
parent: UILoadDemoSavefiles,
initialX: Int,
initialY: Int,
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
companion object {
const val WIDTH = 480
const val HEIGHT = 120
override val width = SAVE_CELL_WIDTH
override val height = SAVE_CELL_HEIGHT
override fun dispose() {
}
private val metaFile: DiskEntry?
}
class UIItemWorldCells(
parent: UILoadDemoSavefiles,
initialX: Int,
initialY: Int,
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
private val metaFile: EntryFile?
private val saveName: String
private val saveMode: Int
private val isQuick: Boolean
private val isAuto: Boolean
private val meta: WriteMeta.WorldMeta?
private val saveDamaged: Boolean
private var saveDamaged: Boolean = false
private val lastPlayedTimestamp: String
init {
printdbg(this, "Rebuilding skimmer for savefile ${skimmer.diskFile.absolutePath}")
skimmer.rebuild()
metaFile = skimmer.requestFile(-1)
metaFile = skimmer.getFile(-1)
if (metaFile == null) saveDamaged = true
saveName = skimmer.getDiskName(Common.CHARSET)
saveMode = skimmer.getSaveMode()
isQuick = (saveMode % 2 == 1)
isAuto = (saveMode.ushr(1) != 0)
meta = if (metaFile != null) ReadMeta.fromDiskEntry(metaFile) else null
saveDamaged = checkForSavegameDamage(skimmer)
saveDamaged = saveDamaged or checkForSavegameDamage(skimmer)
lastPlayedTimestamp = if (meta != null)
Instant.ofEpochSecond(meta.lastplay_t)
.atZone(TimeZone.getDefault().toZoneId())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
"/${parseDuration(meta.playtime_t)}"
else "--:--:--/--h--m--s"
if (metaFile != null) {
val worldJson = JsonReader().parse(ByteArray64Reader(metaFile.bytes, Common.CHARSET))
val lastplay_t = worldJson["lastPlayTime"].asLong()
val playtime_t = worldJson["totalPlayTime"].asLong()
lastPlayedTimestamp =
Instant.ofEpochSecond(lastplay_t)
.atZone(TimeZone.getDefault().toZoneId())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
"/${parseDuration(playtime_t)}"
}
else {
lastPlayedTimestamp = "--:--:--/--h--m--s"
}
}
private fun parseDuration(seconds: Long): String {
@@ -367,8 +386,8 @@ class UIItemDemoSaveCells(
"${d}d${h.toString().padStart(2,'0')}h${m.toString().padStart(2,'0')}m${s.toString().padStart(2,'0')}s"
}
override val width: Int = WIDTH
override val height: Int = HEIGHT
override val width: Int = SAVE_CELL_WIDTH
override val height: Int = SAVE_CELL_HEIGHT
private var thumbPixmap: Pixmap? = null
private var thumb: TextureRegion? = null

View File

@@ -2,12 +2,8 @@ package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.Second
import net.torvald.terrarum.serialise.LoadSavegame
import net.torvald.terrarum.tvda.VDUtil
import net.torvald.terrarum.ui.UICanvas
import java.util.logging.Level
/**
* Created by minjaesong on 2021-09-13.
@@ -31,11 +27,11 @@ class UIProxyLoadLatestSave : UICanvas() {
}
override fun endOpening(delta: Float) {
if (App.savegames.size > 0) {
LoadSavegame(VDUtil.readDiskArchive(App.savegames[0].diskFile, Level.INFO) {
System.err.println("Possibly damaged savefile ${App.savegames[0].diskFile.absolutePath}:\n$it")
})
}
// do something!
}
override fun endClosing(delta: Float) {

View File

@@ -12,6 +12,7 @@ object UITitleRemoConYaml {
* The class must be the UICanvas
*/
private val menuBase = """
- MENU_LABEL_NEW_GAME : net.torvald.terrarum.modulebasegame.ui.UIProxyNewRandomGame
- MENU_OPTIONS
- MENU_LABEL_GRAPHICS : net.torvald.terrarum.modulebasegame.ui.GraphicsControlPanel
- MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel
@@ -27,17 +28,15 @@ object UITitleRemoConYaml {
private val menuWithSavefile = """
- MENU_LABEL_CONTINUE : net.torvald.terrarum.modulebasegame.ui.UIProxyLoadLatestSave
- MENU_LABEL_NEW_GAME : net.torvald.terrarum.modulebasegame.ui.UIProxyNewRandomGame
- MENU_IO_LOAD : net.torvald.terrarum.modulebasegame.ui.UILoadDemoSavefiles
- MENU_LABEL_RETURN
"""
- MENU_LABEL_NEW_WORLD
- MENU_LABEL_RETURN"""
private val menuNewGame = """
- MENU_LABEL_NEW_GAME : net.torvald.terrarum.modulebasegame.ui.UIProxyNewRandomGame
"""
operator fun invoke(hasSave: Boolean) =
Yaml((if (hasSave) menuWithSavefile else menuNewGame) + menuBase).parse()
Yaml((if (!hasSave) menuWithSavefile else menuNewGame) + menuBase).parse()
}

View File

@@ -1,7 +1,6 @@
package net.torvald.terrarum.serialise
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App
import net.torvald.terrarum.ccG
import net.torvald.terrarum.ccW
import net.torvald.terrarum.console.Echo
@@ -42,7 +41,7 @@ abstract class SavingThread(private val ingame: TerrarumIngame) : Runnable {
/**
* Created by minjaesong on 2021-09-14.
*/
class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : SavingThread(ingame) {
class WorldSavingThread(val time_t: Long, val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : SavingThread(ingame) {
override fun save() {
@@ -71,8 +70,7 @@ class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Te
Echo("Writing metadata...")
val creation_t = ingame.creationTime
val time_t = App.getTIME_T()
val creation_t = ingame.world.creationTime
if (hasThumbnail) {
@@ -86,45 +84,10 @@ class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Te
WriteSavegame.saveProgress += 1f
// Write BlockCodex//
// val blockCodexContent = EntryFile(zip(ByteArray64.fromByteArray(Common.jsoner.toJson(BlockCodex).toByteArray(Common.CHARSET))))
// val blocks = DiskEntry(-16, 0, "blocks".toByteArray(Common.CHARSET), creation_t, time_t, blockCodexContent)
// addFile(disk, blocks)
// Commented out; nothing to write
// Write ItemCodex//
// val itemCodexContent = EntryFile(Common.zip(ByteArray64.fromByteArray(Common.jsoner.toJson(ItemCodex).toByteArray(Common.CHARSET))))
// val items = DiskEntry(-17, 0, creation_t, time_t, itemCodexContent)
// addFile(disk, items)
// Gotta save dynamicIDs
// Write WireCodex//
// val wireCodexContent = EntryFile(zip(ByteArray64.fromByteArray(Common.jsoner.toJson(WireCodex).toByteArray(Common.CHARSET))))
// val wires = DiskEntry(-18, 0, "wires".toByteArray(Common.CHARSET), creation_t, time_t, wireCodexContent)
// addFile(disk, wires)
// Commented out; nothing to write
// Write MaterialCodex//
// val materialCodexContent = EntryFile(zip(ByteArray64.fromByteArray(Common.jsoner.toJson(MaterialCodex).toByteArray(Common.CHARSET))))
// val materials = DiskEntry(-19, 0, "materials".toByteArray(Common.CHARSET), creation_t, time_t, materialCodexContent)
// addFile(disk, materials)
// Commented out; nothing to write
// Write FactionCodex//
// val factionCodexContent = EntryFile(zip(ByteArray64.fromByteArray(Common.jsoner.toJson(FactionCodex).toByteArray(Common.CHARSET))))
// val factions = DiskEntry(-20, 0, "factions".toByteArray(Common.CHARSET), creation_t, time_t, factionCodexContent)
// addFile(disk, factions)
// Write Apocryphas//
// val apocryphasContent = EntryFile(Common.zip(ByteArray64.fromByteArray(Common.jsoner.toJson(Apocryphas).toByteArray(Common.CHARSET))))
// val apocryphas = DiskEntry(-1024, 0, creation_t, time_t, apocryphasContent)
// addFile(disk, apocryphas)
// Write World //
// record all player's last position
playersList.forEach {
ingame.world.playersLastStatus[it.uuid] = PlayerLastStatus(it)
ingame.world.playersLastStatus[it.uuid] = PlayerLastStatus(it, ingame.isMultiplayer)
}
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t))
val world = DiskEntry(-1L, 0, creation_t, time_t, worldMeta)
@@ -192,14 +155,14 @@ class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Te
*
* Created by minjaesong on 2021-10-08
*/
class PlayerSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : SavingThread(ingame) {
class PlayerSavingThread(val time_t: Long, val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : SavingThread(ingame) {
override fun save() {
disk.saveMode = 2 * isAuto.toInt() // no quick
disk.capacity = 0L
Echo("Writing The Player...")
WritePlayer(ingame.actorGamer, disk)
WritePlayer(ingame.actorGamer, disk, ingame, time_t)
VDUtil.dumpToRealMachine(disk, outFile)
callback()

View File

@@ -1,22 +1,23 @@
package net.torvald.terrarum.serialise
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App
import net.torvald.terrarum.ccG
import net.torvald.terrarum.ccW
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.toInt
import net.torvald.terrarum.tvda.*
import net.torvald.terrarum.utils.PlayerLastStatus
import java.io.File
import java.util.zip.GZIPOutputStream
/**
* Created by minjaesong on 2021-09-29.
*/
class QuickSaveThread(val disk: VirtualDisk, val file: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : Runnable {
class QuickSingleplayerWorldSavingThread(val time_t: Long, val disk: VirtualDisk, val file: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : Runnable {
/**
* Will happily overwrite existing entry
@@ -34,11 +35,6 @@ class QuickSaveThread(val disk: VirtualDisk, val file: File, val ingame: Terraru
override fun run() {
callback()
return
// TODO //
val skimmer = DiskSkimmer(file, Common.CHARSET)
if (hasThumbnail) {
@@ -47,7 +43,10 @@ class QuickSaveThread(val disk: VirtualDisk, val file: File, val ingame: Terraru
}
}
val actorsList = listOf(ingame.actorContainerActive).flatMap { it.filter { WriteWorld.actorAcceptable(it) } }
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
val playersList: List<IngamePlayer> = allTheActors.filter{ it is IngamePlayer } as List<IngamePlayer>
val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
val chunks = ingame.modifiedChunks
val chunkCount = chunks.map { it.size }.sum()
@@ -63,14 +62,8 @@ class QuickSaveThread(val disk: VirtualDisk, val file: File, val ingame: Terraru
Echo("Writing metadata...")
val creation_t = ingame.creationTime
val time_t = App.getTIME_T()
val creation_t = ingame.world.creationTime
// Write Meta //
val metaContent = EntryFile(WriteMeta.encodeToByteArray64(ingame, time_t))
val meta = DiskEntry(-1, 0, creation_t, time_t, metaContent)
addFile(disk, meta); skimmer.appendEntryOnly(meta)
if (hasThumbnail) {
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
@@ -78,16 +71,18 @@ class QuickSaveThread(val disk: VirtualDisk, val file: File, val ingame: Terraru
val thumbContent = EntryFile(tgaout.toByteArray64())
val thumb = DiskEntry(-2, 0, creation_t, time_t, thumbContent)
addFile(disk, thumb); skimmer.appendEntryOnly(thumb)
addFile(disk, thumb)
}
WriteSavegame.saveProgress += 1f
// Write World //
val worldNum = ingame.world.worldIndex
// record all player's last position
playersList.forEach {
ingame.world.playersLastStatus[it.uuid] = PlayerLastStatus(it, ingame.isMultiplayer)
}
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t))
val world = DiskEntry(-1-1-1-1-1-1-1, 0, creation_t, time_t, worldMeta)
val world = DiskEntry(-1L, 0, creation_t, time_t, worldMeta)
addFile(disk, world); skimmer.appendEntryOnly(world)
WriteSavegame.saveProgress += 1f

View File

@@ -3,8 +3,6 @@ package net.torvald.terrarum.serialise
import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.spriteassembler.ADProperties
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.NoSuchActorWithIDException
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody
@@ -62,8 +60,21 @@ object WritePlayer {
if (!dir.contains(file.entryID)) dir.add(file.entryID)
}
operator fun invoke(player: IngamePlayer, playerDisk: VirtualDisk) {
val time_t = App.getTIME_T()
operator fun invoke(player: IngamePlayer, playerDisk: VirtualDisk, ingame: TerrarumIngame, time_t: Long) {
player.lastPlayTime = time_t
player.totalPlayTime += time_t - ingame.loadedTime_t
// restore player prop backup created on load-time
if (ingame.world.playersLastStatus[player.uuid] != null) {
player.setPosition(player.unauthorisedPlayerProps.physics.position)
if (ingame.isMultiplayer) {
player.actorValue = player.unauthorisedPlayerProps.actorValue!!
player.inventory = player.unauthorisedPlayerProps.inventory!!
}
}
val actorJson = WriteActor.encodeToByteArray64(player)
val adl = player.animDesc!!.getRawADL()
val adlGlow = player.animDescGlow?.getRawADL() // NULLABLE!

View File

@@ -1,64 +1,10 @@
package net.torvald.terrarum.serialise
import net.torvald.terrarum.App
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.tvda.*
import net.torvald.terrarum.weather.WeatherMixer
/**
* Created by minjaesong on 2021-08-23.
*/
object WriteMeta {
operator fun invoke(ingame: TerrarumIngame, time_t: Long): String {
val world = ingame.world
val currentPlayTime_t = time_t - ingame.loadedTime_t
val meta = WorldMeta(
genver = Common.GENVER,
savename = world.worldName,
randseed0 = RoguelikeRandomiser.RNG.state0,
randseed1 = RoguelikeRandomiser.RNG.state1,
weatseed0 = WeatherMixer.RNG.state0,
weatseed1 = WeatherMixer.RNG.state1,
creation_t = ingame.creationTime,
lastplay_t = time_t,
playtime_t = ingame.totalPlayTime + currentPlayTime_t,
loadorder = ModMgr.loadOrder.toTypedArray(),
//worlds = ingame.gameworldIndices.toTypedArray()
)
return Common.jsoner.toJson(meta)
}
fun encodeToByteArray64(ingame: TerrarumIngame, time_t: Long): ByteArray64 {
val ba = ByteArray64()
this.invoke(ingame, time_t).toByteArray(Common.CHARSET).forEach { ba.add(it) }
return ba
}
data class WorldMeta(
val genver: Int = -1,
val savename: String = "",
val randseed0: Long = 0,
val randseed1: Long = 0,
val weatseed0: Long = 0,
val weatseed1: Long = 0,
val playerid: ActorID = 0,
val creation_t: Long = 0,
val lastplay_t: Long = 0,
val playtime_t: Long = 0,
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
) {
override fun equals(other: Any?): Boolean {
throw UnsupportedOperationException()
}
}
private fun modnameToOrnamentalHeader(s: String) =
"\n\n${"#".repeat(16 + s.length)}\n" +
@@ -72,15 +18,5 @@ object WriteMeta {
*/
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)
}
fun fromDiskEntry(metaFile: DiskEntry): WriteMeta.WorldMeta {
val metaReader = ByteArray64Reader((metaFile.contents as EntryFile).getContent(), Common.CHARSET)
return Common.jsoner.fromJson(WriteMeta.WorldMeta::class.java, metaReader)
}
}

View File

@@ -11,10 +11,9 @@ import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.serialise.Common.getUnzipInputStream
import net.torvald.terrarum.tvda.ByteArray64
import net.torvald.terrarum.tvda.ByteArray64Reader
import net.torvald.terrarum.tvda.VDUtil
import net.torvald.terrarum.tvda.SimpleFileSystem
import net.torvald.terrarum.tvda.VirtualDisk
import java.io.File
import java.io.Reader
@@ -27,20 +26,21 @@ import java.io.Reader
object WriteSavegame {
enum class SaveMode {
META, PLAYER, WORLD, SHARED
META, PLAYER, WORLD, SHARED, QUICK_PLAYER, QUICK_WORLD
}
@Volatile var savingStatus = -1 // -1: not started, 0: saving in progress, 255: saving finished
@Volatile var saveProgress = 0f
@Volatile var saveProgressMax = 1f
private fun getSaveThread(mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, callback: () -> Unit = {}) = when (mode) {
SaveMode.WORLD -> WorldSavingThread(disk, outFile, ingame, hasThumbnail, isAuto, callback)
SaveMode.PLAYER -> PlayerSavingThread(disk, outFile, ingame, hasThumbnail, isAuto, callback)
private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, callback: () -> Unit = {}) = when (mode) {
SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback)
SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback)
SaveMode.QUICK_PLAYER -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback)
else -> throw IllegalArgumentException("$mode")
}
operator fun invoke(mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) {
operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) {
savingStatus = 0
Echo("Save queued")
@@ -60,7 +60,7 @@ object WriteSavegame {
}
IngameRenderer.screencapRequested = true
val savingThread = Thread(getSaveThread(mode, disk, outFile, ingame, true, isAuto, callback), "TerrarumBasegameGameSaveThread")
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, true, isAuto, callback), "TerrarumBasegameGameSaveThread")
savingThread.start()
// it is caller's job to keep the game paused or keep a "save in progress" ui up
@@ -68,23 +68,23 @@ object WriteSavegame {
}
fun immediate(mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, callback: () -> Unit = {}) {
fun immediate(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, callback: () -> Unit = {}) {
savingStatus = 0
Echo("Immediate save fired")
val savingThread = Thread(getSaveThread(mode, disk, outFile, ingame, false, isAuto, callback), "TerrarumBasegameGameSaveThread")
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, false, isAuto, callback), "TerrarumBasegameGameSaveThread")
savingThread.start()
// it is caller's job to keep the game paused or keep a "save in progress" ui up
// use field 'savingStatus' to know when the saving is done
}
fun quick(disk: VirtualDisk, file: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) {
return
fun quick(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) {
if (ingame.isMultiplayer) TODO()
// TODO //
return // TODO //
savingStatus = 0
@@ -105,7 +105,7 @@ object WriteSavegame {
}
IngameRenderer.screencapRequested = true
val savingThread = Thread(QuickSaveThread(disk, file, ingame, true, isAuto, callback), "TerrarumBasegameGameSaveThread")
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, false, isAuto, callback), "TerrarumBasegameGameSaveThread")
savingThread.start()
// it is caller's job to keep the game paused or keep a "save in progress" ui up
@@ -125,48 +125,33 @@ object WriteSavegame {
*/
object LoadSavegame {
fun getFileBytes(disk: VirtualDisk, id: Long): ByteArray64 = VDUtil.getAsNormalFile(disk, id).getContent()
fun getFileReader(disk: VirtualDisk, id: Long): Reader = ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET)
operator fun invoke(disk: VirtualDisk) {
TODO()
fun getFileBytes(disk: SimpleFileSystem, id: Long): ByteArray64 = disk.getFile(id)!!.bytes
fun getFileReader(disk: SimpleFileSystem, id: Long): Reader = ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET)
operator fun invoke(playerDisk: SimpleFileSystem) {
val newIngame = TerrarumIngame(App.batch)
val player = ReadActor.invoke(playerDisk, ByteArray64Reader(playerDisk.getFile(-1L)!!.bytes, Common.CHARSET)) as IngamePlayer
val meta = ReadMeta(disk)
val currentWorldId = player.worldCurrentlyPlaying
val worldDisk = App.savegameWorlds[currentWorldId]!!
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(-1L)!!.bytes, Common.CHARSET))
// NOTE: do NOT set ingame.actorNowPlaying as one read directly from the disk;
// you'll inevitably read the player actor twice, and they're separate instances of the player!
val currentWorld = (ReadActor.readActorBare(getFileReader(disk, Terrarum.PLAYER_REF_ID.toLong())) as IngamePlayer).worldCurrentlyPlaying
val world = ReadWorld(getFileReader(disk, -1-1-1-1-1-1))
// set lateinit vars on the gameworld FIRST
world.layerTerrain = BlockLayer(world.width, world.height)
world.layerWall = BlockLayer(world.width, world.height)
newIngame.creationTime = meta.creation_t
newIngame.lastPlayTime = meta.lastplay_t
newIngame.totalPlayTime = meta.playtime_t
newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed
val loadJob = { it: LoadScreenBase ->
val loadscreen = it as ChunkLoadingLoadScreen
loadscreen.addMessage(Lang["MENU_IO_LOADING"])
val actors = world.actors.distinct()//.map { ReadActor(getFileReader(disk, it.toLong())) }
// val block = Common.jsoner.fromJson(BlockCodex.javaClass, getUnzipInputStream(getFileBytes(disk, -16)))
val item = Common.jsoner.fromJson(ItemCodex.javaClass, getUnzipInputStream(getFileBytes(disk, -17)))
// val wire = Common.jsoner.fromJson(WireCodex.javaClass, getUnzipInputStream(getFileBytes(disk, -18)))
// val material = Common.jsoner.fromJson(MaterialCodex.javaClass, getUnzipInputStream(getFileBytes(disk, -19)))
// val faction = Common.jsoner.fromJson(FactionCodex.javaClass, getUnzipInputStream(getFileBytes(disk, -20)))
val apocryphas = Common.jsoner.fromJson(Apocryphas.javaClass, getUnzipInputStream(getFileBytes(disk, -1024)))
val actors = world.actors.distinct()
val worldParam = TerrarumIngame.Codices(worldDisk, world, actors, player)
val worldParam = TerrarumIngame.Codices(disk, meta, item, apocryphas, actors)
newIngame.gameLoadInfoPayload = worldParam
newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM
@@ -180,27 +165,25 @@ object LoadSavegame {
for (layer in worldLayer.indices) {
loadscreen.addMessage("${Lang["MENU_IO_LOADING"]} ${chunk*worldLayer.size+layer+1}/${chunkCount*2}")
val chunkFile = VDUtil.getAsNormalFile(disk, 0x1_0000_0000L or layer.toLong().shl(24) or chunk)
val chunkFile = worldDisk.getFile(0x1_0000_0000L or layer.toLong().shl(24) or chunk)!!
val chunkXY = LandUtil.chunkNumToChunkXY(world, chunk.toInt())
ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, chunkXY.x, chunkXY.y)
}
}
loadscreen.addMessage("Updating Block Mappings...")
world.renumberTilesAfterLoad()
Echo("${ccW}Savegame loaded from $ccY${disk.getDiskNameString(Common.CHARSET)}")
printdbg(this, "Savegame loaded from ${disk.getDiskNameString(Common.CHARSET)}")
Echo("${ccW}World loaded: $ccY${worldDisk.getDiskName(Common.CHARSET)}")
printdbg(this, "World loaded: ${worldDisk.getDiskName(Common.CHARSET)}")
}
val loadScreen = ChunkLoadingLoadScreen(newIngame, world.width, world.height, loadJob)
Terrarum.setCurrentIngameInstance(newIngame)
App.setLoadScreen(loadScreen)
}
}

View File

@@ -10,9 +10,11 @@ import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.GameWorldTitleScreen
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.tvda.ByteArray64
import net.torvald.terrarum.tvda.ByteArray64Writer
import net.torvald.terrarum.weather.WeatherMixer
import java.io.Reader
/**
@@ -43,6 +45,11 @@ object WriteWorld {
world.actors.clear()
world.actors.addAll(actorIDbuf.sorted().distinct())
world.randSeeds[0] = RoguelikeRandomiser.RNG.state0
world.randSeeds[1] = RoguelikeRandomiser.RNG.state1
world.randSeeds[2] = WeatherMixer.RNG.state0
world.randSeeds[3] = WeatherMixer.RNG.state1
return world
}

View File

@@ -1,9 +1,7 @@
package net.torvald.terrarum.tvda
import net.torvald.terrarum.serialise.Common
import java.io.*
import java.nio.charset.Charset
import java.nio.file.NoSuchFileException
import java.util.*
import java.util.logging.Level
import kotlin.experimental.and
@@ -344,7 +342,7 @@ removefile:
return fa.read()
}
fun getDiskName(charset: Charset): String {
override fun getDiskName(charset: Charset): String {
val bytes = ByteArray(268)
fa.seek(10L)
fa.read(bytes, 0, 32)

View File

@@ -1,9 +1,12 @@
package net.torvald.terrarum.tvda
import java.nio.charset.Charset
/**
* Created by minjaesong on 2021-10-07.
*/
interface SimpleFileSystem {
fun getEntry(id: EntryID): DiskEntry?
fun getFile(id: EntryID): EntryFile?
fun getDiskName(charset: Charset): String
}

View File

@@ -56,7 +56,7 @@ object VDUtil {
throw RuntimeException("Invalid Virtual Disk file!")
val diskSize = inbytes.sliceArray64(4L..9L).toInt48Big()
val diskName = inbytes.sliceArray64(10L..10L + 31)
val diskName = inbytes.sliceArray(10..10 + 31) + inbytes.sliceArray(10+32+22..10+32+22+235)
val diskCRC = inbytes.sliceArray64(10L + 32..10L + 32 + 3).toIntBig() // to check with completed vdisk
val diskSpecVersion = inbytes[10L + 32 + 4]
val footers = inbytes.sliceArray64(10L+32+6..10L+32+21)
@@ -64,7 +64,7 @@ object VDUtil {
if (diskSpecVersion != specversion)
throw RuntimeException("Unsupported disk format version: current internal version is $specversion; the file's version is $diskSpecVersion")
val vdisk = VirtualDisk(diskSize, diskName.toByteArray())
val vdisk = VirtualDisk(diskSize, diskName)
vdisk.__internalSetFooter__(footers)

View File

@@ -133,7 +133,7 @@ class VirtualDisk(
var saveMode: Int
set(value) { extraInfoBytes[1] = value.toByte() }
get() = extraInfoBytes[1].toUint()
fun getDiskNameString(charset: Charset) = diskName.toCanonicalString(charset)
override fun getDiskName(charset: Charset) = diskName.toCanonicalString(charset)
val root: DiskEntry
get() = entries[0]!!
@@ -207,7 +207,7 @@ class VirtualDisk(
}
override fun equals(other: Any?) = if (other == null) false else this.hashCode() == other.hashCode()
override fun toString() = "VirtualDisk(name: ${getDiskNameString(Charsets.UTF_8)}, capacity: $capacity bytes, crc: ${hashCode().toHex()})"
override fun toString() = "VirtualDisk(name: ${getDiskName(Charsets.UTF_8)}, capacity: $capacity bytes, crc: ${hashCode().toHex()})"
companion object {
val HEADER_SIZE = 300L // according to the spec

View File

@@ -24,14 +24,16 @@ class HashedWiringGraph: HashMap<BlockAddress, WiringGraphMap>()
class MetaModuleCSVPair: HashMap<String, ZipCodedStr>()
class PlayersLastStatus: HashMap<UUID, PlayerLastStatus>()
class PlayerLastStatus() {
var physics = PhysicalStatus(); private set
var inventory = ActorInventory(); private set
var actorValue = ActorValue(); private set
var physics = PhysicalStatus(); private set // mandatory
var inventory: ActorInventory? = null; private set // optional (multiplayer only)
var actorValue: ActorValue? = null; private set // optional (multiplayer only)
constructor(player: IngamePlayer) : this() {
constructor(player: IngamePlayer, isMultiplayer: Boolean) : this() {
physics = PhysicalStatus(player)
inventory = player.inventory
actorValue = player.actorValue
if (isMultiplayer) {
inventory = player.inventory
actorValue = player.actorValue
}
}
}
/**