Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save uncaught exception to file #7247

Merged
merged 4 commits into from
Jun 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions android/src/com/unciv/app/AndroidLauncher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.badlogic.gdx.backends.android.AndroidGraphics
import com.badlogic.gdx.math.Rectangle
import com.unciv.UncivGame
import com.unciv.UncivGameParameters
import com.unciv.logic.GameSaver
import com.unciv.logic.UncivFiles
import com.unciv.logic.event.EventBus
import com.unciv.ui.UncivStage
import com.unciv.ui.utils.BaseScreen
Expand All @@ -40,7 +40,7 @@ open class AndroidLauncher : AndroidApplication() {
useImmersiveMode = true
}

val settings = GameSaver.getSettingsForPlatformLaunchers(filesDir.path)
val settings = UncivFiles.getSettingsForPlatformLaunchers(filesDir.path)
val fontFamily = settings.fontFamily

// Manage orientation lock and display cutout
Expand Down Expand Up @@ -119,10 +119,10 @@ open class AndroidLauncher : AndroidApplication() {
if (UncivGame.isCurrentInitialized()
&& UncivGame.Current.gameInfo != null
&& UncivGame.Current.settings.multiplayer.turnCheckerEnabled
&& UncivGame.Current.gameSaver.getMultiplayerSaves().any()
&& UncivGame.Current.files.getMultiplayerSaves().any()
) {
MultiplayerTurnCheckWorker.startTurnChecker(
applicationContext, UncivGame.Current.gameSaver,
applicationContext, UncivGame.Current.files,
UncivGame.Current.gameInfo!!, UncivGame.Current.settings.multiplayer
)
}
Expand Down
23 changes: 11 additions & 12 deletions android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.backends.android.AndroidApplication
import com.badlogic.gdx.backends.android.DefaultAndroidFiles
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.UncivFiles
import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached
import com.unciv.models.metadata.GameSettings
import com.unciv.logic.multiplayer.storage.OnlineMultiplayerGameSaver
import com.unciv.logic.multiplayer.storage.OnlineMultiplayerFiles
import com.unciv.models.metadata.GameSettingsMultiplayer
import kotlinx.coroutines.runBlocking
import java.io.FileNotFoundException
Expand Down Expand Up @@ -181,16 +180,16 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
}
}

fun startTurnChecker(applicationContext: Context, gameSaver: GameSaver, currentGameInfo: GameInfo, settings: GameSettingsMultiplayer) {
fun startTurnChecker(applicationContext: Context, files: UncivFiles, currentGameInfo: GameInfo, settings: GameSettingsMultiplayer) {
Log.i(LOG_TAG, "startTurnChecker")
val gameFiles = gameSaver.getMultiplayerSaves()
val gameFiles = files.getMultiplayerSaves()
val gameIds = Array(gameFiles.count()) {""}
val gameNames = Array(gameFiles.count()) {""}

var count = 0
for (gameFile in gameFiles) {
try {
val gamePreview = gameSaver.loadGamePreviewFromFile(gameFile)
val gamePreview = files.loadGamePreviewFromFile(gameFile)
gameIds[count] = gamePreview.gameId
gameNames[count] = gameFile.name()
count++
Expand Down Expand Up @@ -260,14 +259,14 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
*/
private val notFoundRemotely = mutableMapOf<String, Boolean>()

private val gameSaver: GameSaver
private val files: UncivFiles
init {
// We can't use Gdx.files since that is only initialized within a com.badlogic.gdx.backends.android.AndroidApplication.
// Worker instances may be stopped & recreated by the Android WorkManager, so no AndroidApplication and thus no Gdx.files available
val files = DefaultAndroidFiles(applicationContext.assets, ContextWrapper(applicationContext), true)
val gdxFiles = DefaultAndroidFiles(applicationContext.assets, ContextWrapper(applicationContext), true)
// GDX's AndroidFileHandle uses Gdx.files internally, so we need to set that to our new instance
Gdx.files = files
gameSaver = GameSaver(files, null, true)
Gdx.files = gdxFiles
files = UncivFiles(gdxFiles, null, true)
}

override fun doWork(): Result = runBlocking {
Expand Down Expand Up @@ -297,7 +296,7 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame

try {
Log.d(LOG_TAG, "doWork download ${gameId}")
val gamePreview = OnlineMultiplayerGameSaver(fileStorage).tryDownloadGamePreview(gameId)
val gamePreview = OnlineMultiplayerFiles(fileStorage).tryDownloadGamePreview(gameId)
Log.d(LOG_TAG, "doWork download ${gameId} done")
val currentTurnPlayer = gamePreview.getCivilization(gamePreview.currentPlayer)

Expand All @@ -310,7 +309,7 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
Lets hope it works with gamePreview as they are a lot smaller and faster to save
*/
Log.i(LOG_TAG, "doWork save gameName: ${gameNames[idx]}")
gameSaver.saveGame(gamePreview, gameNames[idx])
files.saveGame(gamePreview, gameNames[idx])
Log.i(LOG_TAG, "doWork save ${gameNames[idx]} done")

if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame == null) {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ buildscript {
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${com.unciv.build.BuildConfig.kotlinVersion}")
classpath("de.richsource.gradle.plugins:gwt-gradle-plugin:0.6")
classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.android.tools.build:gradle:7.0.4")
classpath("com.mobidevelop.robovm:robovm-gradle-plugin:2.3.1")

// This is for wrapping the .jar file into a standalone executable
Expand Down
4 changes: 2 additions & 2 deletions core/src/com/unciv/MainMenuScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
val column1 = Table().apply { defaults().pad(10f).fillX() }
val column2 = if (singleColumn) column1 else Table().apply { defaults().pad(10f).fillX() }

if (game.gameSaver.autosaveExists()) {
if (game.files.autosaveExists()) {
val resumeTable = getMenuButton("Resume","OtherIcons/Resume", 'r')
{ resumeGame() }
column1.add(resumeTable).row()
Expand All @@ -153,7 +153,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
{ game.pushScreen(NewGameScreen()) }
column1.add(newGameButton).row()

if (game.gameSaver.getSaves().any()) {
if (game.files.getSaves().any()) {
val loadGameTable = getMenuButton("Load game", "OtherIcons/Load", 'l')
{ game.pushScreen(LoadGameScreen(this)) }
column1.add(loadGameTable).row()
Expand Down
24 changes: 16 additions & 8 deletions core/src/com/unciv/UncivGame.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.badlogic.gdx.Screen
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.utils.Align
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.UncivFiles
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.multiplayer.OnlineMultiplayer
import com.unciv.models.metadata.GameSettings
Expand Down Expand Up @@ -37,6 +37,7 @@ import com.unciv.utils.concurrency.launchOnGLThread
import com.unciv.utils.concurrency.withGLContext
import com.unciv.utils.concurrency.withThreadPoolContext
import com.unciv.utils.debug
import java.io.PrintWriter
import java.util.*
import kotlin.collections.ArrayDeque

Expand All @@ -59,7 +60,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
lateinit var settings: GameSettings
lateinit var musicController: MusicController
lateinit var onlineMultiplayer: OnlineMultiplayer
lateinit var gameSaver: GameSaver
lateinit var files: UncivFiles

/**
* This exists so that when debugging we can see the entire map.
Expand Down Expand Up @@ -95,7 +96,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
viewEntireMapForDebug = false
}
Current = this
gameSaver = GameSaver(Gdx.files, customSaveLocationHelper, platformSpecificHelper?.shouldPreferExternalStorage() == true)
files = UncivFiles(Gdx.files, customSaveLocationHelper, platformSpecificHelper?.shouldPreferExternalStorage() == true)

// If this takes too long players, especially with older phones, get ANR problems.
// Whatever needs graphics needs to be done on the main thread,
Expand All @@ -109,7 +110,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
* - Skin (hence BaseScreen.setSkin())
* - Font (hence Fonts.resetFont() inside setSkin())
*/
settings = gameSaver.getGeneralSettings() // needed for the screen
settings = files.getGeneralSettings() // needed for the screen
setAsRootScreen(GameStartScreen()) // NOT dependent on any atlas or skin
GameSounds.init()

Expand Down Expand Up @@ -355,7 +356,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {

override fun pause() {
val curGameInfo = gameInfo
if (curGameInfo != null) gameSaver.requestAutoSave(curGameInfo)
if (curGameInfo != null) files.requestAutoSave(curGameInfo)
musicController.pause()
super.pause()
}
Expand All @@ -375,15 +376,15 @@ class UncivGame(parameters: UncivGameParameters) : Game() {

val curGameInfo = gameInfo
if (curGameInfo != null) {
val autoSaveJob = gameSaver.autoSaveJob
val autoSaveJob = files.autoSaveJob
if (autoSaveJob != null && autoSaveJob.isActive) {
// auto save is already in progress (e.g. started by onPause() event)
// let's allow it to finish and do not try to autosave second time
Concurrency.runBlocking {
autoSaveJob.join()
}
} else {
gameSaver.autoSave(curGameInfo) // NO new thread
files.autoSave(curGameInfo) // NO new thread
}
}
settings.save()
Expand All @@ -405,6 +406,13 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
/** Handles an uncaught exception or error. First attempts a platform-specific handler, and if that didn't handle the exception or error, brings the game to a [CrashScreen]. */
fun handleUncaughtThrowable(ex: Throwable) {
Log.error("Uncaught throwable", ex)
try {
PrintWriter(files.fileWriter("lasterror.txt")).use {
ex.printStackTrace(it)
}
} catch (ex: Exception) {
// ignore
}
if (platformSpecificHelper?.handleUncaughtThrowable(ex) == true) return
Gdx.app.postRunnable {
setAsRootScreen(CrashScreen(ex))
Expand All @@ -419,7 +427,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
fun goToMainMenu(): MainMenuScreen {
val curGameInfo = gameInfo
if (curGameInfo != null) {
gameSaver.requestAutoSaveUnCloned(curGameInfo) // Can save gameInfo directly because the user can't modify it on the MainMenuScreen
files.requestAutoSaveUnCloned(curGameInfo) // Can save gameInfo directly because the user can't modify it on the MainMenuScreen
}
val mainMenuScreen = MainMenuScreen()
pushScreen(mainMenuScreen)
Expand Down
4 changes: 2 additions & 2 deletions core/src/com/unciv/logic/CustomFileLocationHelper.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.unciv.logic

import com.unciv.logic.GameSaver.CustomLoadResult
import com.unciv.logic.GameSaver.CustomSaveResult
import com.unciv.logic.UncivFiles.CustomLoadResult
import com.unciv.logic.UncivFiles.CustomSaveResult
import com.unciv.utils.concurrency.Concurrency
import java.io.InputStream
import java.io.OutputStream
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import com.unciv.utils.Log
import com.unciv.utils.debug
import kotlinx.coroutines.Job
import java.io.File
import java.io.Writer

private const val SAVE_FILES_FOLDER = "SaveFiles"
private const val MULTIPLAYER_FILES_FOLDER = "MultiplayerGames"
private const val AUTOSAVE_FILE_NAME = "Autosave"
private const val SETTINGS_FILE_NAME = "GameSettings.json"

class GameSaver(
class UncivFiles(
/**
* This is necessary because the Android turn check background worker does not hold any reference to the actual [com.badlogic.gdx.Application],
* which is normally responsible for keeping the [Gdx] static variables from being garbage collected.
Expand All @@ -32,7 +33,7 @@ class GameSaver(
private val preferExternalStorage: Boolean = false
) {
init {
debug("Creating GameSaver, localStoragePath: %s, externalStoragePath: %s, preferExternalStorage: %s",
debug("Creating UncivFiles, localStoragePath: %s, externalStoragePath: %s, preferExternalStorage: %s",
files.localStoragePath, files.externalStoragePath, preferExternalStorage)
}
//region Data
Expand Down Expand Up @@ -66,6 +67,18 @@ class GameSaver(
return toReturn
}

/**
* @throws GdxRuntimeException if the [path] represents a directory
*/
fun fileWriter(path: String, append: Boolean = false): Writer {
val file = if (preferExternalStorage && files.isExternalStorageAvailable) {
files.external(path)
} else {
files.local(path)
}
return file.writer(append)
}

fun getMultiplayerSaves(): Sequence<FileHandle> {
return getSaves(MULTIPLAYER_FILES_FOLDER)
}
Expand Down
Loading