From 1de866c7ed4bb7f573965e0d541ae5efe7f1fb02 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Tue, 16 May 2023 12:00:44 +0200 Subject: [PATCH] Local and/or jpg mod previews (#9394) * Allow mod preview image to be a jpg * Allow preview images from WIP mods lacking github url * Wiki preview.jpg * Allow mod preview image to be a jpg - review --- .../unciv/ui/screens/pickerscreens/GitHub.kt | 6 +- .../pickerscreens/ModManagementScreen.kt | 74 ++++++++++++------- docs/Modders/Mods.md | 2 +- 3 files changed, 54 insertions(+), 28 deletions(-) diff --git a/core/src/com/unciv/ui/screens/pickerscreens/GitHub.kt b/core/src/com/unciv/ui/screens/pickerscreens/GitHub.kt index 3f9115e72c..d5940461cf 100644 --- a/core/src/com/unciv/ui/screens/pickerscreens/GitHub.kt +++ b/core/src/com/unciv/ui/screens/pickerscreens/GitHub.kt @@ -253,9 +253,11 @@ object Github { } fun tryGetPreviewImage(modUrl:String, defaultBranch: String): Pixmap?{ - val fileLocation = "$modUrl/$defaultBranch/preview.png" + val fileLocation = "$modUrl/$defaultBranch/preview" .replace("github.com", "raw.githubusercontent.com") - val file = download(fileLocation) ?: return null + val file = download("$fileLocation.jpg") + ?: download("$fileLocation.png") + ?: return null val byteArray = file.readBytes() val buffer = ByteBuffer.allocateDirect(byteArray.size).put(byteArray).position(0) return Pixmap(buffer) diff --git a/core/src/com/unciv/ui/screens/pickerscreens/ModManagementScreen.kt b/core/src/com/unciv/ui/screens/pickerscreens/ModManagementScreen.kt index 9ba2b62cc3..236ecef951 100644 --- a/core/src/com/unciv/ui/screens/pickerscreens/ModManagementScreen.kt +++ b/core/src/com/unciv/ui/screens/pickerscreens/ModManagementScreen.kt @@ -2,6 +2,7 @@ package com.unciv.ui.screens.pickerscreens import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Touchable @@ -52,6 +53,7 @@ import com.unciv.utils.launchOnGLThread import kotlinx.coroutines.Job import kotlinx.coroutines.isActive import java.io.IOException +import java.nio.ByteBuffer import kotlin.math.max /** @@ -64,6 +66,18 @@ class ModManagementScreen( previousInstalledMods: HashMap? = null, previousOnlineMods: HashMap? = null ): PickerScreen(disableScroll = true), RecreateOnResize { + companion object { + // Tweakable constants + /** For preview.png */ + const val maxAllowedPreviewImageSize = 200f + /** Github queries use this limit */ + const val amountPerPage = 100 + + val modsToHideAsUrl by lazy { + val blockedModsFile = Gdx.files.internal("jsons/ManuallyBlockedMods.json") + json().fromJsonFile(Array::class.java, blockedModsFile) + } + } private val modTable = Table().apply { defaults().pad(10f) } private val scrollInstalledMods = AutoScrollPane(modTable) @@ -72,8 +86,6 @@ class ModManagementScreen( private val modActionTable = Table().apply { defaults().pad(10f) } private val optionsManager = ModManagementOptions(this) - val amountPerPage = 100 - private var lastSelectedButton: Button? = null private var lastSyncMarkedButton: Button? = null private var selectedMod: Github.Repo? = null @@ -95,6 +107,7 @@ class ModManagementScreen( // cleanup - background processing needs to be stopped on exit and memory freed private var runningSearchJob: Job? = null private var stopBackgroundTasks = false + override fun dispose() { // make sure the worker threads will not continue trying their time-intensive job runningSearchJob?.cancel() @@ -349,14 +362,18 @@ class ModManagementScreen( * @param repo: the repository instance as received from the GitHub api */ private fun addModInfoToActionTable(repo: Github.Repo) { - addModInfoToActionTable(repo.html_url, repo.default_branch, repo.pushed_at, repo.owner.login, repo.size) + addModInfoToActionTable( + repo.name, repo.html_url, repo.default_branch, + repo.pushed_at, repo.owner.login, repo.size + ) } /** Recreate the information part of the right-hand column * @param modName: The mod name (name from the RuleSet) * @param modOptions: The ModOptions as enriched by us with GitHub metadata when originally downloaded */ - private fun addModInfoToActionTable(modOptions: ModOptions) { + private fun addModInfoToActionTable(modName: String, modOptions: ModOptions) { addModInfoToActionTable( + modName, modOptions.modUrl, modOptions.defaultBranch, modOptions.lastUpdated, @@ -368,6 +385,7 @@ class ModManagementScreen( private val repoUrlToPreviewImage = HashMap() private fun addModInfoToActionTable( + modName: String, repoUrl: String, default_branch: String, updatedAt: String, @@ -380,14 +398,17 @@ class ModManagementScreen( val imageHolder = Table() - addPreviewImage(imageHolder, repoUrl, default_branch) + if (repoUrl.isEmpty()) + addLocalPreviewImage(imageHolder, modName) + else + addPreviewImage(imageHolder, repoUrl, default_branch) modActionTable.add(imageHolder).row() if (author.isNotEmpty()) modActionTable.add("Author: [$author]".toLabel()).row() - if (modSize > 0){ + if (modSize > 0) { if (modSize < 2048) modActionTable.add("Size: [$modSize] kB".toLabel()).padBottom(15f).row() else @@ -395,7 +416,7 @@ class ModManagementScreen( } // offer link to open the repo itself in a browser - if (repoUrl != "") { + if (repoUrl.isNotEmpty()) { modActionTable.add("Open Github page".toTextButton().onClick { Gdx.net.openURI(repoUrl) }).row() @@ -409,6 +430,15 @@ class ModManagementScreen( } } + private fun setTextureAsPreview(imageHolder: Table, texture: Texture) { + val cell = imageHolder.add(Image(texture)) + val largestImageSize = max(texture.width, texture.height) + if (largestImageSize > maxAllowedPreviewImageSize) { + val resizeRatio = maxAllowedPreviewImageSize / largestImageSize + cell.size(texture.width * resizeRatio, texture.height * resizeRatio) + } + } + private fun addPreviewImage( imageHolder: Table, repoUrl: String, @@ -416,19 +446,10 @@ class ModManagementScreen( ) { if (!repoUrl.startsWith("http")) return // invalid url - fun setTextureAsPreview(texture: Texture) { - val maxAllowedImageSize = 200f - val cell = imageHolder.add(Image(texture)) - val largestImageSize = max(texture.width, texture.height) - if (largestImageSize > maxAllowedImageSize) { - val resizeRatio = maxAllowedImageSize / largestImageSize - cell.size(texture.width * resizeRatio, texture.height * resizeRatio) - } - } if (repoUrlToPreviewImage.containsKey(repoUrl)) { val texture = repoUrlToPreviewImage[repoUrl] - if (texture != null) setTextureAsPreview(texture) + if (texture != null) setTextureAsPreview(imageHolder, texture) return } @@ -443,11 +464,20 @@ class ModManagementScreen( val texture = Texture(imagePixmap) imagePixmap.dispose() repoUrlToPreviewImage[repoUrl] = texture - setTextureAsPreview(texture) + setTextureAsPreview(imageHolder, texture) } } } + private fun addLocalPreviewImage(imageHolder: Table, modName: String) { + // No concurrency, order of magnitude 20ms + val modFolder = Gdx.files.local("mods/$modName") + val previewFile = modFolder.child("preview.jpg").takeIf { it.exists() } + ?: modFolder.child("preview.png").takeIf { it.exists() } + ?: return + setTextureAsPreview(imageHolder, Texture(previewFile)) + } + /** Create the special "Download from URL" button */ private fun getDownloadFromUrlButton(): TextButton { val downloadButton = "Download mod from URL".toTextButton() @@ -586,7 +616,7 @@ class ModManagementScreen( selectedMod = null modActionTable.clear() // show mod information first - addModInfoToActionTable(mod.modOptions) + addModInfoToActionTable(mod.name, mod.modOptions) // offer 'permanent visual mod' toggle val visualMods = game.settings.visualMods @@ -727,10 +757,4 @@ class ModManagementScreen( override fun recreate(): BaseScreen = ModManagementScreen(installedModInfo, onlineModInfo) - companion object { - val modsToHideAsUrl by lazy { - val blockedModsFile = Gdx.files.internal("jsons/ManuallyBlockedMods.json") - json().fromJsonFile(Array::class.java, blockedModsFile) - } - } } diff --git a/docs/Modders/Mods.md b/docs/Modders/Mods.md index f843269899..7e317d61e5 100644 --- a/docs/Modders/Mods.md +++ b/docs/Modders/Mods.md @@ -115,7 +115,7 @@ When loading a mod, it needs to be in its own folder in `/mods` - this is how yo ## Other -You can add an image that will be displayed to users in the mod management by adding a "preview.png" file. +You can add an image that will be displayed to users in the mod management by adding a "preview.jpg" _or_ "preview.png" file. Existing mods can be found [here](https://github.com/topics/unciv-mod)!