mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-12 11:34:05 +09:00
new savegame loading wip
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
"GAME_32BIT_WARNING2": "Please download and install the latest 64-Bit Java at:",
|
"GAME_32BIT_WARNING2": "Please download and install the latest 64-Bit Java at:",
|
||||||
"GAME_32BIT_WARNING3": "https://www.java.com/en/download/",
|
"GAME_32BIT_WARNING3": "https://www.java.com/en/download/",
|
||||||
"GAME_APPLE_ROSETTA_WARNING1": "It seems you are using a Mac with Apple Silicon but running the x86 build of the game.",
|
"GAME_APPLE_ROSETTA_WARNING1": "It seems you are using a Mac with Apple Silicon but running the x86 build of the game.",
|
||||||
"GAME_APPLE_ROSETTA_WARNING2": "Please use the native build for improved performance and the gameplay experiences.",
|
"GAME_APPLE_ROSETTA_WARNING2": "Please use the native build for improved performance and gameplay experiences.",
|
||||||
"MENU_OPTION_STREAMERS_LAYOUT": "Chat Overlay",
|
"MENU_OPTION_STREAMERS_LAYOUT": "Chat Overlay",
|
||||||
"MENU_LABEL_RESTART_REQUIRED": "Restart Required",
|
"MENU_LABEL_RESTART_REQUIRED": "Restart Required",
|
||||||
"MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout",
|
"MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout",
|
||||||
@@ -33,5 +33,8 @@
|
|||||||
"MENU_OPTIONS_JVM_HEAP_MAX": "Max JVM Heap Memory",
|
"MENU_OPTIONS_JVM_HEAP_MAX": "Max JVM Heap Memory",
|
||||||
"MENU_OPTIONS_AUTOSAVE": "Autosave",
|
"MENU_OPTIONS_AUTOSAVE": "Autosave",
|
||||||
"CONTEXT_TIME_MINUTE_PLURAL": "Minutes",
|
"CONTEXT_TIME_MINUTE_PLURAL": "Minutes",
|
||||||
"MENU_LABEL_SYSTEM_INFO": "System Info"
|
"MENU_LABEL_SYSTEM_INFO": "System Info",
|
||||||
|
"GAME_PREV_SAVE_WAS_LOADED": "The most recently saved game was corrupted.\nThe previously saved game was loaded.",
|
||||||
|
"GAME_MORE_RECENT_AUTOSAVE1": "The Autosave is more recent than the manual save.",
|
||||||
|
"GAME_MORE_RECENT_AUTOSAVE2": "Please select the saved game you want to load:"
|
||||||
}
|
}
|
||||||
@@ -207,11 +207,11 @@ 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<UUID> sortedSavegameWorlds = new ArrayList();
|
public static ArrayList<UUID> sortedSavegameWorlds = new ArrayList();
|
||||||
public static HashMap<UUID, DiskSkimmer> savegameWorlds = new HashMap<>(); // UNSORTED even with the TreeMap
|
public static HashMap<UUID, SavegameCollection> savegameWorlds = new HashMap<>(); // UNSORTED even with the TreeMap
|
||||||
public static HashMap<UUID, String> savegameWorldsName = new HashMap<>();
|
public static HashMap<UUID, String> savegameWorldsName = new HashMap<>();
|
||||||
|
|
||||||
public static ArrayList<UUID> sortedPlayers = new ArrayList();
|
public static ArrayList<UUID> sortedPlayers = new ArrayList();
|
||||||
public static HashMap<UUID, DiskSkimmer> savegamePlayers = new HashMap<>();
|
public static HashMap<UUID, SavegameCollection> savegamePlayers = new HashMap<>();
|
||||||
public static HashMap<UUID, String> savegamePlayersName = new HashMap<>();
|
public static HashMap<UUID, String> savegamePlayersName = new HashMap<>();
|
||||||
|
|
||||||
public static void updateListOfSavegames() {
|
public static void updateListOfSavegames() {
|
||||||
|
|||||||
37
src/net/torvald/terrarum/SavegameCollection.kt
Normal file
37
src/net/torvald/terrarum/SavegameCollection.kt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package net.torvald.terrarum
|
||||||
|
|
||||||
|
import net.torvald.terrarum.savegame.DiskSkimmer
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2023-06-24.
|
||||||
|
*/
|
||||||
|
class SavegameCollection(files0: List<DiskSkimmer>) {
|
||||||
|
|
||||||
|
/** Sorted in reverse by the last modified time of the files, index zero being the most recent */
|
||||||
|
val files = files0.sortedByDescending { it.getLastModifiedTime() }
|
||||||
|
/** Sorted in reverse by the last modified time of the files, index zero being the most recent */
|
||||||
|
val autoSaves = files.filter { it.diskFile.extension.matches(Regex("[a-z]")) }
|
||||||
|
/** Sorted in reverse by the last modified time of the files, index zero being the most recent */
|
||||||
|
val manualSaves = files.filter { !it.diskFile.extension.matches(Regex("[a-z]")) }
|
||||||
|
|
||||||
|
init {
|
||||||
|
files.forEach { it.rebuild() }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun collectFromBaseFilename(basedir: File, name: String): SavegameCollection {
|
||||||
|
val files = basedir.listFiles().filter { it.name.startsWith(name) }
|
||||||
|
.mapNotNull { try { DiskSkimmer(it, true) } catch (e: Throwable) { null } }
|
||||||
|
return SavegameCollection(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most recent not-corrupted file
|
||||||
|
*/
|
||||||
|
fun loadable(): DiskSkimmer {
|
||||||
|
return files.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ import net.torvald.terrarum.savegame.DiskSkimmer
|
|||||||
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
||||||
import net.torvald.terrarum.serialise.Common
|
import net.torvald.terrarum.serialise.Common
|
||||||
import net.torvald.terrarum.ui.UICanvas
|
import net.torvald.terrarum.ui.UICanvas
|
||||||
|
import net.torvald.terrarum.utils.JsonFetcher
|
||||||
|
import net.torvald.terrarum.utils.forEachSiblings
|
||||||
import net.torvald.terrarum.worlddrawer.WorldCamera
|
import net.torvald.terrarum.worlddrawer.WorldCamera
|
||||||
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
|
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
|
||||||
import net.torvald.unsafe.UnsafeHelper
|
import net.torvald.unsafe.UnsafeHelper
|
||||||
@@ -797,15 +799,17 @@ fun AppUpdateListOfSavegames() {
|
|||||||
}
|
}
|
||||||
}.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it ->
|
}.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it ->
|
||||||
println("${index+1}.\t${it.diskFile.absolutePath}")
|
println("${index+1}.\t${it.diskFile.absolutePath}")
|
||||||
it.rebuild() // disk skimmer was created without initialisation, so do it now
|
it.rebuild()
|
||||||
|
|
||||||
val jsonFile = it.getFile(SAVEGAMEINFO)!!
|
val jsonFile = it.getFile(SAVEGAMEINFO)!!
|
||||||
val json = JsonReader().parse(ByteArray64Reader(jsonFile.bytes, Common.CHARSET).readText())
|
var worldUUID: UUID? = null
|
||||||
val worldUUID = UUID.fromString(json.getString("worldIndex"))
|
JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value ->
|
||||||
|
if (name == "worldIndex") worldUUID = UUID.fromString(value.asString())
|
||||||
|
}
|
||||||
|
|
||||||
// if multiple valid savegames with same UUID exist, only the most recent one is retained
|
// if multiple valid savegames with same UUID exist, only the most recent one is retained
|
||||||
if (!App.savegameWorlds.contains(worldUUID)) {
|
if (!App.savegameWorlds.contains(worldUUID)) {
|
||||||
App.savegameWorlds[worldUUID] = it
|
App.savegameWorlds[worldUUID] = SavegameCollection.collectFromBaseFilename(File(worldsDir), it.diskFile.name)
|
||||||
App.savegameWorldsName[worldUUID] = it.getDiskName(Common.CHARSET)
|
App.savegameWorldsName[worldUUID] = it.getDiskName(Common.CHARSET)
|
||||||
App.sortedSavegameWorlds.add(worldUUID)
|
App.sortedSavegameWorlds.add(worldUUID)
|
||||||
}
|
}
|
||||||
@@ -827,15 +831,17 @@ fun AppUpdateListOfSavegames() {
|
|||||||
}
|
}
|
||||||
}.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it ->
|
}.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it ->
|
||||||
println("${index+1}.\t${it.diskFile.absolutePath}")
|
println("${index+1}.\t${it.diskFile.absolutePath}")
|
||||||
it.rebuild() // disk skimmer was created without initialisation, so do it now
|
it.rebuild()
|
||||||
|
|
||||||
val jsonFile = it.getFile(SAVEGAMEINFO)!!
|
val jsonFile = it.getFile(SAVEGAMEINFO)!!
|
||||||
val json = JsonReader().parse(ByteArray64Reader(jsonFile.bytes, Common.CHARSET).readText())
|
var playerUUID: UUID? = null
|
||||||
val playerUUID = UUID.fromString(json.getString("uuid"))
|
JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value ->
|
||||||
|
if (name == "uuid") playerUUID = UUID.fromString(value.asString())
|
||||||
|
}
|
||||||
|
|
||||||
// if multiple valid savegames with same UUID exist, only the most recent one is retained
|
// if multiple valid savegames with same UUID exist, only the most recent one is retained
|
||||||
if (!App.savegamePlayers.contains(playerUUID)) {
|
if (!App.savegamePlayers.contains(playerUUID)) {
|
||||||
App.savegamePlayers[playerUUID] = it
|
App.savegamePlayers[playerUUID] = SavegameCollection.collectFromBaseFilename(File(playersDir), it.diskFile.name)
|
||||||
App.savegamePlayersName[playerUUID] = it.getDiskName(Common.CHARSET)
|
App.savegamePlayersName[playerUUID] = it.getDiskName(Common.CHARSET)
|
||||||
App.sortedPlayers.add(playerUUID)
|
App.sortedPlayers.add(playerUUID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ object LoadSavegame {
|
|||||||
printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}")
|
printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}")
|
||||||
|
|
||||||
val currentWorldId = player.worldCurrentlyPlaying
|
val currentWorldId = player.worldCurrentlyPlaying
|
||||||
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!
|
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable()
|
||||||
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET), worldDisk.diskFile)
|
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET), worldDisk.diskFile)
|
||||||
|
|
||||||
world.layerTerrain = BlockLayer(world.width, world.height)
|
world.layerTerrain = BlockLayer(world.width, world.height)
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import net.torvald.terrarum.ui.Movement
|
|||||||
import net.torvald.terrarum.ui.Toolkit
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
import net.torvald.terrarum.ui.UICanvas
|
import net.torvald.terrarum.ui.UICanvas
|
||||||
import net.torvald.terrarum.ui.UIItem
|
import net.torvald.terrarum.ui.UIItem
|
||||||
|
import net.torvald.terrarum.utils.JsonFetcher
|
||||||
|
import net.torvald.terrarum.utils.forEachSiblings
|
||||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
@@ -74,12 +76,16 @@ object UILoadGovernor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class Advanceable : UICanvas() {
|
||||||
|
abstract fun advanceMode()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only works if current screen set by the App is [TitleScreen]
|
* Only works if current screen set by the App is [TitleScreen]
|
||||||
*
|
*
|
||||||
* Created by minjaesong on 2021-09-09.
|
* Created by minjaesong on 2021-09-09.
|
||||||
*/
|
*/
|
||||||
class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() {
|
||||||
|
|
||||||
// private val hash = RandomWordsName(3)
|
// private val hash = RandomWordsName(3)
|
||||||
|
|
||||||
@@ -155,7 +161,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
this.mode = mode
|
this.mode = mode
|
||||||
}
|
}
|
||||||
|
|
||||||
fun advanceMode() {
|
override fun advanceMode() {
|
||||||
mode += 1
|
mode += 1
|
||||||
uiScroll = 0f
|
uiScroll = 0f
|
||||||
scrollFrom = 0
|
scrollFrom = 0
|
||||||
@@ -175,7 +181,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
// read savegames
|
// read savegames
|
||||||
var savegamesCount = 0
|
var savegamesCount = 0
|
||||||
App.sortedSavegameWorlds.forEach { uuid ->
|
App.sortedSavegameWorlds.forEach { uuid ->
|
||||||
val skimmer = App.savegameWorlds[uuid]!!
|
val skimmer = App.savegameWorlds[uuid]!!.loadable()
|
||||||
val x = uiX
|
val x = uiX
|
||||||
val y = titleTopGradEnd + cellInterval * savegamesCount
|
val y = titleTopGradEnd + cellInterval * savegamesCount
|
||||||
try {
|
try {
|
||||||
@@ -190,7 +196,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
|
|
||||||
savegamesCount = 0
|
savegamesCount = 0
|
||||||
App.sortedPlayers.forEach { uuid ->
|
App.sortedPlayers.forEach { uuid ->
|
||||||
val skimmer = App.savegamePlayers[uuid]!!
|
val skimmer = App.savegamePlayers[uuid]!!.loadable()
|
||||||
val x = uiX
|
val x = uiX
|
||||||
val y = titleTopGradEnd + cellInterval * savegamesCount
|
val y = titleTopGradEnd + cellInterval * savegamesCount
|
||||||
try {
|
try {
|
||||||
@@ -470,7 +476,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
|
|
||||||
|
|
||||||
class UIItemPlayerCells(
|
class UIItemPlayerCells(
|
||||||
parent: UILoadDemoSavefiles,
|
parent: Advanceable,
|
||||||
initialX: Int,
|
initialX: Int,
|
||||||
initialY: Int,
|
initialY: Int,
|
||||||
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
|
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
|
||||||
@@ -492,24 +498,22 @@ class UIItemPlayerCells(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
skimmer.getFile(SAVEGAMEINFO)?.bytes?.let {
|
skimmer.getFile(SAVEGAMEINFO)?.bytes?.let {
|
||||||
val json = JsonReader().parse(ByteArray64Reader(it, Common.CHARSET))
|
var playerUUID: UUID? = null
|
||||||
|
var worldUUID: UUID? = null
|
||||||
|
var lastPlayTime0 = 0L
|
||||||
|
|
||||||
playerUUID = UUID.fromString(json["uuid"]?.asString())
|
JsonFetcher.readFromJsonString(ByteArray64Reader(it, Common.CHARSET)).forEachSiblings { name, value ->
|
||||||
val worldUUID = UUID.fromString(json["worldCurrentlyPlaying"]?.asString())
|
if (name == "uuid") playerUUID = UUID.fromString(value.asString())
|
||||||
|
if (name == "worldCurrentlyPlaying") worldUUID = UUID.fromString(value.asString())
|
||||||
|
if (name == "totalPlayTime") totalPlayTime = parseDuration(value.asLong())
|
||||||
|
if (name == "lastPlayTime") lastPlayTime0 = value.asLong()
|
||||||
|
}
|
||||||
|
|
||||||
App.savegamePlayersName[playerUUID]?.let { if (it.isNotBlank()) playerName = it else "(name)" }
|
App.savegamePlayersName[playerUUID]?.let { if (it.isNotBlank()) playerName = it else "(name)" }
|
||||||
App.savegameWorldsName[worldUUID]?.let { if (it.isNotBlank()) worldName = it }
|
App.savegameWorldsName[worldUUID]?.let { if (it.isNotBlank()) worldName = it }
|
||||||
/*json["lastPlayTime"]?.asString()?.let {
|
lastPlayTime = Instant.ofEpochSecond(lastPlayTime0)
|
||||||
lastPlayTime = Instant.ofEpochSecond(it.toLong())
|
|
||||||
.atZone(TimeZone.getDefault().toZoneId())
|
|
||||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
|
||||||
}*/
|
|
||||||
lastPlayTime = Instant.ofEpochSecond(skimmer.getLastModifiedTime())
|
|
||||||
.atZone(TimeZone.getDefault().toZoneId())
|
.atZone(TimeZone.getDefault().toZoneId())
|
||||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||||
json["totalPlayTime"]?.asString()?.let {
|
|
||||||
totalPlayTime = parseDuration(it.toLong())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,7 +674,7 @@ class UIItemPlayerCells(
|
|||||||
|
|
||||||
|
|
||||||
class UIItemWorldCells(
|
class UIItemWorldCells(
|
||||||
parent: UILoadDemoSavefiles,
|
parent: Advanceable,
|
||||||
initialX: Int,
|
initialX: Int,
|
||||||
initialY: Int,
|
initialY: Int,
|
||||||
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
|
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
|
||||||
@@ -685,9 +689,6 @@ class UIItemWorldCells(
|
|||||||
private val lastPlayedTimestamp: String
|
private val lastPlayedTimestamp: String
|
||||||
|
|
||||||
init {
|
init {
|
||||||
printdbg(this, "Rebuilding skimmer for savefile ${skimmer.diskFile.absolutePath}")
|
|
||||||
skimmer.rebuild()
|
|
||||||
|
|
||||||
metaFile = skimmer.getFile(-1)
|
metaFile = skimmer.getFile(-1)
|
||||||
if (metaFile == null) saveDamaged = true
|
if (metaFile == null) saveDamaged = true
|
||||||
|
|
||||||
@@ -699,14 +700,18 @@ class UIItemWorldCells(
|
|||||||
saveDamaged = saveDamaged or checkForSavegameDamage(skimmer)
|
saveDamaged = saveDamaged or checkForSavegameDamage(skimmer)
|
||||||
|
|
||||||
if (metaFile != null) {
|
if (metaFile != null) {
|
||||||
val worldJson = JsonReader().parse(ByteArray64Reader(metaFile.bytes, Common.CHARSET))
|
// val lastplay_t = skimmer.getLastModifiedTime()//worldJson["lastPlayTime"].asLong()
|
||||||
val lastplay_t = skimmer.getLastModifiedTime()//worldJson["lastPlayTime"].asLong()
|
var playtime_t = ""
|
||||||
val playtime_t = worldJson["totalPlayTime"].asLong()
|
var lastplay_t = 0L
|
||||||
|
JsonFetcher.readFromJsonString(ByteArray64Reader(metaFile.bytes, Common.CHARSET)).forEachSiblings { name, value ->
|
||||||
|
if (name == "lastPlayTime") lastplay_t = value.asLong()
|
||||||
|
if (name == "totalPlayTime") playtime_t = parseDuration(value.asLong())
|
||||||
|
}
|
||||||
lastPlayedTimestamp =
|
lastPlayedTimestamp =
|
||||||
Instant.ofEpochSecond(lastplay_t)
|
Instant.ofEpochSecond(lastplay_t)
|
||||||
.atZone(TimeZone.getDefault().toZoneId())
|
.atZone(TimeZone.getDefault().toZoneId())
|
||||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
|
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
|
||||||
"/${parseDuration(playtime_t)}"
|
"/$playtime_t"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lastPlayedTimestamp = "--:--:--/--h--m--s"
|
lastPlayedTimestamp = "--:--:--/--h--m--s"
|
||||||
|
|||||||
433
src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt
Normal file
433
src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.ui
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
|
import com.badlogic.gdx.Input
|
||||||
|
import com.badlogic.gdx.graphics.*
|
||||||
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
|
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||||
|
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
||||||
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
|
||||||
|
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||||
|
import com.badlogic.gdx.utils.JsonReader
|
||||||
|
import com.jme3.math.FastMath
|
||||||
|
import net.torvald.unicode.EMDASH
|
||||||
|
import net.torvald.unicode.getKeycapConsole
|
||||||
|
import net.torvald.unicode.getKeycapPC
|
||||||
|
import net.torvald.terrarum.*
|
||||||
|
import net.torvald.terrarum.App.printdbg
|
||||||
|
import net.torvald.terrarum.langpack.Lang
|
||||||
|
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.CELL_COL
|
||||||
|
import net.torvald.terrarum.savegame.ByteArray64InputStream
|
||||||
|
import net.torvald.terrarum.savegame.ByteArray64Reader
|
||||||
|
import net.torvald.terrarum.savegame.DiskSkimmer
|
||||||
|
import net.torvald.terrarum.savegame.EntryFile
|
||||||
|
import net.torvald.terrarum.serialise.Common
|
||||||
|
import net.torvald.terrarum.serialise.SaveLoadError
|
||||||
|
import net.torvald.terrarum.modulebasegame.serialise.LoadSavegame
|
||||||
|
import net.torvald.terrarum.savegame.VDFileID.BODYPART_TO_ENTRY_MAP
|
||||||
|
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
||||||
|
import net.torvald.terrarum.savegame.VDFileID.SPRITEDEF
|
||||||
|
import net.torvald.terrarum.spriteassembler.ADProperties
|
||||||
|
import net.torvald.terrarum.spriteassembler.ADProperties.Companion.EXTRA_HEADROOM_X
|
||||||
|
import net.torvald.terrarum.spriteassembler.ADProperties.Companion.EXTRA_HEADROOM_Y
|
||||||
|
import net.torvald.terrarum.spriteassembler.AssembleFrameBase
|
||||||
|
import net.torvald.terrarum.spriteassembler.AssembleSheetPixmap
|
||||||
|
import net.torvald.terrarum.ui.Movement
|
||||||
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
|
import net.torvald.terrarum.ui.UICanvas
|
||||||
|
import net.torvald.terrarum.ui.UIItem
|
||||||
|
import net.torvald.terrarum.utils.JsonFetcher
|
||||||
|
import net.torvald.terrarum.utils.forEachSiblings
|
||||||
|
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.*
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only works if current screen set by the App is [TitleScreen]
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2023-06-24.
|
||||||
|
*/
|
||||||
|
class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
||||||
|
|
||||||
|
// private val hash = RandomWordsName(3)
|
||||||
|
|
||||||
|
init {
|
||||||
|
CommonResourcePool.addToLoadingList("terrarum-defaultsavegamethumb") {
|
||||||
|
TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/savegame_thumb_placeholder.png")))
|
||||||
|
}
|
||||||
|
CommonResourcePool.addToLoadingList("savegame_status_icon") {
|
||||||
|
TextureRegionPack("assets/graphics/gui/savegame_status_icon.tga", 24, 24)
|
||||||
|
}
|
||||||
|
CommonResourcePool.loadAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
override var width: Int
|
||||||
|
get() = Toolkit.drawWidth
|
||||||
|
set(value) {}
|
||||||
|
override var height: Int
|
||||||
|
get() = App.scr.height
|
||||||
|
set(value) {}
|
||||||
|
override var openCloseTime: Second = OPENCLOSE_GENERIC
|
||||||
|
|
||||||
|
|
||||||
|
private val shapeRenderer = App.makeShapeRenderer()
|
||||||
|
|
||||||
|
|
||||||
|
internal val uiWidth = SAVE_CELL_WIDTH
|
||||||
|
internal val uiX: Int
|
||||||
|
get() = (Toolkit.drawWidth - uiWidth) / 2
|
||||||
|
internal val uiXdiffChatOverlay = App.scr.chatWidth / 2
|
||||||
|
|
||||||
|
internal val textH = App.fontGame.lineHeight.toInt()
|
||||||
|
|
||||||
|
internal val cellGap = 20
|
||||||
|
internal val cellInterval = cellGap + SAVE_CELL_HEIGHT
|
||||||
|
internal val gradAreaHeight = 32
|
||||||
|
|
||||||
|
internal val titleTextPosY: Int = App.scr.tvSafeGraphicsHeight + 10
|
||||||
|
internal val titleTopGradStart: Int = titleTextPosY + textH
|
||||||
|
internal val titleTopGradEnd: Int = titleTopGradStart + gradAreaHeight
|
||||||
|
internal val titleBottomGradStart: Int = height - App.scr.tvSafeGraphicsHeight - gradAreaHeight
|
||||||
|
internal val titleBottomGradEnd: Int = titleBottomGradStart + gradAreaHeight
|
||||||
|
internal val controlHelperY: Int = titleBottomGradStart + gradAreaHeight - textH
|
||||||
|
|
||||||
|
|
||||||
|
private val controlHelp: String
|
||||||
|
get() = if (App.environment == RunningEnvironment.PC)
|
||||||
|
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" +
|
||||||
|
" ${Lang["MENU_CONTROLS_SCROLL"]}"
|
||||||
|
else
|
||||||
|
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
|
||||||
|
|
||||||
|
|
||||||
|
private var scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
|
||||||
|
private var listScroll = 0 // only update when animation is finished
|
||||||
|
private var savesVisible = (scrollAreaHeight + cellGap) / cellInterval
|
||||||
|
|
||||||
|
private var uiScroll = 0f
|
||||||
|
private var scrollFrom = 0
|
||||||
|
private var scrollTarget = 0
|
||||||
|
private var scrollAnimCounter = 0f
|
||||||
|
private val scrollAnimLen = 0.1f
|
||||||
|
|
||||||
|
private var sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, false)
|
||||||
|
|
||||||
|
private var showSpinner = false
|
||||||
|
|
||||||
|
private val playerCells = ArrayList<UIItemPlayerCells>()
|
||||||
|
|
||||||
|
var mode = 0; private set// 0: show players, 1: show worlds
|
||||||
|
|
||||||
|
override fun advanceMode() {
|
||||||
|
mode += 1
|
||||||
|
uiScroll = 0f
|
||||||
|
scrollFrom = 0
|
||||||
|
scrollTarget = 0
|
||||||
|
scrollAnimCounter = 0f
|
||||||
|
loadFired = 0
|
||||||
|
|
||||||
|
printdbg(this, "savelist mode: $mode")
|
||||||
|
|
||||||
|
// look for recently played world
|
||||||
|
if (mode == 1) {
|
||||||
|
UILoadGovernor.playerDisk!!.getFile(SAVEGAMEINFO)?.bytes?.let {
|
||||||
|
var worldUUID: UUID? = null
|
||||||
|
JsonFetcher.readFromJsonString(ByteArray64Reader(it, Common.CHARSET)).forEachSiblings { name, value ->
|
||||||
|
if (name == "worldCurrentlyPlaying") worldUUID = UUID.fromString(value.asString())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO select the most recent loadable save by comparing manual and autosaves, NOT JUST going with loadable()
|
||||||
|
UILoadGovernor.worldDisk = App.savegameWorlds[worldUUID!!]!!.loadable()
|
||||||
|
|
||||||
|
mode += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
try {
|
||||||
|
remoCon.handler.lockToggle()
|
||||||
|
showSpinner = true
|
||||||
|
|
||||||
|
Thread {
|
||||||
|
// read savegames
|
||||||
|
var savegamesCount = 0
|
||||||
|
App.sortedPlayers.forEach { uuid ->
|
||||||
|
val skimmer = App.savegamePlayers[uuid]!!.loadable()
|
||||||
|
val x = uiX
|
||||||
|
val y = titleTopGradEnd + cellInterval * savegamesCount
|
||||||
|
try {
|
||||||
|
playerCells.add(UIItemPlayerCells(this, x, y, skimmer))
|
||||||
|
savegamesCount += 1
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
System.err.println("[UILoadDemoSavefiles] Error while loading Player '${skimmer.diskFile.absolutePath}'")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
remoCon.handler.unlockToggle()
|
||||||
|
showSpinner = false
|
||||||
|
}.start()
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (e: UninitializedPropertyAccessException) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hide() {
|
||||||
|
playerCells.forEach { it.dispose() }
|
||||||
|
playerCells.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCells() = playerCells
|
||||||
|
private var loadFired = 0
|
||||||
|
private var oldMode = -1
|
||||||
|
|
||||||
|
private val mode1Node = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse()
|
||||||
|
private val mode2Node = Yaml(UITitleRemoConYaml.injectedMenuSingleWorldSel).parse()
|
||||||
|
|
||||||
|
private val menus = listOf(mode1Node, mode2Node)
|
||||||
|
private val titles = listOf("CONTEXT_CHARACTER", "MENU_LABEL_WORLD")
|
||||||
|
|
||||||
|
init {
|
||||||
|
// this UI will NOT persist; the parent of the mode1Node must be set using an absolute value (e.g. treeRoot, not remoCon.currentRemoConContents)
|
||||||
|
|
||||||
|
//printdbg(this, "UILoadDemoSaveFiles called, from:")
|
||||||
|
//printStackTrace(this)
|
||||||
|
|
||||||
|
mode1Node.parent = remoCon.treeRoot
|
||||||
|
mode2Node.parent = mode1Node
|
||||||
|
|
||||||
|
mode1Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadDemoSavefiles"
|
||||||
|
mode2Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadDemoSavefiles"
|
||||||
|
|
||||||
|
// printdbg(this, "mode1Node parent: ${mode1Node.parent?.data}") // will be 'null' because the parent is the root node
|
||||||
|
// printdbg(this, "mode1Node data: ${mode1Node.data}")
|
||||||
|
// printdbg(this, "mode2Node data: ${mode2Node.data}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun modeChangedHandler(mode: Int) {
|
||||||
|
remoCon.setNewRemoConContents(menus[mode])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateUI(delta: Float) {
|
||||||
|
|
||||||
|
if (mode < 2) {
|
||||||
|
|
||||||
|
if (oldMode != mode) {
|
||||||
|
modeChangedHandler(mode)
|
||||||
|
oldMode = mode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrollTarget != listScroll) {
|
||||||
|
if (scrollAnimCounter < scrollAnimLen) {
|
||||||
|
scrollAnimCounter += delta
|
||||||
|
uiScroll = Movement.fastPullOut(
|
||||||
|
scrollAnimCounter / scrollAnimLen,
|
||||||
|
listScroll * cellInterval.toFloat(),
|
||||||
|
scrollTarget * cellInterval.toFloat()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scrollAnimCounter = 0f
|
||||||
|
listScroll = scrollTarget
|
||||||
|
uiScroll = cellInterval.toFloat() * scrollTarget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cells = getCells()
|
||||||
|
|
||||||
|
for (index in 0 until cells.size) {
|
||||||
|
|
||||||
|
|
||||||
|
val it = cells[index]
|
||||||
|
if (index in listScroll - 2 until listScroll + savesVisible + 2) {
|
||||||
|
// re-position
|
||||||
|
it.posY = (it.initialY - uiScroll).roundToInt()
|
||||||
|
it.update(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||||
|
|
||||||
|
if (mode == 2) {
|
||||||
|
loadFired += 1
|
||||||
|
// to hide the "flipped skybox" artefact
|
||||||
|
batch.end()
|
||||||
|
|
||||||
|
gdxClearAndEnableBlend(.094f, .094f, .094f, 0f)
|
||||||
|
|
||||||
|
batch.begin()
|
||||||
|
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
val txt = Lang["MENU_IO_LOADING"]
|
||||||
|
App.fontGame.draw(batch, txt, (App.scr.width - App.fontGame.getWidth(txt)) / 2f, (App.scr.height - App.fontGame.lineHeight) / 2f)
|
||||||
|
|
||||||
|
if (loadFired == 2) {
|
||||||
|
LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
batch.end()
|
||||||
|
|
||||||
|
val cells = getCells()
|
||||||
|
|
||||||
|
lateinit var savePixmap: Pixmap
|
||||||
|
sliderFBO.inAction(camera as OrthographicCamera, batch) {
|
||||||
|
gdxClearAndEnableBlend(0f, 0f, 0f, 0f)
|
||||||
|
|
||||||
|
setCameraPosition(batch, camera, 0f, 0f)
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
batch.inUse {
|
||||||
|
for (index in 0 until cells.size) {
|
||||||
|
val it = cells[index]
|
||||||
|
|
||||||
|
if (App.getConfigBoolean("fx_streamerslayout"))
|
||||||
|
it.posX += uiXdiffChatOverlay
|
||||||
|
|
||||||
|
if (index in listScroll - 2 until listScroll + savesVisible + 2)
|
||||||
|
it.render(batch, camera)
|
||||||
|
|
||||||
|
if (App.getConfigBoolean("fx_streamerslayout"))
|
||||||
|
it.posX -= uiXdiffChatOverlay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
savePixmap = Pixmap.createFromFrameBuffer(0, 0, sliderFBO.width, sliderFBO.height)
|
||||||
|
savePixmap.blending = Pixmap.Blending.None
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// implement "wipe-out" by CPU-rendering (*deep exhale*)
|
||||||
|
//savePixmap.setColor(1f,1f,1f,0f)
|
||||||
|
savePixmap.setColor(0f, 0f, 0f, 0f)
|
||||||
|
savePixmap.fillRectangle(0, savePixmap.height - titleTopGradStart, savePixmap.width, titleTopGradStart)
|
||||||
|
// top grad
|
||||||
|
for (y in titleTopGradStart until titleTopGradEnd) {
|
||||||
|
val alpha = (y - titleTopGradStart).toFloat() / gradAreaHeight
|
||||||
|
for (x in 0 until savePixmap.width) {
|
||||||
|
val col = savePixmap.getPixel(x, savePixmap.height - y)
|
||||||
|
val blendAlpha = (col.and(0xFF) * alpha).roundToInt()
|
||||||
|
savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// bottom grad
|
||||||
|
for (y in titleBottomGradStart until titleBottomGradEnd) {
|
||||||
|
val alpha = 1f - ((y - titleBottomGradStart).toFloat() / gradAreaHeight)
|
||||||
|
for (x in 0 until savePixmap.width) {
|
||||||
|
val col = savePixmap.getPixel(x, savePixmap.height - y)
|
||||||
|
val blendAlpha = (col.and(0xFF) * alpha).roundToInt()
|
||||||
|
savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
savePixmap.setColor(0f, 0f, 0f, 0f)
|
||||||
|
savePixmap.fillRectangle(0, 0, savePixmap.width, height - titleBottomGradEnd + 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setCameraPosition(batch, camera, 0f, 0f)
|
||||||
|
val saveTex = TextureRegion(Texture(savePixmap)); saveTex.flip(false, true)
|
||||||
|
batch.inUse {
|
||||||
|
batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f)
|
||||||
|
|
||||||
|
// draw texts
|
||||||
|
val loadGameTitleStr = Lang[titles[mode]]// + "$EMDASH$hash"
|
||||||
|
// "Game Load"
|
||||||
|
App.fontUITitle.draw(batch, loadGameTitleStr, (width - App.fontUITitle.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||||
|
// Control help
|
||||||
|
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
saveTex.texture.dispose()
|
||||||
|
savePixmap.dispose()
|
||||||
|
|
||||||
|
batch.begin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun keyDown(keycode: Int): Boolean {
|
||||||
|
if (this.isVisible) {
|
||||||
|
val cells = getCells()
|
||||||
|
|
||||||
|
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) {
|
||||||
|
scrollFrom = listScroll
|
||||||
|
scrollTarget -= 1
|
||||||
|
scrollAnimCounter = 0f
|
||||||
|
}
|
||||||
|
else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("control_key_down")) && scrollTarget < cells.size - savesVisible) {
|
||||||
|
scrollFrom = listScroll
|
||||||
|
scrollTarget += 1
|
||||||
|
scrollAnimCounter = 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||||
|
getCells().forEach { it.touchDown(screenX, screenY, pointer, button) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||||
|
getCells().forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||||
|
if (this.isVisible) {
|
||||||
|
val cells = getCells()
|
||||||
|
|
||||||
|
if (amountY <= -1f && scrollTarget > 0) {
|
||||||
|
scrollFrom = listScroll
|
||||||
|
scrollTarget -= 1
|
||||||
|
scrollAnimCounter = 0f
|
||||||
|
}
|
||||||
|
else if (amountY >= 1f && scrollTarget < cells.size - savesVisible) {
|
||||||
|
scrollFrom = listScroll
|
||||||
|
scrollTarget += 1
|
||||||
|
scrollAnimCounter = 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun endClosing(delta: Float) {
|
||||||
|
super.endClosing(delta)
|
||||||
|
listScroll = 0
|
||||||
|
scrollTarget = 0
|
||||||
|
uiScroll = 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dispose() {
|
||||||
|
try { shapeRenderer.dispose() } catch (e: IllegalArgumentException) {}
|
||||||
|
try { sliderFBO.dispose() } catch (e: IllegalArgumentException) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resize(width: Int, height: Int) {
|
||||||
|
super.resize(width, height)
|
||||||
|
scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
|
||||||
|
savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + SAVE_CELL_HEIGHT)
|
||||||
|
|
||||||
|
listScroll = 0
|
||||||
|
scrollTarget = 0
|
||||||
|
uiScroll = 0f
|
||||||
|
|
||||||
|
sliderFBO.dispose()
|
||||||
|
sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setCameraPosition(batch: SpriteBatch, camera: Camera, newX: Float, newY: Float) {
|
||||||
|
camera.position.set((-newX + App.scr.halfw).round(), (-newY + App.scr.halfh).round(), 0f)
|
||||||
|
camera.update()
|
||||||
|
batch.projectionMatrix = camera.combined
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ object UITitleRemoConYaml {
|
|||||||
* The class must be the UICanvas
|
* The class must be the UICanvas
|
||||||
*/
|
*/
|
||||||
val menuBase = """
|
val menuBase = """
|
||||||
- MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadDemoSavefiles
|
- MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame
|
||||||
- MENU_OPTIONS
|
- MENU_OPTIONS
|
||||||
- MENU_LABEL_GRAPHICS : net.torvald.terrarum.modulebasegame.ui.UIGraphicsControlPanel
|
- MENU_LABEL_GRAPHICS : net.torvald.terrarum.modulebasegame.ui.UIGraphicsControlPanel
|
||||||
- MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel
|
- MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel
|
||||||
|
|||||||
@@ -174,8 +174,9 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
|||||||
worldList.clear()
|
worldList.clear()
|
||||||
(INGAME.actorGamer.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(",").filter { it.isNotBlank() }.map {
|
(INGAME.actorGamer.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(",").filter { it.isNotBlank() }.map {
|
||||||
it.ascii85toUUID().let { it to App.savegameWorlds[it] }
|
it.ascii85toUUID().let { it to App.savegameWorlds[it] }
|
||||||
}.filter { it.second != null }.mapIndexed { index, (uuid, disk) ->
|
}.filter { it.second != null }.mapIndexed { index, (uuid, disk0) ->
|
||||||
|
|
||||||
|
val disk = disk0!!.loadable()
|
||||||
var chunksCount = 0
|
var chunksCount = 0
|
||||||
var seed = 0L
|
var seed = 0L
|
||||||
var lastPlayed = 0L
|
var lastPlayed = 0L
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ removefile:
|
|||||||
|
|
||||||
fun checkFileSanity() {
|
fun checkFileSanity() {
|
||||||
if (!diskFile.exists()) throw NoSuchFileException(diskFile.absoluteFile)
|
if (!diskFile.exists()) throw NoSuchFileException(diskFile.absoluteFile)
|
||||||
if (diskFile.length() < 310L) throw RuntimeException("Invalid Virtual Disk file!")
|
if (diskFile.length() < 310L) throw RuntimeException("Invalid Virtual Disk file: ${diskFile.path}")
|
||||||
|
|
||||||
// check magic
|
// check magic
|
||||||
val fis = FileInputStream(diskFile)
|
val fis = FileInputStream(diskFile)
|
||||||
|
|
||||||
val magic = ByteArray(4).let { fis.read(it); it }
|
val magic = ByteArray(4).let { fis.read(it); it }
|
||||||
if (!magic.contentEquals(VirtualDisk.MAGIC)) throw RuntimeException("Invalid Virtual Disk file!")
|
if (!magic.contentEquals(VirtualDisk.MAGIC)) throw RuntimeException("Invalid Virtual Disk file: ${diskFile.path}")
|
||||||
|
|
||||||
fis.close()
|
fis.close()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user