mirror of
https://github.com/yairm210/Unciv.git
synced 2024-12-22 23:04:25 +07:00
Run mod compatibility checks on another thread, to avoid ANRs in new game screen
This commit is contained in:
parent
d08f2a2939
commit
ab2c848f9a
@ -1,9 +1,7 @@
|
||||
package com.unciv.ui.screens.newgamescreen
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
@ -15,6 +13,7 @@ import com.unciv.ui.components.input.onChange
|
||||
import com.unciv.ui.components.widgets.ExpanderTab
|
||||
import com.unciv.ui.popups.ToastPopup
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
|
||||
/**
|
||||
* A widget containing one expander for extension mods.
|
||||
@ -58,10 +57,9 @@ class ModCheckboxTable(
|
||||
|
||||
for (mod in modRulesets.sortedBy { it.name }) {
|
||||
val checkBox = mod.name.toCheckBox(mod.name in mods)
|
||||
checkBox.onChange {
|
||||
if (checkBoxChanged(checkBox, it!!, mod)) {
|
||||
onUpdate(mod.name)
|
||||
}
|
||||
checkBox.onChange {
|
||||
// Checks are run in parallel thread to avoid ANRs
|
||||
Concurrency.run { checkBoxChanged(checkBox, mod) }
|
||||
}
|
||||
checkBox.left()
|
||||
modWidgets += ModWithCheckBox(mod, checkBox)
|
||||
@ -108,7 +106,7 @@ class ModCheckboxTable(
|
||||
|
||||
disableIncompatibleMods()
|
||||
|
||||
runComplexModCheck()
|
||||
Concurrency.run { complexModCheckReturnsErrors() }
|
||||
}
|
||||
|
||||
fun disableAllCheckboxes() {
|
||||
@ -124,49 +122,44 @@ class ModCheckboxTable(
|
||||
onUpdate("-") // should match no mod
|
||||
}
|
||||
|
||||
private fun runComplexModCheck(): Boolean {
|
||||
// Disable user input to avoid ANRs
|
||||
val currentInputProcessor = Gdx.input.inputProcessor
|
||||
Gdx.input.inputProcessor = null
|
||||
|
||||
/** Runs in parallel thread */
|
||||
private fun complexModCheckReturnsErrors(): Boolean {
|
||||
// Check over complete combination of selected mods
|
||||
val complexModLinkCheck = RulesetCache.checkCombinedModLinks(mods, baseRulesetName)
|
||||
if (!complexModLinkCheck.isWarnUser()){
|
||||
savedModcheckResult = null
|
||||
Gdx.input.inputProcessor = currentInputProcessor
|
||||
return false
|
||||
}
|
||||
savedModcheckResult = complexModLinkCheck.getErrorText()
|
||||
complexModLinkCheck.showWarnOrErrorToast(screen)
|
||||
|
||||
Gdx.input.inputProcessor = currentInputProcessor
|
||||
return complexModLinkCheck.isError()
|
||||
}
|
||||
|
||||
/** Runs in parallel thread so as not to block main thread - running complex mod check can be expensive */
|
||||
private fun checkBoxChanged(
|
||||
checkBox: CheckBox,
|
||||
changeEvent: ChangeListener.ChangeEvent,
|
||||
mod: Ruleset
|
||||
): Boolean {
|
||||
if (disableChangeEvents) return false
|
||||
) {
|
||||
if (disableChangeEvents) return
|
||||
|
||||
if (checkBox.isChecked) {
|
||||
// First the quick standalone check
|
||||
val modLinkErrors = mod.getErrorList()
|
||||
if (modLinkErrors.isError()) {
|
||||
modLinkErrors.showWarnOrErrorToast(screen)
|
||||
changeEvent.cancel() // Cancel event to reset to previous state - see Button.setChecked()
|
||||
return false
|
||||
Concurrency.runOnGLThread { checkBox.isChecked = false } // Cancel event to reset to previous state
|
||||
return
|
||||
}
|
||||
|
||||
mods.add(mod.name)
|
||||
|
||||
// Check over complete combination of selected mods
|
||||
if (runComplexModCheck()) {
|
||||
changeEvent.cancel() // Cancel event to reset to previous state - see Button.setChecked()
|
||||
if (complexModCheckReturnsErrors()) {
|
||||
// Cancel event to reset to previous state
|
||||
Concurrency.runOnGLThread { checkBox.isChecked = false } // Cancel event to reset to previous state
|
||||
mods.remove(mod.name)
|
||||
savedModcheckResult = null // we just fixed it
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -177,18 +170,20 @@ class ModCheckboxTable(
|
||||
|
||||
mods.remove(mod.name)
|
||||
|
||||
if (runComplexModCheck()) {
|
||||
changeEvent.cancel() // Cancel event to reset to previous state - see Button.setChecked()
|
||||
if (complexModCheckReturnsErrors()) {
|
||||
// Cancel event to reset to previous state
|
||||
Concurrency.runOnGLThread { checkBox.isChecked = true }
|
||||
mods.add(mod.name)
|
||||
savedModcheckResult = null // we just fixed it
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
disableIncompatibleMods()
|
||||
|
||||
return true
|
||||
Concurrency.runOnGLThread {
|
||||
disableIncompatibleMods()
|
||||
onUpdate(mod.name) // Only run if we can the checks and they succeeded
|
||||
}
|
||||
}
|
||||
|
||||
/** Deselect incompatible mods after [skipCheckBox] was selected.
|
||||
|
@ -5,6 +5,7 @@ import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.popups.ToastPopup
|
||||
import com.unciv.ui.popups.popups
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
|
||||
/**
|
||||
* Show a [ToastPopup] for this if severity is at least [isWarnUser][RulesetErrorList.isWarnUser].
|
||||
@ -19,6 +20,8 @@ fun RulesetErrorList.showWarnOrErrorToast(screen: BaseScreen) {
|
||||
else "{The mod combination you selected «GOLD»has problems«».}\n" +
|
||||
"{You can play it, but «GOLDENROD»don't expect everything to work!«»}"
|
||||
val toastMessage = headerText.tr() + "\n\n{" + getErrorText() + "}"
|
||||
for (oldToast in screen.popups.filterIsInstance<ToastPopup>()) { oldToast.close() }
|
||||
ToastPopup(toastMessage, screen, 5000L)
|
||||
Concurrency.runOnGLThread {
|
||||
for (oldToast in screen.popups.filterIsInstance<ToastPopup>()) oldToast.close()
|
||||
ToastPopup(toastMessage, screen, 5000L)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user