Fix TileSet mods can lock user out (#9023)

This commit is contained in:
SomeTroglodyte 2023-03-29 10:42:34 +02:00 committed by GitHub
parent 13f4dd0756
commit be69f8b52d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 46 deletions

View File

@ -189,7 +189,9 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
ImageGetter.resetAtlases()
ImageGetter.setNewRuleset(ImageGetter.ruleset) // This needs to come after the settings, since we may have default visual mods
if (settings.tileSet !in ImageGetter.getAvailableTilesets()) { // If one of the tilesets is no longer available, default back
val availableTileSets = ImageGetter.getAvailableTilesets().toSet()
.intersect(TileSetCache.getAvailableTilesets().toSet())
if (settings.tileSet !in availableTileSets) { // If the configured tileset is no longer available, default back
settings.tileSet = Constants.defaultTileset
}

View File

@ -80,4 +80,20 @@ object TileSetCache : HashMap<String, TileSet>() {
set(name, tileset)
}
}
/** Determines potentially available TileSets - by scanning for TileSet jsons.
*
* Available before initialization finishes.
* To get more reliable info, either wait until `this` is fully initialized,
* or intersect with [ImageGetter.getAvailableTilesets]
*/
fun getAvailableTilesets() = sequence<FileHandle> {
yieldAll(FileHandle("jsons/TileSets").list().asIterable())
for (modFolder in FileHandle("mods").list()) {
if (!modFolder.isDirectory || modFolder.name().startsWith('.'))
continue
yieldAll(modFolder.child("jsons/TileSets").list().asIterable())
}
}.filter { it.exists() }
.map { it.nameWithoutExtension().removeSuffix("Config") }
}

View File

@ -448,8 +448,16 @@ object ImageGetter {
fun getAvailableSkins() = ninePatchDrawables.keys.asSequence().map { it.split("/")[1] }.distinct()
fun getAvailableTilesets() = textureRegionDrawables.keys.asSequence().filter { it.startsWith("TileSets") && !it.contains("/Units/") }
.map { it.split("/")[1] }.distinct()
/** Determines available TileSets from the currently loaded Texture paths.
*
* Note [TileSetCache] will not necessarily load all of them, e.g. if a Mod fails
* to provide a config json for a graphic with a Tileset path.
*
* Intersect with [TileSetCache.getAvailableTilesets] for a more reliable answer
*/
fun getAvailableTilesets() = textureRegionDrawables.keys.asSequence()
.filter { it.startsWith("TileSets") && !it.contains("/Units/") }
.map { it.split("/")[1] }.distinct()
fun getAvailableUnitsets() = textureRegionDrawables.keys.asSequence().filter { it.contains("/Units/") }
.map { it.split("/")[1] }.distinct()

View File

@ -21,6 +21,7 @@ import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.tilesets.TileSetCache
import com.unciv.ui.components.AutoScrollPane
import com.unciv.ui.components.KeyCharAndCode
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
@ -51,6 +52,7 @@ import com.unciv.ui.screens.worldscreen.WorldScreen
import com.unciv.ui.screens.worldscreen.mainmenu.WorldScreenMenuPopup
import com.unciv.utils.concurrency.Concurrency
import com.unciv.utils.concurrency.launchOnGLThread
import kotlinx.coroutines.CoroutineScope
import kotlin.math.min
@ -104,51 +106,14 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
// will not exist unless we reset the ruleset and images
ImageGetter.ruleset = RulesetCache.getVanillaRuleset()
Concurrency.run("ShowMapBackground") {
var scale = 1f
var mapWidth = stage.width / TileGroupMap.groupHorizontalAdvance
var mapHeight = stage.height / TileGroupMap.groupSize
if (mapWidth * mapHeight > 3000f) { // 3000 as max estimated number of tiles is arbitrary (we had typically 721 before)
scale = mapWidth * mapHeight / 3000f
mapWidth /= scale
mapHeight /= scale
scale = min(scale, 20f)
// This is an extreme safeguard - should an invalid settings.tileSet ever make it past the
// guard in UncivGame.create, simply omit the background so the user can at least get to options
// (let him crash when loading a game but avoid locking him out entirely)
if (game.settings.tileSet in TileSetCache)
Concurrency.run("ShowMapBackground") {
showMapBackground()
}
val baseRuleset = RulesetCache.getVanillaRuleset()
easterEggRuleset = EasterEggRulesets.getTodayEasterEggRuleset()?.let {
RulesetCache.getComplexRuleset(baseRuleset, listOf(it))
}
val mapRuleset = if (game.settings.enableEasterEggs) easterEggRuleset ?: baseRuleset else baseRuleset
val newMap = MapGenerator(mapRuleset)
.generateMap(MapParameters().apply {
shape = MapShape.rectangular
mapSize = MapSizeNew(MapSize.Small)
type = MapType.pangaea
temperatureExtremeness = .7f
waterThreshold = -0.1f // mainly land, gets about 30% water
modifyForEasterEgg()
})
launchOnGLThread { // for GL context
ImageGetter.setNewRuleset(mapRuleset)
val mapHolder = EditorMapHolder(
this@MainMenuScreen,
newMap
) {}
mapHolder.setScale(scale)
backgroundTable.addAction(Actions.sequence(
Actions.fadeOut(0f),
Actions.run {
backgroundTable.addActor(mapHolder)
mapHolder.center(backgroundTable)
},
Actions.fadeIn(0.3f)
))
}
}
val column1 = Table().apply { defaults().pad(10f).fillX() }
val column2 = if (singleColumn) column1 else Table().apply { defaults().pad(10f).fillX() }
@ -220,6 +185,50 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
stage.addActor(helpButton)
}
private fun CoroutineScope.showMapBackground() {
var scale = 1f
var mapWidth = stage.width / TileGroupMap.groupHorizontalAdvance
var mapHeight = stage.height / TileGroupMap.groupSize
if (mapWidth * mapHeight > 3000f) { // 3000 as max estimated number of tiles is arbitrary (we had typically 721 before)
scale = mapWidth * mapHeight / 3000f
mapWidth /= scale
mapHeight /= scale
scale = min(scale, 20f)
}
val baseRuleset = RulesetCache.getVanillaRuleset()
easterEggRuleset = EasterEggRulesets.getTodayEasterEggRuleset()?.let {
RulesetCache.getComplexRuleset(baseRuleset, listOf(it))
}
val mapRuleset = if (game.settings.enableEasterEggs) easterEggRuleset ?: baseRuleset else baseRuleset
val newMap = MapGenerator(mapRuleset)
.generateMap(MapParameters().apply {
shape = MapShape.rectangular
mapSize = MapSizeNew(MapSize.Small)
type = MapType.pangaea
temperatureExtremeness = 0.7f
waterThreshold = -0.1f // mainly land, gets about 30% water
modifyForEasterEgg()
})
launchOnGLThread { // for GL context
ImageGetter.setNewRuleset(mapRuleset)
val mapHolder = EditorMapHolder(
this@MainMenuScreen,
newMap
) {}
mapHolder.setScale(scale)
backgroundTable.addAction(Actions.sequence(
Actions.fadeOut(0f),
Actions.run {
backgroundTable.addActor(mapHolder)
mapHolder.center(backgroundTable)
},
Actions.fadeIn(0.3f)
))
}
}
private fun resumeGame() {
if (GUI.isWorldLoaded()) {