mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-07 14:02:48 +07:00
Separate higher-level autosave functions from lower-level file functions
This commit is contained in:
parent
7a28f15106
commit
851ab2e7b8
@ -47,10 +47,10 @@ import com.unciv.utils.debug
|
||||
import com.unciv.utils.launchOnGLThread
|
||||
import com.unciv.utils.withGLContext
|
||||
import com.unciv.utils.withThreadPoolContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import java.io.PrintWriter
|
||||
import java.util.EnumSet
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
||||
open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpecific {
|
||||
|
||||
@ -376,7 +376,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
// already has, but if we do _our_ pause before the MusicController timer notices, it will at least remember the current track.
|
||||
if (::musicController.isInitialized) musicController.pause()
|
||||
val curGameInfo = gameInfo
|
||||
if (curGameInfo != null) files.requestAutoSave(curGameInfo)
|
||||
if (curGameInfo != null) files.autosaves.requestAutoSave(curGameInfo)
|
||||
super.pause()
|
||||
}
|
||||
|
||||
@ -395,7 +395,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
|
||||
val curGameInfo = gameInfo
|
||||
if (curGameInfo != null) {
|
||||
val autoSaveJob = files.autoSaveJob
|
||||
val autoSaveJob = files.autosaves.autoSaveJob
|
||||
if (autoSaveJob != null && autoSaveJob.isActive) {
|
||||
// auto save is already in progress (e.g. started by onPause() event)
|
||||
// let's allow it to finish and do not try to autosave second time
|
||||
@ -403,7 +403,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
autoSaveJob.join()
|
||||
}
|
||||
} else {
|
||||
files.autoSave(curGameInfo) // NO new thread
|
||||
files.autosaves.autoSave(curGameInfo) // NO new thread
|
||||
}
|
||||
}
|
||||
settings.save()
|
||||
@ -450,7 +450,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
fun goToMainMenu(): MainMenuScreen {
|
||||
val curGameInfo = gameInfo
|
||||
if (curGameInfo != null) {
|
||||
files.requestAutoSaveUnCloned(curGameInfo) // Can save gameInfo directly because the user can't modify it on the MainMenuScreen
|
||||
files.autosaves.requestAutoSaveUnCloned(curGameInfo) // Can save gameInfo directly because the user can't modify it on the MainMenuScreen
|
||||
}
|
||||
val mainMenuScreen = MainMenuScreen()
|
||||
pushScreen(mainMenuScreen)
|
||||
|
@ -22,9 +22,9 @@ import com.unciv.ui.screens.savescreens.Gzip
|
||||
import com.unciv.utils.Concurrency
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.debug
|
||||
import kotlinx.coroutines.Job
|
||||
import java.io.File
|
||||
import java.io.Writer
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
private const val SAVE_FILES_FOLDER = "SaveFiles"
|
||||
private const val MULTIPLAYER_FILES_FOLDER = "MultiplayerGames"
|
||||
@ -42,11 +42,9 @@ class UncivFiles(
|
||||
debug("Creating UncivFiles, localStoragePath: %s, externalStoragePath: %s",
|
||||
files.localStoragePath, files.externalStoragePath)
|
||||
}
|
||||
//region Data
|
||||
|
||||
var autoSaveJob: Job? = null
|
||||
val autosaves = Autosaves(this)
|
||||
|
||||
//endregion
|
||||
//region Helpers
|
||||
|
||||
fun getSave(gameName: String): FileHandle {
|
||||
@ -64,8 +62,8 @@ class UncivFiles(
|
||||
val externalFile = files.external(location)
|
||||
|
||||
val toReturn = if (files.isExternalStorageAvailable && (
|
||||
preferExternalStorage && (externalFile.exists() || !localFile.exists())
|
||||
|| !preferExternalStorage && (externalFile.exists() && !localFile.exists())
|
||||
externalFile.exists() && !localFile.exists() || // external file is only valid choice
|
||||
preferExternalStorage && (externalFile.exists() || !localFile.exists()) // unless local file is only valid choice, choose external
|
||||
) ) {
|
||||
externalFile
|
||||
} else {
|
||||
@ -80,14 +78,16 @@ class UncivFiles(
|
||||
* @throws GdxRuntimeException if the [path] represents a directory
|
||||
*/
|
||||
fun fileWriter(path: String, append: Boolean = false): Writer {
|
||||
val file = if (preferExternalStorage && files.isExternalStorageAvailable) {
|
||||
files.external(path)
|
||||
} else {
|
||||
files.local(path)
|
||||
}
|
||||
val file = pathToFileHandler(path)
|
||||
return file.writer(append, Charsets.UTF_8.name())
|
||||
}
|
||||
|
||||
fun pathToFileHandler(path: String): FileHandle {
|
||||
return if (preferExternalStorage && files.isExternalStorageAvailable) files.external(path)
|
||||
else files.local(path)
|
||||
}
|
||||
|
||||
|
||||
fun getMultiplayerSaves(): Sequence<FileHandle> {
|
||||
return getSaves(MULTIPLAYER_FILES_FOLDER)
|
||||
}
|
||||
@ -387,9 +387,11 @@ class UncivFiles(
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Autosave
|
||||
class Autosaves(val files: UncivFiles) {
|
||||
|
||||
var autoSaveJob: Job? = null
|
||||
|
||||
/**
|
||||
* Auto-saves a snapshot of the [gameInfo] in a new thread.
|
||||
@ -414,7 +416,7 @@ class UncivFiles(
|
||||
|
||||
fun autoSave(gameInfo: GameInfo, nextTurn: Boolean = false) {
|
||||
try {
|
||||
saveGame(gameInfo, AUTOSAVE_FILE_NAME)
|
||||
files.saveGame(gameInfo, AUTOSAVE_FILE_NAME)
|
||||
} catch (oom: OutOfMemoryError) {
|
||||
Log.error("Ran out of memory during autosave", oom)
|
||||
return // not much we can do here
|
||||
@ -424,40 +426,32 @@ class UncivFiles(
|
||||
if (nextTurn) {
|
||||
val newAutosaveFilename =
|
||||
SAVE_FILES_FOLDER + File.separator + AUTOSAVE_FILE_NAME + "-${gameInfo.currentPlayer}-${gameInfo.turns}"
|
||||
val file =
|
||||
if (preferExternalStorage && files.isExternalStorageAvailable)
|
||||
files.external(newAutosaveFilename)
|
||||
else
|
||||
files.local(newAutosaveFilename)
|
||||
getSave(AUTOSAVE_FILE_NAME).copyTo(file)
|
||||
val file = files.pathToFileHandler(newAutosaveFilename)
|
||||
files.getSave(AUTOSAVE_FILE_NAME).copyTo(file)
|
||||
|
||||
fun getAutosaves(): Sequence<FileHandle> {
|
||||
return getSaves().filter { it.name().startsWith(AUTOSAVE_FILE_NAME) }
|
||||
return files.getSaves().filter { it.name().startsWith(AUTOSAVE_FILE_NAME) }
|
||||
}
|
||||
while (getAutosaves().count() > 10) {
|
||||
val saveToDelete = getAutosaves().minByOrNull { it.lastModified() }!!
|
||||
deleteSave(saveToDelete.name())
|
||||
files.deleteSave(saveToDelete.name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadLatestAutosave(): GameInfo {
|
||||
return try {
|
||||
loadGameByName(AUTOSAVE_FILE_NAME)
|
||||
files.loadGameByName(AUTOSAVE_FILE_NAME)
|
||||
} catch (_: Exception) {
|
||||
// silent fail if we can't read the autosave for any reason - try to load the last autosave by turn number first
|
||||
val autosaves = getSaves().filter { it.name() != AUTOSAVE_FILE_NAME && it.name().startsWith(
|
||||
val autosaves = files.getSaves().filter { it.name() != AUTOSAVE_FILE_NAME && it.name().startsWith(
|
||||
AUTOSAVE_FILE_NAME
|
||||
) }
|
||||
loadGameFromFile(autosaves.maxByOrNull { it.lastModified() }!!)
|
||||
files.loadGameFromFile(autosaves.maxByOrNull { it.lastModified() }!!)
|
||||
}
|
||||
}
|
||||
|
||||
fun autosaveExists(): Boolean {
|
||||
return getSave(AUTOSAVE_FILE_NAME).exists()
|
||||
}
|
||||
|
||||
// endregion
|
||||
fun autosaveExists(): Boolean = files.getSave(AUTOSAVE_FILE_NAME).exists()
|
||||
}
|
||||
|
||||
class IncompatibleGameInfoVersionException(
|
||||
|
@ -137,7 +137,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
|
||||
val column1 = Table().apply { defaults().pad(10f).fillX() }
|
||||
val column2 = if (singleColumn) column1 else Table().apply { defaults().pad(10f).fillX() }
|
||||
|
||||
if (game.files.autosaveExists()) {
|
||||
if (game.files.autosaves.autosaveExists()) {
|
||||
val resumeTable = getMenuButton("Resume","OtherIcons/Resume", KeyboardBinding.Resume)
|
||||
{ resumeGame() }
|
||||
column1.add(resumeTable).row()
|
||||
|
@ -322,7 +322,7 @@ class NewGameScreen(
|
||||
newGame.isUpToDate = true // So we don't try to download it from dropbox the second after we upload it - the file is not yet ready for loading!
|
||||
try {
|
||||
game.onlineMultiplayer.createGame(newGame)
|
||||
game.files.requestAutoSave(newGame)
|
||||
game.files.autosaves.requestAutoSave(newGame)
|
||||
} catch (ex: FileStorageRateLimitReached) {
|
||||
launchOnGLThread {
|
||||
popup.reuseWith("Server limit reached! Please wait for [${ex.limitRemainingSeconds}] seconds", true)
|
||||
|
@ -71,7 +71,7 @@ object QuickSave {
|
||||
|
||||
val savedGame: GameInfo
|
||||
try {
|
||||
savedGame = screen.game.files.loadLatestAutosave()
|
||||
savedGame = screen.game.files.autosaves.loadLatestAutosave()
|
||||
} catch (_: OutOfMemoryError) {
|
||||
outOfMemory()
|
||||
return@run
|
||||
|
@ -811,7 +811,7 @@ class WorldScreen(
|
||||
fun autoSave() {
|
||||
waitingForAutosave = true
|
||||
shouldUpdate = true
|
||||
UncivGame.Current.files.requestAutoSave(gameInfo, true).invokeOnCompletion {
|
||||
UncivGame.Current.files.autosaves.requestAutoSave(gameInfo, true).invokeOnCompletion {
|
||||
// only enable the user to next turn once we've saved the current one
|
||||
waitingForAutosave = false
|
||||
shouldUpdate = true
|
||||
|
@ -2137,6 +2137,9 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
|
||||
|
||||
Applicable to: TriggerCondition
|
||||
|
||||
??? example "<upon turn end>"
|
||||
Applicable to: TriggerCondition
|
||||
|
||||
??? example "<upon founding a Pantheon>"
|
||||
Applicable to: TriggerCondition
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user