From 28d3c1214232fe88c81351b9c2bbb3b127d1d55a Mon Sep 17 00:00:00 2001 From: GGGuenni Date: Mon, 13 Dec 2021 11:14:30 -0800 Subject: [PATCH] Improvements to TurnChecker data usage (#5789) * Added necessary utils Added the FileNotFoundException which is also part of #5593 * Added preview download to MultiplayerScreen * Added preview download to TurnChecker * Fixed uncomplete commit * Removed debug print in MultiplayerScreen --- .../unciv/app/MultiplayerTurnCheckWorker.kt | 58 ++++++++++++------- core/src/com/unciv/logic/GameInfo.kt | 14 +++++ core/src/com/unciv/logic/GameSaver.kt | 16 ++--- .../unciv/ui/multiplayer/MultiplayerScreen.kt | 40 ++++++++++++- .../unciv/ui/worldscreen/mainmenu/DropBox.kt | 17 ++---- 5 files changed, 98 insertions(+), 47 deletions(-) diff --git a/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt b/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt index ca59c1a160..7abc9b21b9 100644 --- a/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt +++ b/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt @@ -16,6 +16,7 @@ import com.unciv.logic.GameInfo import com.unciv.logic.GameSaver import com.unciv.models.metadata.GameSettings import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer +import java.io.FileNotFoundException import java.io.PrintWriter import java.io.StringWriter import java.io.Writer @@ -170,11 +171,14 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame var count = 0 for (gameFile in gameFiles) { try { - gameIds[count] = GameSaver.getGameIdFromFile(gameFile) - gameNames[count] = gameFile.name() - count++ + val gamePreview = GameSaver.loadGamePreviewFromFile(gameFile) + if (gamePreview.turnNotification) { + gameIds[count] = gamePreview.gameId + gameNames[count] = gameFile.name() + count++ + } } catch (ex: Throwable) { - //only getGameIdFromFile can throw an exception + //only loadGamePreviewFromFile can throw an exception //nothing will be added to the arrays if it fails //just skip one file } @@ -231,35 +235,45 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame var arrayIndex = 0 // We only want to notify the user or update persisted notification once but still want // to download all games to update the files hence this bool - var foundGame = false + var foundGame = "" for (gameId in gameIds){ //gameId could be an empty string if startTurnChecker fails to load all files if (gameId.isEmpty()) continue - val game = OnlineMultiplayer().tryDownloadGameUninitialized(gameId) - val currentTurnPlayer = game.getCivilization(game.currentPlayer) + try { + val gamePreview = OnlineMultiplayer().tryDownloadGamePreview(gameId) + val currentTurnPlayer = gamePreview.getCivilization(gamePreview.currentPlayer) - //Save game so MultiplayerScreen gets updated - /* - I received multiple reports regarding broken save games. - All of them where missing a few thousand chars at the end of the save game. - I assume this happened because the TurnCheckerWorker gets canceled by the AndroidLauncher - while saves are getting saved right here. - */ - //GameSaver.saveGame(game, gameNames[arrayIndex], true) + //Save game so MultiplayerScreen gets updated + /* + I received multiple reports regarding broken save games. + All of them where missing a few thousand chars at the end of the save game. + I assume this happened because the TurnCheckerWorker gets canceled by the AndroidLauncher + while saves are getting saved right here. + Lets hope it works with gamePreview as they are a lot smaller and faster to save + */ + GameSaver.saveGame(gamePreview, gameNames[arrayIndex]) - if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!!) { - foundGame = true - //As we do not need to look any further we can just break here - break + if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame.isEmpty()) { + // We only save the first found game as the player will go into the + // multiplayer screen anyway to join the game and see the other ones + foundGame = gameNames[arrayIndex] + } + arrayIndex++ + } catch (ex: FileNotFoundException){ + // FileNotFoundException is thrown by OnlineMultiplayer().tryDownloadGamePreview(gameId) + // and indicates that there is no game preview present for this game + // in the dropbox so we should not check for this game in the future anymore + val currentGamePreview = GameSaver.loadGamePreviewByName(gameNames[arrayIndex]) + currentGamePreview.turnNotification = false + GameSaver.saveGame(currentGamePreview, gameNames[arrayIndex]) } - arrayIndex++ } - if (foundGame){ - notifyUserAboutTurn(applicationContext, gameNames[arrayIndex]) + if (foundGame.isNotEmpty()){ + notifyUserAboutTurn(applicationContext, foundGame) with(NotificationManagerCompat.from(applicationContext)) { cancel(NOTIFICATION_ID_SERVICE) } diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 894f34311e..b722dcd130 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -495,4 +495,18 @@ class GameInfoPreview() { return this } + + /** + * Updates the current player and turn information in the GameInfoPreview object with the + * help of another GameInfoPreview object. + */ + fun updateCurrentTurn(gameInfo: GameInfoPreview) : GameInfoPreview { + currentPlayer = gameInfo.currentPlayer + turns = gameInfo.turns + currentTurnStartTime = gameInfo.currentTurnStartTime + //We update the civilizations in case someone is removed from the game (resign/kick) + civilizations = gameInfo.civilizations + + return this + } } diff --git a/core/src/com/unciv/logic/GameSaver.kt b/core/src/com/unciv/logic/GameSaver.kt index ca6b2aee28..f15d9c26b0 100644 --- a/core/src/com/unciv/logic/GameSaver.kt +++ b/core/src/com/unciv/logic/GameSaver.kt @@ -63,8 +63,8 @@ object GameSaver { customSaveLocationHelper!!.saveGame(game, GameName, forcePrompt = true, saveCompleteCallback = saveCompletionCallback) } - fun loadGameByName(GameName: String, multiplayer: Boolean = false) = - loadGameFromFile(getSave(GameName, multiplayer)) + fun loadGameByName(GameName: String) = + loadGameFromFile(getSave(GameName)) fun loadGameFromFile(gameFile: FileHandle): GameInfo { val game = json().fromJson(GameInfo::class.java, gameFile) @@ -72,6 +72,9 @@ object GameSaver { return game } + fun loadGamePreviewByName(GameName: String) = + loadGamePreviewFromFile(getSave(GameName, true)) + fun loadGamePreviewFromFile(gameFile: FileHandle): GameInfoPreview { return json().fromJson(GameInfoPreview::class.java, gameFile) } @@ -168,13 +171,4 @@ object GameSaver { deleteSave(saveToDelete.name()) } } - - /** - * Returns the gameId from a GameInfo which was saved as JSON for multiplayer - * Does not initialize transitive GameInfo data. - * It is therefore stateless and save to call for Multiplayer Turn Notifier. - */ - fun getGameIdFromFile(gameFile: FileHandle): String { - return json().fromJson(GameInfo::class.java, gameFile).gameId - } } diff --git a/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt b/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt index 3d7a2947ab..8e4e72673f 100644 --- a/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt +++ b/core/src/com/unciv/ui/multiplayer/MultiplayerScreen.kt @@ -8,6 +8,7 @@ import com.unciv.models.translations.tr import com.unciv.ui.pickerscreens.PickerScreen import com.unciv.ui.utils.* import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer +import java.io.FileNotFoundException import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.concurrent.thread @@ -144,13 +145,32 @@ class MultiplayerScreen(previousScreen: BaseScreen) : PickerScreen() { try { // The tryDownload can take more than 500ms. Therefore, to avoid ANRs, // we need to run it in a different thread. - val gamePreview = OnlineMultiplayer().tryDownloadGame(gameId.trim()).asPreview() + val gamePreview = OnlineMultiplayer().tryDownloadGamePreview(gameId.trim()) if (gameName == "") GameSaver.saveGame(gamePreview, gamePreview.gameId) else GameSaver.saveGame(gamePreview, gameName) Gdx.app.postRunnable { reloadGameListUI() } + } catch (ex: FileNotFoundException) { + // Game is so old that a preview could not be found on dropbox lets try the real gameInfo instead + try { + val gamePreview = OnlineMultiplayer().tryDownloadGame(gameId.trim()).asPreview() + if (gameName == "") + GameSaver.saveGame(gamePreview, gamePreview.gameId) + else + GameSaver.saveGame(gamePreview, gameName) + + Gdx.app.postRunnable { reloadGameListUI() } + } catch (ex: Exception) { + Gdx.app.postRunnable { + val errorPopup = Popup(this) + errorPopup.addGoodSizedLabel("Could not download game!") + errorPopup.row() + errorPopup.addCloseButton() + errorPopup.open() + } + } } catch (ex: Exception) { Gdx.app.postRunnable { val errorPopup = Popup(this) @@ -313,16 +333,30 @@ class MultiplayerScreen(previousScreen: BaseScreen) : PickerScreen() { thread(name = "multiplayerGameDownload") { for ((fileHandle, gameInfo) in multiplayerGames) { try { - val game = gameInfo.updateCurrentTurn(OnlineMultiplayer().tryDownloadGame(gameInfo.gameId)) + // Update game without overriding multiplayer settings + val game = gameInfo.updateCurrentTurn(OnlineMultiplayer().tryDownloadGamePreview(gameInfo.gameId)) GameSaver.saveGame(game, fileHandle.name()) multiplayerGames[fileHandle] = game + + } catch (ex: FileNotFoundException) { + // Game is so old that a preview could not be found on dropbox lets try the real gameInfo instead + try { + // Update game without overriding multiplayer settings + val game = gameInfo.updateCurrentTurn(OnlineMultiplayer().tryDownloadGame(gameInfo.gameId)) + GameSaver.saveGame(game, fileHandle.name()) + multiplayerGames[fileHandle] = game + + } catch (ex: Exception) { + Gdx.app.postRunnable { + ToastPopup("Could not download game!" + " ${fileHandle.name()}", this) + } + } } catch (ex: Exception) { //skipping one is not fatal //Trying to use as many prev. used strings as possible Gdx.app.postRunnable { ToastPopup("Could not download game!" + " ${fileHandle.name()}", this) } - continue } } diff --git a/core/src/com/unciv/ui/worldscreen/mainmenu/DropBox.kt b/core/src/com/unciv/ui/worldscreen/mainmenu/DropBox.kt index a3a6f84a3f..f30d9f9aa5 100644 --- a/core/src/com/unciv/ui/worldscreen/mainmenu/DropBox.kt +++ b/core/src/com/unciv/ui/worldscreen/mainmenu/DropBox.kt @@ -38,7 +38,12 @@ object DropBox { } catch (ex: Exception) { println(ex.message) val reader = BufferedReader(InputStreamReader(errorStream)) - println(reader.readText()) + val responseString = reader.readText() + println(responseString) + + // Throw Exceptions based on the HTTP response from dropbox + if (responseString.contains("path/not_found/")) + throw FileNotFoundException() return null } catch (error: Error) { println(error.message) @@ -145,14 +150,4 @@ class OnlineMultiplayer { val zippedGameInfo = DropBox.downloadFileAsString("${getGameLocation(gameId)}_Preview") return GameSaver.gameInfoPreviewFromString(Gzip.unzip(zippedGameInfo)) } - - /** - * WARNING! - * Does not initialize transitive GameInfo data. - * It is therefore stateless and safe to call for Multiplayer Turn Notifier, unlike tryDownloadGame(). - */ - fun tryDownloadGameUninitialized(gameId: String): GameInfo { - val zippedGameInfo = DropBox.downloadFileAsString(getGameLocation(gameId)) - return GameSaver.gameInfoFromStringWithoutTransients(Gzip.unzip(zippedGameInfo)) - } }