Cache online mod list for fast load

This commit is contained in:
yairm210 2024-10-14 17:06:06 +03:00
parent f5946bc08f
commit f9d21d2767
4 changed files with 46 additions and 17 deletions

1
.gitignore vendored
View File

@ -144,6 +144,7 @@ android/release/
# Transient Unciv files # Transient Unciv files
android/assets/GameSettings.json android/assets/GameSettings.json
/android/assets/ModListCache.json
android/assets/lasterror.txt android/assets/lasterror.txt
android/assets/fonts/ android/assets/fonts/
android/assets/maps/ android/assets/maps/

View File

@ -22,6 +22,7 @@ import com.unciv.models.metadata.GameSettings
import com.unciv.models.metadata.doMigrations import com.unciv.models.metadata.doMigrations
import com.unciv.models.metadata.isMigrationNecessary import com.unciv.models.metadata.isMigrationNecessary
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.screens.modmanager.ModUIData
import com.unciv.ui.screens.savescreens.Gzip import com.unciv.ui.screens.savescreens.Gzip
import com.unciv.utils.Concurrency import com.unciv.utils.Concurrency
import com.unciv.utils.Log 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 MULTIPLAYER_FILES_FOLDER = "MultiplayerGames"
private const val AUTOSAVE_FILE_NAME = "Autosave" private const val AUTOSAVE_FILE_NAME = "Autosave"
const val SETTINGS_FILE_NAME = "GameSettings.json" const val SETTINGS_FILE_NAME = "GameSettings.json"
const val MOD_LIST_CACHE_FILE_NAME = "ModListCache.json"
class UncivFiles( class UncivFiles(
/** /**
@ -142,14 +144,6 @@ class UncivFiles(
return deleteSave(getSave(gameName)) 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! * Only use this with a [FileHandle] obtained by one of the methods of this class!
* *
@ -162,6 +156,7 @@ class UncivFiles(
} }
//endregion //endregion
//region Saving //region Saving
fun saveGame(game: GameInfo, gameName: String, saveCompletionCallback: (Exception?) -> Unit = { if (it != null) throw it }): FileHandle { fun saveGame(game: GameInfo, gameName: String, saveCompletionCallback: (Exception?) -> Unit = { if (it != null) throw it }): FileHandle {
@ -253,9 +248,6 @@ class UncivFiles(
return gameInfoFromString(gameData) return gameInfoFromString(gameData)
} }
fun loadGamePreviewByName(gameName: String) =
loadGamePreviewFromFile(getMultiplayerSave(gameName))
fun loadGamePreviewFromFile(gameFile: FileHandle): GameInfoPreview { fun loadGamePreviewFromFile(gameFile: FileHandle): GameInfoPreview {
return json().fromJson(GameInfoPreview::class.java, gameFile) ?: throw emptyFile(gameFile) return json().fromJson(GameInfoPreview::class.java, gameFile) ?: throw emptyFile(gameFile)
} }
@ -296,6 +288,7 @@ class UncivFiles(
//endregion //endregion
//region Settings //region Settings
private fun getGeneralSettingsFile(): FileHandle { private fun getGeneralSettingsFile(): FileHandle {
@ -357,6 +350,34 @@ class UncivFiles(
return game return game
} }
//endregion
//region Mod caching
fun saveModCache(modDataList: List<ModUIData>){
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<ModUIData>{
val file = getLocalFile(MOD_LIST_CACHE_FILE_NAME)
if (!file.exists()) return emptyList()
try {
return json().fromJsonFile(Array<ModUIData>::class.java, file)
.toList()
}
catch (ex: Exception){ // Not a huge deal if this fails
Log.error("Error loading mod cache", ex)
return emptyList()
}
}
//endregion //endregion
companion object { companion object {

View File

@ -109,8 +109,8 @@ class ModManagementScreen private constructor(
// Enable re-sorting and syncing entries in 'installed' and 'repo search' ScrollPanes // Enable re-sorting and syncing entries in 'installed' and 'repo search' ScrollPanes
// Keep metadata and buttons in separate pools // Keep metadata and buttons in separate pools
private val installedModInfo = previousInstalledMods ?: HashMap(10) // HashMap<String, ModUIData> inferred private val installedModInfo = previousInstalledMods ?: HashMap(10)
private val onlineModInfo = previousOnlineMods ?: HashMap(90) // HashMap<String, ModUIData> inferred private val onlineModInfo = previousOnlineMods ?: HashMap(game.files.loadModCache().associateBy { it.name })
private val modButtons: HashMap<ModUIData, ModDecoratedButton> = HashMap(100) private val modButtons: HashMap<ModUIData, ModDecoratedButton> = HashMap(100)
// cleanup - background processing needs to be stopped on exit and memory freed // 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() 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 // Now the tasks after the 'page' of search results has been fully processed
// The search has reached the last page! // The search has reached the last page!
if (repoSearch.items.size < amountPerPage) { if (repoSearch.items.size < amountPerPage) {

View File

@ -12,14 +12,17 @@ import com.unciv.ui.components.fonts.Fonts
* (This is important on resize - ModUIData are passed to the new screen) * (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. * 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 name: String,
val description: String, val description: String,
val ruleset: Ruleset?, val ruleset: Ruleset? = null,
val repo: GithubAPI.Repo?, val repo: GithubAPI.Repo? = null,
var isVisual: Boolean = false, var isVisual: Boolean = false,
var hasUpdate: Boolean = false var hasUpdate: Boolean = false
) { ) {
// For deserialization from cache file
constructor():this("","")
constructor(ruleset: Ruleset, isVisual: Boolean): this ( constructor(ruleset: Ruleset, isVisual: Boolean): this (
ruleset.name, ruleset.name,
ruleset.getSummary().let { ruleset.getSummary().let {
@ -46,7 +49,7 @@ internal class ModUIData private constructor(
else -> "" else -> ""
} }
fun matchesFilter(filter: ModManagementOptions.Filter): Boolean = when { internal fun matchesFilter(filter: ModManagementOptions.Filter): Boolean = when {
!matchesCategory(filter) -> false !matchesCategory(filter) -> false
filter.text.isEmpty() -> true filter.text.isEmpty() -> true
name.contains(filter.text, true) -> true name.contains(filter.text, true) -> true