From bffeb10144ced94cefeb3ccbb61dbfe25f57a663 Mon Sep 17 00:00:00 2001 From: yairm210 Date: Thu, 8 Aug 2024 13:51:12 +0300 Subject: [PATCH] Auto-download missing mods when joining multiplayer game --- .../multiplayerscreens/MultiplayerScreen.kt | 38 +++++++---- .../ui/screens/savescreens/LoadGameScreen.kt | 64 +++++++++++-------- 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/core/src/com/unciv/ui/screens/multiplayerscreens/MultiplayerScreen.kt b/core/src/com/unciv/ui/screens/multiplayerscreens/MultiplayerScreen.kt index 0acb2c33c7..681f0477ae 100644 --- a/core/src/com/unciv/ui/screens/multiplayerscreens/MultiplayerScreen.kt +++ b/core/src/com/unciv/ui/screens/multiplayerscreens/MultiplayerScreen.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.unciv.Constants import com.unciv.logic.multiplayer.MultiplayerGame import com.unciv.logic.multiplayer.storage.MultiplayerAuthException +import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.tr import com.unciv.ui.components.widgets.UncivTextField import com.unciv.ui.components.extensions.disable @@ -64,14 +65,29 @@ class MultiplayerScreen : PickerScreen() { pickerPane.topTable.background = skinStrings.getUiBackground("MultiplayerScreen/TopTable", tintColor = skinStrings.skinConfig.clearColor) } - fun onGameDeleted(gameName:String){ + private fun onGameDeleted(gameName:String){ if (selectedGame?.name == gameName) unselectGame() gameList.update() } private fun setupRightSideButton() { rightSideButton.setText("Join game".tr()) - rightSideButton.onClick { MultiplayerHelpers.loadMultiplayerGame(this, selectedGame!!) } + rightSideButton.onClick { + val missingMods = selectedGame!!.preview!!.gameParameters.getModsAndBaseRuleset() + .filter { !RulesetCache.containsKey(it) } + if (missingMods.isEmpty()) return@onClick MultiplayerHelpers.loadMultiplayerGame(this, selectedGame!!) + + // Download missing mods + Concurrency.runOnNonDaemonThreadPool(LoadGameScreen.downloadMissingMods) { + LoadGameScreen.loadMissingMods(missingMods, onModDownloaded = { + Concurrency.runOnGLThread { ToastPopup("[$it] Downloaded!", this@MultiplayerScreen) } + }, + onCompleted = { + RulesetCache.loadRulesets() + Concurrency.runOnGLThread { MultiplayerHelpers.loadMultiplayerGame(this@MultiplayerScreen, selectedGame!!) } + }) + } + } } private fun createRightSideTable(): Table { @@ -98,13 +114,13 @@ class MultiplayerScreen : PickerScreen() { return table } - fun createRefreshButton(): TextButton { + private fun createRefreshButton(): TextButton { val btn = "Refresh list".toTextButton() btn.onClick { game.onlineMultiplayer.requestUpdate() } return btn } - fun createAddGameButton(): TextButton { + private fun createAddGameButton(): TextButton { val btn = "Add multiplayer game".toTextButton() btn.onClick { game.pushScreen(AddMultiplayerGameScreen(this)) @@ -112,7 +128,7 @@ class MultiplayerScreen : PickerScreen() { return btn } - fun createResignButton(): TextButton { + private fun createResignButton(): TextButton { val negativeButtonStyle = skin.get("negative", TextButton.TextButtonStyle::class.java) val resignButton = "Resign".toTextButton(negativeButtonStyle).apply { disable() } resignButton.onClick { @@ -128,7 +144,7 @@ class MultiplayerScreen : PickerScreen() { return resignButton } - fun createForceResignButton(): TextButton { + private fun createForceResignButton(): TextButton { val negativeButtonStyle = skin.get("negative", TextButton.TextButtonStyle::class.java) val resignButton = "Force current player to resign".toTextButton(negativeButtonStyle).apply { isVisible = false } resignButton.onClick { @@ -184,7 +200,7 @@ class MultiplayerScreen : PickerScreen() { } } - fun createDeleteButton(): TextButton { + private fun createDeleteButton(): TextButton { val negativeButtonStyle = skin.get("negative", TextButton.TextButtonStyle::class.java) val deleteButton = "Delete save".toTextButton(negativeButtonStyle).apply { disable() } deleteButton.onClick { @@ -206,7 +222,7 @@ class MultiplayerScreen : PickerScreen() { return deleteButton } - fun createRenameButton(): TextButton { + private fun createRenameButton(): TextButton { val btn = "Rename".toTextButton().apply { disable() } btn.onClick { Popup(this).apply { @@ -235,7 +251,7 @@ class MultiplayerScreen : PickerScreen() { return btn } - fun createCopyGameIdButton(): TextButton { + private fun createCopyGameIdButton(): TextButton { val btn = "Copy game ID".toTextButton().apply { disable() } btn.onClick { val gameInfo = selectedGame?.preview @@ -247,7 +263,7 @@ class MultiplayerScreen : PickerScreen() { return btn } - fun createFriendsListButton(): TextButton { + private fun createFriendsListButton(): TextButton { val btn = "Friends list".toTextButton() btn.onClick { game.pushScreen(ViewFriendsListScreen()) @@ -306,7 +322,7 @@ class MultiplayerScreen : PickerScreen() { descriptionLabel.setText("") } - fun selectGame(name: String) { + private fun selectGame(name: String) { val multiplayerGame = game.onlineMultiplayer.multiplayerFiles.getGameByName(name) if (multiplayerGame == null) { // Should never happen diff --git a/core/src/com/unciv/ui/screens/savescreens/LoadGameScreen.kt b/core/src/com/unciv/ui/screens/savescreens/LoadGameScreen.kt index e2fac131c0..d976a147be 100644 --- a/core/src/com/unciv/ui/screens/savescreens/LoadGameScreen.kt +++ b/core/src/com/unciv/ui/screens/savescreens/LoadGameScreen.kt @@ -45,7 +45,7 @@ class LoadGameScreen : LoadOrSaveScreen() { private const val loadFromCustomLocation = "Load from custom location" private const val loadFromClipboard = "Load copied data" private const val copyExistingSaveToClipboard = "Copy saved game to clipboard" - private const val downloadMissingMods = "Download missing mods" + internal const val downloadMissingMods = "Download missing mods" /** Gets a translated exception message to show to the user. * @return The first returned value is the message, the second is signifying if the user can likely fix this problem. */ @@ -78,6 +78,25 @@ class LoadGameScreen : LoadOrSaveScreen() { } return Pair(errorText.toString(), isUserFixable) } + + fun loadMissingMods(missingMods: Iterable, onModDownloaded:(String)->Unit, onCompleted:()->Unit){ + + for (rawName in missingMods) { + val modName = rawName.folderNameToRepoName().lowercase() + val repos = Github.tryGetGithubReposWithTopic(10, 1, modName) + ?: throw UncivShowableException("Could not download mod list.") + val repo = repos.items.firstOrNull { it.name.lowercase() == modName } + ?: throw UncivShowableException("Could not find a mod named \"[$modName]\".") + val modFolder = Github.downloadAndExtract( + repo, + UncivGame.Current.files.getModsFolder() + ) + ?: throw Exception("Unexpected 404 error") // downloadAndExtract returns null for 404 errors and the like -> display something! + Github.rewriteModOptions(repo, modFolder) + onModDownloaded(repo.name) + } + onCompleted() + } } init { @@ -258,31 +277,24 @@ class LoadGameScreen : LoadOrSaveScreen() { descriptionLabel.setText(Constants.loading.tr()) Concurrency.runOnNonDaemonThreadPool(downloadMissingMods) { try { - for (rawName in missingModsToLoad) { - val modName = rawName.folderNameToRepoName().lowercase() - val repos = Github.tryGetGithubReposWithTopic(10, 1, modName) - ?: throw UncivShowableException("Could not download mod list.") - val repo = repos.items.firstOrNull { it.name.lowercase() == modName } - ?: throw UncivShowableException("Could not find a mod named \"[$modName]\".") - val modFolder = Github.downloadAndExtract( - repo, - UncivGame.Current.files.getModsFolder() - ) - ?: throw Exception("Unexpected 404 error") // downloadAndExtract returns null for 404 errors and the like -> display something! - Github.rewriteModOptions(repo, modFolder) - val labelText = descriptionLabel.text // Surprise - a StringBuilder - labelText.appendLine() - labelText.append("[${repo.name}] Downloaded!".tr()) - launchOnGLThread { descriptionLabel.setText(labelText) } - } - launchOnGLThread { - RulesetCache.loadRulesets() - missingModsToLoad = emptyList() - loadMissingModsButton.isVisible = false - errorLabel.isVisible = false - rightSideTable.pack() - ToastPopup("Missing mods are downloaded successfully.", this@LoadGameScreen) - } + Companion.loadMissingMods(missingModsToLoad, + onModDownloaded = { + val labelText = descriptionLabel.text // Surprise - a StringBuilder + labelText.appendLine() + labelText.append("[$it] Downloaded!".tr()) + launchOnGLThread { descriptionLabel.setText(labelText) } + }, + onCompleted = { + launchOnGLThread { + RulesetCache.loadRulesets() + missingModsToLoad = emptyList() + loadMissingModsButton.isVisible = false + errorLabel.isVisible = false + rightSideTable.pack() + ToastPopup("Missing mods are downloaded successfully.", this@LoadGameScreen) + } + } + ) } catch (ex: Exception) { handleLoadGameException(ex, "Could not load the missing mods!") } finally {