From aec6fea49ef3d06cbb56ee145778e6d8e79c8ff4 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 8 Oct 2021 14:25:59 +0900 Subject: [PATCH] new player save format writing --- SAVE_FORMAT.md | 2 +- .../basegame/creatures/CreatureHuman.json | 5 +- .../creatures/CreatureWerebeastBase.json | 5 +- .../spriteanimation/HasAssembledSprite.kt | 4 +- .../torvald/spriteassembler/ADProperties.kt | 10 +- src/net/torvald/terrarum/App.java | 2 +- src/net/torvald/terrarum/IngameInstance.kt | 33 ++++--- src/net/torvald/terrarum/gameactors/AVKey.kt | 6 +- src/net/torvald/terrarum/gameitem/GameItem.kt | 2 +- .../torvald/terrarum/gameworld/GameWorld.kt | 19 +++- .../terrarum/modulebasegame/TerrarumIngame.kt | 76 ++++++++++----- .../terrarum/modulebasegame/console/Save.kt | 6 +- .../modulebasegame/gameactors/IngamePlayer.kt | 96 +++++++++++++++++-- .../modulebasegame/ui/UIInventoryEscMenu.kt | 4 +- .../modulebasegame/ui/UIProxyNewRandomGame.kt | 9 +- src/net/torvald/terrarum/serialise/Common.kt | 3 + .../terrarum/serialise/GameSavingThread.kt | 77 ++++++++------- .../torvald/terrarum/serialise/WriteActor.kt | 6 +- .../terrarum/serialise/WriteSavegame.kt | 22 +++-- .../torvald/terrarum/serialise/WriteWorld.kt | 4 +- src/net/torvald/terrarum/utils/HashArray.kt | 5 +- 21 files changed, 280 insertions(+), 116 deletions(-) diff --git a/SAVE_FORMAT.md b/SAVE_FORMAT.md index a829bbe3a..57593eab9 100644 --- a/SAVE_FORMAT.md +++ b/SAVE_FORMAT.md @@ -14,7 +14,7 @@ The main game directory is composed of following directories: - , TEVD { * } - + Worlds - - , TVDA { (-1) WriteWorld, (actorID) actors (mainly fixtures) JSON, (0x1_0000_0000 + (layerNumber << 24) + chunkNumber) chunk data, (-2) screenshot.tga.gz taken by the last player } + - , TVDA { (-1) WriteWorld, (actorID) actors (mainly fixtures) JSON, (0x1_0000_0000L or (layerNumber shl 24) or chunkNumber) chunk data, (-2) screenshot.tga.gz taken by the last player } ``` (TEVD stands for Terrarum Virtual Disk spec version 3, TVDA stands for spec version 254; both have MAGIC header of `TEVd`) diff --git a/assets/mods/basegame/creatures/CreatureHuman.json b/assets/mods/basegame/creatures/CreatureHuman.json index 7955756d3..f0c26eab4 100644 --- a/assets/mods/basegame/creatures/CreatureHuman.json +++ b/assets/mods/basegame/creatures/CreatureHuman.json @@ -8,7 +8,6 @@ "strengthmult": [90,95,98,100,102,105,110], "accel": 0.67, - "accelbuff": 1.0, "speed": 3.0, "speedmult": [100,100,100,100,100,100,100], @@ -22,8 +21,8 @@ "scalemult": [100,100,100,100,100,100,100], "encumbrance": 1000, - "basedefence": 100, - "basereach": 84, + "defence": 100, + "reach": 84, "toolsize": 15, diff --git a/assets/mods/basegame/creatures/CreatureWerebeastBase.json b/assets/mods/basegame/creatures/CreatureWerebeastBase.json index 4cdbcca47..c399533f3 100644 --- a/assets/mods/basegame/creatures/CreatureWerebeastBase.json +++ b/assets/mods/basegame/creatures/CreatureWerebeastBase.json @@ -8,7 +8,6 @@ "strengthmult": [90,95,98,100,102,105,110], "accel": 0.67, - "accelbuff": 1.0, "speed": 6.0, "speedmult": [100,100,100,100,100,100,100], @@ -22,8 +21,8 @@ "scalemult": [100,100,100,100,100,100,100], "encumbrance": 10000, - "basedefence": 100, - "basereach": 180, + "defence": 100, + "reach": 180, "toolsize": 40, diff --git a/src/net/torvald/spriteanimation/HasAssembledSprite.kt b/src/net/torvald/spriteanimation/HasAssembledSprite.kt index f228dd3df..84468f2ad 100644 --- a/src/net/torvald/spriteanimation/HasAssembledSprite.kt +++ b/src/net/torvald/spriteanimation/HasAssembledSprite.kt @@ -13,9 +13,9 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack */ interface HasAssembledSprite { - /** ADL path for main sprite. Necessary. */ + /** ADL for main sprite. Necessary. */ var animDesc: ADProperties? - /** ADL path for glow sprite. Optional. */ + /** ADL for glow sprite. Optional. */ var animDescGlow: ADProperties? // FIXME sometimes the animmation is invisible (row and nFrames mismatch -- row is changed to 1 but it's drawing 3rd frame?) diff --git a/src/net/torvald/spriteassembler/ADProperties.kt b/src/net/torvald/spriteassembler/ADProperties.kt index c999c8922..f27708920 100644 --- a/src/net/torvald/spriteassembler/ADProperties.kt +++ b/src/net/torvald/spriteassembler/ADProperties.kt @@ -34,7 +34,7 @@ internal data class Transform(val joint: Joint, val translate: ADPropertyObject. class ADProperties { private var fileFrom = "" - private var adlString = "" + @Transient private var adlString = "" private val javaProp = Properties() /** Every key is CAPITALISED */ @@ -52,8 +52,8 @@ class ADProperties { /** an "animation frame" property (ANIM_RUN_1, ANIM_RUN_2) */ internal lateinit var transforms: HashMap>; private set - private val reservedProps = listOf("SPRITESHEET", "EXTENSION", "CONFIG", "BODYPARTS") - private val animMustContain = listOf("DELAY", "ROW", "SKELETON") + @Transient private val reservedProps = listOf("SPRITESHEET", "EXTENSION", "CONFIG", "BODYPARTS") + @Transient private val animMustContain = listOf("DELAY", "ROW", "SKELETON") lateinit var baseFilename: String; private set lateinit var extension: String; private set @@ -63,9 +63,9 @@ class ADProperties { internal val origin: ADPropertyObject.Vector2i get() = ADPropertyObject.Vector2i(originX, 0) - private val animFrameSuffixRegex = Regex("""_[0-9]+""") + @Transient private val animFrameSuffixRegex = Regex("""_[0-9]+""") - private val ALL_JOINT = Joint(ALL_JOINT_SELECT_KEY, ADPropertyObject.Vector2i(0, 0)) + @Transient private val ALL_JOINT = Joint(ALL_JOINT_SELECT_KEY, ADPropertyObject.Vector2i(0, 0)) var rows = -1; private set var cols = -1; private set diff --git a/src/net/torvald/terrarum/App.java b/src/net/torvald/terrarum/App.java index f7aa3a14e..b76333c3c 100644 --- a/src/net/torvald/terrarum/App.java +++ b/src/net/torvald/terrarum/App.java @@ -979,7 +979,7 @@ public class App implements ApplicationListener { } private static void createDirs() { - File[] dirs = {new File(saveDir)}; + File[] dirs = {new File(saveDir), new File(saveSharedDir), new File(playersDir), new File(worldsDir)}; for (File it : dirs) { if (!it.exists()) diff --git a/src/net/torvald/terrarum/IngameInstance.kt b/src/net/torvald/terrarum/IngameInstance.kt index a5af533a5..bf5b528e1 100644 --- a/src/net/torvald/terrarum/IngameInstance.kt +++ b/src/net/torvald/terrarum/IngameInstance.kt @@ -12,6 +12,7 @@ import net.torvald.terrarum.gameitem.ItemID import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.modulebasegame.IngameRenderer import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.ui.Notification import net.torvald.terrarum.modulebasegame.ui.UITooltip import net.torvald.terrarum.realestate.LandUtil @@ -48,8 +49,8 @@ open class IngameInstance(val batch: SpriteBatch) : Screen { } } - lateinit var savegameArchive: VirtualDisk - internal set + lateinit var worldDisk: VirtualDisk; internal set + lateinit var playerDisk: VirtualDisk; internal set var savegameNickname: String = "SplinesReticulated" internal set @@ -106,8 +107,7 @@ open class IngameInstance(val batch: SpriteBatch) : Screen { /** * The actual gamer */ - val actorGamer: ActorHumanoid - get() = getActorByID(Terrarum.PLAYER_REF_ID) as ActorHumanoid + open lateinit var actorGamer: IngamePlayer open var gameInitialised = false internal set @@ -362,29 +362,34 @@ open class IngameInstance(val batch: SpriteBatch) : Screen { } } + fun makeSavegameBackupCopy() { + System.err.println("This function is deleteh") + printStackTrace(this) + } + /** * Copies most recent `save` to `save.1`, leaving `save` for overwriting, previous `save.1` will be copied to `save.2` */ - fun makeSavegameBackupCopy() { + fun makeSavegameBackupCopy(file: File) { try { // do not overwrite clean .2 with dirty .1 - val file2 = File(App.saveDir, INGAME.savegameNickname + ".3") - val file1 = File(App.saveDir, INGAME.savegameNickname + ".2") + val file2 = File("${file.absolutePath}.3") + val file1 = File("${file.absolutePath}.2") val flags2 = FileInputStream(file2).let { it.skip(49L); val r = it.read(); it.close(); r } val flags1 = FileInputStream(file1).let { it.skip(49L); val r = it.read(); it.close(); r } if (!(flags2 == 0 && flags1 != 0) || !file2.exists()) file1.copyTo(file2, true) } catch (e: NoSuchFileException) {} catch (e: FileNotFoundException) {} try { // do not overwrite clean .2 with dirty .1 - val file2 = File(App.saveDir, INGAME.savegameNickname + ".2") - val file1 = File(App.saveDir, INGAME.savegameNickname + ".1") + val file2 = File("${file.absolutePath}.2") + val file1 = File("${file.absolutePath}.1") val flags2 = FileInputStream(file2).let { it.skip(49L); val r = it.read(); it.close(); r } val flags1 = FileInputStream(file1).let { it.skip(49L); val r = it.read(); it.close(); r } if (!(flags2 == 0 && flags1 != 0) || !file2.exists()) file1.copyTo(file2, true) } catch (e: NoSuchFileException) {} catch (e: FileNotFoundException) {} try { - File(App.saveDir, INGAME.savegameNickname).copyTo( - File(App.saveDir, INGAME.savegameNickname + ".1"), // don't use .bak as it's used by the savecracker + file.copyTo( + File("${file.absolutePath}.1"), // don't use .bak as it's used by the savecracker true ) } catch (e: NoSuchFileException) {} @@ -392,8 +397,10 @@ open class IngameInstance(val batch: SpriteBatch) : Screen { - fun getSaveFileMain() = File(App.saveDir, savegameNickname) - +// fun getSaveFileMain() = File(App.saveDir, savegameNickname) + fun getWorldSaveFiledesc(filename: String) = File(App.worldsDir, filename) + fun getPlayerSaveFiledesc(filename: String) = File(App.playersDir, filename) + fun getSharedSaveFiledesc(filename: String) = File(App.saveSharedDir, filename) // simple euclidean norm, squared diff --git a/src/net/torvald/terrarum/gameactors/AVKey.kt b/src/net/torvald/terrarum/gameactors/AVKey.kt index 3413cac5a..af9887730 100644 --- a/src/net/torvald/terrarum/gameactors/AVKey.kt +++ b/src/net/torvald/terrarum/gameactors/AVKey.kt @@ -88,12 +88,14 @@ object AVKey { /** (unit TBA) * base defence point of the species */ - const val BASEDEFENCE = "basedefence" + const val DEFENCE = "defence" + const val DEFENCEBUFF = "$DEFENCE$BUFF" /** Pixels * base hand reach of the species (only affects the gameplay as the player) */ - const val BASEREACH = "basereach" + const val REACH = "reach" + const val REACHBUFF = "$REACH$BUFF" /** (unit TBA) * current defence point of worn armour(s) diff --git a/src/net/torvald/terrarum/gameitem/GameItem.kt b/src/net/torvald/terrarum/gameitem/GameItem.kt index 19dc838d0..5836cc900 100644 --- a/src/net/torvald/terrarum/gameitem/GameItem.kt +++ b/src/net/torvald/terrarum/gameitem/GameItem.kt @@ -329,7 +329,7 @@ fun inInteractableRange(actor: ActorWithBody, action: () -> Boolean): Boolean { val mousePos3 = Vector2(Terrarum.mouseX - INGAME.world.width * TILE_SIZED, Terrarum.mouseY) val actorPos = actor.centrePosVector val dist = minOf(actorPos.distanceSquared(mousePos1), actorPos.distanceSquared(mousePos2), actorPos.distanceSquared(mousePos3)) - val distMax = actor.actorValue.getAsInt(AVKey.BASEREACH)!! * actor.scale // perform some error checking here + val distMax = actor.actorValue.getAsDouble(AVKey.REACH)!! * (actor.actorValue.getAsDouble(AVKey.REACHBUFF) ?: 1.0) * actor.scale // perform some error checking here if (dist <= distMax.sqr()) return action() else return false } fun IntRange.pickRandom() = HQRNG().nextInt(this.endInclusive - this.start + 1) + this.start // count() on 200 million entries? Se on vitun hyvää idea diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index f35cbbf54..011cceb72 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -10,6 +10,7 @@ import net.torvald.terrarum.blockproperties.Fluid import net.torvald.terrarum.gameactors.ActorID import net.torvald.terrarum.gameactors.WireActor import net.torvald.terrarum.gameitem.ItemID +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.utils.* import net.torvald.util.SortedArrayList @@ -22,6 +23,18 @@ import kotlin.math.absoluteValue typealias BlockAddress = Long +class PhysicalStatus() { + // bottom-center point + var position = Point2d() + // some actorvalues + var scale = 1.0 + + constructor(player: IngamePlayer) : this() { + this.position = Point2d(player.hitbox.canonicalX, player.hitbox.canonicalY) + this.scale = player.avBaseScale + } +} + /** * Special version of GameWorld where layer data are not transient */ @@ -38,6 +51,8 @@ open class GameWorld() : Disposable { var width: Int = 999; private set var height: Int = 999; private set + var playersLastStatus = PlayersLastPhysics() // only gets used when the game saves and loads + /** Creation time for this world, NOT the entire savegame */ internal var creationTime: Long = App.getTIME_T() internal set @@ -106,8 +121,8 @@ open class GameWorld() : Disposable { val extraFields = HashMap() - internal var genver = -1 - internal var comp = -1 + internal var genver = -1 // only gets used when the game saves and loads + internal var comp = -1 // only gets used when the game saves and loads @Deprecated("This value is only used for savegames; DO NOT USE THIS", ReplaceWith("INGAME.actorContainerActive", "net.torvald.terrarum.INGAME")) internal val actors = ArrayList() // only filled up on save and load; DO NOT USE THIS diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index a85f8fd5f..c0fd304e0 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -46,7 +46,11 @@ import net.torvald.terrarum.worlddrawer.FeaturesDrawer import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.util.CircularArray import org.khelekore.prtree.PRTree +import java.util.* import java.util.concurrent.locks.ReentrantLock +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.collections.HashSet /** @@ -218,7 +222,7 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { // gameLoadMode and gameLoadInfoPayload must be set beforehand!! when (gameLoadMode) { - GameLoadMode.CREATE_NEW -> enterCreateNewWorld(gameLoadInfoPayload as NewWorldParameters) + GameLoadMode.CREATE_NEW -> enterCreateNewWorld(gameLoadInfoPayload as NewGameParams) GameLoadMode.LOAD_FROM -> enterLoadFromSave(gameLoadInfoPayload as Codices) } @@ -228,6 +232,11 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { super.show() // this function sets gameInitialised = true } + data class NewGameParams( + val player: IngamePlayer, + val newWorldParams: NewWorldParameters + ) + data class NewWorldParameters( val width: Int, val height: Int, @@ -254,14 +263,7 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { val actors: List ) - private fun setTheRealGamerFirstTime(actor: IngamePlayer) { - if (actor.referenceID != Terrarum.PLAYER_REF_ID) { - throw Error() - } - actorNowPlaying = actor - addNewActor(actorNowPlaying) - } /** * Init instance by loading saved world @@ -308,33 +310,49 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { // go to spawn position printdbg(this, "World Spawn position: (${world.spawnX}, ${world.spawnY})") -// setTheRealGamerFirstTime(PlayerBuilderSigrid()) - setTheRealGamerFirstTime(PlayerBuilderTestSubject1()) -// setTheRealGamerFirstTime(PlayerBuilderWerebeastTest()) + val worldSavefileName = savegameNickname + val playerSavefileName = actorGamer.actorValue.getAsString(AVKey.NAME) ?: "Player-${actorGamer.uuid}" - savegameArchive = VDUtil.createNewDisk( + worldDisk = VDUtil.createNewDisk( 1L shl 60, - savegameNickname, + worldSavefileName, Common.CHARSET ) - actorNowPlaying!!.setPosition( + playerDisk = VDUtil.createNewDisk( + 1L shl 60, + playerSavefileName, + Common.CHARSET + ) + + actorGamer.setPosition( world.spawnX * TILE_SIZED, world.spawnY * TILE_SIZED ) // make initial savefile + // we're not writing multiple files at one go because: + // 1. lighten the IO burden + // 2. cannot sync up the "counter" to determine whether both are finished uiAutosaveNotifier.setAsOpen() - WriteSavegame.immediate(savegameArchive, getSaveFileMain(), this, true) { - makeSavegameBackupCopy() // don't put it on the postInit() or render(); must be called using callback - uiAutosaveNotifier.setAsClose() + WriteSavegame.immediate(WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, false, true) { + makeSavegameBackupCopy(getPlayerSaveFiledesc(playerSavefileName)) + + WriteSavegame.immediate(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() + } } } /** * Init instance by creating new world */ - private fun enterCreateNewWorld(worldParams: NewWorldParameters) { + private fun enterCreateNewWorld(newGameParams: NewGameParams) { + + val player = newGameParams.player + val worldParams = newGameParams.newWorldParams + printdbg(this, "Ingame called") printStackTrace(this) @@ -343,6 +361,7 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { } else { App.getLoadScreen().addMessage("${App.GAME_NAME} version ${App.getVERSION_STRING()}") + App.getLoadScreen().addMessage("Creating new world") @@ -364,6 +383,19 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { historicalFigureIDBucket = ArrayList() savegameNickname = worldParams.savegameName + + + + player.worldCurrentlyPlaying = UUID.fromString(world.worldIndex.toString()) + world.worldCreator = UUID.fromString(player.uuid.toString()) + + printdbg(this, "new woridIndex: ${world.worldIndex}") + printdbg(this, "worldCurrentlyPlaying: ${player.worldCurrentlyPlaying}") + + actorNowPlaying = player + actorGamer = player + addNewActor(player) + } KeyToggler.forceSet(Input.Keys.Q, false) @@ -978,13 +1010,13 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { fun queueAutosave() { val start = System.nanoTime() - uiAutosaveNotifier.setAsOpen() + /*uiAutosaveNotifier.setAsOpen() makeSavegameBackupCopy() WriteSavegame.quick(savegameArchive, getSaveFileMain(), this, true) { uiAutosaveNotifier.setAsClose() debugTimers.put("Last Autosave Duration", System.nanoTime() - start) - } + }*/ } @@ -1022,8 +1054,8 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) { override fun removeActor(actor: Actor?) { if (actor == null) return - if (actor.referenceID == actorGamer.referenceID || actor.referenceID == 0x51621D) // do not delete this magic - throw ProtectedActorRemovalException("Player") +// if (actor.referenceID == actorGamer.referenceID || actor.referenceID == 0x51621D) // do not delete this magic +// throw ProtectedActorRemovalException("Player") forceRemoveActor(actor) } diff --git a/src/net/torvald/terrarum/modulebasegame/console/Save.kt b/src/net/torvald/terrarum/modulebasegame/console/Save.kt index ffe0f0d17..ae158ec0d 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/Save.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/Save.kt @@ -24,7 +24,7 @@ object Save : ConsoleCommand { val disk = VDUtil.createNewDisk(1L shl 60, savename, Common.CHARSET) val file = File(App.saveDir + "/${args[1]}") - WriteSavegame(disk, file, ingame, false) +// WriteSavegame(disk, file, ingame, false) } catch (e: IOException) { @@ -47,9 +47,7 @@ object Quicksave : ConsoleCommand { override fun execute(args: Array) { val ingame = Terrarum.ingame!! as TerrarumIngame - WriteSavegame.quick(ingame.savegameArchive, ingame.getSaveFileMain(), ingame, false) { - - } +// WriteSavegame.quick(ingame.savegameArchive, ingame.getSaveFileMain(), ingame, false) {} } override fun printUsage() { diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt index da6906d57..a42fe1681 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt @@ -1,10 +1,15 @@ 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.Terrarum import net.torvald.terrarum.gameactors.AVKey +import net.torvald.terrarum.tvda.SimpleFileSystem +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import java.util.* @@ -14,12 +19,16 @@ import java.util.* * Created by minjaesong on 2015-12-31. */ -class IngamePlayer : ActorHumanoid, HasAssembledSprite { +class IngamePlayer : ActorHumanoid { + + var uuid = UUID.randomUUID(); private set + var worldCurrentlyPlaying: UUID = UUID(0L,0L) // only filled up on save and load; DO NOT USE THIS + + /** ADL for main sprite. Necessary. */ + @Transient var animDesc: ADProperties? = null + /** ADL for glow sprite. Optional. */ + @Transient var animDescGlow: ADProperties? = null - var UUID = UUID(0L,0L); private set - internal var worldCurrentlyPlaying: UUID = UUID(0L,0L) // only filled up on save and load; DO NOT USE THIS - override var animDesc: ADProperties? = null - override var animDescGlow: ADProperties? = null private constructor() @@ -38,7 +47,82 @@ class IngamePlayer : ActorHumanoid, HasAssembledSprite { referenceID = Terrarum.PLAYER_REF_ID // TODO assign random ID density = BASE_DENSITY collisionType = COLLISION_KINEMATIC - worldCurrentlyPlaying = Terrarum.ingame?.world?.worldIndex ?: UUID(0L,0L) } + + + + /** + * Example usage: + * ``` + * this.animDescPath = "..." + * this.animDescPathGlow = "..." + * this.sprite = SpriteAnimation(actor) + * this.spriteGlow = SpriteAnimation(actor) + * reassembleSprite(this.sprite, this.spriteGlow) + * ``` + */ + fun reassembleSprite(sprite: SpriteAnimation?, anim: ADProperties?, spriteGlow: SpriteAnimation? = null, animGlow: ADProperties? = null) { + if (anim != null && sprite != null) + _rebuild(anim, sprite) + if (animGlow != null && spriteGlow != null) + _rebuild(animGlow, spriteGlow) + } + + fun reassembleSprite(disk: SimpleFileSystem, sprite: SpriteAnimation?, anim: ADProperties?, spriteGlow: SpriteAnimation? = null, animGlow: ADProperties? = null) { + if (anim != null && sprite != null) + _rebuild(disk, anim, sprite) + if (animGlow != null && spriteGlow != null) + _rebuild(disk, animGlow, spriteGlow) + } + + private fun _rebuild(ad: ADProperties, sprite: SpriteAnimation) { + // TODO injecting held item/armour pictures? Would it be AssembleSheetPixmap's job? + + val pixmap = AssembleSheetPixmap.fromAssetsDir(ad) + val texture = Texture(pixmap) + texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + pixmap.dispose() + val regionPack = TextureRegionPack(texture, ad.frameWidth, ad.frameHeight) + + val newAnimDelays = FloatArray(ad.animations.size) + val newAnimFrames = IntArray(ad.animations.size) + + ad.animations.forEach { t, u -> + val index = u.row - 1 + newAnimDelays[index] = u.delay + newAnimFrames[index] = u.frames + } + + sprite.setSpriteImage(regionPack) + sprite.delays = newAnimDelays + sprite.nFrames = newAnimFrames + sprite.nRows = newAnimDelays.size + } + + private fun _rebuild(disk: SimpleFileSystem, ad: ADProperties, sprite: SpriteAnimation) { + // TODO injecting held item/armour pictures? Would it be AssembleSheetPixmap's job? + + val pixmap = AssembleSheetPixmap.fromVirtualDisk(disk, ad) + val texture = Texture(pixmap) + texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + pixmap.dispose() + val regionPack = TextureRegionPack(texture, ad.frameWidth, ad.frameHeight) + + val newAnimDelays = FloatArray(ad.animations.size) + val newAnimFrames = IntArray(ad.animations.size) + + ad.animations.forEach { t, u -> + val index = u.row - 1 + newAnimDelays[index] = u.delay + newAnimFrames[index] = u.frames + } + + sprite.setSpriteImage(regionPack) + sprite.delays = newAnimDelays + sprite.nFrames = newAnimFrames + sprite.nRows = newAnimDelays.size + } + + } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryEscMenu.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryEscMenu.kt index 54221d6c7..014d1d344 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryEscMenu.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryEscMenu.kt @@ -90,13 +90,13 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() { INGAME.makeSavegameBackupCopy() // save the game - WriteSavegame(INGAME.savegameArchive, File(App.saveDir, INGAME.savegameNickname), Terrarum.ingame!! as TerrarumIngame, false) { + /*WriteSavegame(INGAME.savegameArchive, File(App.saveDir, INGAME.savegameNickname), Terrarum.ingame!! as TerrarumIngame, false) { // callback: System.gc() screen = 0 full.handler.unlockToggle() full.unlockTransition() - } + }*/ } 2 -> { screen = 4; gameMenuButtons.deselect() diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt index 5b6ecc200..7e8aa66b3 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt @@ -9,6 +9,7 @@ import net.torvald.terrarum.Second import net.torvald.terrarum.Terrarum import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.WorldgenLoadScreen +import net.torvald.terrarum.modulebasegame.gameactors.PlayerBuilderTestSubject1 import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.utils.RandomWordsName @@ -36,9 +37,11 @@ class UIProxyNewRandomGame : UICanvas() { override fun endOpening(delta: Float) { printdbg(this, "endOpening") - val ingame = TerrarumIngame(App.batch) - val worldParam = TerrarumIngame.NewWorldParameters(2880, 1350, HQRNG().nextLong(), RandomWordsName(4)) + val worldParam = TerrarumIngame.NewGameParams( + PlayerBuilderTestSubject1(), + TerrarumIngame.NewWorldParameters(2880, 1350, HQRNG().nextLong(), RandomWordsName(4)) + ) // val worldParam = TerrarumIngame.NewWorldParameters(2880, 1350, 0x51621D) // val worldParam = TerrarumIngame.NewWorldParameters(6030, 1800, HQRNG().nextLong()) // small @@ -51,7 +54,7 @@ class UIProxyNewRandomGame : UICanvas() { Terrarum.setCurrentIngameInstance(ingame) //LoadScreen.screenToLoad = ingame //AppLoader.setScreen(LoadScreen) - val loadScreen = WorldgenLoadScreen(ingame, worldParam.width, worldParam.height) + val loadScreen = WorldgenLoadScreen(ingame, worldParam.newWorldParams.width, worldParam.newWorldParams.height) App.setLoadScreen(loadScreen) } diff --git a/src/net/torvald/terrarum/serialise/Common.kt b/src/net/torvald/terrarum/serialise/Common.kt index 41486235f..3c5d24f92 100644 --- a/src/net/torvald/terrarum/serialise/Common.kt +++ b/src/net/torvald/terrarum/serialise/Common.kt @@ -43,6 +43,9 @@ object Common { // install custom (de)serialiser init { + jsoner.ignoreUnknownFields = true + + // BigInteger jsoner.setSerializer(BigInteger::class.java, object : Json.Serializer { override fun write(json: Json, obj: BigInteger?, knownType: Class<*>?) { diff --git a/src/net/torvald/terrarum/serialise/GameSavingThread.kt b/src/net/torvald/terrarum/serialise/GameSavingThread.kt index 562d043f3..416776412 100644 --- a/src/net/torvald/terrarum/serialise/GameSavingThread.kt +++ b/src/net/torvald/terrarum/serialise/GameSavingThread.kt @@ -3,36 +3,31 @@ package net.torvald.terrarum.serialise import net.torvald.gdx.graphics.PixmapIO2 import net.torvald.terrarum.* import net.torvald.terrarum.console.Echo +import net.torvald.terrarum.gameworld.PhysicalStatus 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.tvda.* import java.io.File import java.util.zip.GZIPOutputStream +/** + * Will happily overwrite existing entry + */ +private fun addFile(disk: VirtualDisk, file: DiskEntry) { + disk.entries[file.entryID] = file + file.parentEntryID = 0 + val dir = VDUtil.getAsDirectory(disk, 0) + if (!dir.contains(file.entryID)) dir.add(file.entryID) +} + /** * Created by minjaesong on 2021-09-14. */ -class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : Runnable { - - /** - * Will happily overwrite existing entry - */ - private fun addFile(disk: VirtualDisk, file: DiskEntry) { - disk.entries[file.entryID] = file - file.parentEntryID = 0 - val dir = VDUtil.getAsDirectory(disk, 0) - if (!dir.contains(file.entryID)) dir.add(file.entryID) - } - - private val chunkProgressMultiplier = 1f - private val actorProgressMultiplier = 1f +class WorldSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: TerrarumIngame, val hasThumbnail: Boolean, val isAuto: Boolean, val callback: () -> Unit) : Runnable { override fun run() { - callback() - return - - // TODO // disk.saveMode = 2 * isAuto.toInt() // no quick @@ -42,15 +37,14 @@ class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Ter } } + val playersList: List = listOf(ingame.actorContainerActive, ingame.actorContainerInactive).flatMap{ it.filter { it is IngamePlayer } } as List val actorsList = listOf(ingame.actorContainerActive, ingame.actorContainerInactive).flatMap { it.filter { WriteWorld.actorAcceptable(it) } } val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) } val cw = ingame.world.width / LandUtil.CHUNK_W val ch = ingame.world.height / LandUtil.CHUNK_H WriteSavegame.saveProgress = 0f - WriteSavegame.saveProgressMax = 2f + - (cw * ch * layers.size) * chunkProgressMultiplier + - actorsList.size * actorProgressMultiplier + WriteSavegame.saveProgressMax = 2f + (cw * ch * layers.size) + actorsList.size val tgaout = ByteArray64GrowableOutputStream() @@ -62,11 +56,6 @@ class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Ter 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) - if (hasThumbnail) { PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) IngameRenderer.fboRGBexport.dispose() @@ -114,10 +103,13 @@ class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Ter // addFile(disk, apocryphas) // Write World // -// val worldNum = ingame.world.worldIndex -// val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t)) -// val world = DiskEntry(worldNum.toLong(), 0, creation_t, time_t, worldMeta) -// addFile(disk, world) + // record all player's last position + playersList.forEach { + ingame.world.playersLastStatus[it.uuid] = PhysicalStatus(it) + } + val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t)) + val world = DiskEntry(-1L, 0, creation_t, time_t, worldMeta) + addFile(disk, world) WriteSavegame.saveProgress += 1f @@ -137,7 +129,7 @@ class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Ter // "W1L0-92,15" addFile(disk, entry) - WriteSavegame.saveProgress += chunkProgressMultiplier + WriteSavegame.saveProgress += 1 } } } @@ -151,7 +143,7 @@ class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Ter val actor = DiskEntry(it.referenceID.toLong(), 0, creation_t, time_t, actorContent) addFile(disk, actor) - WriteSavegame.saveProgress += actorProgressMultiplier + WriteSavegame.saveProgress += 1 } @@ -174,4 +166,23 @@ class GameSavingThread(val disk: VirtualDisk, val outFile: File, val ingame: Ter callback() } -} \ No newline at end of file +} + +/** + * This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map + * + * 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) : Runnable { + + override fun run() { + disk.saveMode = 2 * isAuto.toInt() // no quick + disk.capacity = 0L + + Echo("Writing The Player...") + WritePlayer(ingame.actorGamer, disk) + VDUtil.dumpToRealMachine(disk, outFile) + + callback() + } +} diff --git a/src/net/torvald/terrarum/serialise/WriteActor.kt b/src/net/torvald/terrarum/serialise/WriteActor.kt index 166ae65b9..50baaa806 100644 --- a/src/net/torvald/terrarum/serialise/WriteActor.kt +++ b/src/net/torvald/terrarum/serialise/WriteActor.kt @@ -46,7 +46,7 @@ object WriteActor { } /** - * Player-specific [WriteActor]. + * Player-specific [WriteActor]. Will write JSON and Animation Description Languages * * Created by minjaesong on 2021-10-07. */ @@ -74,12 +74,12 @@ object WritePlayer { val adlContents = EntryFile(ByteArray64.fromByteArray(adl.toByteArray(Common.CHARSET))) val adlCreationDate = playerDisk.getEntry(-2)?.creationDate ?: time_t - addFile(playerDisk, DiskEntry(-1L, 0L, adlCreationDate, time_t, adlContents)) + addFile(playerDisk, DiskEntry(-2L, 0L, adlCreationDate, time_t, adlContents)) if (adlGlow != null) { val adlGlowContents = EntryFile(ByteArray64.fromByteArray(adlGlow.toByteArray(Common.CHARSET))) val adlGlowCreationDate = playerDisk.getEntry(-3)?.creationDate ?: time_t - addFile(playerDisk, DiskEntry(-1L, 0L, adlGlowCreationDate, time_t, adlGlowContents)) + addFile(playerDisk, DiskEntry(-3L, 0L, adlGlowCreationDate, time_t, adlGlowContents)) } diff --git a/src/net/torvald/terrarum/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/serialise/WriteSavegame.kt index 1d283a267..2784d945e 100644 --- a/src/net/torvald/terrarum/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/serialise/WriteSavegame.kt @@ -23,11 +23,21 @@ import java.io.Reader */ object WriteSavegame { + enum class SaveMode { + META, PLAYER, WORLD, SHARED + } + @Volatile var savingStatus = -1 // -1: not started, 0: saving in progress, 255: saving finished @Volatile var saveProgress = 0f @Volatile var saveProgressMax = 1f - operator fun invoke(disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) { + 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) + else -> throw IllegalArgumentException("$mode") + } + + operator fun invoke(mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) { savingStatus = 0 Echo("Save queued") @@ -47,7 +57,7 @@ object WriteSavegame { } IngameRenderer.screencapRequested = true - val savingThread = Thread(GameSavingThread(disk, outFile, ingame, true, isAuto, callback), "TerrarumBasegameGameSaveThread") + val savingThread = Thread(getSaveThread(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 @@ -55,16 +65,13 @@ object WriteSavegame { } - fun immediate(disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, callback: () -> Unit = {}) { - return - - // TODO // + fun immediate(mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, callback: () -> Unit = {}) { savingStatus = 0 Echo("Immediate save fired") - val savingThread = Thread(GameSavingThread(disk, outFile, ingame, false, isAuto, callback), "TerrarumBasegameGameSaveThread") + val savingThread = Thread(getSaveThread(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 @@ -134,7 +141,6 @@ object LoadSavegame { world.layerTerrain = BlockLayer(world.width, world.height) world.layerWall = BlockLayer(world.width, world.height) - newIngame.savegameArchive = disk newIngame.creationTime = meta.creation_t newIngame.lastPlayTime = meta.lastplay_t newIngame.totalPlayTime = meta.playtime_t diff --git a/src/net/torvald/terrarum/serialise/WriteWorld.kt b/src/net/torvald/terrarum/serialise/WriteWorld.kt index 025cc56da..61ff1b5bb 100644 --- a/src/net/torvald/terrarum/serialise/WriteWorld.kt +++ b/src/net/torvald/terrarum/serialise/WriteWorld.kt @@ -9,6 +9,7 @@ import net.torvald.terrarum.gameworld.BlockLayer 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.realestate.LandUtil import net.torvald.terrarum.tvda.ByteArray64 import net.torvald.terrarum.tvda.ByteArray64Writer @@ -22,7 +23,8 @@ object WriteWorld { fun actorAcceptable(actor: Actor): Boolean { return actor.referenceID !in ReferencingRanges.ACTORS_WIRES && actor.referenceID !in ReferencingRanges.ACTORS_WIRES_HELPER && - actor != (CommonResourcePool.get("blockmarking_actor") as BlockMarkerActor) + actor != (CommonResourcePool.get("blockmarking_actor") as BlockMarkerActor) && + actor !is IngamePlayer // IngamePlayers must not be saved with the world } private fun preWrite(ingame: TerrarumIngame, time_t: Long): GameWorld { diff --git a/src/net/torvald/terrarum/utils/HashArray.kt b/src/net/torvald/terrarum/utils/HashArray.kt index 766139752..1067812b6 100644 --- a/src/net/torvald/terrarum/utils/HashArray.kt +++ b/src/net/torvald/terrarum/utils/HashArray.kt @@ -7,9 +7,12 @@ import net.torvald.terrarum.gameitem.ItemID import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.FluidType import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.PhysicalStatus import net.torvald.terrarum.tvda.ByteArray64Reader import net.torvald.terrarum.serialise.Common import java.io.StringReader +import java.util.* +import kotlin.collections.HashMap /** * Created by minjaesong on 2021-08-26. @@ -22,7 +25,7 @@ class HashedFluidType: HashMap() class HashedWirings: HashMap() class HashedWiringGraph: HashMap() class MetaModuleCSVPair: HashMap() - +class PlayersLastPhysics: HashMap() /** * @param doc plaintext *