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.logic.GameSaver
import com.unciv.models.metadata.GameSettings import com.unciv.models.metadata.GameSettings
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
import java.io.FileNotFoundException
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.io.Writer import java.io.Writer
@ -170,11 +171,14 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
var count = 0 var count = 0
for (gameFile in gameFiles) { for (gameFile in gameFiles) {
try { try {
gameIds[count] = GameSaver.getGameIdFromFile(gameFile) val gamePreview = GameSaver.loadGamePreviewFromFile(gameFile)
if (gamePreview.turnNotification) {
gameIds[count] = gamePreview.gameId
gameNames[count] = gameFile.name() gameNames[count] = gameFile.name()
count++ count++
}
} catch (ex: Throwable) { } 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 //nothing will be added to the arrays if it fails
//just skip one file //just skip one file
} }
@ -231,15 +235,16 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
var arrayIndex = 0 var arrayIndex = 0
// We only want to notify the user or update persisted notification once but still want // 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 // to download all games to update the files hence this bool
var foundGame = false var foundGame = ""
for (gameId in gameIds){ for (gameId in gameIds){
//gameId could be an empty string if startTurnChecker fails to load all files //gameId could be an empty string if startTurnChecker fails to load all files
if (gameId.isEmpty()) if (gameId.isEmpty())
continue continue
val game = OnlineMultiplayer().tryDownloadGameUninitialized(gameId) try {
val currentTurnPlayer = game.getCivilization(game.currentPlayer) val gamePreview = OnlineMultiplayer().tryDownloadGamePreview(gameId)
val currentTurnPlayer = gamePreview.getCivilization(gamePreview.currentPlayer)
//Save game so MultiplayerScreen gets updated //Save game so MultiplayerScreen gets updated
/* /*
@ -247,19 +252,28 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
All of them where missing a few thousand chars at the end of the save game. 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 I assume this happened because the TurnCheckerWorker gets canceled by the AndroidLauncher
while saves are getting saved right here. 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(game, gameNames[arrayIndex], true) GameSaver.saveGame(gamePreview, gameNames[arrayIndex])
if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!!) { if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame.isEmpty()) {
foundGame = true // We only save the first found game as the player will go into the
//As we do not need to look any further we can just break here // multiplayer screen anyway to join the game and see the other ones
break foundGame = gameNames[arrayIndex]
} }
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])
}
} }
if (foundGame){ if (foundGame.isNotEmpty()){
notifyUserAboutTurn(applicationContext, gameNames[arrayIndex]) notifyUserAboutTurn(applicationContext, foundGame)
with(NotificationManagerCompat.from(applicationContext)) { with(NotificationManagerCompat.from(applicationContext)) {
cancel(NOTIFICATION_ID_SERVICE) cancel(NOTIFICATION_ID_SERVICE)
} }

View File

@ -495,4 +495,18 @@ class GameInfoPreview() {
return this 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) customSaveLocationHelper!!.saveGame(game, GameName, forcePrompt = true, saveCompleteCallback = saveCompletionCallback)
} }
fun loadGameByName(GameName: String, multiplayer: Boolean = false) = fun loadGameByName(GameName: String) =
loadGameFromFile(getSave(GameName, multiplayer)) loadGameFromFile(getSave(GameName))
fun loadGameFromFile(gameFile: FileHandle): GameInfo { fun loadGameFromFile(gameFile: FileHandle): GameInfo {
val game = json().fromJson(GameInfo::class.java, gameFile) val game = json().fromJson(GameInfo::class.java, gameFile)
@ -72,6 +72,9 @@ object GameSaver {
return game return game
} }
fun loadGamePreviewByName(GameName: String) =
loadGamePreviewFromFile(getSave(GameName, true))
fun loadGamePreviewFromFile(gameFile: FileHandle): GameInfoPreview { fun loadGamePreviewFromFile(gameFile: FileHandle): GameInfoPreview {
return json().fromJson(GameInfoPreview::class.java, gameFile) return json().fromJson(GameInfoPreview::class.java, gameFile)
} }
@ -168,13 +171,4 @@ object GameSaver {
deleteSave(saveToDelete.name()) 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.pickerscreens.PickerScreen
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
import java.io.FileNotFoundException
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -144,6 +145,16 @@ class MultiplayerScreen(previousScreen: BaseScreen) : PickerScreen() {
try { try {
// The tryDownload can take more than 500ms. Therefore, to avoid ANRs, // The tryDownload can take more than 500ms. Therefore, to avoid ANRs,
// we need to run it in a different thread. // we need to run it in a different thread.
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() val gamePreview = OnlineMultiplayer().tryDownloadGame(gameId.trim()).asPreview()
if (gameName == "") if (gameName == "")
GameSaver.saveGame(gamePreview, gamePreview.gameId) GameSaver.saveGame(gamePreview, gamePreview.gameId)
@ -160,6 +171,15 @@ class MultiplayerScreen(previousScreen: BaseScreen) : PickerScreen() {
errorPopup.open() errorPopup.open()
} }
} }
} catch (ex: Exception) {
Gdx.app.postRunnable {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Could not download game!")
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
}
}
Gdx.app.postRunnable { Gdx.app.postRunnable {
addGameButton.setText(addGameText) addGameButton.setText(addGameText)
addGameButton.enable() addGameButton.enable()
@ -313,16 +333,30 @@ class MultiplayerScreen(previousScreen: BaseScreen) : PickerScreen() {
thread(name = "multiplayerGameDownload") { thread(name = "multiplayerGameDownload") {
for ((fileHandle, gameInfo) in multiplayerGames) { for ((fileHandle, gameInfo) in multiplayerGames) {
try { try {
// 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)) val game = gameInfo.updateCurrentTurn(OnlineMultiplayer().tryDownloadGame(gameInfo.gameId))
GameSaver.saveGame(game, fileHandle.name()) GameSaver.saveGame(game, fileHandle.name())
multiplayerGames[fileHandle] = game multiplayerGames[fileHandle] = game
} catch (ex: Exception) {
Gdx.app.postRunnable {
ToastPopup("Could not download game!" + " ${fileHandle.name()}", this)
}
}
} catch (ex: Exception) { } catch (ex: Exception) {
//skipping one is not fatal //skipping one is not fatal
//Trying to use as many prev. used strings as possible //Trying to use as many prev. used strings as possible
Gdx.app.postRunnable { Gdx.app.postRunnable {
ToastPopup("Could not download game!" + " ${fileHandle.name()}", this) ToastPopup("Could not download game!" + " ${fileHandle.name()}", this)
} }
continue
} }
} }

View File

@ -38,7 +38,12 @@ object DropBox {
} catch (ex: Exception) { } catch (ex: Exception) {
println(ex.message) println(ex.message)
val reader = BufferedReader(InputStreamReader(errorStream)) 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 return null
} catch (error: Error) { } catch (error: Error) {
println(error.message) println(error.message)
@ -145,14 +150,4 @@ class OnlineMultiplayer {
val zippedGameInfo = DropBox.downloadFileAsString("${getGameLocation(gameId)}_Preview") val zippedGameInfo = DropBox.downloadFileAsString("${getGameLocation(gameId)}_Preview")
return GameSaver.gameInfoPreviewFromString(Gzip.unzip(zippedGameInfo)) 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))
}
} }