mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-08 01:24:04 +09:00
163 lines
5.7 KiB
Kotlin
163 lines
5.7 KiB
Kotlin
package net.torvald.terrarum.modulebasegame.serialise
|
|
|
|
import net.torvald.gdx.graphics.PixmapIO2
|
|
import net.torvald.terrarum.App.printdbg
|
|
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.ROOT
|
|
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
|
import net.torvald.terrarum.savegame.VDFileID.WORLD_SCREENSHOT
|
|
import net.torvald.terrarum.toInt
|
|
import net.torvald.terrarum.utils.PlayerLastStatus
|
|
import java.io.File
|
|
import java.util.zip.GZIPOutputStream
|
|
|
|
/**
|
|
* Created by minjaesong on 2021-09-29.
|
|
*/
|
|
class QuickSingleplayerWorldSavingThread(
|
|
val time_t: Long,
|
|
val disk: VirtualDisk,
|
|
val outFile: File,
|
|
val ingame: TerrarumIngame,
|
|
val isAuto: Boolean,
|
|
val callback: () -> Unit,
|
|
val errorHandler: (Throwable) -> Unit
|
|
) : SavingThread(errorHandler) {
|
|
|
|
/**
|
|
* Will happily overwrite existing entry
|
|
*/
|
|
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
|
disk.entries[file.entryID] = file
|
|
file.parentEntryID = 0
|
|
val dir = VDUtil.getAsDirectory(disk, 0)
|
|
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
|
}
|
|
|
|
|
|
private val chunkProgressMultiplier = 1f
|
|
private val actorProgressMultiplier = 1f
|
|
|
|
|
|
override fun save() {
|
|
printdbg(this, "outFile: ${outFile.path}")
|
|
|
|
val skimmer = DiskSkimmer(outFile)
|
|
|
|
// wait for screencap
|
|
var emergencyStopCnt = 0
|
|
while (IngameRenderer.screencapBusy) {
|
|
// printdbg(this, "spinning for screencap to be taken")
|
|
Thread.sleep(4L)
|
|
emergencyStopCnt += 1
|
|
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
|
}
|
|
|
|
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 chunkCount = chunks.map { it.size }.sum()
|
|
|
|
WriteSavegame.saveProgress = 0f
|
|
WriteSavegame.saveProgressMax = 2f +
|
|
(chunkCount) * chunkProgressMultiplier +
|
|
actorsList.size * actorProgressMultiplier
|
|
|
|
|
|
val tgaout = ByteArray64GrowableOutputStream()
|
|
val gzout = GZIPOutputStream(tgaout)
|
|
|
|
printdbg(this, "Writing metadata...")
|
|
|
|
val creation_t = ingame.world.creationTime
|
|
|
|
|
|
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
|
IngameRenderer.fboRGBexport.dispose()
|
|
|
|
val thumbContent = EntryFile(tgaout.toByteArray64())
|
|
val thumb = DiskEntry(WORLD_SCREENSHOT, ROOT, creation_t, time_t, thumbContent)
|
|
addFile(disk, thumb)
|
|
|
|
|
|
|
|
WriteSavegame.saveProgress += 1f
|
|
|
|
// Write World //
|
|
// 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, actorsList, playersList))
|
|
val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta)
|
|
addFile(disk, world); skimmer.appendEntry(world)
|
|
|
|
WriteSavegame.saveProgress += 1f
|
|
|
|
var chunksWrote = 1
|
|
chunks.forEachIndexed { layerNum, chunks ->
|
|
|
|
if (chunks.size != 0) {
|
|
ingame.world.getLayer(layerNum)?.let { layer ->
|
|
chunks.forEach { chunkNumber ->
|
|
|
|
// Echo("Writing chunks... $chunksWrote/$chunkCount")
|
|
|
|
val chunkXY = LandUtil.chunkNumToChunkXY(ingame.world, chunkNumber)
|
|
|
|
// println("Chunk xy from number $chunkNumber -> (${chunkXY.x}, ${chunkXY.y})")
|
|
|
|
val chunkBytes = WriteWorld.encodeChunk(layer, chunkXY.x, chunkXY.y)
|
|
val entryID = 0x1_0000_0000L or layerNum.toLong().shl(24) or chunkNumber.toLong()
|
|
|
|
val entryContent = EntryFile(chunkBytes)
|
|
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
|
|
// "W1L0-92,15"
|
|
addFile(disk, entry); skimmer.appendEntry(entry)
|
|
|
|
WriteSavegame.saveProgress += chunkProgressMultiplier
|
|
chunksWrote += 1
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Write Actors //
|
|
actorsList.forEachIndexed { count, it ->
|
|
printdbg(this, "Writing actors... ${count+1}/${actorsList.size} (${it.javaClass.canonicalName})")
|
|
|
|
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
|
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)
|
|
addFile(disk, actor); skimmer.appendEntry(actor)
|
|
|
|
WriteSavegame.saveProgress += actorProgressMultiplier
|
|
}
|
|
|
|
|
|
disk.saveKind = VDSaveKind.WORLD_DATA
|
|
|
|
// skimmer.rewriteDirectories()
|
|
skimmer.injectDiskCRC(disk.hashCode())
|
|
skimmer.setSaveMode(1 + 2 * isAuto.toInt())
|
|
skimmer.setSaveKind(VDSaveKind.WORLD_DATA)
|
|
|
|
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
|
|
|
|
|
// IngameRenderer.screencapBusy = false
|
|
WriteSavegame.savingStatus = 255
|
|
ingame.clearModifiedChunks()
|
|
|
|
callback()
|
|
}
|
|
|
|
} |