diff --git a/core/src/com/unciv/ui/popups/AuthPopup.kt b/core/src/com/unciv/ui/popups/AuthPopup.kt index 7b332917c9..dee4f35a73 100644 --- a/core/src/com/unciv/ui/popups/AuthPopup.kt +++ b/core/src/com/unciv/ui/popups/AuthPopup.kt @@ -8,16 +8,17 @@ import com.unciv.ui.components.input.onClick import com.unciv.ui.components.extensions.toTextButton import com.unciv.ui.screens.basescreen.BaseScreen -class AuthPopup(stage: Stage, authSuccessful: ((Boolean) -> Unit)? = null) +class AuthPopup(stage: Stage, private val authSuccessful: ((Boolean) -> Unit)? = null) : Popup(stage) { constructor(screen: BaseScreen, authSuccessful: ((Boolean) -> Unit)? = null) : this(screen.stage, authSuccessful) - init { - val passwordField = UncivTextField("Password") - val button = "Authenticate".toTextButton() - val negativeButtonStyle = BaseScreen.skin.get("negative", TextButton.TextButtonStyle::class.java) + private val passwordField: UncivTextField = UncivTextField("Password") + private val button: TextButton = "Authenticate".toTextButton() + private val negativeButtonStyle: TextButton.TextButtonStyle = + BaseScreen.skin.get("negative", TextButton.TextButtonStyle::class.java) + init { button.onClick { try { UncivGame.Current.onlineMultiplayer.multiplayerServer.authenticate(passwordField.text) @@ -25,17 +26,18 @@ class AuthPopup(stage: Stage, authSuccessful: ((Boolean) -> Unit)? = null) close() } catch (_: Exception) { clear() - addGoodSizedLabel("Authentication failed").colspan(2).row() - add(passwordField).colspan(2).growX().pad(16f, 0f, 16f, 0f).row() - addCloseButton(style = negativeButtonStyle) { authSuccessful?.invoke(false) }.growX().padRight(8f) - add(button).growX().padLeft(8f) - return@onClick + addComponents("Authentication failed") } } - - addGoodSizedLabel("Please enter your server password").colspan(2).row() + addComponents("Please enter your server password") + } + + private fun addComponents(headerLabelText: String) { + addGoodSizedLabel(headerLabelText).colspan(2).row() add(passwordField).colspan(2).growX().pad(16f, 0f, 16f, 0f).row() - addCloseButton(style = negativeButtonStyle) { authSuccessful?.invoke(false) }.growX().padRight(8f) + addCloseButton(style = negativeButtonStyle) { + authSuccessful?.invoke(false) + }.growX().padRight(8f) add(button).growX().padLeft(8f) } } diff --git a/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt index d6a852cd4d..dbad4284d0 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt @@ -70,6 +70,7 @@ import com.unciv.utils.debug import com.unciv.utils.launchOnGLThread import com.unciv.utils.launchOnThreadPool import com.unciv.utils.withGLContext +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import yairm210.purity.annotations.Readonly @@ -595,17 +596,34 @@ class WorldScreen( gameInfoClone.nextTurn(progressBar) if (originalGameInfo.gameParameters.isOnlineMultiplayer) { + // outer try-catch for non-auth exceptions try { - game.onlineMultiplayer.updateGame(gameInfoClone) - }catch (ex: Exception) { - when (ex) { - is MultiplayerAuthException -> { + // keep retrying if upload fails AND reauthentication succeeds + var retryUpload: Boolean + do { + try { + game.onlineMultiplayer.updateGame(gameInfoClone) + // upload succeeded + retryUpload = false + } catch (_: MultiplayerAuthException) { + // true only if authentication succeeds (the popup permits retries) + // false only if user closes the auth popup or the popup init crashes + val authResult = CompletableDeferred() launchOnGLThread { - AuthPopup(this@WorldScreen) { - success -> if (success) nextTurn() - }.open(true) + try { + AuthPopup(this@WorldScreen, authResult::complete).open(true) + } catch (ex: Exception) { + // GL thread crashed during AuthPopup init, let's wrap up + authResult.complete(false) + // ensure exception is passed to crash handler + throw ex + } } + retryUpload = authResult.await() } + } while (retryUpload) + } catch (ex: Exception) { // non-auth exceptions + when (ex) { is FileStorageRateLimitReached -> { val message = "Server limit reached! Please wait for [${ex.limitRemainingSeconds}] seconds" launchOnGLThread {