Rename save/load completion callbacks for custom locations and implement error handling

This commit is contained in:
William Brawner 2020-09-20 12:34:39 -07:00
parent f9b43c9b61
commit a4d3e80f57
6 changed files with 102 additions and 54 deletions

View file

@ -28,7 +28,7 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
@GuardedBy("this")
private val callbacks = ArrayList<IndexedCallback>()
override fun saveGame(gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, block: (() -> Unit)?) {
override fun saveGame(gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, saveCompleteCallback: ((Exception?) -> Unit)?) {
val callbackIndex = synchronized(this) {
val index = callbackIndex++
callbacks.add(IndexedCallback(
@ -36,8 +36,10 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
{ uri ->
if (uri != null) {
saveGame(gameInfo, uri)
saveCompleteCallback?.invoke(null)
} else {
saveCompleteCallback?.invoke(RuntimeException("Uri was null"))
}
block?.invoke()
}
))
index
@ -76,25 +78,33 @@ class CustomSaveLocationHelperAndroid(private val activity: Activity) : CustomSa
}
}
override fun loadGame(block: (GameInfo) -> Unit) {
override fun loadGame(loadCompleteCallback: (GameInfo?, Exception?) -> Unit) {
val callbackIndex = synchronized(this) {
val index = callbackIndex++
callbacks.add(IndexedCallback(
index,
callback@{ uri ->
if (uri == null) return@callback
val game = activity.contentResolver.openInputStream(uri)
var exception: Exception? = null
val game = try {
activity.contentResolver.openInputStream(uri)
?.reader()
?.readText()
?.run {
GameSaver.gameInfoFromString(this)
}
} catch (e: Exception) {
exception = e
null
}
if (game != null) {
// 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
// here
game.customSaveLocation = uri.toString()
block(game)
loadCompleteCallback(game, null)
} else {
loadCompleteCallback(null, RuntimeException("Failed to load save game", exception))
}
}
))

View file

@ -6,8 +6,8 @@ package com.unciv.logic
*/
interface CustomSaveLocationHelper {
/**
* Saves a game asynchronously with a given default name and then calls the [block] callback
* upon completion. The [block] callback will be called from the same thread that this method
* Saves a game asynchronously with a given default name and then calls the [saveCompleteCallback] callback
* upon completion. The [saveCompleteCallback] callback will be called from the same thread that this method
* is called from. If the [GameInfo] object already has the
* [customSaveLocation][GameInfo.customSaveLocation] property defined (not null), then the user
* will not be prompted to select a location for the save unless [forcePrompt] is set to true
@ -17,12 +17,12 @@ interface CustomSaveLocationHelper {
gameInfo: GameInfo,
gameName: String,
forcePrompt: Boolean = false,
block: (() -> Unit)? = null
saveCompleteCallback: ((Exception?) -> Unit)? = null
)
/**
* Loads a game from an external source asynchronously, then calls [block] with the loaded
* Loads a game from an external source asynchronously, then calls [loadCompleteCallback] with the loaded
* [GameInfo]
*/
fun loadGame(block: (GameInfo) -> Unit)
fun loadGame(loadCompleteCallback: (GameInfo?, Exception?) -> Unit)
}

View file

@ -39,19 +39,19 @@ object GameSaver {
return localSaves + Gdx.files.absolute(externalFilesDirForAndroid + "/${getSubfolder(multiplayer)}").list().asSequence()
}
fun saveGame(game: GameInfo, GameName: String, multiplayer: Boolean = false, forcePrompt: Boolean = false, block: (() -> Unit)? = null) {
fun saveGame(game: GameInfo, GameName: String, multiplayer: Boolean = false, forcePrompt: Boolean = false, saveCompletionCallback: ((Exception?) -> Unit)? = null) {
val customSaveLocation = game.customSaveLocation
val customSaveLocationHelper = this.customSaveLocationHelper
if (customSaveLocation != null && customSaveLocationHelper != null) {
customSaveLocationHelper.saveGame(game, GameName, forcePrompt, block)
customSaveLocationHelper.saveGame(game, GameName, forcePrompt, saveCompletionCallback)
} else {
json().toJson(game, getSave(GameName, multiplayer))
block?.invoke()
saveCompletionCallback?.invoke(null)
}
}
fun saveGameToCustomLocation(game: GameInfo, GameName: String, block: () -> Unit) {
customSaveLocationHelper!!.saveGame(game, GameName, forcePrompt = true, block =block)
fun saveGameToCustomLocation(game: GameInfo, GameName: String, saveCompletionCallback: (Exception?) -> Unit) {
customSaveLocationHelper!!.saveGame(game, GameName, forcePrompt = true, saveCompleteCallback = saveCompletionCallback)
}
fun loadGameByName(GameName: String, multiplayer: Boolean = false) =
@ -63,9 +63,9 @@ object GameSaver {
return game
}
fun loadGameFromCustomLocation(block: (GameInfo) -> Unit) {
customSaveLocationHelper!!.loadGame { game ->
block(game.apply { setTransients() })
fun loadGameFromCustomLocation(loadCompletionCallback: (GameInfo?, Exception?) -> Unit) {
customSaveLocationHelper!!.loadGame { game, e ->
loadCompletionCallback(game?.apply { setTransients() }, e)
}
}

View file

@ -13,6 +13,7 @@ import com.unciv.ui.pickerscreens.PickerScreen
import com.unciv.ui.utils.*
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.CancellationException
import kotlin.concurrent.thread
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
@ -81,8 +82,13 @@ class LoadGameScreen(previousScreen:CameraStageBaseScreen) : PickerScreen() {
if (GameSaver.canLoadFromCustomSaveLocation()) {
val loadFromCustomLocation = "Load from custom location".toTextButton()
loadFromCustomLocation.onClick {
GameSaver.loadGameFromCustomLocation {
game.loadGame(it)
GameSaver.loadGameFromCustomLocation { gameInfo, exception ->
if (gameInfo != null) {
game.loadGame(gameInfo)
} else if (exception !is CancellationException) {
errorLabel.setText("Could not load game from custom location!".tr())
exception?.printStackTrace()
}
}
}
rightSideTable.add(loadFromCustomLocation).pad(10f).row()

View file

@ -1,6 +1,7 @@
package com.unciv.ui.saves
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextField
@ -10,6 +11,7 @@ import com.unciv.logic.GameSaver
import com.unciv.models.translations.tr
import com.unciv.ui.pickerscreens.PickerScreen
import com.unciv.ui.utils.*
import java.util.concurrent.CancellationException
import kotlin.concurrent.thread
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
@ -42,17 +44,26 @@ class SaveGameScreen : PickerScreen() {
newSave.add(copyJsonButton).row()
if (GameSaver.canLoadFromCustomSaveLocation()) {
val saveToCustomLocation = "Save to custom location".toTextButton()
val errorLabel = "".toLabel(Color.RED)
saveToCustomLocation.enable()
saveToCustomLocation.onClick {
errorLabel.setText("")
saveToCustomLocation.setText("Saving...".tr())
saveToCustomLocation.disable()
thread(name = "SaveGame") {
GameSaver.saveGameToCustomLocation(UncivGame.Current.gameInfo, textField.text) {
GameSaver.saveGameToCustomLocation(UncivGame.Current.gameInfo, textField.text) { e ->
if (e == null) {
Gdx.app.postRunnable { UncivGame.Current.setWorldScreen() }
} else if (e !is CancellationException) {
errorLabel.setText("Could not save game to custom location".tr())
e.printStackTrace()
}
saveToCustomLocation.enable()
}
}
}
newSave.add(saveToCustomLocation).pad(10f).row()
newSave.add(errorLabel).pad(0f, 10f, 10f, 10f).row()
}

View file

@ -7,19 +7,24 @@ import com.unciv.logic.GameSaver
import com.unciv.logic.GameSaver.json
import java.awt.event.WindowEvent
import java.io.File
import java.util.concurrent.CancellationException
import javax.swing.JFileChooser
import javax.swing.JFrame
class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
override fun saveGame(gameInfo: GameInfo, gameName: String, forcePrompt: Boolean, block: (() -> 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(json().toJson(gameInfo))
}
block?.invoke()
saveCompleteCallback?.invoke(null)
} catch (e: Exception) {
saveCompleteCallback?.invoke(e)
}
return
}
@ -36,18 +41,25 @@ class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING))
}
val file = fileChooser.selectedFile
var exception: Exception? = null
if (file != null) {
gameInfo.customSaveLocation = file.absolutePath
try {
file.outputStream()
.writer()
.use {
it.write(json().toJson(gameInfo))
}
} catch (e: Exception) {
exception = e
}
block?.invoke()
} else {
exception = CancellationException()
}
saveCompleteCallback?.invoke(exception)
}
override fun loadGame(block: (GameInfo) -> Unit) {
override fun loadGame(loadCompleteCallback: (GameInfo?, Exception?) -> Unit) {
val fileChooser = JFileChooser().apply fileChooser@{
currentDirectory = Gdx.files.local("").file()
}
@ -60,7 +72,10 @@ class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
dispatchEvent(WindowEvent(this, WindowEvent.WINDOW_CLOSING))
}
val file = fileChooser.selectedFile
var exception: Exception? = null
var gameInfo: GameInfo? = null
if (file != null) {
try {
file.inputStream()
.reader()
.readText()
@ -70,8 +85,14 @@ class CustomSaveLocationHelperDesktop : CustomSaveLocationHelper {
// then the save location might not be right so we have to correct for that
// here
customSaveLocation = file.absolutePath
block(this)
}
}
gameInfo = this
}
} catch (e: Exception) {
exception = e
}
} else {
exception = CancellationException()
}
loadCompleteCallback(gameInfo, exception)
}
}