Fix multiplayer turn check worker exception (#6915)

This commit is contained in:
Timo T
2022-05-23 09:48:08 +02:00
committed by GitHub
parent 6836038252
commit b59b890ced
9 changed files with 75 additions and 70 deletions

View File

@ -27,14 +27,14 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
@GuardedBy("this")
private val callbacks = ArrayList<IndexedCallback>()
override fun saveGame(gameSaver: GameSaver, gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, saveCompleteCallback: ((Exception?) -> Unit)?) {
override fun saveGame(gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, saveCompleteCallback: ((Exception?) -> Unit)?) {
val callbackIndex = synchronized(this) {
val index = callbackIndex++
callbacks.add(IndexedCallback(
index,
{ uri ->
if (uri != null) {
saveGame(gameSaver, gameInfo, uri)
saveGame(gameInfo, uri)
saveCompleteCallback?.invoke(null)
} else {
saveCompleteCallback?.invoke(RuntimeException("Uri was null"))
@ -68,16 +68,16 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
}
}
private fun saveGame(gameSaver: GameSaver, gameInfo: GameInfo, uri: Uri) {
private fun saveGame(gameInfo: GameInfo, uri: Uri) {
gameInfo.customSaveLocation = uri.toString()
activity.contentResolver.openOutputStream(uri, "rwt")
?.writer()
?.use {
it.write(gameSaver.gameInfoToString(gameInfo))
it.write(GameSaver.gameInfoToString(gameInfo))
}
}
override fun loadGame(gameSaver: GameSaver, loadCompleteCallback: (GameInfo?, Exception?) -> Unit) {
override fun loadGame(loadCompleteCallback: (GameInfo?, Exception?) -> Unit) {
val callbackIndex = synchronized(this) {
val index = callbackIndex++
callbacks.add(IndexedCallback(
@ -90,7 +90,7 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
?.reader()
?.readText()
?.run {
gameSaver.gameInfoFromString(this)
GameSaver.gameInfoFromString(this)
}
} catch (e: Exception) {
exception = e

View File

@ -20,11 +20,10 @@ interface CustomSaveLocationHelper {
* @param saveCompleteCallback Action to call upon completion (success _and_ failure)
*/
fun saveGame(
gameSaver: GameSaver,
gameInfo: GameInfo,
gameName: String,
forcePrompt: Boolean = false,
saveCompleteCallback: ((Exception?) -> Unit)? = null
gameInfo: GameInfo,
gameName: String,
forcePrompt: Boolean = false,
saveCompleteCallback: ((Exception?) -> Unit)? = null
)
/**### Load from custom location
@ -34,5 +33,5 @@ interface CustomSaveLocationHelper {
*
* @param loadCompleteCallback Action to call upon completion (success _and_ failure)
*/
fun loadGame(gameSaver: GameSaver, loadCompleteCallback: (GameInfo?, Exception?) -> Unit)
fun loadGame(loadCompleteCallback: (GameInfo?, Exception?) -> Unit)
}

View File

@ -31,8 +31,6 @@ class GameSaver(
) {
//region Data
var saveZipped = false
var autoSaveJob: Job? = null
//endregion
@ -107,17 +105,6 @@ class GameSaver(
}
}
/** Returns gzipped serialization of [game], optionally gzipped ([forceZip] overrides [saveZipped]) */
fun gameInfoToString(game: GameInfo, forceZip: Boolean? = null): String {
val plainJson = json().toJson(game)
return if (forceZip ?: saveZipped) Gzip.zip(plainJson) else plainJson
}
/** Returns gzipped serialization of preview [game] */
fun gameInfoToString(game: GameInfoPreview): String {
return Gzip.zip(json().toJson(game))
}
/**
* Overload of function saveGame to save a GameInfoPreview in the MultiplayerGames folder
*/
@ -140,7 +127,7 @@ class GameSaver(
}
fun saveGameToCustomLocation(game: GameInfo, GameName: String, saveCompletionCallback: (Exception?) -> Unit) {
customSaveLocationHelper!!.saveGame(this, game, GameName, forcePrompt = true, saveCompleteCallback = saveCompletionCallback)
customSaveLocationHelper!!.saveGame(game, GameName, forcePrompt = true, saveCompleteCallback = saveCompletionCallback)
}
//endregion
@ -161,40 +148,11 @@ class GameSaver(
}
fun loadGameFromCustomLocation(loadCompletionCallback: (GameInfo?, Exception?) -> Unit) {
customSaveLocationHelper!!.loadGame(this) { game, e ->
customSaveLocationHelper!!.loadGame { game, e ->
loadCompletionCallback(game?.apply { setTransients() }, e)
}
}
fun gameInfoFromString(gameData: String): GameInfo {
return gameInfoFromStringWithoutTransients(gameData).apply {
setTransients()
}
}
/**
* Parses [gameData] as gzipped serialization of a [GameInfoPreview]
* @throws SerializationException
*/
fun gameInfoPreviewFromString(gameData: String): GameInfoPreview {
return json().fromJson(GameInfoPreview::class.java, Gzip.unzip(gameData))
}
/**
* WARNING! transitive GameInfo data not initialized
* The returned GameInfo can not be used for most circumstances because its not initialized!
* It is therefore stateless and save to call for Multiplayer Turn Notifier, unlike gameInfoFromString().
*
* @throws SerializationException
*/
private fun gameInfoFromStringWithoutTransients(gameData: String): GameInfo {
val unzippedJson = try {
Gzip.unzip(gameData)
} catch (ex: Exception) {
gameData
}
return json().fromJson(GameInfo::class.java, unzippedJson)
}
//endregion
//region Settings
@ -229,6 +187,9 @@ class GameSaver(
}
companion object {
var saveZipped = false
/** Specialized function to access settings before Gdx is initialized.
*
* @param base Path to the directory where the file should be - if not set, the OS current directory is used (which is "/" on Android)
@ -244,6 +205,48 @@ class GameSaver(
)
else GameSettings().apply { isFreshlyCreated = true }
}
fun gameInfoFromString(gameData: String): GameInfo {
return gameInfoFromStringWithoutTransients(gameData).apply {
setTransients()
}
}
/**
* Parses [gameData] as gzipped serialization of a [GameInfoPreview]
* @throws SerializationException
*/
fun gameInfoPreviewFromString(gameData: String): GameInfoPreview {
return json().fromJson(GameInfoPreview::class.java, Gzip.unzip(gameData))
}
/**
* WARNING! transitive GameInfo data not initialized
* The returned GameInfo can not be used for most circumstances because its not initialized!
* It is therefore stateless and save to call for Multiplayer Turn Notifier, unlike gameInfoFromString().
*
* @throws SerializationException
*/
private fun gameInfoFromStringWithoutTransients(gameData: String): GameInfo {
val unzippedJson = try {
Gzip.unzip(gameData)
} catch (ex: Exception) {
gameData
}
return json().fromJson(GameInfo::class.java, unzippedJson)
}
/** Returns gzipped serialization of [game], optionally gzipped ([forceZip] overrides [saveZipped]) */
fun gameInfoToString(game: GameInfo, forceZip: Boolean? = null): String {
val plainJson = json().toJson(game)
return if (forceZip ?: saveZipped) Gzip.zip(plainJson) else plainJson
}
/** Returns gzipped serialization of preview [game] */
fun gameInfoToString(game: GameInfoPreview): String {
return Gzip.zip(json().toJson(game))
}
}
//endregion

View File

@ -4,6 +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
/**
* Allows access to games stored on a server for multiplayer purposes.
@ -19,7 +20,6 @@ import com.unciv.logic.GameInfoPreview
class OnlineMultiplayerGameSaver(
private var fileStorageIdentifier: String? = null
) {
private val gameSaver = UncivGame.Current.gameSaver
fun fileStorage(): FileStorage {
val identifier = if (fileStorageIdentifier == null) UncivGame.Current.settings.multiplayerServer else fileStorageIdentifier
@ -34,7 +34,7 @@ class OnlineMultiplayerGameSaver(
tryUploadGamePreview(gameInfo.asPreview())
}
val zippedGameInfo = gameSaver.gameInfoToString(gameInfo, forceZip = true)
val zippedGameInfo = GameSaver.gameInfoToString(gameInfo, forceZip = true)
fileStorage().saveFileData(gameInfo.gameId, zippedGameInfo, true)
}
@ -49,7 +49,7 @@ class OnlineMultiplayerGameSaver(
* @see GameInfo.asPreview
*/
suspend fun tryUploadGamePreview(gameInfo: GameInfoPreview) {
val zippedGameInfo = gameSaver.gameInfoToString(gameInfo)
val zippedGameInfo = GameSaver.gameInfoToString(gameInfo)
fileStorage().saveFileData("${gameInfo.gameId}_Preview", zippedGameInfo, true)
}
@ -59,7 +59,7 @@ class OnlineMultiplayerGameSaver(
*/
suspend fun tryDownloadGame(gameId: String): GameInfo {
val zippedGameInfo = fileStorage().loadFileData(gameId)
return gameSaver.gameInfoFromString(zippedGameInfo)
return GameSaver.gameInfoFromString(zippedGameInfo)
}
/**
@ -68,6 +68,6 @@ class OnlineMultiplayerGameSaver(
*/
suspend fun tryDownloadGamePreview(gameId: String): GameInfoPreview {
val zippedGameInfo = fileStorage().loadFileData("${gameId}_Preview")
return gameSaver.gameInfoPreviewFromString(zippedGameInfo)
return GameSaver.gameInfoPreviewFromString(zippedGameInfo)
}
}

View File

@ -8,6 +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.models.ruleset.RulesetCache
import com.unciv.ui.images.IconTextButton
import com.unciv.ui.images.ImageGetter
@ -55,7 +56,7 @@ class CrashScreen(val exception: Throwable): BaseScreen() {
return ""
return "\n**Save Data:**\n<details><summary>Show Saved Game</summary>\n\n```" +
try {
game.gameSaver.gameInfoToString(UncivGame.Current.gameInfo, forceZip = true)
GameSaver.gameInfoToString(UncivGame.Current.gameInfo, forceZip = true)
} catch (e: Throwable) {
"No save data: $e" // In theory .toString() could still error here.
} + "\n```\n</details>\n"

View File

@ -42,8 +42,8 @@ fun debugTab() = Table(BaseScreen.skin).apply {
game.gameInfo.gameParameters.godMode = it
}).colspan(2).row()
}
add("Save games compressed".toCheckBox(game.gameSaver.saveZipped) {
game.gameSaver.saveZipped = it
add("Save games compressed".toCheckBox(GameSaver.saveZipped) {
GameSaver.saveZipped = it
}).colspan(2).row()
add("Save maps compressed".toCheckBox(MapSaver.saveZipped) {
MapSaver.saveZipped = it

View File

@ -9,6 +9,7 @@ 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.UncivGame
import com.unciv.logic.GameSaver
import com.unciv.logic.MissingModsException
import com.unciv.logic.UncivShowableException
import com.unciv.models.ruleset.RulesetCache
@ -88,7 +89,7 @@ class LoadGameScreen(previousScreen:BaseScreen) : PickerScreen(disableScroll = t
loadFromClipboardButton.onClick {
try {
val clipboardContentsString = Gdx.app.clipboard.contents.trim()
val loadedGame = game.gameSaver.gameInfoFromString(clipboardContentsString)
val loadedGame = GameSaver.gameInfoFromString(clipboardContentsString)
UncivGame.Current.loadGame(loadedGame)
} catch (ex: Exception) {
handleLoadGameException("Could not load game from clipboard!", ex)

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextField
import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.models.translations.tr
import com.unciv.ui.crashhandling.launchCrashHandling
import com.unciv.ui.crashhandling.postCrashHandlingRunnable
@ -43,7 +44,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : PickerScreen(disableScroll = true
copyJsonButton.onClick {
thread(name="Copy to clipboard") { // the Gzip rarely leads to ANRs
try {
Gdx.app.clipboard.contents = game.gameSaver.gameInfoToString(gameInfo, forceZip = true)
Gdx.app.clipboard.contents = GameSaver.gameInfoToString(gameInfo, forceZip = true)
} catch (OOM: OutOfMemoryError) {
// you don't get a special toast, this isn't nearly common enough, this is a total edge-case
}

View File

@ -12,14 +12,14 @@ import javax.swing.JFileChooser
import javax.swing.JFrame
class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
override fun saveGame(gameSaver: GameSaver, gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, saveCompleteCallback: ((Exception?) -> Unit)?) {
override fun saveGame(gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, saveCompleteCallback: ((Exception?) -> Unit)?) {
val customSaveLocation = gameInfo.customSaveLocation
if (customSaveLocation != null && !forcePrompt) {
try {
File(customSaveLocation).outputStream()
.writer()
.use { writer ->
writer.write(gameSaver.gameInfoToString(gameInfo))
writer.write(GameSaver.gameInfoToString(gameInfo))
}
saveCompleteCallback?.invoke(null)
} catch (e: Exception) {
@ -59,7 +59,7 @@ class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
saveCompleteCallback?.invoke(exception)
}
override fun loadGame(gameSaver: GameSaver, loadCompleteCallback: (GameInfo?, Exception?) -> Unit) {
override fun loadGame(loadCompleteCallback: (GameInfo?, Exception?) -> Unit) {
val fileChooser = JFileChooser().apply fileChooser@{
currentDirectory = Gdx.files.local("").file()
}
@ -79,7 +79,7 @@ class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
file.inputStream()
.reader()
.readText()
.run { gameSaver.gameInfoFromString(this) }
.run { GameSaver.gameInfoFromString(this) }
.apply {
// If the user has saved the game from another platform (like Android),
// then the save location might not be right so we have to correct for that