diff --git a/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt b/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt index 428cafb6dc..a12502ef34 100644 --- a/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt +++ b/android/src/com/unciv/app/CustomSaveLocationHelperAndroid.kt @@ -27,14 +27,14 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa @GuardedBy("this") private val callbacks = ArrayList() - 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 diff --git a/core/src/com/unciv/logic/CustomSaveLocationHelper.kt b/core/src/com/unciv/logic/CustomSaveLocationHelper.kt index 829d3f589c..4d33a9a279 100644 --- a/core/src/com/unciv/logic/CustomSaveLocationHelper.kt +++ b/core/src/com/unciv/logic/CustomSaveLocationHelper.kt @@ -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) } diff --git a/core/src/com/unciv/logic/GameSaver.kt b/core/src/com/unciv/logic/GameSaver.kt index e418fec407..eb7544cb00 100644 --- a/core/src/com/unciv/logic/GameSaver.kt +++ b/core/src/com/unciv/logic/GameSaver.kt @@ -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 diff --git a/core/src/com/unciv/logic/multiplayer/storage/OnlineMultiplayerGameSaver.kt b/core/src/com/unciv/logic/multiplayer/storage/OnlineMultiplayerGameSaver.kt index 2c17a99be7..5f8d726008 100644 --- a/core/src/com/unciv/logic/multiplayer/storage/OnlineMultiplayerGameSaver.kt +++ b/core/src/com/unciv/logic/multiplayer/storage/OnlineMultiplayerGameSaver.kt @@ -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) } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/crashhandling/CrashScreen.kt b/core/src/com/unciv/ui/crashhandling/CrashScreen.kt index 212de149cb..29ea0d7bbe 100644 --- a/core/src/com/unciv/ui/crashhandling/CrashScreen.kt +++ b/core/src/com/unciv/ui/crashhandling/CrashScreen.kt @@ -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
Show Saved Game\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
\n" diff --git a/core/src/com/unciv/ui/options/DebugTab.kt b/core/src/com/unciv/ui/options/DebugTab.kt index 4873ba464d..070bc83ef9 100644 --- a/core/src/com/unciv/ui/options/DebugTab.kt +++ b/core/src/com/unciv/ui/options/DebugTab.kt @@ -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 diff --git a/core/src/com/unciv/ui/saves/LoadGameScreen.kt b/core/src/com/unciv/ui/saves/LoadGameScreen.kt index 247ba80eea..a61a73596c 100644 --- a/core/src/com/unciv/ui/saves/LoadGameScreen.kt +++ b/core/src/com/unciv/ui/saves/LoadGameScreen.kt @@ -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) diff --git a/core/src/com/unciv/ui/saves/SaveGameScreen.kt b/core/src/com/unciv/ui/saves/SaveGameScreen.kt index 985cb1b7d2..43bc0b8009 100644 --- a/core/src/com/unciv/ui/saves/SaveGameScreen.kt +++ b/core/src/com/unciv/ui/saves/SaveGameScreen.kt @@ -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 } diff --git a/desktop/src/com/unciv/app/desktop/CustomSaveLocationHelperDesktop.kt b/desktop/src/com/unciv/app/desktop/CustomSaveLocationHelperDesktop.kt index fc025adfc3..af6e281a9a 100644 --- a/desktop/src/com/unciv/app/desktop/CustomSaveLocationHelperDesktop.kt +++ b/desktop/src/com/unciv/app/desktop/CustomSaveLocationHelperDesktop.kt @@ -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