mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
thumb generation for player saves
This commit is contained in:
@@ -14,6 +14,7 @@ import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.toInt
|
||||
import net.torvald.terrarum.savegame.*
|
||||
import net.torvald.terrarum.savegame.VDFileID.LOADORDER
|
||||
import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT
|
||||
import net.torvald.terrarum.savegame.VDFileID.ROOT
|
||||
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
||||
import net.torvald.terrarum.savegame.VDFileID.THUMBNAIL
|
||||
@@ -53,7 +54,6 @@ class WorldSavingThread(
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val hasThumbnail: Boolean,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
@@ -64,10 +64,8 @@ class WorldSavingThread(
|
||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||
disk.saveKind = VDSaveKind.WORLD_DATA
|
||||
|
||||
if (hasThumbnail) {
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
|
||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||
@@ -123,14 +121,14 @@ class WorldSavingThread(
|
||||
|
||||
|
||||
|
||||
if (hasThumbnail) {
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
}
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
@@ -199,7 +197,7 @@ class WorldSavingThread(
|
||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||
|
||||
|
||||
if (hasThumbnail) IngameRenderer.fboRGBexportedLatch = false
|
||||
IngameRenderer.fboRGBexportedLatch = false
|
||||
WriteSavegame.savingStatus = 255
|
||||
|
||||
|
||||
@@ -217,7 +215,6 @@ class PlayerSavingThread(
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val hasThumbnail: Boolean,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
@@ -230,11 +227,30 @@ class PlayerSavingThread(
|
||||
|
||||
WriteSavegame.saveProgress = 0f
|
||||
|
||||
// wait for screencap
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
|
||||
// write screencap
|
||||
val tgaout = ByteArray64GrowableOutputStream()
|
||||
val gzout = GZIPOutputStream(tgaout)
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(PLAYER_SCREENSHOT, ROOT, ingame.world.creationTime, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
|
||||
|
||||
|
||||
printdbg(this, "Writing The Player...")
|
||||
WritePlayer(ingame.actorGamer, disk, ingame, time_t)
|
||||
disk.entries[0]!!.modificationDate = time_t
|
||||
VDUtil.dumpToRealMachine(disk, outFile)
|
||||
|
||||
|
||||
IngameRenderer.fboRGBexportedLatch = false
|
||||
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ class QuickSingleplayerWorldSavingThread(
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val hasThumbnail: Boolean,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
@@ -46,14 +45,10 @@ class QuickSingleplayerWorldSavingThread(
|
||||
|
||||
|
||||
override fun save() {
|
||||
printdbg(this, "Quicksaveworld has thumbnail: $hasThumbnail")
|
||||
|
||||
val skimmer = DiskSkimmer(outFile)
|
||||
|
||||
if (hasThumbnail) {
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
|
||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||
@@ -78,14 +73,14 @@ class QuickSingleplayerWorldSavingThread(
|
||||
val creation_t = ingame.world.creationTime
|
||||
|
||||
|
||||
if (hasThumbnail) {
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
}
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
@@ -152,7 +147,7 @@ class QuickSingleplayerWorldSavingThread(
|
||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||
|
||||
|
||||
if (hasThumbnail) IngameRenderer.fboRGBexportedLatch = false
|
||||
IngameRenderer.fboRGBexportedLatch = false
|
||||
WriteSavegame.savingStatus = 255
|
||||
ingame.clearModifiedChunks()
|
||||
|
||||
|
||||
@@ -36,43 +36,40 @@ object WriteSavegame {
|
||||
@Volatile var saveProgress = 0f
|
||||
@Volatile var saveProgressMax = 1f
|
||||
|
||||
private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) = when (mode) {
|
||||
SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler)
|
||||
SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler)
|
||||
SaveMode.QUICK_WORLD -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler)
|
||||
private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) = when (mode) {
|
||||
SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler)
|
||||
SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler)
|
||||
SaveMode.QUICK_WORLD -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler)
|
||||
else -> throw IllegalArgumentException("$mode")
|
||||
}
|
||||
|
||||
operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
||||
savingStatus = 0
|
||||
val hasThumbnail = (mode == SaveMode.WORLD || mode == SaveMode.QUICK_WORLD)
|
||||
printdbg(this, "Save queued")
|
||||
|
||||
if (hasThumbnail) {
|
||||
IngameRenderer.screencapExportCallback = { fb ->
|
||||
printdbg(this, "Generating thumbnail...")
|
||||
IngameRenderer.screencapExportCallback = { fb ->
|
||||
printdbg(this, "Generating thumbnail...")
|
||||
|
||||
val w = 960
|
||||
val h = 640
|
||||
val w = 960
|
||||
val h = 640
|
||||
|
||||
val cx = /*1-*/(WorldCamera.x % 2)
|
||||
val cy = /*1-*/(WorldCamera.y % 2)
|
||||
val cx = /*1-*/(WorldCamera.x % 2)
|
||||
val cy = /*1-*/(WorldCamera.y % 2)
|
||||
|
||||
val x = (fb.width - w) / 2 - cx // force the even-numbered position
|
||||
val y = (fb.height - h) / 2 - cy // force the even-numbered position
|
||||
val x = (fb.width - w) / 2 - cx // force the even-numbered position
|
||||
val y = (fb.height - h) / 2 - cy // force the even-numbered position
|
||||
|
||||
val p = Pixmap.createFromFrameBuffer(x, y, w, h)
|
||||
IngameRenderer.fboRGBexport = p
|
||||
//PixmapIO2._writeTGA(gzout, p, true, true)
|
||||
//p.dispose()
|
||||
IngameRenderer.fboRGBexportedLatch = true
|
||||
val p = Pixmap.createFromFrameBuffer(x, y, w, h)
|
||||
IngameRenderer.fboRGBexport = p
|
||||
//PixmapIO2._writeTGA(gzout, p, true, true)
|
||||
//p.dispose()
|
||||
IngameRenderer.fboRGBexportedLatch = true
|
||||
|
||||
printdbg(this, "Done thumbnail generation")
|
||||
}
|
||||
IngameRenderer.screencapRequested = true
|
||||
printdbg(this, "Done thumbnail generation")
|
||||
}
|
||||
IngameRenderer.screencapRequested = true
|
||||
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, hasThumbnail, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
savingThread.start()
|
||||
|
||||
// it is caller's job to keep the game paused or keep a "save in progress" ui up
|
||||
@@ -86,7 +83,7 @@ object WriteSavegame {
|
||||
|
||||
printdbg(this, "Immediate save fired")
|
||||
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, false, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
savingThread.start()
|
||||
|
||||
// it is caller's job to keep the game paused or keep a "save in progress" ui up
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.savegame.ByteArray64InputStream
|
||||
import net.torvald.terrarum.savegame.EntryFile
|
||||
import net.torvald.terrarum.modulebasegame.serialise.LoadSavegame
|
||||
import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT
|
||||
import net.torvald.terrarum.ui.*
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import java.time.Instant
|
||||
@@ -116,7 +117,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
||||
private val disposablePool = ArrayList<Disposable>()
|
||||
|
||||
private fun DiskPair.getThumbnail(): TextureRegion {
|
||||
return this.world.requestFile(-2).let { file ->
|
||||
return this.player.requestFile(PLAYER_SCREENSHOT).let { file ->
|
||||
CommonResourcePool.getAsTextureRegion("terrarum-defaultsavegamethumb")
|
||||
|
||||
if (file != null) {
|
||||
|
||||
@@ -503,8 +503,8 @@ object VDUtil {
|
||||
* Throws an exception if specified size cannot fit into the disk
|
||||
*/
|
||||
fun VirtualDisk.checkCapacity(newSize: Long) {
|
||||
if (this.usedBytes + newSize > this.capacity)
|
||||
throw IOException("Not enough space on the disk")
|
||||
// if (this.usedBytes + newSize > this.capacity)
|
||||
// throw IOException("Not enough space on the disk")
|
||||
}
|
||||
fun ByteArray64.toIntBig(): Int {
|
||||
if (this.size != 4L)
|
||||
|
||||
@@ -145,9 +145,7 @@ class VirtualDisk(
|
||||
|
||||
var extraInfoBytes = ByteArray(16)
|
||||
val entries = HashMap<EntryID, DiskEntry>()
|
||||
var isReadOnly: Boolean
|
||||
set(value) { extraInfoBytes[0] = (extraInfoBytes[0] and 0xFE.toByte()) or value.toBit() }
|
||||
get() = capacity == 0L || (extraInfoBytes.size > 0 && extraInfoBytes[0].and(1) == 1.toByte())
|
||||
val isReadOnly = false
|
||||
var saveMode: Int
|
||||
set(value) { extraInfoBytes[1] = value.toByte() }
|
||||
get() = extraInfoBytes[1].toUint()
|
||||
@@ -260,6 +258,7 @@ object VDFileID {
|
||||
const val SPRITEDEF = -2L
|
||||
const val SPRITEDEF_GLOW = -3L
|
||||
const val LOADORDER = -4L
|
||||
const val PLAYER_SCREENSHOT = -5L
|
||||
const val BODYPART_TO_ENTRY_MAP = -1025L
|
||||
const val BODYPARTGLOW_TO_ENTRY_MAP = -1026L
|
||||
}
|
||||
@@ -279,6 +278,11 @@ fun diskIDtoReadableFilename(id: EntryID, saveKind: Int?): String = when (id) {
|
||||
"spritedef-glow"
|
||||
else
|
||||
"file #$id"
|
||||
VDFileID.PLAYER_SCREENSHOT ->
|
||||
if (saveKind == PLAYER_DATA)
|
||||
"screenshot.tga.gz"
|
||||
else
|
||||
"file #$id"
|
||||
VDFileID.LOADORDER -> "loadOrder.txt"
|
||||
// -16L -> "blockcodex.json.gz"
|
||||
// -17L -> "itemcodex.json.gz"
|
||||
|
||||
@@ -415,41 +415,6 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() {
|
||||
}
|
||||
}
|
||||
})
|
||||
menuEdit.add("Resize Disk…").addMouseListener(object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent?) {
|
||||
if (vdisk != null) {
|
||||
try {
|
||||
val dialog = OptionSize()
|
||||
val confirmed = dialog.showDialog("Input") == JOptionPane.OK_OPTION
|
||||
if (confirmed) {
|
||||
vdisk!!.capacity = (dialog.capacity.value as Long).toLong()
|
||||
updateDiskInfo()
|
||||
setStat("Disk resized")
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
popupError(e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
menuEdit.addSeparator()
|
||||
menuEdit.add("Set/Unset Write Protection").addMouseListener(object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent?) {
|
||||
if (vdisk != null) {
|
||||
try {
|
||||
vdisk!!.isReadOnly = vdisk!!.isReadOnly.not()
|
||||
updateDiskInfo()
|
||||
setStat("Disk write protection ${if (vdisk!!.isReadOnly) "" else "dis"}engaged")
|
||||
}
|
||||
catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
popupError(e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
menuBar.add(menuEdit)
|
||||
|
||||
val menuManage = JMenu("Manage")
|
||||
@@ -638,8 +603,7 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() {
|
||||
}
|
||||
private fun getDiskInfoText(disk: VirtualDisk): String {
|
||||
return """Name: ${String(disk.diskName, sysCharset)}
|
||||
Capacity: ${disk.capacity} bytes (${disk.usedBytes} bytes used, ${disk.capacity - disk.usedBytes} bytes free)
|
||||
Write protected: ${disk.isReadOnly.toEnglish()}"""
|
||||
Capacity: ${disk.capacity} bytes (${disk.usedBytes} bytes used, ${disk.capacity - disk.usedBytes} bytes free)"""
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ The main game directory is composed of following directories:
|
||||
[-2] spritedef,
|
||||
[-3] !optional! spritedef-glow,
|
||||
[-4] loadOrder.txt
|
||||
[-5] screenshot.tga.gz
|
||||
[-1025] !optional! sprite-bodypart-name-to-entry-number-map.properties,
|
||||
[-1026] !optional! spriteglow-bodypart-name-to-entry-number-map.properties,
|
||||
[1+] !optional! bodyparts tga.gz
|
||||
|
||||
Reference in New Issue
Block a user