mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-13 15:16:07 +09:00
new load wip
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ object ThreadExecutor {
|
|||||||
allFinished = true
|
allFinished = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun killAll() {
|
||||||
|
executor.shutdownNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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!
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user