diff --git a/core/src/com/unciv/UncivGame.kt b/core/src/com/unciv/UncivGame.kt index 37ca2f5f..f0d44042 100644 --- a/core/src/com/unciv/UncivGame.kt +++ b/core/src/com/unciv/UncivGame.kt @@ -24,7 +24,8 @@ import kotlin.concurrent.thread class UncivGame( val version: String, private val crashReportSender: CrashReportSender? = null, - val exitEvent: (()->Unit)? = null + val exitEvent: (()->Unit)? = null, + val cancelDiscordEvent: (()->Unit)? = null ) : Game() { // we need this secondary constructor because Java code for iOS can't handle Kotlin lambda parameters constructor(version: String) : this(version, null) @@ -177,10 +178,19 @@ class UncivGame( } override fun dispose() { - if (::gameInfo.isInitialized) { - GameSaver().autoSave(gameInfo) + cancelDiscordEvent?.invoke() + if (::gameInfo.isInitialized){ + GameSaver().autoSaveSingleThreaded(gameInfo) // NO new thread settings.save() } + + // Log still running threads (should be only this one and "DestroyJavaVM") + val numThreads = Thread.activeCount() + val threadList = Array(numThreads) { _ -> Thread() } + Thread.enumerate(threadList) + threadList.filter { it !== Thread.currentThread() && it.name != "DestroyJavaVM"}.forEach { + println (" Thread ${it.name} still running in UncivGame.dispose().") + } } companion object { diff --git a/core/src/com/unciv/logic/GameSaver.kt b/core/src/com/unciv/logic/GameSaver.kt index fedfc4b8..133590b6 100644 --- a/core/src/com/unciv/logic/GameSaver.kt +++ b/core/src/com/unciv/logic/GameSaver.kt @@ -80,25 +80,26 @@ class GameSaver { // On the other hand if we alter the game data while it's being serialized we could get a concurrent modification exception. // So what we do is we clone all the game data and serialize the clone. val gameInfoClone = gameInfo.clone() - thread(name="Autosave") { - saveGame(gameInfoClone, "Autosave") - - // keep auto-saves for the last 10 turns for debugging purposes - val newAutosaveFilename = saveFilesFolder + File.separator + "Autosave-${gameInfo.currentPlayer}-${gameInfoClone.turns}" - getSave("Autosave").copyTo(Gdx.files.local(newAutosaveFilename)) - - fun getAutosaves(): List { return getSaves().filter { it.startsWith("Autosave") } } - while(getAutosaves().size>10){ - val saveToDelete = getAutosaves().minBy { getSave(it).lastModified() }!! - deleteSave(saveToDelete) - } - + thread(name = "Autosave") { + autoSaveSingleThreaded(gameInfoClone) // do this on main thread Gdx.app.postRunnable { postRunnable() } } + } + fun autoSaveSingleThreaded (gameInfo: GameInfo) { + saveGame(gameInfo, "Autosave") + // keep auto-saves for the last 10 turns for debugging purposes + val newAutosaveFilename = saveFilesFolder + File.separator + "Autosave-${gameInfo.currentPlayer}-${gameInfo.turns}" + getSave("Autosave").copyTo(Gdx.files.local(newAutosaveFilename)) + + fun getAutosaves(): List { return getSaves().filter { it.startsWith("Autosave") } } + while(getAutosaves().size>10){ + val saveToDelete = getAutosaves().minBy { getSave(it).lastModified() }!! + deleteSave(saveToDelete) + } } /** diff --git a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt index ec24fc78..07ece0fc 100644 --- a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt +++ b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt @@ -11,11 +11,14 @@ import com.badlogic.gdx.tools.texturepacker.TexturePacker import com.unciv.UncivGame import com.unciv.models.translations.tr import java.io.File -import kotlin.concurrent.thread +import java.util.* +import kotlin.concurrent.timer import kotlin.system.exitProcess internal object DesktopLauncher { + private var discordTimer: Timer? = null + @JvmStatic fun main(arg: Array) { @@ -27,9 +30,9 @@ internal object DesktopLauncher { config.title = "Unciv" config.useHDPI = true - val versionFromJar = DesktopLauncher.javaClass.`package`.specificationVersion + val versionFromJar = DesktopLauncher.javaClass.`package`.specificationVersion ?: "Desktop" - val game = UncivGame(if (versionFromJar != null) versionFromJar else "Desktop", null){exitProcess(0)} + val game = UncivGame ( versionFromJar, null, { exitProcess(0) }, { discordTimer?.cancel() } ) if(!RaspberryPiDetector.isRaspberryPi()) // No discord RPC for Raspberry Pi, see https://github.com/yairm210/Unciv/issues/1624 tryActivateDiscord(game) @@ -71,27 +74,23 @@ internal object DesktopLauncher { } private fun tryActivateDiscord(game: UncivGame) { - try { val handlers = DiscordEventHandlers() DiscordRPC.INSTANCE.Discord_Initialize("647066573147996161", handlers, true, null) Runtime.getRuntime().addShutdownHook(Thread { DiscordRPC.INSTANCE.Discord_Shutdown() }) - thread { - while (true) { - try { - updateRpc(game) - }catch (ex:Exception){} - Thread.sleep(1000) - } + discordTimer = timer(name = "Discord", daemon = true, period = 1000) { + try { + updateRpc(game) + } catch (ex:Exception){} } } catch (ex: Exception) { - print("Could not initialize Discord") + println("Could not initialize Discord") } } - fun updateRpc(game: UncivGame) { + private fun updateRpc(game: UncivGame) { if(!game.isInitialized) return val presence = DiscordRichPresence() val currentPlayerCiv = game.gameInfo.getCurrentPlayerCivilization()