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
This commit is contained in:
GGGuenni
2021-12-13 11:14:30 -08:00
committed by GitHub
parent 270c26c850
commit 28d3c12142
5 changed files with 98 additions and 47 deletions

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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))
}
}