diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/Account.kt b/src/main/java/de/bixilon/minosoft/data/accounts/Account.kt index 322f0a629..cf6c678ba 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/Account.kt +++ b/src/main/java/de/bixilon/minosoft/data/accounts/Account.kt @@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf +import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.kutil.watcher.DataWatcher.Companion.watched import de.bixilon.minosoft.config.profile.profiles.eros.server.entries.Server import de.bixilon.minosoft.data.accounts.types.microsoft.MicrosoftAccount @@ -48,10 +49,10 @@ abstract class Account( abstract fun join(serverId: String) abstract fun logout(clientToken: String) - abstract fun check(clientToken: String) + abstract fun check(latch: CountUpAndDownLatch?, clientToken: String) @Synchronized - open fun tryCheck(clientToken: String) { + open fun tryCheck(latch: CountUpAndDownLatch?, clientToken: String) { if (state == AccountStates.CHECKING || state == AccountStates.REFRESHING) { // already checking return @@ -60,6 +61,6 @@ abstract class Account( // Nothing to do return } - check(clientToken) + check(latch, clientToken) } } diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/types/microsoft/MicrosoftAccount.kt b/src/main/java/de/bixilon/minosoft/data/accounts/types/microsoft/MicrosoftAccount.kt index 9d26ec905..c1096823e 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/types/microsoft/MicrosoftAccount.kt +++ b/src/main/java/de/bixilon/minosoft/data/accounts/types/microsoft/MicrosoftAccount.kt @@ -15,6 +15,7 @@ package de.bixilon.minosoft.data.accounts.types.microsoft import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonProperty +import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.accounts.AccountStates import de.bixilon.minosoft.data.player.properties.PlayerProperties @@ -44,18 +45,23 @@ class MicrosoftAccount( override fun logout(clientToken: String) = Unit @Synchronized - override fun check(@Nullable clientToken: String) { + override fun check(latch: CountUpAndDownLatch?, @Nullable clientToken: String) { if (accessToken != null) { return } + val innerLatch = CountUpAndDownLatch(3, latch) try { state = AccountStates.REFRESHING val (xboxLiveToken, userHash) = MicrosoftOAuthUtils.getXboxLiveToken(authorizationToken) + innerLatch.dec() val xstsToken = MicrosoftOAuthUtils.getXSTSToken(xboxLiveToken) + innerLatch.dec() accessToken = MicrosoftOAuthUtils.getMinecraftBearerAccessToken(userHash, xstsToken) + innerLatch.dec() state = AccountStates.WORKING } catch (exception: Throwable) { + innerLatch.count = 0 this.error = exception this.state = AccountStates.ERRORED throw exception diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/types/mojang/MojangAccount.kt b/src/main/java/de/bixilon/minosoft/data/accounts/types/mojang/MojangAccount.kt index f497aa0c2..06e5ca42a 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/types/mojang/MojangAccount.kt +++ b/src/main/java/de/bixilon/minosoft/data/accounts/types/mojang/MojangAccount.kt @@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import de.bixilon.kutil.cast.CastUtil.nullCast import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.json.JsonUtil.asJsonObject +import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.kutil.uuid.UUIDUtil.toUUID import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.accounts.AccountStates @@ -63,17 +64,19 @@ class MojangAccount( Log.log(LogMessageType.AUTHENTICATION, LogLevels.VERBOSE) { "Mojang account login successful (username=$username)" } } - override fun check(clientToken: String) { + override fun check(latch: CountUpAndDownLatch?, clientToken: String) { if (refreshed) { return } try { + latch?.inc() refresh(clientToken) } catch (exception: Throwable) { this.error = exception state = AccountStates.ERRORED throw exception } + latch?.dec() } @Synchronized diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/types/offline/OfflineAccount.kt b/src/main/java/de/bixilon/minosoft/data/accounts/types/offline/OfflineAccount.kt index d1b169d17..2708cc138 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/types/offline/OfflineAccount.kt +++ b/src/main/java/de/bixilon/minosoft/data/accounts/types/offline/OfflineAccount.kt @@ -14,6 +14,7 @@ package de.bixilon.minosoft.data.accounts.types.offline import com.fasterxml.jackson.annotation.JsonIgnore +import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.data.accounts.AccountStates import de.bixilon.minosoft.data.player.properties.PlayerProperties @@ -35,7 +36,7 @@ class OfflineAccount(username: String) : Account(username) { override fun logout(clientToken: String) = Unit - override fun check(clientToken: String) = Unit + override fun check(latch: CountUpAndDownLatch?, clientToken: String) = Unit override fun toString(): String { return "OfflineAccount{$username}" diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt b/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt index 69fdfed6e..da92fb9dc 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt @@ -14,12 +14,10 @@ package de.bixilon.minosoft.gui.eros.main import de.bixilon.kutil.cast.CastUtil.unsafeCast -import de.bixilon.kutil.concurrent.pool.DefaultThreadPool import de.bixilon.minosoft.ShutdownReasons import de.bixilon.minosoft.config.profile.delegate.watcher.SimpleProfileDelegateWatcher.Companion.profileWatchFX import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager import de.bixilon.minosoft.data.accounts.Account -import de.bixilon.minosoft.data.accounts.AccountStates import de.bixilon.minosoft.gui.eros.controller.EmbeddedJavaFXController import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController import de.bixilon.minosoft.gui.eros.main.account.AccountController @@ -150,15 +148,8 @@ class MainErosController : JavaFXWindowController() { activity = ErosMainActivities.ACCOUNT return } - if (account.state == AccountStates.WORKING) { - DefaultThreadPool += { onSuccess(account) } - return - } - if (account.state == AccountStates.CHECKING || account.state == AccountStates.REFRESHING) { - return - } - getController(ErosMainActivities.ACCOUNT).unsafeCast().checkAccount(account, false) + getController(ErosMainActivities.ACCOUNT).unsafeCast().checkAccount(account, false, onSuccess = onSuccess) } diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/main/account/AccountController.kt b/src/main/java/de/bixilon/minosoft/gui/eros/main/account/AccountController.kt index 03f74864b..1ff093a15 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/main/account/AccountController.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/main/account/AccountController.kt @@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.eros.main.account import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.collections.CollectionUtil.extend import de.bixilon.kutil.concurrent.pool.DefaultThreadPool +import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.kutil.primitive.BooleanUtil.decide import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.config.profile.delegate.watcher.entry.MapProfileDelegateWatcher.Companion.profileWatchMapFX @@ -113,17 +114,31 @@ class AccountController : EmbeddedJavaFXController() { } - fun checkAccount(account: Account, select: Boolean, checkOnly: Boolean = false) { + fun checkAccount(account: Account, select: Boolean, checkOnly: Boolean = false, onSuccess: ((Account) -> Unit)? = null) { + if (account.state == AccountStates.WORKING) { + onSuccess?.let { DefaultThreadPool += { it(account) } } + return + } + if (account.state == AccountStates.CHECKING || account.state == AccountStates.REFRESHING) { + return + } Log.log(LogMessageType.AUTHENTICATION, LogLevels.INFO) { "Checking account $account" } + val latch = CountUpAndDownLatch(2) + val dialog = CheckingDialog(latch, account) + dialog.show() val profile = ErosProfileManager.selected.general.accountProfile DefaultThreadPool += { + latch.dec() try { - account.tryCheck(profile.clientToken) // ToDo: Show error + account.tryCheck(latch, profile.clientToken) // ToDo: Show error if (select) { profile.selected = account } Log.log(LogMessageType.AUTHENTICATION, LogLevels.INFO) { "Account is working: $account" } + JavaFXUtil.runLater { dialog.close() } + onSuccess?.invoke(account) } catch (exception: Throwable) { + JavaFXUtil.runLater { dialog.close() } Log.log(LogMessageType.AUTHENTICATION, LogLevels.INFO) { "Error while checking account $account: $exception" } exception.printStackTrace() if (account.state == AccountStates.ERRORED || account.state == AccountStates.EXPIRED) { diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/main/account/CheckingDialog.kt b/src/main/java/de/bixilon/minosoft/gui/eros/main/account/CheckingDialog.kt new file mode 100644 index 000000000..d1dadceae --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/eros/main/account/CheckingDialog.kt @@ -0,0 +1,67 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.eros.main.account + +import de.bixilon.kutil.latch.CountUpAndDownLatch +import de.bixilon.minosoft.data.accounts.Account +import de.bixilon.minosoft.gui.eros.controller.DialogController +import de.bixilon.minosoft.gui.eros.util.JavaFXUtil +import de.bixilon.minosoft.gui.eros.util.JavaFXUtil.text +import de.bixilon.minosoft.util.KUtil.toResourceLocation +import javafx.fxml.FXML +import javafx.scene.control.Button +import javafx.scene.control.ProgressBar +import javafx.scene.text.TextFlow + +class CheckingDialog( + val latch: CountUpAndDownLatch, + val account: Account, +) : DialogController() { + @FXML private lateinit var headerFX: TextFlow + @FXML private lateinit var progressFX: ProgressBar + @FXML private lateinit var cancelButtonFX: Button + + fun show() { + JavaFXUtil.openModalAsync(TITLE, LAYOUT, this) { + update() + stage.show() + latch += { update() } + } + } + + + override fun init() { + headerFX.text = HEADER + } + + private fun update() { + if (latch.count == 0) { + return close() + } + val progress = 1.0 - (latch.count.toDouble() / latch.total) + progressFX.progress = progress + } + + @FXML + fun cancel() { + TODO("Not yet implemented!") + } + + companion object { + private val LAYOUT = "minosoft:eros/main/account/checking.fxml".toResourceLocation() + + private val TITLE = "minosoft:main.account.checking_dialog.title".toResourceLocation() + private val HEADER = "minosoft:main.account.checking_dialog.header".toResourceLocation() + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/account/microsoft/MicrosoftOAuthUtils.kt b/src/main/java/de/bixilon/minosoft/util/account/microsoft/MicrosoftOAuthUtils.kt index 522d93f98..f7b2221ac 100644 --- a/src/main/java/de/bixilon/minosoft/util/account/microsoft/MicrosoftOAuthUtils.kt +++ b/src/main/java/de/bixilon/minosoft/util/account/microsoft/MicrosoftOAuthUtils.kt @@ -55,7 +55,7 @@ object MicrosoftOAuthUtils { account.state = AccountStates.WORKING account.accessToken = accessToken - account.check("") // client token does not exist for microsoft accounts + account.check(null, "") // client token does not exist for microsoft accounts Log.log(LogMessageType.AUTHENTICATION, LogLevels.INFO) { "Microsoft account login successful (uuid=${account.uuid})" } diff --git a/src/main/resources/assets/minosoft/eros/dialog/connection/verify_assets.fxml b/src/main/resources/assets/minosoft/eros/dialog/connection/verify_assets.fxml index 9884dd27a..515400601 100644 --- a/src/main/resources/assets/minosoft/eros/dialog/connection/verify_assets.fxml +++ b/src/main/resources/assets/minosoft/eros/dialog/connection/verify_assets.fxml @@ -72,7 +72,7 @@ - + + + + + + diff --git a/src/main/resources/assets/minosoft/language/en_us.lang b/src/main/resources/assets/minosoft/language/en_us.lang index def8598f2..221051613 100644 --- a/src/main/resources/assets/minosoft/language/en_us.lang +++ b/src/main/resources/assets/minosoft/language/en_us.lang @@ -99,6 +99,9 @@ minosoft:main.account.state.working=Working minosoft:main.account.state.expired=Expired minosoft:main.account.state.errored=Errored +minosoft:main.account.checking_dialog.title=Checking account... - Minosoft +minosoft:main.account.checking_dialog.header=Checking account... Please wait + minosoft:main.account.card.connection_count=%1$s connections