mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-05 07:49:17 +07:00
Fix multiplayer turn check worker exception (#6915)
This commit is contained in:
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user