mirror of
https://github.com/yairm210/Unciv.git
synced 2024-12-23 01:14:30 +07:00
Save uncaught exception to file (#7247)
* Refactor: Rename GameSaver to UncivFiles * Save last uncaught exception to file Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
parent
98af4207ec
commit
bc9a42e452
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
@ -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++
|
||||
@ -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 {
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
@ -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()
|
||||
|
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
@ -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,
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
}
|
||||
@ -375,7 +376,7 @@ 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
|
||||
@ -383,7 +384,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
||||
autoSaveJob.join()
|
||||
}
|
||||
} else {
|
||||
gameSaver.autoSave(curGameInfo) // NO new thread
|
||||
files.autoSave(curGameInfo) // NO new thread
|
||||
}
|
||||
}
|
||||
settings.save()
|
||||
@ -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))
|
||||
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
@ -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
|
||||
@ -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)
|
||||
}
|
@ -8,7 +8,7 @@ import com.unciv.logic.GameInfoPreview
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.event.EventBus
|
||||
import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached
|
||||
import com.unciv.logic.multiplayer.storage.OnlineMultiplayerGameSaver
|
||||
import com.unciv.logic.multiplayer.storage.OnlineMultiplayerFiles
|
||||
import com.unciv.ui.utils.extensions.isLargerThan
|
||||
import com.unciv.utils.concurrency.Concurrency
|
||||
import com.unciv.utils.concurrency.Dispatcher
|
||||
@ -39,8 +39,8 @@ private val FILE_UPDATE_THROTTLE_PERIOD = Duration.ofSeconds(60)
|
||||
* See the file of [com.unciv.logic.multiplayer.MultiplayerGameAdded] for all available [EventBus] events.
|
||||
*/
|
||||
class OnlineMultiplayer {
|
||||
private val gameSaver = UncivGame.Current.gameSaver
|
||||
private val onlineGameSaver = OnlineMultiplayerGameSaver()
|
||||
private val files = UncivGame.Current.files
|
||||
private val multiplayerFiles = OnlineMultiplayerFiles()
|
||||
|
||||
private val savedGames: MutableMap<FileHandle, OnlineMultiplayerGame> = Collections.synchronizedMap(mutableMapOf())
|
||||
|
||||
@ -100,7 +100,7 @@ class OnlineMultiplayer {
|
||||
}
|
||||
|
||||
private suspend fun updateSavesFromFiles() {
|
||||
val saves = gameSaver.getMultiplayerSaves()
|
||||
val saves = files.getMultiplayerSaves()
|
||||
|
||||
val removedSaves = savedGames.keys - saves.toSet()
|
||||
for (saveFile in removedSaves) {
|
||||
@ -120,7 +120,7 @@ class OnlineMultiplayer {
|
||||
* @throws FileStorageRateLimitReached if the file storage backend can't handle any additional actions for a time
|
||||
*/
|
||||
suspend fun createGame(newGame: GameInfo) {
|
||||
onlineGameSaver.tryUploadGame(newGame, withPreview = true)
|
||||
multiplayerFiles.tryUploadGame(newGame, withPreview = true)
|
||||
addGame(newGame)
|
||||
}
|
||||
|
||||
@ -136,10 +136,10 @@ class OnlineMultiplayer {
|
||||
val saveFileName = if (gameName.isNullOrBlank()) gameId else gameName
|
||||
var gamePreview: GameInfoPreview
|
||||
try {
|
||||
gamePreview = onlineGameSaver.tryDownloadGamePreview(gameId)
|
||||
gamePreview = multiplayerFiles.tryDownloadGamePreview(gameId)
|
||||
} catch (ex: FileNotFoundException) {
|
||||
// Game is so old that a preview could not be found on dropbox lets try the real gameInfo instead
|
||||
gamePreview = onlineGameSaver.tryDownloadGame(gameId).asPreview()
|
||||
gamePreview = multiplayerFiles.tryDownloadGame(gameId).asPreview()
|
||||
}
|
||||
addGame(gamePreview, saveFileName)
|
||||
}
|
||||
@ -150,11 +150,11 @@ class OnlineMultiplayer {
|
||||
}
|
||||
|
||||
private suspend fun addGame(preview: GameInfoPreview, saveFileName: String) {
|
||||
val fileHandle = gameSaver.saveGame(preview, saveFileName)
|
||||
val fileHandle = files.saveGame(preview, saveFileName)
|
||||
return addGame(fileHandle, preview)
|
||||
}
|
||||
|
||||
private suspend fun addGame(fileHandle: FileHandle, preview: GameInfoPreview = gameSaver.loadGamePreviewFromFile(fileHandle)) {
|
||||
private suspend fun addGame(fileHandle: FileHandle, preview: GameInfoPreview = files.loadGamePreviewFromFile(fileHandle)) {
|
||||
debug("Adding game %s", preview.gameId)
|
||||
val game = OnlineMultiplayerGame(fileHandle, preview, Instant.now())
|
||||
savedGames[fileHandle] = game
|
||||
@ -184,7 +184,7 @@ class OnlineMultiplayer {
|
||||
suspend fun resign(game: OnlineMultiplayerGame): Boolean {
|
||||
val preview = game.preview ?: throw game.error!!
|
||||
// download to work with the latest game state
|
||||
val gameInfo = onlineGameSaver.tryDownloadGame(preview.gameId)
|
||||
val gameInfo = multiplayerFiles.tryDownloadGame(preview.gameId)
|
||||
val playerCiv = gameInfo.currentPlayerCiv
|
||||
|
||||
if (!gameInfo.isUsersTurn()) {
|
||||
@ -205,8 +205,8 @@ class OnlineMultiplayer {
|
||||
}
|
||||
|
||||
val newPreview = gameInfo.asPreview()
|
||||
gameSaver.saveGame(newPreview, game.fileHandle)
|
||||
onlineGameSaver.tryUploadGame(gameInfo, withPreview = true)
|
||||
files.saveGame(newPreview, game.fileHandle)
|
||||
multiplayerFiles.tryUploadGame(gameInfo, withPreview = true)
|
||||
game.doManualUpdate(newPreview)
|
||||
return true
|
||||
}
|
||||
@ -242,7 +242,7 @@ class OnlineMultiplayer {
|
||||
*/
|
||||
suspend fun loadGame(gameInfo: GameInfo) = coroutineScope {
|
||||
val gameId = gameInfo.gameId
|
||||
val preview = onlineGameSaver.tryDownloadGamePreview(gameId)
|
||||
val preview = multiplayerFiles.tryDownloadGamePreview(gameId)
|
||||
if (hasLatestGameState(gameInfo, preview)) {
|
||||
gameInfo.isUpToDate = true
|
||||
UncivGame.Current.loadGame(gameInfo)
|
||||
@ -256,7 +256,7 @@ class OnlineMultiplayer {
|
||||
* @throws FileNotFoundException if the file can't be found
|
||||
*/
|
||||
suspend fun downloadGame(gameId: String): GameInfo {
|
||||
val latestGame = onlineGameSaver.tryDownloadGame(gameId)
|
||||
val latestGame = multiplayerFiles.tryDownloadGame(gameId)
|
||||
latestGame.isUpToDate = true
|
||||
return latestGame
|
||||
}
|
||||
@ -271,7 +271,7 @@ class OnlineMultiplayer {
|
||||
}
|
||||
|
||||
private fun deleteGame(fileHandle: FileHandle) {
|
||||
gameSaver.deleteSave(fileHandle)
|
||||
files.deleteSave(fileHandle)
|
||||
|
||||
val game = savedGames[fileHandle]
|
||||
if (game == null) return
|
||||
@ -291,8 +291,8 @@ class OnlineMultiplayer {
|
||||
val oldName = game.name
|
||||
|
||||
savedGames.remove(game.fileHandle)
|
||||
gameSaver.deleteSave(game.fileHandle)
|
||||
val newFileHandle = gameSaver.saveGame(oldPreview, newName)
|
||||
files.deleteSave(game.fileHandle)
|
||||
val newFileHandle = files.saveGame(oldPreview, newName)
|
||||
|
||||
val newGame = OnlineMultiplayerGame(newFileHandle, oldPreview, oldLastUpdate)
|
||||
savedGames[newFileHandle] = newGame
|
||||
@ -305,7 +305,7 @@ class OnlineMultiplayer {
|
||||
*/
|
||||
suspend fun updateGame(gameInfo: GameInfo) {
|
||||
debug("Updating remote game %s", gameInfo.gameId)
|
||||
onlineGameSaver.tryUploadGame(gameInfo, withPreview = true)
|
||||
multiplayerFiles.tryUploadGame(gameInfo, withPreview = true)
|
||||
val game = getGameByGameId(gameInfo.gameId)
|
||||
debug("Existing OnlineMultiplayerGame: %s", game)
|
||||
if (game == null) {
|
||||
|
@ -5,9 +5,8 @@ import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameInfoPreview
|
||||
import com.unciv.logic.event.EventBus
|
||||
import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached
|
||||
import com.unciv.logic.multiplayer.storage.OnlineMultiplayerGameSaver
|
||||
import com.unciv.logic.multiplayer.storage.OnlineMultiplayerFiles
|
||||
import com.unciv.ui.utils.extensions.isLargerThan
|
||||
import com.unciv.utils.concurrency.Concurrency
|
||||
import com.unciv.utils.concurrency.launchOnGLThread
|
||||
import com.unciv.utils.concurrency.withGLContext
|
||||
import com.unciv.utils.debug
|
||||
@ -53,7 +52,7 @@ class OnlineMultiplayerGame(
|
||||
}
|
||||
|
||||
private fun loadPreviewFromFile(): GameInfoPreview {
|
||||
val previewFromFile = UncivGame.Current.gameSaver.loadGamePreviewFromFile(fileHandle)
|
||||
val previewFromFile = UncivGame.Current.files.loadGamePreviewFromFile(fileHandle)
|
||||
preview = previewFromFile
|
||||
return previewFromFile
|
||||
}
|
||||
@ -104,9 +103,9 @@ class OnlineMultiplayerGame(
|
||||
|
||||
private suspend fun update(): GameUpdateResult {
|
||||
val curPreview = if (preview != null) preview!! else loadPreviewFromFile()
|
||||
val newPreview = OnlineMultiplayerGameSaver().tryDownloadGamePreview(curPreview.gameId)
|
||||
val newPreview = OnlineMultiplayerFiles().tryDownloadGamePreview(curPreview.gameId)
|
||||
if (newPreview.turns == curPreview.turns && newPreview.currentPlayer == curPreview.currentPlayer) return GameUpdateResult.UNCHANGED
|
||||
UncivGame.Current.gameSaver.saveGame(newPreview, fileHandle)
|
||||
UncivGame.Current.files.saveGame(newPreview, fileHandle)
|
||||
preview = newPreview
|
||||
return GameUpdateResult.CHANGED
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.GameInfoPreview
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
|
||||
/**
|
||||
* Allows access to games stored on a server for multiplayer purposes.
|
||||
@ -17,7 +17,7 @@ import com.unciv.logic.GameSaver
|
||||
* @see UncivGame.Current.settings.multiplayerServer
|
||||
*/
|
||||
@Suppress("RedundantSuspendModifier") // Methods can take a long time, so force users to use them in a coroutine to not get ANRs on Android
|
||||
class OnlineMultiplayerGameSaver(
|
||||
class OnlineMultiplayerFiles(
|
||||
private var fileStorageIdentifier: String? = null
|
||||
) {
|
||||
fun fileStorage(): FileStorage {
|
||||
@ -28,7 +28,7 @@ class OnlineMultiplayerGameSaver(
|
||||
|
||||
/** @throws FileStorageRateLimitReached if the file storage backend can't handle any additional actions for a time */
|
||||
suspend fun tryUploadGame(gameInfo: GameInfo, withPreview: Boolean) {
|
||||
val zippedGameInfo = GameSaver.gameInfoToString(gameInfo, forceZip = true)
|
||||
val zippedGameInfo = UncivFiles.gameInfoToString(gameInfo, forceZip = true)
|
||||
fileStorage().saveFileData(gameInfo.gameId, zippedGameInfo, true)
|
||||
|
||||
// We upload the preview after the game because otherwise the following race condition will happen:
|
||||
@ -53,7 +53,7 @@ class OnlineMultiplayerGameSaver(
|
||||
* @see GameInfo.asPreview
|
||||
*/
|
||||
suspend fun tryUploadGamePreview(gameInfo: GameInfoPreview) {
|
||||
val zippedGameInfo = GameSaver.gameInfoToString(gameInfo)
|
||||
val zippedGameInfo = UncivFiles.gameInfoToString(gameInfo)
|
||||
fileStorage().saveFileData("${gameInfo.gameId}_Preview", zippedGameInfo, true)
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ class OnlineMultiplayerGameSaver(
|
||||
*/
|
||||
suspend fun tryDownloadGame(gameId: String): GameInfo {
|
||||
val zippedGameInfo = fileStorage().loadFileData(gameId)
|
||||
return GameSaver.gameInfoFromString(zippedGameInfo)
|
||||
return UncivFiles.gameInfoFromString(zippedGameInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,6 +72,6 @@ class OnlineMultiplayerGameSaver(
|
||||
*/
|
||||
suspend fun tryDownloadGamePreview(gameId: String): GameInfoPreview {
|
||||
val zippedGameInfo = fileStorage().loadFileData("${gameId}_Preview")
|
||||
return GameSaver.gameInfoPreviewFromString(zippedGameInfo)
|
||||
return UncivFiles.gameInfoPreviewFromString(zippedGameInfo)
|
||||
}
|
||||
}
|
@ -91,7 +91,7 @@ class GameSettings {
|
||||
if (!isFreshlyCreated && Gdx.app?.type == Application.ApplicationType.Desktop) {
|
||||
windowState = WindowState(Gdx.graphics.width, Gdx.graphics.height)
|
||||
}
|
||||
UncivGame.Current.gameSaver.setGeneralSettings(this)
|
||||
UncivGame.Current.files.setGeneralSettings(this)
|
||||
}
|
||||
|
||||
fun addCompletedTutorialTask(tutorialTask: String) {
|
||||
|
@ -8,7 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.ui.images.IconTextButton
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
@ -19,10 +19,8 @@ import com.unciv.ui.utils.extensions.addBorder
|
||||
import com.unciv.ui.utils.extensions.onClick
|
||||
import com.unciv.ui.utils.extensions.setFontSize
|
||||
import com.unciv.ui.utils.extensions.toLabel
|
||||
import com.unciv.utils.Log
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/** Screen to crash to when an otherwise unhandled exception or error is thrown. */
|
||||
class CrashScreen(val exception: Throwable): BaseScreen() {
|
||||
@ -52,7 +50,7 @@ class CrashScreen(val exception: Throwable): BaseScreen() {
|
||||
return ""
|
||||
return "\n**Save Data:**\n<details><summary>Show Saved Game</summary>\n\n```\n" +
|
||||
try {
|
||||
GameSaver.gameInfoToString(UncivGame.Current.gameInfo!!, forceZip = true)
|
||||
UncivFiles.gameInfoToString(UncivGame.Current.gameInfo!!, forceZip = true)
|
||||
} catch (e: Throwable) {
|
||||
"No save data: $e" // In theory .toString() could still error here.
|
||||
} + "\n```\n</details>\n"
|
||||
|
@ -272,7 +272,7 @@ class NewGameScreen(
|
||||
newGame.isUpToDate = true // So we don't try to download it from dropbox the second after we upload it - the file is not yet ready for loading!
|
||||
try {
|
||||
game.onlineMultiplayer.createGame(newGame)
|
||||
game.gameSaver.requestAutoSave(newGame)
|
||||
game.files.requestAutoSave(newGame)
|
||||
} catch (ex: FileStorageRateLimitReached) {
|
||||
launchOnGLThread {
|
||||
popup.reuseWith("Server limit reached! Please wait for [${ex.limitRemainingSeconds}] seconds", true)
|
||||
|
@ -2,7 +2,7 @@ package com.unciv.ui.options
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.logic.MapSaver
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
@ -51,8 +51,8 @@ fun debugTab() = Table(BaseScreen.skin).apply {
|
||||
curGameInfo.gameParameters.godMode = it
|
||||
}).colspan(2).row()
|
||||
}
|
||||
add("Save games compressed".toCheckBox(GameSaver.saveZipped) {
|
||||
GameSaver.saveZipped = it
|
||||
add("Save games compressed".toCheckBox(UncivFiles.saveZipped) {
|
||||
UncivFiles.saveZipped = it
|
||||
}).colspan(2).row()
|
||||
add("Save maps compressed".toCheckBox(MapSaver.saveZipped) {
|
||||
MapSaver.saveZipped = it
|
||||
|
@ -7,7 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||
import com.badlogic.gdx.utils.SerializationException
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.logic.MissingModsException
|
||||
import com.unciv.logic.UncivShowableException
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
@ -82,7 +82,7 @@ class LoadGameScreen(previousScreen:BaseScreen) : LoadOrSaveScreen() {
|
||||
Concurrency.run(loadGame) {
|
||||
try {
|
||||
// This is what can lead to ANRs - reading the file and setting the transients, that's why this is in another thread
|
||||
val loadedGame = game.gameSaver.loadGameByName(selectedSave)
|
||||
val loadedGame = game.files.loadGameByName(selectedSave)
|
||||
game.loadGame(loadedGame)
|
||||
} catch (ex: Exception) {
|
||||
launchOnGLThread {
|
||||
@ -119,7 +119,7 @@ class LoadGameScreen(previousScreen:BaseScreen) : LoadOrSaveScreen() {
|
||||
Concurrency.run(loadFromClipboard) {
|
||||
try {
|
||||
val clipboardContentsString = Gdx.app.clipboard.contents.trim()
|
||||
val loadedGame = GameSaver.gameInfoFromString(clipboardContentsString)
|
||||
val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString)
|
||||
game.loadGame(loadedGame)
|
||||
} catch (ex: Exception) {
|
||||
launchOnGLThread { handleLoadGameException("Could not load game from clipboard!", ex) }
|
||||
@ -133,14 +133,14 @@ class LoadGameScreen(previousScreen:BaseScreen) : LoadOrSaveScreen() {
|
||||
}
|
||||
|
||||
private fun Table.addLoadFromCustomLocationButton() {
|
||||
if (!game.gameSaver.canLoadFromCustomSaveLocation()) return
|
||||
if (!game.files.canLoadFromCustomSaveLocation()) return
|
||||
val loadFromCustomLocation = loadFromCustomLocation.toTextButton()
|
||||
loadFromCustomLocation.onClick {
|
||||
errorLabel.isVisible = false
|
||||
loadFromCustomLocation.setText(Constants.loading.tr())
|
||||
loadFromCustomLocation.disable()
|
||||
Concurrency.run(Companion.loadFromCustomLocation) {
|
||||
game.gameSaver.loadGameFromCustomLocation { result ->
|
||||
game.files.loadGameFromCustomLocation { result ->
|
||||
if (result.isError()) {
|
||||
handleLoadGameException("Could not load game from custom location!", result.exception)
|
||||
} else if (result.isSuccessful()) {
|
||||
@ -159,7 +159,7 @@ class LoadGameScreen(previousScreen:BaseScreen) : LoadOrSaveScreen() {
|
||||
copyButton.onActivation {
|
||||
Concurrency.run(copyExistingSaveToClipboard) {
|
||||
try {
|
||||
val gameText = game.gameSaver.getSave(selectedSave).readString()
|
||||
val gameText = game.files.getSave(selectedSave).readString()
|
||||
Gdx.app.clipboard.contents = if (gameText[0] == '{') Gzip.zip(gameText) else gameText
|
||||
} catch (ex: Throwable) {
|
||||
ex.printStackTrace()
|
||||
|
@ -77,7 +77,7 @@ abstract class LoadOrSaveScreen(
|
||||
if (selectedSave.isEmpty()) return
|
||||
ConfirmPopup(this, "Are you sure you want to delete this save?", "Delete save") {
|
||||
val result = try {
|
||||
if (game.gameSaver.deleteSave(selectedSave)) {
|
||||
if (game.files.deleteSave(selectedSave)) {
|
||||
resetWindowState()
|
||||
"[$selectedSave] deleted successfully."
|
||||
} else {
|
||||
@ -93,7 +93,7 @@ abstract class LoadOrSaveScreen(
|
||||
}
|
||||
|
||||
private fun updateShownSaves(showAutosaves: Boolean) {
|
||||
savesScrollPane.updateSaveGames(game.gameSaver, showAutosaves)
|
||||
savesScrollPane.updateSaveGames(game.files, showAutosaves)
|
||||
}
|
||||
|
||||
private fun selectExistingSave(saveGameFile: FileHandle) {
|
||||
@ -109,7 +109,7 @@ abstract class LoadOrSaveScreen(
|
||||
Concurrency.run("LoadMetaData") { // Even loading the game to get its metadata can take a long time on older phones
|
||||
val textToSet = try {
|
||||
val savedAt = Date(saveGameFile.lastModified())
|
||||
val game = game.gameSaver.loadGamePreviewFromFile(saveGameFile)
|
||||
val game = game.files.loadGamePreviewFromFile(saveGameFile)
|
||||
val playerCivNames = game.civilizations
|
||||
.filter { it.isPlayerCivilization() }.joinToString { it.civName.tr() }
|
||||
val mods = if (game.gameParameters.mods.isEmpty()) ""
|
||||
|
@ -17,10 +17,10 @@ import com.unciv.utils.Log
|
||||
|
||||
object QuickSave {
|
||||
fun save(gameInfo: GameInfo, screen: WorldScreen) {
|
||||
val gameSaver = UncivGame.Current.gameSaver
|
||||
val files = UncivGame.Current.files
|
||||
val toast = ToastPopup("Quicksaving...", screen)
|
||||
Concurrency.runOnNonDaemonThreadPool("QuickSaveGame") {
|
||||
gameSaver.saveGame(gameInfo, "QuickSave") {
|
||||
files.saveGame(gameInfo, "QuickSave") {
|
||||
launchOnGLThread {
|
||||
toast.close()
|
||||
if (it != null)
|
||||
@ -33,11 +33,11 @@ object QuickSave {
|
||||
}
|
||||
|
||||
fun load(screen: WorldScreen) {
|
||||
val gameSaver = UncivGame.Current.gameSaver
|
||||
val files = UncivGame.Current.files
|
||||
val toast = ToastPopup("Quickloading...", screen)
|
||||
Concurrency.run("QuickLoadGame") {
|
||||
try {
|
||||
val loadedGame = gameSaver.loadGameByName("QuickSave")
|
||||
val loadedGame = files.loadGameByName("QuickSave")
|
||||
launchOnGLThread {
|
||||
toast.close()
|
||||
UncivGame.Current.loadGame(loadedGame)
|
||||
@ -67,7 +67,7 @@ object QuickSave {
|
||||
|
||||
val savedGame: GameInfo
|
||||
try {
|
||||
savedGame = screen.game.gameSaver.loadLatestAutosave()
|
||||
savedGame = screen.game.files.loadLatestAutosave()
|
||||
} catch (oom: OutOfMemoryError) {
|
||||
outOfMemory()
|
||||
return@run
|
||||
|
@ -6,7 +6,7 @@ import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.popup.ConfirmPopup
|
||||
import com.unciv.ui.popup.ToastPopup
|
||||
@ -34,7 +34,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : LoadOrSaveScreen("Current saves")
|
||||
|
||||
rightSideButton.setText("Save game".tr())
|
||||
rightSideButton.onActivation {
|
||||
if (game.gameSaver.getSave(gameNameTextField.text).exists())
|
||||
if (game.files.getSave(gameNameTextField.text).exists())
|
||||
ConfirmPopup(
|
||||
this,
|
||||
"Overwrite existing file?",
|
||||
@ -78,7 +78,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : LoadOrSaveScreen("Current saves")
|
||||
Concurrency.run("Copy game to clipboard") {
|
||||
// the Gzip rarely leads to ANRs
|
||||
try {
|
||||
Gdx.app.clipboard.contents = GameSaver.gameInfoToString(gameInfo, forceZip = true)
|
||||
Gdx.app.clipboard.contents = UncivFiles.gameInfoToString(gameInfo, forceZip = true)
|
||||
} catch (ex: Throwable) {
|
||||
ex.printStackTrace()
|
||||
launchOnGLThread {
|
||||
@ -89,7 +89,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : LoadOrSaveScreen("Current saves")
|
||||
}
|
||||
|
||||
private fun Table.addSaveToCustomLocation() {
|
||||
if (!game.gameSaver.canLoadFromCustomSaveLocation()) return
|
||||
if (!game.files.canLoadFromCustomSaveLocation()) return
|
||||
val saveToCustomLocation = "Save to custom location".toTextButton()
|
||||
val errorLabel = "".toLabel(Color.RED)
|
||||
saveToCustomLocation.onClick {
|
||||
@ -97,7 +97,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : LoadOrSaveScreen("Current saves")
|
||||
saveToCustomLocation.setText("Saving...".tr())
|
||||
saveToCustomLocation.disable()
|
||||
Concurrency.runOnNonDaemonThreadPool("Save to custom location") {
|
||||
game.gameSaver.saveGameToCustomLocation(gameInfo, gameNameTextField.text) { result ->
|
||||
game.files.saveGameToCustomLocation(gameInfo, gameNameTextField.text) { result ->
|
||||
if (result.isError()) {
|
||||
errorLabel.setText("Could not save game to custom location!".tr())
|
||||
result.exception?.printStackTrace()
|
||||
@ -115,7 +115,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : LoadOrSaveScreen("Current saves")
|
||||
private fun saveGame() {
|
||||
rightSideButton.setText("Saving...".tr())
|
||||
Concurrency.runOnNonDaemonThreadPool("SaveGame") {
|
||||
game.gameSaver.saveGame(gameInfo, gameNameTextField.text) {
|
||||
game.files.saveGame(gameInfo, gameNameTextField.text) {
|
||||
launchOnGLThread {
|
||||
if (it != null) ToastPopup("Could not save game!", this@SaveGameScreen)
|
||||
else UncivGame.Current.popScreen()
|
||||
|
@ -7,7 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.utils.AutoScrollPane
|
||||
import com.unciv.ui.utils.BaseScreen
|
||||
@ -48,8 +48,8 @@ class VerticalFileListScrollPane(
|
||||
}
|
||||
|
||||
/** repopulate with existing saved games */
|
||||
fun updateSaveGames(gameSaver: GameSaver, showAutosaves: Boolean) {
|
||||
update(gameSaver.getSaves(showAutosaves)
|
||||
fun updateSaveGames(files: UncivFiles, showAutosaves: Boolean) {
|
||||
update(files.getSaves(showAutosaves)
|
||||
.sortedByDescending { it.lastModified() })
|
||||
}
|
||||
|
||||
|
@ -816,7 +816,7 @@ class WorldScreen(
|
||||
fun autoSave() {
|
||||
waitingForAutosave = true
|
||||
shouldUpdate = true
|
||||
UncivGame.Current.gameSaver.requestAutoSave(gameInfo).invokeOnCompletion {
|
||||
UncivGame.Current.files.requestAutoSave(gameInfo).invokeOnCompletion {
|
||||
// only enable the user to next turn once we've saved the current one
|
||||
waitingForAutosave = false
|
||||
shouldUpdate = true
|
||||
|
@ -9,7 +9,7 @@ import com.badlogic.gdx.graphics.glutils.HdpiMode
|
||||
import com.sun.jna.Native
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.UncivGameParameters
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.debug
|
||||
@ -42,7 +42,7 @@ internal object DesktopLauncher {
|
||||
// Note that means config.setAudioConfig() would be ignored too, those would need to go into the HardenedGdxAudio constructor.
|
||||
config.disableAudio(true)
|
||||
|
||||
val settings = GameSaver.getSettingsForPlatformLaunchers()
|
||||
val settings = UncivFiles.getSettingsForPlatformLaunchers()
|
||||
if (!settings.isFreshlyCreated) {
|
||||
config.setWindowedMode(settings.windowState.width.coerceAtLeast(120), settings.windowState.height.coerceAtLeast(80))
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import com.badlogic.gdx.Gdx
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.json.json
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.UncivFiles
|
||||
import com.unciv.logic.GameStarter
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.map.MapParameters
|
||||
@ -61,10 +61,10 @@ class SerializationTests {
|
||||
}
|
||||
val setup = GameSetupInfo(param, mapParameters)
|
||||
UncivGame.Current = UncivGame("")
|
||||
UncivGame.Current.gameSaver = GameSaver(Gdx.files)
|
||||
UncivGame.Current.files = UncivFiles(Gdx.files)
|
||||
|
||||
// Both startNewGame and makeCivilizationsMeet will cause a save to storage of our empty settings
|
||||
settingsBackup = UncivGame.Current.gameSaver.getGeneralSettings()
|
||||
settingsBackup = UncivGame.Current.files.getGeneralSettings()
|
||||
|
||||
UncivGame.Current.settings = GameSettings()
|
||||
game = GameStarter.startNewGame(setup)
|
||||
|
Loading…
Reference in New Issue
Block a user