more chunk pool and savegame migration codes

This commit is contained in:
minjaesong
2024-10-08 12:55:30 +09:00
parent b4523b8492
commit e0fcd00d82
10 changed files with 261 additions and 172 deletions

View File

@@ -0,0 +1,75 @@
package net.torvald.terrarum.modulebasegame
import net.torvald.reflection.extortField
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.ClusteredFormatDOM
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.archivers.Clustfile
import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.toBig64
import java.io.File
/**
* Created by minjaesong on 2024-10-08.
*/
object SavegameConverter {
fun type254toType11(infile: File, outFile: File) {
val type254DOM = VDUtil.readDiskArchive(infile)
type254DOMtoType11Disk(type254DOM, outFile)
}
private val getGenver = Regex("""(?<="genver" ?: ?)[0-9]+""")
private fun type254DOMtoType11Disk(type254DOM: VirtualDisk, outFile: File) {
val version: Long = when (type254DOM.saveKind) {
VDSaveKind.PLAYER_DATA, VDSaveKind.WORLD_DATA -> {
val savegameInfo = ByteArray64Reader(type254DOM.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET)
CharArray(128).let {
savegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
}
else -> 0L
}
val newDisk = ClusteredFormatDOM.createNewArchive(
outFile,
Common.CHARSET,
type254DOM.getDiskName(Common.CHARSET),
ClusteredFormatDOM.MAX_CAPA_IN_SECTORS,
byteArrayOf(
-1, // pad
byteArrayOf(15, -1)[type254DOM.saveOrigin.ushr(4).and(1)], // imported/native
byteArrayOf(0x00, 0x50, 0x57)[type254DOM.saveKind], // player/world
byteArrayOf(0x6d, 0x6d, 0x61, 0x61)[type254DOM.saveMode], // manual/auto
type254DOM.extraInfoBytes[4], type254DOM.extraInfoBytes[5], // snapshot info
0, 0,// reserved
) + version.toBig64() // VERSION_RAW in big-endian
)
val DOM = ClusteredFormatDOM(newDisk)
val root = DOM.getRootFile()
// do filecopy
type254DOM.entries.filter { it.key != 0L }.forEach { entryID, diskEntry ->
val filename = Common.type254EntryIDtoType17Filename(entryID)
if (diskEntry.contents !is EntryFile) throw IllegalStateException("Entry in the savegame is not a file (${diskEntry.contents.javaClass.simpleName})")
val entry = diskEntry.contents as EntryFile
val oldBytes = entry.bytes.toByteArray()
Clustfile(DOM, root, filename).let { file ->
// write bytes
file.createNewFile()
file.writeBytes(oldBytes)
// modify attributes
val FAT = file.extortField<ClusteredFormatDOM.FATEntry>("FAT")!!
FAT.creationDate = diskEntry.creationDate
FAT.modificationDate = diskEntry.modificationDate
}
}
DOM.dispose()
}
}

View File

@@ -0,0 +1,141 @@
package net.torvald.terrarum.modulebasegame.serialise
import net.torvald.terrarum.*
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.BlockLayerI16F16
import net.torvald.terrarum.gameworld.BlockLayerOresI16I8
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.FancyWorldReadLoadScreen
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.serialise.Common
import java.io.Reader
import java.util.logging.Level
import kotlin.experimental.or
/**
* Load and setup the game for the first load.
*
* To load additional actors/worlds, use ReadActor/ReadWorld.
*
* Created by minjaesong on 2021-09-03.
*/
object LoadSavegame {
fun getWorldName(worldDisk: SimpleFileSystem) = worldDisk.getDiskName(Common.CHARSET)
fun getWorldSavefileName(world: GameWorld) = "${world.worldIndex}"
fun getPlayerSavefileName(player: IngamePlayer) = "${player.uuid}"
fun getFileBytes(disk: SimpleFileSystem, id: Long): ByteArray64 = disk.getFile(id)!!.bytes
fun getFileReader(disk: SimpleFileSystem, id: Long): Reader =
ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET)
operator fun invoke(diskPair: DiskPair) = invoke(diskPair.player, diskPair.world)
private val getGenver = Regex("""(?<="genver" ?: ?)[0-9]+""")
/**
* @param playerDisk DiskSkimmer representing the Player.
* @param worldDisk0 DiskSkimmer representing the World to be loaded.
* If unset, last played world for the Player will be loaded.
*/
operator fun invoke(playerDisk: DiskSkimmer, worldDisk0: DiskSkimmer? = null) {
val newIngame = TerrarumIngame(App.batch)
playerDisk.rebuild()
val playerDiskSavegameInfo =
ByteArray64Reader(playerDisk.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val player = ReadActor.invoke(playerDisk, playerDiskSavegameInfo) as IngamePlayer
App.printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}")
val currentWorldId = player.worldCurrentlyPlaying
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable()
worldDisk.rebuild()
val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(VDFileID.SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile)
world.layerTerrain = BlockLayerI16(world.width, world.height)
world.layerWall = BlockLayerI16(world.width, world.height)
world.layerOres = BlockLayerOresI16I8(world.width, world.height)
world.layerFluids = BlockLayerI16F16(world.width, world.height)
world.chunkFlags = Array(world.height / LandUtil.CHUNK_H) { ByteArray(world.width / LandUtil.CHUNK_W) }
newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed
newIngame.worldDisk = VDUtil.readDiskArchive(worldDisk.diskFile, Level.INFO)
newIngame.playerDisk = VDUtil.readDiskArchive(playerDisk.diskFile, Level.INFO)
newIngame.worldName = getWorldName(worldDisk)
newIngame.worldSavefileName = getWorldSavefileName(world)
newIngame.playerSavefileName = getPlayerSavefileName(player)
// worldDisk.dispose()
// playerDisk.dispose()
val loadJob = { it: LoadScreenBase ->
val loadscreen = it as FancyWorldReadLoadScreen
loadscreen.addMessage(Lang["MENU_IO_LOADING"])
val worldGenver = CharArray(128).let {
worldDiskSavegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
val playerGenver = CharArray(128).let {
playerDiskSavegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
val actors = world.actors.distinct()
val worldParam =
TerrarumIngame.Codices(newIngame.worldDisk, world, actors, player, worldGenver, playerGenver) {}
newIngame.gameLoadInfoPayload = worldParam
newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM
App.printdbg(
this,
"World dim: ${world.width}x${world.height}, ${world.width / LandUtil.CHUNK_W}x${world.height / LandUtil.CHUNK_H}"
)
// load all the world blocklayer chunks
val cw = LandUtil.CHUNK_W
val ch = LandUtil.CHUNK_H
val chunkCount = world.width.toLong() * world.height / (cw * ch)
val worldLayer = intArrayOf(GameWorld.TERRAIN, GameWorld.WALL, GameWorld.ORES, GameWorld.FLUID).map { world.getLayer(it) }
for (chunk in 0L until chunkCount) {
for (layer in worldLayer.indices) {
loadscreen.addMessage(Lang["MENU_IO_LOADING"])
newIngame.worldDisk.getFile(Common.layerAndChunkNumToEntryID(layer, chunk))?.let { chunkFile ->
val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk)
ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy)
world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or GameWorld.CHUNK_LOADED
}
}
loadscreen.progress.getAndAdd(1)
}
loadscreen.addMessage(Lang["MENU_IO_LOAD_UPDATING_BLOCK_MAPPINGS"])
world.renumberTilesAfterLoad()
Echo("${ccW}World loaded: $ccY${newIngame.worldDisk.getDiskName(Common.CHARSET)}")
App.printdbg(this, "World loaded: ${newIngame.worldDisk.getDiskName(Common.CHARSET)}")
}
val loadScreen = FancyWorldReadLoadScreen(newIngame, world.width, world.height, loadJob)
Terrarum.setCurrentIngameInstance(newIngame)
App.setLoadScreen(loadScreen)
}
}

View File

@@ -136,13 +136,13 @@ class WorldSavingThread(
for (cx in 0 until cw) {
for (cy in 0 until ch) {
val chunkFlag = ingame.world.chunkFlags[cy][cx]
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong()
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy)
if (chunkFlag and 0x7F == CHUNK_LOADED) {
// Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}")
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
val entryID = Common.layerAndChunkNumToEntryID(layer, chunkNumber)
val entryContent = EntryFile(chunkBytes)
val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent)

View File

@@ -1,32 +1,12 @@
package net.torvald.terrarum.modulebasegame.serialise
import com.badlogic.gdx.graphics.Pixmap
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.gameworld.BlockLayerI16
import net.torvald.terrarum.gameworld.BlockLayerI16F16
import net.torvald.terrarum.gameworld.BlockLayerOresI16I8
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.GameWorld.Companion.CHUNK_LOADED
import net.torvald.terrarum.gameworld.GameWorld.Companion.FLUID
import net.torvald.terrarum.gameworld.GameWorld.Companion.ORES
import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN
import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.FancyWorldReadLoadScreen
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.worlddrawer.WorldCamera
import java.io.File
import java.io.Reader
import java.util.logging.Level
import kotlin.experimental.or
/**
* It's your responsibility to create a new VirtualDisk if your save is new, and create a backup for modifying existing save.
@@ -106,123 +86,3 @@ object WriteSavegame {
}
}
/**
* Load and setup the game for the first load.
*
* To load additional actors/worlds, use ReadActor/ReadWorld.
*
* Created by minjaesong on 2021-09-03.
*/
object LoadSavegame {
fun getWorldName(worldDisk: SimpleFileSystem) = worldDisk.getDiskName(Common.CHARSET)
fun getWorldSavefileName(world: GameWorld) = "${world.worldIndex}"
fun getPlayerSavefileName(player: IngamePlayer) = "${player.uuid}"
fun getFileBytes(disk: SimpleFileSystem, id: Long): ByteArray64 = disk.getFile(id)!!.bytes
fun getFileReader(disk: SimpleFileSystem, id: Long): Reader = ByteArray64Reader(getFileBytes(disk, id), Common.CHARSET)
operator fun invoke(diskPair: DiskPair) = invoke(diskPair.player, diskPair.world)
private val getGenver = Regex("""(?<="genver" ?: ?)[0-9]+""")
/**
* @param playerDisk DiskSkimmer representing the Player.
* @param worldDisk0 DiskSkimmer representing the World to be loaded.
* If unset, last played world for the Player will be loaded.
*/
operator fun invoke(playerDisk: DiskSkimmer, worldDisk0: DiskSkimmer? = null) {
val newIngame = TerrarumIngame(App.batch)
playerDisk.rebuild()
val playerDiskSavegameInfo = ByteArray64Reader(playerDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val player = ReadActor.invoke(playerDisk, playerDiskSavegameInfo) as IngamePlayer
printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}")
val currentWorldId = player.worldCurrentlyPlaying
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable()
worldDisk.rebuild()
val worldDiskSavegameInfo = ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)
val world = ReadWorld(worldDiskSavegameInfo, worldDisk.diskFile)
world.layerTerrain = BlockLayerI16(world.width, world.height)
world.layerWall = BlockLayerI16(world.width, world.height)
world.layerOres = BlockLayerOresI16I8(world.width, world.height)
world.layerFluids = BlockLayerI16F16(world.width, world.height)
world.chunkFlags = Array(world.height / LandUtil.CHUNK_H) { ByteArray(world.width / LandUtil.CHUNK_W) }
newIngame.world = world // must be set before the loadscreen, otherwise the loadscreen will try to read from the NullWorld which is already destroyed
newIngame.worldDisk = VDUtil.readDiskArchive(worldDisk.diskFile, Level.INFO)
newIngame.playerDisk = VDUtil.readDiskArchive(playerDisk.diskFile, Level.INFO)
newIngame.worldName = getWorldName(worldDisk)
newIngame.worldSavefileName = getWorldSavefileName(world)
newIngame.playerSavefileName = getPlayerSavefileName(player)
// worldDisk.dispose()
// playerDisk.dispose()
val loadJob = { it: LoadScreenBase ->
val loadscreen = it as FancyWorldReadLoadScreen
loadscreen.addMessage(Lang["MENU_IO_LOADING"])
val worldGenver = CharArray(128).let {
worldDiskSavegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
val playerGenver = CharArray(128).let {
playerDiskSavegameInfo.read(it, 0, 128)
getGenver.find(String(it))?.value?.toLong()!!
}
val actors = world.actors.distinct()
val worldParam = TerrarumIngame.Codices(newIngame.worldDisk, world, actors, player, worldGenver, playerGenver) {}
newIngame.gameLoadInfoPayload = worldParam
newIngame.gameLoadMode = TerrarumIngame.GameLoadMode.LOAD_FROM
printdbg(this, "World dim: ${world.width}x${world.height}, ${world.width / LandUtil.CHUNK_W}x${world.height / LandUtil.CHUNK_H}")
// load all the world blocklayer chunks
val cw = LandUtil.CHUNK_W
val ch = LandUtil.CHUNK_H
val chunkCount = world.width * world.height / (cw * ch)
val worldLayer = intArrayOf(TERRAIN, WALL, ORES, FLUID).map { world.getLayer(it) }
for (chunk in 0L until chunkCount) {
for (layer in worldLayer.indices) {
loadscreen.addMessage(Lang["MENU_IO_LOADING"])
newIngame.worldDisk.getFile(0x1_0000_0000L or layer.toLong().shl(24) or chunk)?.let { chunkFile ->
val (cx, cy) = LandUtil.chunkNumToChunkXY(world, chunk.toInt())
ReadWorld.decodeChunkToLayer(chunkFile.getContent(), worldLayer[layer]!!, cx, cy)
world.chunkFlags[cy][cx] = world.chunkFlags[cy][cx] or CHUNK_LOADED
}
}
loadscreen.progress.getAndAdd(1)
}
loadscreen.addMessage(Lang["MENU_IO_LOAD_UPDATING_BLOCK_MAPPINGS"])
world.renumberTilesAfterLoad()
Echo("${ccW}World loaded: $ccY${newIngame.worldDisk.getDiskName(Common.CHARSET)}")
printdbg(this, "World loaded: ${newIngame.worldDisk.getDiskName(Common.CHARSET)}")
}
val loadScreen = FancyWorldReadLoadScreen(newIngame, world.width, world.height, loadJob)
Terrarum.setCurrentIngameInstance(newIngame)
App.setLoadScreen(loadScreen)
}
}