From c5874a7f3d3283e8dde0f15b13de431e1da2bea9 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 27 Jun 2023 21:13:51 +0900 Subject: [PATCH] finally working again: create new character todo: make delete character work --- src/net/torvald/terrarum/DefaultConfig.kt | 2 +- .../torvald/terrarum/SavegameCollection.kt | 55 ++-- .../terrarum/console/ScreencapNogui.kt | 2 +- .../terrarum/modulebasegame/IngameRenderer.kt | 20 +- .../terrarum/modulebasegame/TerrarumIngame.kt | 7 +- .../serialise/GameSavingThread.kt | 240 +----------------- .../serialise/PlayerSavingThread.kt | 77 ++++++ .../serialise/QuickSaveThread.kt | 13 +- .../serialise/WorldSavingThread.kt | 189 ++++++++++++++ .../modulebasegame/serialise/WriteSavegame.kt | 24 +- .../modulebasegame/ui/UILoadSavegame.kt | 53 ++-- .../modulebasegame/ui/UINewCharacter.kt | 4 + .../terrarum/modulebasegame/ui/UINewWorld.kt | 3 +- src/net/torvald/terrarum/ui/Toolkit.kt | 2 + .../torvald/terrarum/ui/UIAutosaveNotifier.kt | 2 +- .../terrarum/ui/UIItemTextLineInput.kt | 4 +- 16 files changed, 393 insertions(+), 304 deletions(-) create mode 100644 src/net/torvald/terrarum/modulebasegame/serialise/PlayerSavingThread.kt create mode 100644 src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt diff --git a/src/net/torvald/terrarum/DefaultConfig.kt b/src/net/torvald/terrarum/DefaultConfig.kt index 0a6b08cbe..a272feb07 100644 --- a/src/net/torvald/terrarum/DefaultConfig.kt +++ b/src/net/torvald/terrarum/DefaultConfig.kt @@ -10,7 +10,7 @@ import com.badlogic.gdx.Input object DefaultConfig { val hashMap = hashMapOf( - "jvm_xmx" to 8, + "jvm_xmx" to 4, "jvm_extra_cmd" to "", "displayfps" to 0, // 0: no limit, non-zero: limit "displayfpsidle" to 0, // 0: no limit, non-zero: limit diff --git a/src/net/torvald/terrarum/SavegameCollection.kt b/src/net/torvald/terrarum/SavegameCollection.kt index 24fc8248a..303e262d7 100644 --- a/src/net/torvald/terrarum/SavegameCollection.kt +++ b/src/net/torvald/terrarum/SavegameCollection.kt @@ -39,10 +39,10 @@ class SavegameCollection(files0: List) { class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollection?) { - private lateinit var manualPlayer: DiskSkimmer - private lateinit var manualWorld: DiskSkimmer - private lateinit var autoPlayer: DiskSkimmer - private lateinit var autoWorld: DiskSkimmer + private var manualPlayer: DiskSkimmer? = null + private var manualWorld: DiskSkimmer? = null + private var autoPlayer: DiskSkimmer? = null + private var autoWorld: DiskSkimmer? = null var status = 0 // 0: none available, 1: loadable manual save is newer than loadable auto; 2: loadable autosave is newer than loadable manual private set @@ -55,8 +55,8 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect if (player != null && world != null) { - printdbg(this, player.files.joinToString { it.diskFile.name }) - printdbg(this, world.files.joinToString { it.diskFile.name }) + printdbg(this, "player files: " + player.files.joinToString { it.diskFile.name }) + printdbg(this, "world files:" + world.files.joinToString { it.diskFile.name }) // if a pair of files were saved successfully, they must have identical lastModifiedTime() var pc = 0; val pt = player.files[0].getLastModifiedTime() @@ -75,7 +75,7 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect when (pcf.isAutosaved().toInt(1) or wcf.isAutosaved().toInt()) { 3 -> { - if (!::autoPlayer.isInitialized && !::autoWorld.isInitialized) { + if (autoPlayer == null && autoWorld == null) { autoPlayer = pcf autoWorld = wcf } @@ -83,7 +83,7 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect wc += 1 } 0 -> { - if (!::manualPlayer.isInitialized && !::manualWorld.isInitialized) { + if (manualPlayer == null && manualWorld == null) { manualPlayer = pcf manualWorld = wcf } @@ -105,32 +105,25 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect - if (::manualPlayer.isInitialized && ::manualWorld.isInitialized && ::autoPlayer.isInitialized && ::autoWorld.isInitialized) + if (manualPlayer != null && manualWorld != null && autoPlayer != null && autoWorld != null) break } - if (::manualPlayer.isInitialized && ::manualWorld.isInitialized && ::autoPlayer.isInitialized && ::autoWorld.isInitialized) { - status = if (manualPlayer.getLastModifiedTime() > autoPlayer.getLastModifiedTime()) 1 else 2 - - printdbg(this, "manualPlayer = ${manualPlayer.diskFile.path}") - printdbg(this, "manualWorld = ${manualWorld.diskFile.path}") - printdbg(this, "autoPlayer = ${autoPlayer.diskFile.path}") - printdbg(this, "autoWorld = ${autoWorld.diskFile.path}") + if (manualPlayer != null && manualWorld != null && autoPlayer != null && autoWorld != null) { + status = if (manualPlayer!!.getLastModifiedTime() > autoPlayer!!.getLastModifiedTime()) 1 else 2 } - else if (::manualPlayer.isInitialized && ::manualWorld.isInitialized || ::autoPlayer.isInitialized && ::autoWorld.isInitialized) { + else if (manualPlayer != null && manualWorld != null || autoPlayer != null && autoWorld != null) { status = 1 - if (::manualPlayer.isInitialized) { - printdbg(this, "manualPlayer = ${manualPlayer.diskFile.path}") - printdbg(this, "manualWorld = ${manualWorld.diskFile.path}") - } - else { - printdbg(this, "autoPlayer = ${autoPlayer.diskFile.path}") - printdbg(this, "autoWorld = ${autoWorld.diskFile.path}") - } } else { status = 0 } + + printdbg(this, "manualPlayer = ${manualPlayer?.diskFile?.path}") + printdbg(this, "manualWorld = ${manualWorld?.diskFile?.path}") + printdbg(this, "autoPlayer = ${autoPlayer?.diskFile?.path}") + printdbg(this, "autoWorld = ${autoWorld?.diskFile?.path}") + printdbg(this, "status = $status") } } @@ -149,12 +142,20 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect fun getManualSave(): DiskPair? { if (status == 0) return null - return DiskPair(manualPlayer, manualWorld) + return DiskPair(manualPlayer!!, manualWorld!!) } fun getAutoSave(): DiskPair? { if (status != 2) return null - return DiskPair(autoPlayer, autoWorld) + return DiskPair(autoPlayer!!, autoWorld!!) + } + + fun getLoadableSave(): DiskPair? { + if (status == 0) return null + return if (manualPlayer != null && manualWorld != null) + DiskPair(manualPlayer!!, manualWorld!!) + else + DiskPair(autoPlayer!!, autoWorld!!) } } diff --git a/src/net/torvald/terrarum/console/ScreencapNogui.kt b/src/net/torvald/terrarum/console/ScreencapNogui.kt index 090e6894c..8b2f4d45a 100644 --- a/src/net/torvald/terrarum/console/ScreencapNogui.kt +++ b/src/net/torvald/terrarum/console/ScreencapNogui.kt @@ -16,7 +16,7 @@ object ScreencapNogui: ConsoleCommand { PixmapIO2.writeTGA(Gdx.files.absolute(App.defaultDir + "/Exports/${args[1]}.tga"), p, true) p.dispose() } - IngameRenderer.screencapRequested = true + IngameRenderer.requestScreencap() Echo("FBO exported to$ccG Exports/${args[1]}.tga") } else { diff --git a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt index c232a967c..fccf2f916 100644 --- a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt +++ b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt @@ -11,7 +11,7 @@ import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.GdxRuntimeException import net.torvald.random.HQRNG import net.torvald.terrarum.* -import net.torvald.terrarum.App.measureDebugTime +import net.torvald.terrarum.App.* import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF import net.torvald.terrarum.gameactors.ActorWithBody @@ -21,6 +21,7 @@ import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.weather.WeatherMixer +import net.torvald.terrarum.weather.WeatherMixer.render import net.torvald.terrarum.worlddrawer.BlocksDrawer import net.torvald.terrarum.worlddrawer.FeaturesDrawer import net.torvald.terrarum.worlddrawer.LightmapRenderer @@ -365,13 +366,19 @@ object IngameRenderer : Disposable { /////////////////////////////////////////////////////////////////////// if (screencapRequested) { - screencapRequested = false + printdbg(this, "Screencap was requested, processing...") + var hasError = false try { screencapExportCallback(fboMixedOut) } catch (e: Throwable) { + printdbgerr(this, "An error occured while taking screencap:") e.printStackTrace() + hasError = true } + printdbg(this, "Screencap ${if (hasError) "failed" else "successful"}") + screencapBusy = false + screencapRequested = false } /////////////////////////////////////////////////////////////////////// @@ -416,11 +423,16 @@ object IngameRenderer : Disposable { * This "screencap" will capture the game WITHOUT gui and postprocessors! * To capture the entire game, use [App.requestScreenshot] */ - @Volatile internal var screencapRequested = false - @Volatile internal var fboRGBexportedLatch = false + @Volatile private var screencapRequested = false + @Volatile internal var screencapBusy = false; private set @Volatile internal var screencapExportCallback: (FrameBuffer) -> Unit = {} @Volatile internal lateinit var fboRGBexport: Pixmap + fun requestScreencap() { + screencapRequested = true + screencapBusy = true + } + private fun drawToRGB( actorsRenderBehind: List?, actorsRenderMiddle: List?, diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 79a0439fb..34e6f7812 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -420,10 +420,15 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { // 2. cannot sync up the "counter" to determine whether both are finished uiAutosaveNotifier.setAsOpen() val saveTime_t = App.getTIME_T() + printdbg(this, "Immediate Save") WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, true, autosaveOnErrorAction) { + printdbg(this, "immediate save callback from PLAYER") + makeSavegameBackupCopy(getPlayerSaveFiledesc(playerSavefileName)) WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.WORLD, worldDisk, getWorldSaveFiledesc(worldSavefileName), this, true, autosaveOnErrorAction) { + printdbg(this, "immediate save callback from WORLD") + makeSavegameBackupCopy(getWorldSaveFiledesc(worldSavefileName)) // don't put it on the postInit() or render(); must be called using callback uiAutosaveNotifier.setAsClose() } @@ -472,7 +477,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { world.worldCreator = UUID.fromString(player.uuid.toString()) - printdbg(this, "new woridIndex: ${world.worldIndex}") + printdbg(this, "new worldIndex: ${world.worldIndex}") printdbg(this, "worldCurrentlyPlaying: ${player.worldCurrentlyPlaying}") actorNowPlaying = player diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt index 7507f66dd..9b4c372ee 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/GameSavingThread.kt @@ -1,36 +1,5 @@ package net.torvald.terrarum.modulebasegame.serialise -import net.torvald.gdx.graphics.PixmapIO2 -import net.torvald.terrarum.App.printdbg -import net.torvald.terrarum.ItemCodex -import net.torvald.terrarum.ModMgr -import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM -import net.torvald.terrarum.modulebasegame.IngameRenderer -import net.torvald.terrarum.modulebasegame.TerrarumIngame -import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase -import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer -import net.torvald.terrarum.modulebasegame.gameactors.Pocketed -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 -import net.torvald.terrarum.serialise.Common -import java.io.File -import java.util.zip.GZIPOutputStream - -/** - * 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) -} abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Runnable { abstract fun save() @@ -46,211 +15,4 @@ abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Run } } -/** - * Created by minjaesong on 2021-09-14. - */ -class WorldSavingThread( - 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) { - - override fun save() { - - disk.saveMode = 2 * isAuto.toInt() // no quick - disk.saveKind = VDSaveKind.WORLD_DATA - - while (!IngameRenderer.fboRGBexportedLatch) { - Thread.sleep(1L) - } - - val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList() - - val playersList: List = allTheActors.filterIsInstance() - val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) } - val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) } - val cw = ingame.world.width / LandUtil.CHUNK_W - val ch = ingame.world.height / LandUtil.CHUNK_H - - WriteSavegame.saveProgress = 0f - WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size - - - val tgaout = ByteArray64GrowableOutputStream() - val gzout = GZIPOutputStream(tgaout) - - printdbg(this, "Writing metadata...") - - val creation_t = ingame.world.creationTime - - - // Write subset of Ingame.ItemCodex - // The existing ItemCodex must be rewritten to clear out obsolete records - - // We're assuming the dynamic item generated by players does exist in the world, and it's recorded - // into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable - // can be found in this world without need to look up the players - ingame.world.dynamicToStaticTable.clear() - ingame.world.dynamicItemInventory.clear() - actorsList.filterIsInstance().forEach { actor -> - actor.inventory.forEach { (itemid, _) -> - - printdbg(this, "World side dynamicitem: $itemid contained in $actor") - - if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) { - ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid) - ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!! - } - } - } - actorsList.filterIsInstance().forEach { fixture -> - fixture.inventory?.forEach { (itemid, _) -> - - printdbg(this, "World side dynamicitem: $itemid contained in $fixture") - - if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) { - ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid) - ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!! - } - } - } - - - - 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) - - - - WriteSavegame.saveProgress += 1f - - // Write World // - - val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList)) - val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta) - addFile(disk, world) - - WriteSavegame.saveProgress += 1f - - - for (layer in layers.indices) { - for (cx in 0 until cw) { - for (cy in 0 until ch) { - val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong() - -// 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 entryContent = EntryFile(chunkBytes) - val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent) - // "W1L0-92,15" - addFile(disk, entry) - - WriteSavegame.saveProgress += 1 - } - } - } - - - // Write Actors // - actorsList.forEachIndexed { count, it -> -// Echo("Writing actors... ${count+1}/${actorsList.size}") - - val actorContent = EntryFile(WriteActor.encodeToByteArray64(it)) - val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent) - addFile(disk, actor) - - WriteSavegame.saveProgress += 1 - } - - - // write loadorder // - val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET) - loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n")) - loadOrderBa64Writer.flush(); loadOrderBa64Writer.close() - val loadOrderText = loadOrderBa64Writer.toByteArray64() - val loadOrderContents = EntryFile(loadOrderText) - addFile(disk, DiskEntry(LOADORDER, ROOT, creation_t, time_t, loadOrderContents)) - - - -// Echo("Writing file to disk...") - - disk.entries[0]!!.modificationDate = time_t - // entry zero MUST NOT be used to get lastPlayDate, but we'll update it anyway - // use entry -1 for that purpose! - disk.capacity = 0 - VDUtil.dumpToRealMachine(disk, outFile) - - - - printdbg(this, "Game saved with size of ${outFile.length()} bytes") - - - IngameRenderer.fboRGBexportedLatch = false - WriteSavegame.savingStatus = 255 - - - callback() - } -} - -/** - * This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map - * - * Created by minjaesong on 2021-10-08 - */ -class PlayerSavingThread( - 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) { - - override fun save() { - disk.saveMode = 2 * isAuto.toInt() // no quick - disk.saveKind = VDSaveKind.PLAYER_DATA - disk.capacity = 0L - - 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() - } -} +const val SCREENCAP_WAIT_TRY_MAX = 256 diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/PlayerSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/PlayerSavingThread.kt new file mode 100644 index 000000000..cff3aa675 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/serialise/PlayerSavingThread.kt @@ -0,0 +1,77 @@ +package net.torvald.terrarum.modulebasegame.serialise + +import net.torvald.gdx.graphics.PixmapIO2 +import net.torvald.terrarum.App +import net.torvald.terrarum.modulebasegame.IngameRenderer +import net.torvald.terrarum.modulebasegame.TerrarumIngame +import net.torvald.terrarum.savegame.* +import net.torvald.terrarum.toInt +import java.io.File +import java.util.zip.GZIPOutputStream + +/** + * This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map + * + * Created by minjaesong on 2021-10-08 + */ +class PlayerSavingThread( + 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) + } + + + override fun save() { + App.printdbg(this, "outFile: ${outFile.path}") + + disk.saveMode = 2 * isAuto.toInt() // no quick + disk.saveKind = VDSaveKind.PLAYER_DATA + disk.capacity = 0L + + WriteSavegame.saveProgress = 0f + + // 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") + } + + // 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(VDFileID.PLAYER_SCREENSHOT, VDFileID.ROOT, ingame.world.creationTime, time_t, thumbContent) + addFile(disk, thumb) + + + + App.printdbg(this, "Writing The Player...") + WritePlayer(ingame.actorGamer, disk, ingame, time_t) + disk.entries[0]!!.modificationDate = time_t + VDUtil.dumpToRealMachine(disk, outFile) + + +// IngameRenderer.screencapBusy = false + + callback() + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt index 77a19ae89..006694bc8 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/QuickSaveThread.kt @@ -45,10 +45,17 @@ class QuickSingleplayerWorldSavingThread( override fun save() { + printdbg(this, "outFile: ${outFile.path}") + val skimmer = DiskSkimmer(outFile) - while (!IngameRenderer.fboRGBexportedLatch) { - Thread.sleep(1L) + // 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() @@ -147,7 +154,7 @@ class QuickSingleplayerWorldSavingThread( printdbg(this, "Game saved with size of ${outFile.length()} bytes") - IngameRenderer.fboRGBexportedLatch = false +// IngameRenderer.screencapBusy = false WriteSavegame.savingStatus = 255 ingame.clearModifiedChunks() diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt new file mode 100644 index 000000000..7091877de --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WorldSavingThread.kt @@ -0,0 +1,189 @@ +package net.torvald.terrarum.modulebasegame.serialise + +import net.torvald.gdx.graphics.PixmapIO2 +import net.torvald.terrarum.* +import net.torvald.terrarum.modulebasegame.IngameRenderer +import net.torvald.terrarum.modulebasegame.TerrarumIngame +import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer +import net.torvald.terrarum.modulebasegame.gameactors.Pocketed +import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.savegame.* +import net.torvald.terrarum.serialise.Common +import java.io.File +import java.util.zip.GZIPOutputStream + +/** + * Created by minjaesong on 2021-09-14. + */ +class WorldSavingThread( + 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) + } + + + override fun save() { + App.printdbg(this, "outFile: ${outFile.path}") + + disk.saveMode = 2 * isAuto.toInt() // no quick + disk.saveKind = VDSaveKind.WORLD_DATA + + // 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 = allTheActors.filterIsInstance() + val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) } + val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) } + val cw = ingame.world.width / LandUtil.CHUNK_W + val ch = ingame.world.height / LandUtil.CHUNK_H + + WriteSavegame.saveProgress = 0f + WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size + + + val tgaout = ByteArray64GrowableOutputStream() + val gzout = GZIPOutputStream(tgaout) + + App.printdbg(this, "Writing metadata...") + + val creation_t = ingame.world.creationTime + + + // Write subset of Ingame.ItemCodex + // The existing ItemCodex must be rewritten to clear out obsolete records + + // We're assuming the dynamic item generated by players does exist in the world, and it's recorded + // into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable + // can be found in this world without need to look up the players + ingame.world.dynamicToStaticTable.clear() + ingame.world.dynamicItemInventory.clear() + actorsList.filterIsInstance().forEach { actor -> + actor.inventory.forEach { (itemid, _) -> + + App.printdbg(this, "World side dynamicitem: $itemid contained in $actor") + + if (itemid.startsWith("${ReferencingRanges.PREFIX_DYNAMICITEM}:")) { + ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid) + ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!! + } + } + } + actorsList.filterIsInstance().forEach { fixture -> + fixture.inventory?.forEach { (itemid, _) -> + + App.printdbg(this, "World side dynamicitem: $itemid contained in $fixture") + + if (itemid.startsWith("${ReferencingRanges.PREFIX_DYNAMICITEM}:")) { + ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid) + ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!! + } + } + } + + + + PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) + IngameRenderer.fboRGBexport.dispose() + + val thumbContent = EntryFile(tgaout.toByteArray64()) + val thumb = DiskEntry(VDFileID.THUMBNAIL, VDFileID.ROOT, creation_t, time_t, thumbContent) + addFile(disk, thumb) + + + + WriteSavegame.saveProgress += 1f + + // Write World // + + val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList)) + val world = DiskEntry(VDFileID.SAVEGAMEINFO, VDFileID.ROOT, creation_t, time_t, worldMeta) + addFile(disk, world) + + WriteSavegame.saveProgress += 1f + + + for (layer in layers.indices) { + for (cx in 0 until cw) { + for (cy in 0 until ch) { + val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong() + +// 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 entryContent = EntryFile(chunkBytes) + val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent) + // "W1L0-92,15" + addFile(disk, entry) + + WriteSavegame.saveProgress += 1 + } + } + } + + + // Write Actors // + actorsList.forEachIndexed { count, it -> +// Echo("Writing actors... ${count+1}/${actorsList.size}") + + val actorContent = EntryFile(WriteActor.encodeToByteArray64(it)) + val actor = DiskEntry(it.referenceID.toLong(), VDFileID.ROOT, creation_t, time_t, actorContent) + addFile(disk, actor) + + WriteSavegame.saveProgress += 1 + } + + + // write loadorder // + val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET) + loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n")) + loadOrderBa64Writer.flush(); loadOrderBa64Writer.close() + val loadOrderText = loadOrderBa64Writer.toByteArray64() + val loadOrderContents = EntryFile(loadOrderText) + addFile(disk, DiskEntry(VDFileID.LOADORDER, VDFileID.ROOT, creation_t, time_t, loadOrderContents)) + + + +// Echo("Writing file to disk...") + + disk.entries[0]!!.modificationDate = time_t + // entry zero MUST NOT be used to get lastPlayDate, but we'll update it anyway + // use entry -1 for that purpose! + disk.capacity = 0 + VDUtil.dumpToRealMachine(disk, outFile) + + + + App.printdbg(this, "Game saved with size of ${outFile.length()} bytes") + + +// IngameRenderer.screencapBusy = false + WriteSavegame.savingStatus = 255 + + + callback() + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt index fa74c4355..014c8c22a 100644 --- a/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/serialise/WriteSavegame.kt @@ -43,10 +43,7 @@ object WriteSavegame { 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 - printdbg(this, "Save queued") - + private fun installScreencap() { IngameRenderer.screencapExportCallback = { fb -> printdbg(this, "Generating thumbnail...") @@ -63,11 +60,19 @@ object WriteSavegame { IngameRenderer.fboRGBexport = p //PixmapIO2._writeTGA(gzout, p, true, true) //p.dispose() - IngameRenderer.fboRGBexportedLatch = true printdbg(this, "Done thumbnail generation") } - IngameRenderer.screencapRequested = true + } + + operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) { + savingStatus = 0 + printdbg(this, "Save queued") + + installScreencap() + try { printdbg(this, "ScreencapExport installed: ${IngameRenderer.screencapExportCallback}") } + catch (e: UninitializedPropertyAccessException) { printdbg(this, "ScreencapExport installed: no") } + IngameRenderer.requestScreencap() val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread") savingThread.start() @@ -78,11 +83,14 @@ object WriteSavegame { fun immediate(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) { - savingStatus = 0 - printdbg(this, "Immediate save fired") + installScreencap() + try { printdbg(this, "ScreencapExport installed: ${IngameRenderer.screencapExportCallback}") } + catch (e: UninitializedPropertyAccessException) { printdbg(this, "ScreencapExport installed: no") } + IngameRenderer.requestScreencap() + val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread") savingThread.start() diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt index 04dcba9cc..a9957a9fb 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt @@ -108,6 +108,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { private val MODE_SAVE_MULTIPLE_CHOICES = 2 private val MODE_LOAD_DA_SHIT_ALREADY = 255 private val MODE_SAVE_DAMAGED = 256 + private val MODE_SAVE_DELETE = 512 private lateinit var loadables: SavegameCollectionPair @@ -229,7 +230,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { MODE_SAVE_DAMAGED } else { - val (p, w) = loadables.getManualSave()!! + val (p, w) = loadables.getLoadableSave()!! UILoadGovernor.playerDisk = p; UILoadGovernor.worldDisk = w if (loadables.newerSaveIsDamaged) { @@ -329,10 +330,25 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { private var oldMode = -1 private val mode1Node = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse() - private val mode2Node = Yaml(UITitleRemoConYaml.injectedMenuSingleWorldSel).parse() +// private val mode2Node = Yaml(UITitleRemoConYaml.injectedMenuSingleWorldSel).parse() - private val menus = listOf(mode1Node, mode2Node) - private val titles = listOf("CONTEXT_CHARACTER", "MENU_LABEL_WORLD") +// private val menus = listOf(mode1Node, mode2Node) + + private val deleteCharacterButton = UIItemTextButton( + this, "CONTEXT_CHARACTER_DELETE", + UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 11, + UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 3 + 16, + remoCon.width + UIRemoCon.UIRemoConElement.paddingLeft, + true, + inactiveCol = Toolkit.Theme.COL_RED, + activeCol = Toolkit.Theme.COL_REDD, + hitboxSize = UIRemoCon.UIRemoConElement.lineHeight - 2 + ).also { + it.clickOnceListener = { _,_ -> + mode = MODE_SAVE_DELETE + it.highlighted = true + } + } init { // this UI will NOT persist; the parent of the mode1Node must be set using an absolute value (e.g. treeRoot, not remoCon.currentRemoConContents) @@ -341,22 +357,25 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { //printStackTrace(this) mode1Node.parent = remoCon.treeRoot - mode2Node.parent = mode1Node +// mode2Node.parent = mode1Node mode1Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame" - mode2Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame" +// mode2Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame" // 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]) + printdbg(this, "Change mode: $oldMode -> $mode") +// remoCon.setNewRemoConContents(menus[mode]) + remoCon.setNewRemoConContents(mode1Node) } override fun updateUI(delta: Float) { - if (mode == MODE_SELECT) { + if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { if (oldMode != mode) { modeChangedHandler(mode) @@ -413,7 +432,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk) } } - else if (mode == MODE_SELECT) { + else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { batch.end() val cells = getCells() @@ -474,11 +493,6 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { 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()) } @@ -506,11 +520,16 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { loadAutoThumbButton.render(batch, camera) loadManualThumbButton.render(batch, camera) } + + + if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { + deleteCharacterButton.render(batch, camera) + } } override fun keyDown(keycode: Int): Boolean { - if (this.isVisible) { + if (this.isVisible && (mode == MODE_SELECT || mode == MODE_SAVE_DELETE)) { val cells = getCells() if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) { @@ -531,6 +550,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { if (mode == MODE_SELECT) getCells().forEach { it.touchDown(screenX, screenY, pointer, button) } if (::loadAutoThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadAutoThumbButton.touchDown(screenX, screenY, pointer, button) } if (::loadManualThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadManualThumbButton.touchDown(screenX, screenY, pointer, button) } + if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) deleteCharacterButton.touchDown(screenX, screenY, pointer, button) return true } @@ -538,11 +558,12 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() { if (mode == MODE_SELECT) getCells().forEach { it.touchUp(screenX, screenY, pointer, button) } if (::loadAutoThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadAutoThumbButton.touchUp(screenX, screenY, pointer, button) } if (::loadManualThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadManualThumbButton.touchUp(screenX, screenY, pointer, button) } + if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) deleteCharacterButton.touchDown(screenX, screenY, pointer, button) return true } override fun scrolled(amountX: Float, amountY: Float): Boolean { - if (this.isVisible && mode == MODE_SELECT) { + if (this.isVisible && mode == MODE_SELECT || mode == MODE_SAVE_DELETE) { val cells = getCells() if (amountY <= -1f && scrollTarget > 0) { diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt b/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt index 3aace11b3..96fd42a4a 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UINewCharacter.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Camera import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.g2d.SpriteBatch import net.torvald.terrarum.App +import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.Second import net.torvald.terrarum.Terrarum import net.torvald.terrarum.gameactors.AVKey @@ -64,6 +65,7 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() { val savingThread = Thread({ + printdbg(this, "Player saving thread fired") disk.saveMode = 2 // auto, no quick disk.capacity = 0L @@ -79,9 +81,11 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() { UILoadGovernor.playerDisk = DiskSkimmer(outFile) // comment above if chargen must send gamers back to the charcters list + printdbg(this, "playerdisk: ${UILoadGovernor.playerDisk?.diskFile?.path}") }, "TerrarumBasegameNewCharcterSaveThread") +// savingThread.start() // savingThread.join() remoCon.openUI(UINewWorld(remoCon, savingThread)) // let UINewWorld handle the character file generation diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt b/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt index 8cdb98528..3073e9b53 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UINewWorld.kt @@ -95,10 +95,11 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() { goButton.clickOnceListener = { _, _ -> // after the save is complete, proceed to new world generation + newPlayerCreationThread.start() newPlayerCreationThread.join() -// printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}") + printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}") val ingame = TerrarumIngame(App.batch) val player = ReadActor.invoke(UILoadGovernor.playerDisk!!, ByteArray64Reader(UILoadGovernor.playerDisk!!.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer diff --git a/src/net/torvald/terrarum/ui/Toolkit.kt b/src/net/torvald/terrarum/ui/Toolkit.kt index 61aa22cc0..e12cb3c5a 100644 --- a/src/net/torvald/terrarum/ui/Toolkit.kt +++ b/src/net/torvald/terrarum/ui/Toolkit.kt @@ -28,6 +28,8 @@ object Toolkit : Disposable { val COL_SELECTED = Color(0x00f8ff_ff) // cyan, HIGHLY SATURATED val COL_MOUSE_UP = Color(0xfff066_ff.toInt()) // yellow (all yellows are of low saturation according to the colour science) val COL_DISABLED = Color(0xaaaaaaff.toInt()) + val COL_RED = Color(0xff8888ff.toInt()) + val COL_REDD = Color(0xff4448ff.toInt()) /* Try this for alt colour set: diff --git a/src/net/torvald/terrarum/ui/UIAutosaveNotifier.kt b/src/net/torvald/terrarum/ui/UIAutosaveNotifier.kt index 44b732b6c..2b5aac1ee 100644 --- a/src/net/torvald/terrarum/ui/UIAutosaveNotifier.kt +++ b/src/net/torvald/terrarum/ui/UIAutosaveNotifier.kt @@ -32,7 +32,7 @@ class UIAutosaveNotifier : UICanvas() { private var errored = false private var normalCol = Color.WHITE - private var errorCol = Color(0xFF8888FF.toInt()) + private var errorCol = Toolkit.Theme.COL_RED override fun updateUI(delta: Float) { spinnerTimer += delta diff --git a/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt b/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt index e5e47fb57..0b4725c62 100644 --- a/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt +++ b/src/net/torvald/terrarum/ui/UIItemTextLineInput.kt @@ -80,7 +80,7 @@ class UIItemTextLineInput( companion object { val TEXTINPUT_COL_TEXT = Color.WHITE - val TEXTINPUT_COL_TEXT_NOMORE = Color(0xFF8888FF.toInt()) + val TEXTINPUT_COL_TEXT_NOMORE = Toolkit.Theme.COL_RED val TEXTINPUT_COL_TEXT_DISABLED = Toolkit.Theme.COL_DISABLED val TEXTINPUT_COL_BACKGROUND = Toolkit.Theme.COL_CELL_FILL val TEXTINPUT_COL_BACKGROUND2 = Toolkit.Theme.COL_CELL_FILL.cpy() @@ -155,7 +155,7 @@ class UIItemTextLineInput( private var textColours = arrayOf( Color.WHITE, - Color(0xff8888ff.toInt()), + Toolkit.Theme.COL_RED, Color(0x888888ff.toInt()) )