mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-30 23:41:03 -04:00
Only *display* first 5 missing mods but auto-download all (#9543)
* Only *display* first 5 missing mods but autodownload all * Fix removeMissingTerrainModReferences * Linting
This commit is contained in:
parent
fe1b5825bb
commit
86aa3b842b
@ -544,12 +544,10 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
|||||||
// any mod the saved game lists that is currently not installed causes null pointer
|
// any mod the saved game lists that is currently not installed causes null pointer
|
||||||
// exceptions in this routine unless it contained no new objects or was very simple.
|
// exceptions in this routine unless it contained no new objects or was very simple.
|
||||||
// Player's fault, so better complain early:
|
// Player's fault, so better complain early:
|
||||||
val missingMods = (gameParameters.mods + gameParameters.baseRuleset)
|
val missingMods = (listOf(gameParameters.baseRuleset) + gameParameters.mods)
|
||||||
.filterNot { it in ruleset.mods }
|
.filterNot { it in ruleset.mods }
|
||||||
.joinToString(limit = 5) { it }
|
if (missingMods.isNotEmpty())
|
||||||
if (missingMods.isNotEmpty()) {
|
|
||||||
throw MissingModsException(missingMods)
|
throw MissingModsException(missingMods)
|
||||||
}
|
|
||||||
|
|
||||||
removeMissingModReferences()
|
removeMissingModReferences()
|
||||||
|
|
||||||
|
@ -19,6 +19,14 @@ open class UncivShowableException(
|
|||||||
override fun getLocalizedMessage() = message.tr()
|
override fun getLocalizedMessage() = message.tr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** An [Exception] indicating a game or map cannot be loaded because [mods][com.unciv.models.metadata.GameParameters.mods] are missing.
|
||||||
|
* @param missingMods Any [Iterable] or [Collection] of Strings - will be stored entirely,
|
||||||
|
* but be included in the Exception's message only up to its five first elements.
|
||||||
|
*/
|
||||||
class MissingModsException(
|
class MissingModsException(
|
||||||
val missingMods: String
|
val missingMods: Iterable<String>
|
||||||
) : UncivShowableException("Missing mods: [$missingMods]")
|
) : UncivShowableException("Missing mods: [${shorten(missingMods)}]") {
|
||||||
|
companion object {
|
||||||
|
private fun shorten(missingMods: Iterable<String>) = missingMods.joinToString(limit = 5) { it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -464,16 +464,13 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun removeMissingTerrainModReferences(ruleSet: Ruleset) {
|
fun removeMissingTerrainModReferences(ruleSet: Ruleset) {
|
||||||
|
// This will run before setTransients, so do not rely e.g. on Tile.ruleset being available.
|
||||||
|
// That rules out Tile.removeTerrainFeature, which refreshes object/unique caches
|
||||||
for (tile in this.values) {
|
for (tile in this.values) {
|
||||||
for (terrainFeature in tile.terrainFeatures.filter { !ruleSet.terrains.containsKey(it) })
|
tile.removeMissingTerrainModReferences(ruleSet)
|
||||||
tile.removeTerrainFeature(terrainFeature)
|
|
||||||
if (tile.resource != null && !ruleSet.tileResources.containsKey(tile.resource!!))
|
|
||||||
tile.resource = null
|
|
||||||
if (tile.improvement != null && !ruleSet.tileImprovements.containsKey(tile.improvement!!))
|
|
||||||
tile.changeImprovement(null)
|
|
||||||
}
|
}
|
||||||
for (startingLocation in startingLocations.toList())
|
for (startingLocation in startingLocations.toList())
|
||||||
if (startingLocation.nation !in ruleSet.nations.keys)
|
if (startingLocation.nation !in ruleSet.nations)
|
||||||
startingLocations.remove(startingLocation)
|
startingLocations.remove(startingLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,6 +852,16 @@ open class Tile : IsPartOfGameInfoSerialization {
|
|||||||
fun removeTerrainFeatures() =
|
fun removeTerrainFeatures() =
|
||||||
setTerrainFeatures(listOf())
|
setTerrainFeatures(listOf())
|
||||||
|
|
||||||
|
/** Clean stuff missing in [ruleset] - called from [TileMap.removeMissingTerrainModReferences]
|
||||||
|
* Must be able to run before [setTransients] - and does not need to fix transients.
|
||||||
|
*/
|
||||||
|
fun removeMissingTerrainModReferences(ruleset: Ruleset) {
|
||||||
|
terrainFeatures = terrainFeatures.filter { it in ruleset.terrains }
|
||||||
|
if (resource != null && resource !in ruleset.tileResources)
|
||||||
|
resource = null
|
||||||
|
if (improvement != null && improvement !in ruleset.tileImprovements)
|
||||||
|
changeImprovement(null)
|
||||||
|
}
|
||||||
|
|
||||||
/** If the unit isn't in the ruleset we can't even know what type of unit this is! So check each place
|
/** If the unit isn't in the ruleset we can't even know what type of unit this is! So check each place
|
||||||
* This works with no transients so can be called from gameInfo.setTransients with no fear
|
* This works with no transients so can be called from gameInfo.setTransients with no fear
|
||||||
|
@ -4,7 +4,7 @@ import com.badlogic.gdx.Gdx
|
|||||||
import com.badlogic.gdx.files.FileHandle
|
import com.badlogic.gdx.files.FileHandle
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.unciv.Constants
|
import com.unciv.logic.MissingModsException
|
||||||
import com.unciv.logic.files.MapSaver
|
import com.unciv.logic.files.MapSaver
|
||||||
import com.unciv.logic.UncivShowableException
|
import com.unciv.logic.UncivShowableException
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
@ -112,29 +112,21 @@ class MapEditorLoadTab(
|
|||||||
val map = MapSaver.loadMap(chosenMap!!)
|
val map = MapSaver.loadMap(chosenMap!!)
|
||||||
if (!isActive) return
|
if (!isActive) return
|
||||||
|
|
||||||
val missingMods = map.mapParameters.mods.filter { it !in RulesetCache }.toMutableList()
|
|
||||||
// [TEMPORARY] conversion of old maps with a base ruleset contained in the mods
|
|
||||||
val newBaseRuleset = map.mapParameters.mods.filter { it !in missingMods }.firstOrNull { RulesetCache[it]!!.modOptions.isBaseRuleset }
|
|
||||||
if (newBaseRuleset != null) map.mapParameters.baseRuleset = newBaseRuleset
|
|
||||||
//
|
|
||||||
if (map.mapParameters.baseRuleset !in RulesetCache) missingMods += map.mapParameters.baseRuleset
|
|
||||||
|
|
||||||
if (missingMods.isNotEmpty()) {
|
|
||||||
Concurrency.runOnGLThread {
|
|
||||||
needPopup = false
|
|
||||||
popup?.close()
|
|
||||||
ToastPopup("Missing mods: [${missingMods.joinToString()}]", editorScreen)
|
|
||||||
}
|
|
||||||
} else Concurrency.runOnGLThread {
|
|
||||||
Gdx.input.inputProcessor = null // This is to stop ANRs happening here, until the map editor screen sets up.
|
|
||||||
try {
|
|
||||||
// For deprecated maps, set the base ruleset field if it's still saved in the mods field
|
// For deprecated maps, set the base ruleset field if it's still saved in the mods field
|
||||||
val modBaseRuleset = map.mapParameters.mods.firstOrNull { RulesetCache[it]!!.modOptions.isBaseRuleset }
|
val modBaseRuleset = map.mapParameters.mods.firstOrNull { RulesetCache[it]?.modOptions?.isBaseRuleset == true }
|
||||||
if (modBaseRuleset != null) {
|
if (modBaseRuleset != null) {
|
||||||
map.mapParameters.baseRuleset = modBaseRuleset
|
map.mapParameters.baseRuleset = modBaseRuleset
|
||||||
map.mapParameters.mods -= modBaseRuleset
|
map.mapParameters.mods -= modBaseRuleset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val missingMods = (setOf(map.mapParameters.baseRuleset) + map.mapParameters.mods)
|
||||||
|
.filterNot { it in RulesetCache }
|
||||||
|
if (missingMods.isNotEmpty())
|
||||||
|
throw MissingModsException(missingMods)
|
||||||
|
|
||||||
|
Concurrency.runOnGLThread {
|
||||||
|
Gdx.input.inputProcessor = null // This is to stop ANRs happening here, until the map editor screen sets up.
|
||||||
|
try {
|
||||||
val ruleset = RulesetCache.getComplexRuleset(map.mapParameters)
|
val ruleset = RulesetCache.getComplexRuleset(map.mapParameters)
|
||||||
val rulesetIncompatibilities = map.getRulesetIncompatibility(ruleset)
|
val rulesetIncompatibilities = map.getRulesetIncompatibility(ruleset)
|
||||||
if (rulesetIncompatibilities.isNotEmpty()) {
|
if (rulesetIncompatibilities.isNotEmpty()) {
|
||||||
|
@ -35,7 +35,7 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
|||||||
private val copySavedGameToClipboardButton = getCopyExistingSaveToClipboardButton()
|
private val copySavedGameToClipboardButton = getCopyExistingSaveToClipboardButton()
|
||||||
private val errorLabel = "".toLabel(Color.RED).apply { isVisible = false }
|
private val errorLabel = "".toLabel(Color.RED).apply { isVisible = false }
|
||||||
private val loadMissingModsButton = getLoadMissingModsButton()
|
private val loadMissingModsButton = getLoadMissingModsButton()
|
||||||
private var missingModsToLoad = ""
|
private var missingModsToLoad: Iterable<String> = emptyList()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val loadGame = "Load game"
|
private const val loadGame = "Load game"
|
||||||
@ -61,11 +61,11 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
|||||||
isUserFixable = false
|
isUserFixable = false
|
||||||
}
|
}
|
||||||
is FileNotFoundException -> {
|
is FileNotFoundException -> {
|
||||||
if (ex.cause?.message?.contains("Permission denied") == true) {
|
isUserFixable = if (ex.cause?.message?.contains("Permission denied") == true) {
|
||||||
errorText.append("You do not have sufficient permissions to access the file.".tr())
|
errorText.append("You do not have sufficient permissions to access the file.".tr())
|
||||||
isUserFixable = true
|
true
|
||||||
} else {
|
} else {
|
||||||
isUserFixable = false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -245,8 +245,8 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
|||||||
descriptionLabel.setText(Constants.loading.tr())
|
descriptionLabel.setText(Constants.loading.tr())
|
||||||
Concurrency.runOnNonDaemonThreadPool(downloadMissingMods) {
|
Concurrency.runOnNonDaemonThreadPool(downloadMissingMods) {
|
||||||
try {
|
try {
|
||||||
val mods = missingModsToLoad.replace(' ', '-').lowercase().splitToSequence(",-")
|
for (rawName in missingModsToLoad) {
|
||||||
for (modName in mods) {
|
val modName = rawName.replace(' ', '-').lowercase()
|
||||||
val repos = Github.tryGetGithubReposWithTopic(10, 1, modName)
|
val repos = Github.tryGetGithubReposWithTopic(10, 1, modName)
|
||||||
?: throw UncivShowableException("Could not download mod list.")
|
?: throw UncivShowableException("Could not download mod list.")
|
||||||
val repo = repos.items.firstOrNull { it.name.lowercase() == modName }
|
val repo = repos.items.firstOrNull { it.name.lowercase() == modName }
|
||||||
@ -264,7 +264,7 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
|||||||
}
|
}
|
||||||
launchOnGLThread {
|
launchOnGLThread {
|
||||||
RulesetCache.loadRulesets()
|
RulesetCache.loadRulesets()
|
||||||
missingModsToLoad = ""
|
missingModsToLoad = emptyList()
|
||||||
loadMissingModsButton.isVisible = false
|
loadMissingModsButton.isVisible = false
|
||||||
errorLabel.isVisible = false
|
errorLabel.isVisible = false
|
||||||
rightSideTable.pack()
|
rightSideTable.pack()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user