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 .Terrarum
+ Players + Players
- "${PlayerName}-${UUID}", TVDA { - "${PlayerName}-${UUID}", TVDA {
[-1] player JSON, [-1] player JSON,
[-2] spritedef, [-2] spritedef,
[-3] optional spritedef-glow, [-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`) (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 ## Handling The Player Data
Some of the "player assets" are stored to the world, such assets include: 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 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) 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. Worlds must overwrite new Actor's position to make them spawn in right place.
### Remarks ### Remarks

View File

@@ -55,13 +55,6 @@ public class App implements ApplicationListener {
public static final String GAME_NAME = TerrarumAppConfiguration.GAME_NAME; 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; 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) * 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() { public static void updateListOfSavegames() {
AppUpdateListOfSavegames(); AppUpdateListOfSavegames();
@@ -711,6 +705,7 @@ public class App implements ApplicationListener {
//MinimapComposer.INSTANCE.dispose(); //MinimapComposer.INSTANCE.dispose();
//FloatDrawer.INSTANCE.dispose(); //FloatDrawer.INSTANCE.dispose();
ThreadExecutor.INSTANCE.killAll();
ditherPattern.dispose(); ditherPattern.dispose();
shaderBayerSkyboxFill.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; * 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. * 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> { open protected val actorMBRConverter = object : MBRConverter<ActorWithBody> {
override fun getDimensions(): Int = 2 override fun getDimensions(): Int = 2
@@ -127,10 +127,6 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
val modifiedChunks = Array(16) { TreeSet<Int>() } 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() var loadedTime_t = App.getTIME_T()
protected set 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.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShapeRenderer import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.badlogic.gdx.utils.JsonReader
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.UnsafeHelper import net.torvald.UnsafeHelper
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.App.* 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_SIZE
import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.WireCodex import net.torvald.terrarum.blockproperties.WireCodex
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.faction.FactionCodex import net.torvald.terrarum.gameactors.faction.FactionCodex
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.MaterialCodex import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.* import net.torvald.terrarum.tvda.ByteArray64Reader
import net.torvald.terrarum.tvda.* import net.torvald.terrarum.tvda.DiskSkimmer
import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.GameFontBase import net.torvald.terrarumsansbitmap.gdx.GameFontBase
@@ -36,6 +35,7 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.util.CircularArray import net.torvald.util.CircularArray
import java.io.File import java.io.File
import java.io.PrintStream import java.io.PrintStream
import java.util.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.round import kotlin.math.round
@@ -681,8 +681,11 @@ class Codex : KVHashMap() {
} }
fun AppUpdateListOfSavegames() { fun AppUpdateListOfSavegames() {
App.savegames.clear() App.savegamePlayers.clear()
File(App.saveDir).listFiles().filter { !it.isDirectory && !it.name.contains('.') }.map { file -> App.savegameWorlds.clear()
// create list of worlds
(File(App.worldsDir).listFiles().filter { !it.isDirectory && !it.name.contains('.') }.map { file ->
try { try {
DiskSkimmer(file, Common.CHARSET, true) DiskSkimmer(file, Common.CHARSET, true)
} }
@@ -691,9 +694,14 @@ fun AppUpdateListOfSavegames() {
e.printStackTrace() e.printStackTrace()
null null
} }
}.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() }.forEach { }.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() } as List<DiskSkimmer>).forEach {
App.savegames.add(it!!) // 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: // # check if:
// the world The Player is at actually exists // the world The Player is at actually exists
// all the actors for the world 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 { val currentWorld = (player as EntryFile).bytes.let {
(ReadActor.readActorBare(ByteArray64Reader(it, Common.CHARSET)) as? IngamePlayer (ReadActor.readActorBare(ByteArray64Reader(it, Common.CHARSET)) as? IngamePlayer
?: return true).worldCurrentlyPlaying ?: return true).worldCurrentlyPlaying

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,12 @@ abstract class Actor : Comparable<Actor>, Runnable {
* @return Reference ID. (16777216-0x7FFF_FFFF) * @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. 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 var renderOrder = RenderOrder.MIDDLE
protected constructor() protected constructor()

View File

@@ -23,12 +23,10 @@ typealias BlockAddress = Long
class PhysicalStatus() { class PhysicalStatus() {
// bottom-center point // bottom-center point
var position = Point2d() var position = Point2d()
// some actorvalues // actorvalues are copied separately so don't worry about them here
var scale = 1.0
constructor(player: IngamePlayer) : this() { constructor(player: IngamePlayer) : this() {
this.position = Point2d(player.hitbox.canonicalX, player.hitbox.canonicalY) 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 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 */ /** Creation time for this world, NOT the entire savegame */
internal var creationTime: Long = App.getTIME_T() internal var creationTime: Long = App.getTIME_T()
internal set internal set

View File

@@ -7,7 +7,6 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.EMDASH import net.torvald.EMDASH
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.* 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_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.BlockPropUtil 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.gameparticles.ParticleBase
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldSimulator import net.torvald.terrarum.gameworld.WorldSimulator
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.gameactors.* import net.torvald.terrarum.modulebasegame.gameactors.*
import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver
import net.torvald.terrarum.modulebasegame.gameitems.PickaxeCore 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.Worldgen
import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams
import net.torvald.terrarum.realestate.LandUtil 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.VDUtil
import net.torvald.terrarum.tvda.VirtualDisk
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UIAutosaveNotifier import net.torvald.terrarum.ui.UIAutosaveNotifier
import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UICanvas
@@ -249,15 +250,17 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
} }
data class Codices( data class Codices(
val disk: VirtualDisk, val disk: DiskSkimmer, // WORLD disk
val meta: WriteMeta.WorldMeta, val world: GameWorld,
// val meta: WriteMeta.WorldMeta,
// val block: BlockCodex, // val block: BlockCodex,
val item: ItemCodex, // val item: ItemCodex,
// val wire: WireCodex, // val wire: WireCodex,
// val material: MaterialCodex, // val material: MaterialCodex,
// val faction: FactionCodex, // val faction: FactionCodex,
val apocryphas: Map<String, Any>, // val apocryphas: Map<String, Any>,
val actors: List<ActorID> val actors: List<ActorID>,
val player: IngamePlayer
) )
@@ -272,13 +275,13 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
else { else {
printdbg(this, "Ingame setting things up from the savegame") printdbg(this, "Ingame setting things up from the savegame")
RoguelikeRandomiser.loadFromSave(codices.meta.randseed0, codices.meta.randseed1) RoguelikeRandomiser.loadFromSave(codices.world.randSeeds[0], codices.world.randSeeds[1])
WeatherMixer.loadFromSave(codices.meta.weatseed0, codices.meta.weatseed1) WeatherMixer.loadFromSave(codices.world.randSeeds[2], codices.world.randSeeds[3])
Terrarum.itemCodex.loadFromSave(codices.item) // Terrarum.itemCodex.loadFromSave(codices.item)
Terrarum.apocryphas = HashMap(codices.apocryphas) // 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 { codices.actors.forEach {
try { try {
val actor = ReadActor(codices.disk, LoadSavegame.getFileReader(codices.disk, it.toLong())) 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) { catch (e: NullPointerException) {
System.err.println("Could not read the actor ${it} from the disk") System.err.println("Could not read the actor ${it} from the disk")
e.printStackTrace() 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 // 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 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 // 1. lighten the IO burden
// 2. cannot sync up the "counter" to determine whether both are finished // 2. cannot sync up the "counter" to determine whether both are finished
uiAutosaveNotifier.setAsOpen() 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)) 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 makeSavegameBackupCopy(getWorldSaveFiledesc(worldSavefileName)) // don't put it on the postInit() or render(); must be called using callback
uiAutosaveNotifier.setAsClose() 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.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.serialise.WriteActor import net.torvald.terrarum.serialise.WriteActor
import net.torvald.terrarum.serialise.WriteMeta
import net.torvald.terrarum.serialise.WriteWorld import net.torvald.terrarum.serialise.WriteWorld
import java.io.IOException import java.io.IOException
/** /**
* Created by minjaesong on 2017-07-18. * Created by minjaesong on 2017-07-18.
*/ */
object ExportMeta : ConsoleCommand { /*object ExportMeta : ConsoleCommand {
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
try { 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) val writer = java.io.FileWriter(App.defaultDir + "/Exports/savegame.json", false)
writer.write(str) writer.write(str)
writer.close() writer.close()
@@ -33,7 +32,7 @@ object ExportMeta : ConsoleCommand {
override fun printUsage() { override fun printUsage() {
Echo("Usage: Exportmeta") Echo("Usage: Exportmeta")
} }
} }*/
object ExportWorld : ConsoleCommand { object ExportWorld : ConsoleCommand {
override fun execute(args: Array<String>) { 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.Gdx
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.spriteassembler.ADProperties import net.torvald.spriteassembler.ADProperties
import net.torvald.spriteassembler.AssembleSheetPixmap import net.torvald.spriteassembler.AssembleSheetPixmap
import net.torvald.terrarum.App
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.tvda.SimpleFileSystem import net.torvald.terrarum.tvda.SimpleFileSystem
import net.torvald.terrarum.utils.PlayerLastStatus
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import java.util.* import java.util.*
@@ -21,7 +22,11 @@ import java.util.*
class IngamePlayer : ActorHumanoid { 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 var worldCurrentlyPlaying: UUID = UUID(0L,0L) // only filled up on save and load; DO NOT USE THIS
/** ADL for main sprite. Necessary. */ /** 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.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShapeRenderer import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.utils.JsonReader
import net.torvald.getKeycapConsole import net.torvald.getKeycapConsole
import net.torvald.getKeycapPC import net.torvald.getKeycapPC
import net.torvald.terrarum.* import net.torvald.terrarum.*
@@ -14,8 +15,6 @@ import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.LoadSavegame 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.tvda.*
import net.torvald.terrarum.ui.* import net.torvald.terrarum.ui.*
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
@@ -26,6 +25,9 @@ import java.util.logging.Level
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
import kotlin.math.roundToInt 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] * Only works if current screen set by the App is [TitleScreen]
* *
@@ -55,13 +57,13 @@ class UILoadDemoSavefiles : UICanvas() {
private val shapeRenderer = ShapeRenderer() private val shapeRenderer = ShapeRenderer()
internal val uiWidth = UIItemDemoSaveCells.WIDTH // 480 internal val uiWidth = SAVE_CELL_WIDTH
internal val uiX = (width - uiWidth) / 2 internal val uiX = (width - uiWidth) / 2
internal val textH = App.fontGame.lineHeight.toInt() internal val textH = App.fontGame.lineHeight.toInt()
internal val cellGap = 20 internal val cellGap = 20
internal val cellInterval = cellGap + UIItemDemoSaveCells.HEIGHT internal val cellInterval = cellGap + SAVE_CELL_HEIGHT
internal val gradAreaHeight = 32 internal val gradAreaHeight = 32
internal val titleTextPosY: Int = App.scr.tvSafeGraphicsHeight + 10 internal val titleTextPosY: Int = App.scr.tvSafeGraphicsHeight + 10
@@ -106,15 +108,15 @@ class UILoadDemoSavefiles : UICanvas() {
Thread { Thread {
// read savegames // read savegames
var savegamesCount = 0 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 x = uiX + if (App.getConfigBoolean("fx_streamerslayout")) App.scr.chatWidth / 2 else 0
val y = titleTopGradEnd + cellInterval * savegamesCount val y = titleTopGradEnd + cellInterval * savegamesCount
try { try {
addUIitem(UIItemDemoSaveCells(this, x, y, skimmer)) addUIitem(UIItemWorldCells(this, x, y, skimmer))
savegamesCount += 1 savegamesCount += 1
} }
catch (e: Throwable) { 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() e.printStackTrace()
} }
@@ -290,7 +292,7 @@ class UILoadDemoSavefiles : UICanvas() {
override fun resize(width: Int, height: Int) { override fun resize(width: Int, height: Int) {
super.resize(width, height) super.resize(width, height)
scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64 scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + UIItemDemoSaveCells.HEIGHT) savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + SAVE_CELL_HEIGHT)
listScroll = 0 listScroll = 0
scrollTarget = 0 scrollTarget = 0
@@ -311,49 +313,66 @@ class UILoadDemoSavefiles : UICanvas() {
class UIItemPlayerCells(
class UIItemDemoSaveCells(
parent: UILoadDemoSavefiles, parent: UILoadDemoSavefiles,
initialX: Int, initialX: Int,
initialY: Int, initialY: Int,
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) { val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
companion object { override val width = SAVE_CELL_WIDTH
const val WIDTH = 480 override val height = SAVE_CELL_HEIGHT
const val HEIGHT = 120
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 saveName: String
private val saveMode: Int private val saveMode: Int
private val isQuick: Boolean private val isQuick: Boolean
private val isAuto: Boolean private val isAuto: Boolean
private val meta: WriteMeta.WorldMeta? private var saveDamaged: Boolean = false
private val saveDamaged: Boolean
private val lastPlayedTimestamp: String private val lastPlayedTimestamp: String
init { init {
printdbg(this, "Rebuilding skimmer for savefile ${skimmer.diskFile.absolutePath}") printdbg(this, "Rebuilding skimmer for savefile ${skimmer.diskFile.absolutePath}")
skimmer.rebuild() skimmer.rebuild()
metaFile = skimmer.requestFile(-1) metaFile = skimmer.getFile(-1)
if (metaFile == null) saveDamaged = true
saveName = skimmer.getDiskName(Common.CHARSET) saveName = skimmer.getDiskName(Common.CHARSET)
saveMode = skimmer.getSaveMode() saveMode = skimmer.getSaveMode()
isQuick = (saveMode % 2 == 1) isQuick = (saveMode % 2 == 1)
isAuto = (saveMode.ushr(1) != 0) 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) if (metaFile != null) {
Instant.ofEpochSecond(meta.lastplay_t) val worldJson = JsonReader().parse(ByteArray64Reader(metaFile.bytes, Common.CHARSET))
.atZone(TimeZone.getDefault().toZoneId()) val lastplay_t = worldJson["lastPlayTime"].asLong()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + val playtime_t = worldJson["totalPlayTime"].asLong()
"/${parseDuration(meta.playtime_t)}" lastPlayedTimestamp =
else "--:--:--/--h--m--s" 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 { 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" "${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 width: Int = SAVE_CELL_WIDTH
override val height: Int = HEIGHT override val height: Int = SAVE_CELL_HEIGHT
private var thumbPixmap: Pixmap? = null private var thumbPixmap: Pixmap? = null
private var thumb: TextureRegion? = 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.Camera
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.Second import net.torvald.terrarum.Second
import net.torvald.terrarum.serialise.LoadSavegame
import net.torvald.terrarum.tvda.VDUtil
import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UICanvas
import java.util.logging.Level
/** /**
* Created by minjaesong on 2021-09-13. * Created by minjaesong on 2021-09-13.
@@ -31,11 +27,11 @@ class UIProxyLoadLatestSave : UICanvas() {
} }
override fun endOpening(delta: Float) { 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) { override fun endClosing(delta: Float) {

View File

@@ -12,6 +12,7 @@ object UITitleRemoConYaml {
* The class must be the UICanvas * The class must be the UICanvas
*/ */
private val menuBase = """ private val menuBase = """
- MENU_LABEL_NEW_GAME : net.torvald.terrarum.modulebasegame.ui.UIProxyNewRandomGame
- MENU_OPTIONS - MENU_OPTIONS
- MENU_LABEL_GRAPHICS : net.torvald.terrarum.modulebasegame.ui.GraphicsControlPanel - MENU_LABEL_GRAPHICS : net.torvald.terrarum.modulebasegame.ui.GraphicsControlPanel
- MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel - MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel
@@ -27,17 +28,15 @@ object UITitleRemoConYaml {
private val menuWithSavefile = """ private val menuWithSavefile = """
- MENU_LABEL_CONTINUE : net.torvald.terrarum.modulebasegame.ui.UIProxyLoadLatestSave - 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_IO_LOAD : net.torvald.terrarum.modulebasegame.ui.UILoadDemoSavefiles
- MENU_LABEL_RETURN - MENU_LABEL_NEW_WORLD
""" - MENU_LABEL_RETURN"""
private val menuNewGame = """ private val menuNewGame = """
- MENU_LABEL_NEW_GAME : net.torvald.terrarum.modulebasegame.ui.UIProxyNewRandomGame
""" """
operator fun invoke(hasSave: Boolean) = 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 package net.torvald.terrarum.serialise
import net.torvald.gdx.graphics.PixmapIO2 import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App
import net.torvald.terrarum.ccG import net.torvald.terrarum.ccG
import net.torvald.terrarum.ccW import net.torvald.terrarum.ccW
import net.torvald.terrarum.console.Echo 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. * 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() { override fun save() {
@@ -71,8 +70,7 @@ class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Te
Echo("Writing metadata...") Echo("Writing metadata...")
val creation_t = ingame.creationTime val creation_t = ingame.world.creationTime
val time_t = App.getTIME_T()
if (hasThumbnail) { if (hasThumbnail) {
@@ -86,45 +84,10 @@ class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Te
WriteSavegame.saveProgress += 1f 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 // // Write World //
// record all player's last position // record all player's last position
playersList.forEach { 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 worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t))
val world = DiskEntry(-1L, 0, creation_t, time_t, worldMeta) 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 * 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() { override fun save() {
disk.saveMode = 2 * isAuto.toInt() // no quick disk.saveMode = 2 * isAuto.toInt() // no quick
disk.capacity = 0L disk.capacity = 0L
Echo("Writing The Player...") Echo("Writing The Player...")
WritePlayer(ingame.actorGamer, disk) WritePlayer(ingame.actorGamer, disk, ingame, time_t)
VDUtil.dumpToRealMachine(disk, outFile) VDUtil.dumpToRealMachine(disk, outFile)
callback() callback()

View File

@@ -1,22 +1,23 @@
package net.torvald.terrarum.serialise package net.torvald.terrarum.serialise
import net.torvald.gdx.graphics.PixmapIO2 import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App
import net.torvald.terrarum.ccG import net.torvald.terrarum.ccG
import net.torvald.terrarum.ccW import net.torvald.terrarum.ccW
import net.torvald.terrarum.console.Echo import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.toInt import net.torvald.terrarum.toInt
import net.torvald.terrarum.tvda.* import net.torvald.terrarum.tvda.*
import net.torvald.terrarum.utils.PlayerLastStatus
import java.io.File import java.io.File
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
/** /**
* Created by minjaesong on 2021-09-29. * 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 * Will happily overwrite existing entry
@@ -34,11 +35,6 @@ class QuickSaveThread(val disk: VirtualDisk, val file: File, val ingame: Terraru
override fun run() { override fun run() {
callback()
return
// TODO //
val skimmer = DiskSkimmer(file, Common.CHARSET) val skimmer = DiskSkimmer(file, Common.CHARSET)
if (hasThumbnail) { 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 chunks = ingame.modifiedChunks
val chunkCount = chunks.map { it.size }.sum() 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...") Echo("Writing metadata...")
val creation_t = ingame.creationTime val creation_t = ingame.world.creationTime
val time_t = App.getTIME_T()
// 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) { if (hasThumbnail) {
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) 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 thumbContent = EntryFile(tgaout.toByteArray64())
val thumb = DiskEntry(-2, 0, creation_t, time_t, thumbContent) val thumb = DiskEntry(-2, 0, creation_t, time_t, thumbContent)
addFile(disk, thumb); skimmer.appendEntryOnly(thumb) addFile(disk, thumb)
} }
WriteSavegame.saveProgress += 1f WriteSavegame.saveProgress += 1f
// Write World // // 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 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) addFile(disk, world); skimmer.appendEntryOnly(world)
WriteSavegame.saveProgress += 1f WriteSavegame.saveProgress += 1f

View File

@@ -3,8 +3,6 @@ package net.torvald.terrarum.serialise
import net.torvald.spriteanimation.HasAssembledSprite import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.spriteassembler.ADProperties import net.torvald.spriteassembler.ADProperties
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.NoSuchActorWithIDException import net.torvald.terrarum.NoSuchActorWithIDException
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gameactors.ActorWithBody
@@ -62,8 +60,21 @@ object WritePlayer {
if (!dir.contains(file.entryID)) dir.add(file.entryID) if (!dir.contains(file.entryID)) dir.add(file.entryID)
} }
operator fun invoke(player: IngamePlayer, playerDisk: VirtualDisk) { operator fun invoke(player: IngamePlayer, playerDisk: VirtualDisk, ingame: TerrarumIngame, time_t: Long) {
val time_t = App.getTIME_T() 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 actorJson = WriteActor.encodeToByteArray64(player)
val adl = player.animDesc!!.getRawADL() val adl = player.animDesc!!.getRawADL()
val adlGlow = player.animDescGlow?.getRawADL() // NULLABLE! val adlGlow = player.animDescGlow?.getRawADL() // NULLABLE!

View File

@@ -1,64 +1,10 @@
package net.torvald.terrarum.serialise 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. * Created by minjaesong on 2021-08-23.
*/ */
object WriteMeta { 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) = private fun modnameToOrnamentalHeader(s: String) =
"\n\n${"#".repeat(16 + s.length)}\n" + "\n\n${"#".repeat(16 + s.length)}\n" +
@@ -72,15 +18,5 @@ object WriteMeta {
*/ */
object ReadMeta { 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.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.serialise.Common.getUnzipInputStream
import net.torvald.terrarum.tvda.ByteArray64 import net.torvald.terrarum.tvda.ByteArray64
import net.torvald.terrarum.tvda.ByteArray64Reader 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 net.torvald.terrarum.tvda.VirtualDisk
import java.io.File import java.io.File
import java.io.Reader import java.io.Reader
@@ -27,20 +26,21 @@ import java.io.Reader
object WriteSavegame { object WriteSavegame {
enum class SaveMode { 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 savingStatus = -1 // -1: not started, 0: saving in progress, 255: saving finished
@Volatile var saveProgress = 0f @Volatile var saveProgress = 0f
@Volatile var saveProgressMax = 1f @Volatile var saveProgressMax = 1f
private fun getSaveThread(mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, callback: () -> Unit = {}) = when (mode) { 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(disk, outFile, ingame, hasThumbnail, isAuto, callback) SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback)
SaveMode.PLAYER -> PlayerSavingThread(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") 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 savingStatus = 0
Echo("Save queued") Echo("Save queued")
@@ -60,7 +60,7 @@ object WriteSavegame {
} }
IngameRenderer.screencapRequested = true 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() savingThread.start()
// it is caller's job to keep the game paused or keep a "save in progress" ui up // 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 savingStatus = 0
Echo("Immediate save fired") 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() savingThread.start()
// it is caller's job to keep the game paused or keep a "save in progress" ui up // 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 // use field 'savingStatus' to know when the saving is done
} }
fun quick(disk: VirtualDisk, file: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) { fun quick(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) {
return if (ingame.isMultiplayer) TODO()
// TODO // return // TODO //
savingStatus = 0 savingStatus = 0
@@ -105,7 +105,7 @@ object WriteSavegame {
} }
IngameRenderer.screencapRequested = true 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() savingThread.start()
// it is caller's job to keep the game paused or keep a "save in progress" ui up // 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 { object LoadSavegame {
fun getFileBytes(disk: VirtualDisk, id: Long): ByteArray64 = VDUtil.getAsNormalFile(disk, id).getContent() fun getFileBytes(disk: SimpleFileSystem, id: Long): ByteArray64 = disk.getFile(id)!!.bytes
fun getFileReader(disk: VirtualDisk, id: Long): Reader = ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET) fun getFileReader(disk: SimpleFileSystem, id: Long): Reader = ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET)
operator fun invoke(disk: VirtualDisk) {
TODO()
operator fun invoke(playerDisk: SimpleFileSystem) {
val newIngame = TerrarumIngame(App.batch) 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.layerTerrain = BlockLayer(world.width, world.height)
world.layerWall = 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 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 loadJob = { it: LoadScreenBase ->
val loadscreen = it as ChunkLoadingLoadScreen val loadscreen = it as ChunkLoadingLoadScreen
loadscreen.addMessage(Lang["MENU_IO_LOADING"]) 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 actors = world.actors.distinct()
val item = Common.jsoner.fromJson(ItemCodex.javaClass, getUnzipInputStream(getFileBytes(disk, -17))) val worldParam = TerrarumIngame.Codices(worldDisk, world, actors, player)
// 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 worldParam = TerrarumIngame.Codices(disk, meta, item, apocryphas, actors)
newIngame.gameLoadInfoPayload = worldParam newIngame.gameLoadInfoPayload = worldParam
newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM
@@ -180,27 +165,25 @@ object LoadSavegame {
for (layer in worldLayer.indices) { for (layer in worldLayer.indices) {
loadscreen.addMessage("${Lang["MENU_IO_LOADING"]} ${chunk*worldLayer.size+layer+1}/${chunkCount*2}") 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()) val chunkXY = LandUtil.chunkNumToChunkXY(world, chunk.toInt())
ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, chunkXY.x, chunkXY.y) ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, chunkXY.x, chunkXY.y)
} }
} }
loadscreen.addMessage("Updating Block Mappings...") loadscreen.addMessage("Updating Block Mappings...")
world.renumberTilesAfterLoad() world.renumberTilesAfterLoad()
Echo("${ccW}Savegame loaded from $ccY${disk.getDiskNameString(Common.CHARSET)}") Echo("${ccW}World loaded: $ccY${worldDisk.getDiskName(Common.CHARSET)}")
printdbg(this, "Savegame loaded from ${disk.getDiskNameString(Common.CHARSET)}") printdbg(this, "World loaded: ${worldDisk.getDiskName(Common.CHARSET)}")
} }
val loadScreen = ChunkLoadingLoadScreen(newIngame, world.width, world.height, loadJob) val loadScreen = ChunkLoadingLoadScreen(newIngame, world.width, world.height, loadJob)
Terrarum.setCurrentIngameInstance(newIngame) Terrarum.setCurrentIngameInstance(newIngame)
App.setLoadScreen(loadScreen) 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.gameworld.GameWorldTitleScreen
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.tvda.ByteArray64 import net.torvald.terrarum.tvda.ByteArray64
import net.torvald.terrarum.tvda.ByteArray64Writer import net.torvald.terrarum.tvda.ByteArray64Writer
import net.torvald.terrarum.weather.WeatherMixer
import java.io.Reader import java.io.Reader
/** /**
@@ -43,6 +45,11 @@ object WriteWorld {
world.actors.clear() world.actors.clear()
world.actors.addAll(actorIDbuf.sorted().distinct()) 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 return world
} }

View File

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

View File

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

View File

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

View File

@@ -133,7 +133,7 @@ class VirtualDisk(
var saveMode: Int var saveMode: Int
set(value) { extraInfoBytes[1] = value.toByte() } set(value) { extraInfoBytes[1] = value.toByte() }
get() = extraInfoBytes[1].toUint() get() = extraInfoBytes[1].toUint()
fun getDiskNameString(charset: Charset) = diskName.toCanonicalString(charset) override fun getDiskName(charset: Charset) = diskName.toCanonicalString(charset)
val root: DiskEntry val root: DiskEntry
get() = entries[0]!! 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 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 { companion object {
val HEADER_SIZE = 300L // according to the spec 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 MetaModuleCSVPair: HashMap<String, ZipCodedStr>()
class PlayersLastStatus: HashMap<UUID, PlayerLastStatus>() class PlayersLastStatus: HashMap<UUID, PlayerLastStatus>()
class PlayerLastStatus() { class PlayerLastStatus() {
var physics = PhysicalStatus(); private set var physics = PhysicalStatus(); private set // mandatory
var inventory = ActorInventory(); private set var inventory: ActorInventory? = null; private set // optional (multiplayer only)
var actorValue = ActorValue(); private set var actorValue: ActorValue? = null; private set // optional (multiplayer only)
constructor(player: IngamePlayer) : this() { constructor(player: IngamePlayer, isMultiplayer: Boolean) : this() {
physics = PhysicalStatus(player) physics = PhysicalStatus(player)
inventory = player.inventory if (isMultiplayer) {
actorValue = player.actorValue inventory = player.inventory
actorValue = player.actorValue
}
} }
} }
/** /**