From f9d21d276732ca6ad5d2e8b81290914fe2fad8ec Mon Sep 17 00:00:00 2001 From: yairm210 Date: Mon, 14 Oct 2024 17:06:06 +0300 Subject: [PATCH] Cache online mod list for fast load --- .gitignore | 1 + core/src/com/unciv/logic/files/UncivFiles.kt | 43 ++++++++++++++----- .../screens/modmanager/ModManagementScreen.kt | 8 +++- .../unciv/ui/screens/modmanager/ModUIData.kt | 11 +++-- 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index da05014f5a..204470dfc6 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,7 @@ android/release/ # Transient Unciv files android/assets/GameSettings.json +/android/assets/ModListCache.json android/assets/lasterror.txt android/assets/fonts/ android/assets/maps/ diff --git a/core/src/com/unciv/logic/files/UncivFiles.kt b/core/src/com/unciv/logic/files/UncivFiles.kt index ecc161a81f..c318e74975 100644 --- a/core/src/com/unciv/logic/files/UncivFiles.kt +++ b/core/src/com/unciv/logic/files/UncivFiles.kt @@ -22,6 +22,7 @@ import com.unciv.models.metadata.GameSettings import com.unciv.models.metadata.doMigrations import com.unciv.models.metadata.isMigrationNecessary import com.unciv.models.ruleset.RulesetCache +import com.unciv.ui.screens.modmanager.ModUIData import com.unciv.ui.screens.savescreens.Gzip import com.unciv.utils.Concurrency import com.unciv.utils.Log @@ -34,6 +35,7 @@ private const val SAVE_FILES_FOLDER = "SaveFiles" private const val MULTIPLAYER_FILES_FOLDER = "MultiplayerGames" private const val AUTOSAVE_FILE_NAME = "Autosave" const val SETTINGS_FILE_NAME = "GameSettings.json" +const val MOD_LIST_CACHE_FILE_NAME = "ModListCache.json" class UncivFiles( /** @@ -142,14 +144,6 @@ class UncivFiles( return deleteSave(getSave(gameName)) } - /** - * @return `true` if successful. - * @throws SecurityException when delete access was denied - */ - fun deleteMultiplayerSave(gameName: String): Boolean { - return deleteSave(getMultiplayerSave(gameName)) - } - /** * Only use this with a [FileHandle] obtained by one of the methods of this class! * @@ -162,6 +156,7 @@ class UncivFiles( } //endregion + //region Saving fun saveGame(game: GameInfo, gameName: String, saveCompletionCallback: (Exception?) -> Unit = { if (it != null) throw it }): FileHandle { @@ -253,9 +248,6 @@ class UncivFiles( return gameInfoFromString(gameData) } - fun loadGamePreviewByName(gameName: String) = - loadGamePreviewFromFile(getMultiplayerSave(gameName)) - fun loadGamePreviewFromFile(gameFile: FileHandle): GameInfoPreview { return json().fromJson(GameInfoPreview::class.java, gameFile) ?: throw emptyFile(gameFile) } @@ -296,6 +288,7 @@ class UncivFiles( //endregion + //region Settings private fun getGeneralSettingsFile(): FileHandle { @@ -357,6 +350,34 @@ class UncivFiles( return game } + //endregion + + //region Mod caching + fun saveModCache(modDataList: List){ + val file = getLocalFile(MOD_LIST_CACHE_FILE_NAME) + try { + json().toJson(modDataList, file) + } + catch (ex: Exception){ // Not a huge deal if this fails + Log.error("Error saving mod cache", ex) + } + } + + + fun loadModCache(): List{ + val file = getLocalFile(MOD_LIST_CACHE_FILE_NAME) + if (!file.exists()) return emptyList() + try { + return json().fromJsonFile(Array::class.java, file) + .toList() + } + catch (ex: Exception){ // Not a huge deal if this fails + Log.error("Error loading mod cache", ex) + return emptyList() + } + } + + //endregion companion object { diff --git a/core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt b/core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt index 5c80dffbf7..53919ca1c0 100644 --- a/core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt +++ b/core/src/com/unciv/ui/screens/modmanager/ModManagementScreen.kt @@ -109,8 +109,8 @@ class ModManagementScreen private constructor( // Enable re-sorting and syncing entries in 'installed' and 'repo search' ScrollPanes // Keep metadata and buttons in separate pools - private val installedModInfo = previousInstalledMods ?: HashMap(10) // HashMap inferred - private val onlineModInfo = previousOnlineMods ?: HashMap(90) // HashMap inferred + private val installedModInfo = previousInstalledMods ?: HashMap(10) + private val onlineModInfo = previousOnlineMods ?: HashMap(game.files.loadModCache().associateBy { it.name }) private val modButtons: HashMap = HashMap(100) // cleanup - background processing needs to be stopped on exit and memory freed @@ -349,6 +349,10 @@ class ModManagementScreen private constructor( onlineModsTable.add(getCachedModButton(mod)).row() } + Concurrency.run("Cache mod list"){ + game.files.saveModCache(onlineModInfo.values.toList()) + } + // Now the tasks after the 'page' of search results has been fully processed // The search has reached the last page! if (repoSearch.items.size < amountPerPage) { diff --git a/core/src/com/unciv/ui/screens/modmanager/ModUIData.kt b/core/src/com/unciv/ui/screens/modmanager/ModUIData.kt index f640cadb7f..7e320be777 100644 --- a/core/src/com/unciv/ui/screens/modmanager/ModUIData.kt +++ b/core/src/com/unciv/ui/screens/modmanager/ModUIData.kt @@ -12,14 +12,17 @@ import com.unciv.ui.components.fonts.Fonts * (This is important on resize - ModUIData are passed to the new screen) * Note it is guaranteed either ruleset or repo are non-null, never both. */ -internal class ModUIData private constructor( +class ModUIData private constructor( val name: String, val description: String, - val ruleset: Ruleset?, - val repo: GithubAPI.Repo?, + val ruleset: Ruleset? = null, + val repo: GithubAPI.Repo? = null, var isVisual: Boolean = false, var hasUpdate: Boolean = false ) { + // For deserialization from cache file + constructor():this("","") + constructor(ruleset: Ruleset, isVisual: Boolean): this ( ruleset.name, ruleset.getSummary().let { @@ -46,7 +49,7 @@ internal class ModUIData private constructor( else -> "" } - fun matchesFilter(filter: ModManagementOptions.Filter): Boolean = when { + internal fun matchesFilter(filter: ModManagementOptions.Filter): Boolean = when { !matchesCategory(filter) -> false filter.text.isEmpty() -> true name.contains(filter.text, true) -> true