diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index beba03bdb5..9d495faef7 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -21,6 +21,8 @@ import com.unciv.ui.screens.mapeditorscreen.MapGeneratorSteps import com.unciv.ui.screens.mapeditorscreen.TileInfoNormalizer import com.unciv.utils.Log import com.unciv.utils.debug +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.isActive import kotlin.math.abs import kotlin.math.max import kotlin.math.pow @@ -31,7 +33,8 @@ import kotlin.math.ulp import kotlin.random.Random -class MapGenerator(val ruleset: Ruleset) { +class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineScope? = null) { + companion object { private const val consoleTimings = false } @@ -202,6 +205,7 @@ class MapGenerator(val ruleset: Ruleset) { private fun runAndMeasure(text: String, action: ()->Unit) { + if (coroutineScope?.isActive == false) return if (!consoleTimings) return action() val startNanos = System.nanoTime() action() diff --git a/core/src/com/unciv/ui/screens/mainmenuscreen/MainMenuScreen.kt b/core/src/com/unciv/ui/screens/mainmenuscreen/MainMenuScreen.kt index 4dcf89bf53..b0b7128fe9 100644 --- a/core/src/com/unciv/ui/screens/mainmenuscreen/MainMenuScreen.kt +++ b/core/src/com/unciv/ui/screens/mainmenuscreen/MainMenuScreen.kt @@ -52,7 +52,8 @@ 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 kotlinx.coroutines.Job +import kotlinx.coroutines.isActive import kotlin.math.min @@ -63,6 +64,8 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { private val singleColumn = isCrampedPortrait() private var easterEggRuleset: Ruleset? = null // Cache it so the next 'egg' can be found in Civilopedia + private var backgroundMapGenerationJob: Job? = null + /** Create one **Main Menu Button** including onClick/key binding * @param text The text to display on the button * @param icon The path of the icon to display on the button @@ -86,7 +89,10 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { table.add(text.toLabel(fontSize = 30, alignment = Align.left)).expand().left().minWidth(200f) table.touchable = Touchable.enabled - table.onActivation(function) + table.onActivation { + stopBackgroundMapGeneration() + function() + } if (key != null) { if (!keyVisualOnly) @@ -110,9 +116,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { // 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() - } + startBackgroundMapGeneration() val column1 = Table().apply { defaults().pad(10f).fillX() } val column2 = if (singleColumn) column1 else Table().apply { defaults().pad(10f).fillX() } @@ -185,51 +189,69 @@ 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) - } + private fun startBackgroundMapGeneration() { + stopBackgroundMapGeneration() // shouldn't be necessary as resize re-instantiates this class + backgroundMapGenerationJob = 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) + } - val baseRuleset = RulesetCache.getVanillaRuleset() - easterEggRuleset = EasterEggRulesets.getTodayEasterEggRuleset()?.let { - RulesetCache.getComplexRuleset(baseRuleset, listOf(it)) - } - val mapRuleset = if (game.settings.enableEasterEggs) easterEggRuleset ?: baseRuleset else baseRuleset + 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() - }) + val newMap = MapGenerator(mapRuleset, this) + .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) - )) + 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.clearChildren() + backgroundTable.addActor(mapHolder) + mapHolder.center(backgroundTable) + }, + Actions.fadeIn(0.3f) + )) + } + }.apply { + invokeOnCompletion { + backgroundMapGenerationJob = null + } } } + private fun stopBackgroundMapGeneration() { + val currentJob = backgroundMapGenerationJob + ?: return + backgroundMapGenerationJob = null + if (currentJob.isCancelled) return + currentJob.cancel() + backgroundTable.clearActions() + } + private fun resumeGame() { if (GUI.isWorldLoaded()) { val currentTileSet = GUI.getMap().currentTileSetStrings @@ -293,6 +315,7 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { } private fun openCivilopedia() { + stopBackgroundMapGeneration() val rulesetParameters = game.settings.lastGameSetup?.gameParameters val ruleset = easterEggRuleset ?: if (rulesetParameters == null) @@ -304,5 +327,8 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize { game.pushScreen(CivilopediaScreen(ruleset)) } - override fun recreate(): BaseScreen = MainMenuScreen() + override fun recreate(): BaseScreen { + stopBackgroundMapGeneration() + return MainMenuScreen() + } }