From 569b9f4ba43a69838e0efc489d21cdca88a6a246 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Tue, 22 Dec 2020 18:27:31 +0100 Subject: [PATCH] way improved account login dialog --- .../java/de/bixilon/minosoft/Minosoft.java | 14 +- .../bixilon/minosoft/data/locale/Strings.java | 1 + .../minosoft/gui/main/AccountWindow.java | 85 +---------- .../bixilon/minosoft/gui/main/GUITools.java | 25 +++ .../minosoft/gui/main/ServerListCell.java | 14 +- .../main/dialogs/MojangLoginController.java | 144 ++++++++++++++++++ .../util/mojang/api/MojangAccount.java | 10 ++ .../util/mojang/api/MojangAuthentication.java | 1 + src/main/resources/assets/locale/de_DE.json | 5 +- src/main/resources/assets/locale/en_US.json | 3 +- .../layout/dialogs/login_mojang.fxml | 72 +++++++++ src/main/resources/layout/main.fxml | 2 +- src/main/resources/layout/style.css | 4 + 13 files changed, 281 insertions(+), 99 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/main/dialogs/MojangLoginController.java create mode 100644 src/main/resources/layout/dialogs/login_mojang.fxml diff --git a/src/main/java/de/bixilon/minosoft/Minosoft.java b/src/main/java/de/bixilon/minosoft/Minosoft.java index 2d3e62820..95b6e085c 100644 --- a/src/main/java/de/bixilon/minosoft/Minosoft.java +++ b/src/main/java/de/bixilon/minosoft/Minosoft.java @@ -198,12 +198,14 @@ public final class Minosoft { config.saveToFile(); return; } - MojangAccount.RefreshStates refreshState = account.refreshToken(); - if (refreshState == MojangAccount.RefreshStates.ERROR) { - account.delete(); - AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().remove(account); - selectedAccount = null; - return; + if (account.needsRefresh()) { + MojangAccount.RefreshStates refreshState = account.refreshToken(); + if (refreshState == MojangAccount.RefreshStates.ERROR) { + account.delete(); + AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().remove(account); + selectedAccount = null; + return; + } } config.putString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED, account.getUserId()); if (Launcher.getMainWindow() != null) { diff --git a/src/main/java/de/bixilon/minosoft/data/locale/Strings.java b/src/main/java/de/bixilon/minosoft/data/locale/Strings.java index bd370c541..e6c9a52f3 100644 --- a/src/main/java/de/bixilon/minosoft/data/locale/Strings.java +++ b/src/main/java/de/bixilon/minosoft/data/locale/Strings.java @@ -80,6 +80,7 @@ public enum Strings { LOGIN_DIALOG_TITLE, LOGIN_DIALOG_HEADER, + LOGIN_ACCOUNT_ALREADY_PRESENT, MINOSOFT_STILL_STARTING_TITLE, MINOSOFT_STILL_STARTING_HEADER, diff --git a/src/main/java/de/bixilon/minosoft/gui/main/AccountWindow.java b/src/main/java/de/bixilon/minosoft/gui/main/AccountWindow.java index aeb823d7f..67c20ffe5 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/AccountWindow.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/AccountWindow.java @@ -13,27 +13,19 @@ package de.bixilon.minosoft.gui.main; -import com.jfoenix.controls.*; import de.bixilon.minosoft.Minosoft; import de.bixilon.minosoft.data.locale.LocaleManager; import de.bixilon.minosoft.data.locale.Strings; -import de.bixilon.minosoft.logging.Log; import de.bixilon.minosoft.util.mojang.api.MojangAccount; -import de.bixilon.minosoft.util.mojang.api.MojangAccountAuthenticationAttempt; -import de.bixilon.minosoft.util.mojang.api.MojangAuthentication; -import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; -import javafx.scene.control.Label; import javafx.scene.control.MenuItem; -import javafx.scene.input.KeyCode; import javafx.scene.layout.BorderPane; -import javafx.scene.layout.GridPane; -import javafx.stage.Window; +import javafx.stage.Modality; +import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; @@ -55,72 +47,11 @@ public class AccountWindow implements Initializable { @FXML public void addAccount() { - JFXAlert dialog = new JFXAlert<>(); - dialog.setTitle(LocaleManager.translate(Strings.LOGIN_DIALOG_TITLE)); - GUITools.initializePane(dialog.getDialogPane()); - JFXDialogLayout layout = new JFXDialogLayout(); - layout.setHeading(new Label(LocaleManager.translate(Strings.LOGIN_DIALOG_HEADER))); - - JFXButton loginButton = new JFXButton(LocaleManager.translate(Strings.BUTTON_LOGIN)); - layout.setActions(loginButton); - - GridPane gridPane = new GridPane(); - gridPane.setHgap(15); - gridPane.setVgap(15); - - JFXTextField emailField = new JFXTextField(); - emailField.setPromptText(LocaleManager.translate(Strings.EMAIL)); - - JFXPasswordField passwordField = new JFXPasswordField(); - passwordField.setPromptText(LocaleManager.translate(Strings.PASSWORD)); - - gridPane.add(new Label(LocaleManager.translate(Strings.EMAIL) + ":"), 0, 0); - gridPane.add(emailField, 1, 0); - gridPane.add(new Label(LocaleManager.translate(Strings.PASSWORD) + ":"), 0, 1); - gridPane.add(passwordField, 1, 1); - - emailField.textProperty().addListener((observable, oldValue, newValue) -> loginButton.setDisable(newValue.trim().isEmpty())); - loginButton.setDisable(true); - - layout.setBody(gridPane); - dialog.setContent(layout); - - Platform.runLater(emailField::requestFocus); - loginButton.addEventFilter(ActionEvent.ACTION, event -> { - MojangAccountAuthenticationAttempt attempt = MojangAuthentication.login(emailField.getText(), passwordField.getText()); - if (attempt.succeeded()) { - // login okay - MojangAccount account = attempt.getAccount(); - Minosoft.getConfig().putMojangAccount(account); - account.saveToConfig(); - AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().add(account); - Log.info(String.format("Added and saved account (playerName=%s, email=%s, uuid=%s)", account.getPlayerName(), account.getMojangUserName(), account.getUUID())); - dialog.close(); - return; - } - event.consume(); - Label error = new Label(); - error.setStyle("-fx-text-fill: red"); - error.setText(attempt.getError()); - - gridPane.add(new Label(LocaleManager.translate(Strings.ERROR)), 0, 2); - gridPane.add(error, 1, 2); - // ToDo resize window - }); - - Window window = dialog.getDialogPane().getScene().getWindow(); - window.setOnCloseRequest(windowEvent -> window.hide()); - - dialog.getDialogPane().setOnKeyReleased(keyEvent -> { - if (keyEvent.getCode() != KeyCode.ENTER) { - return; - } - if (emailField.getText().trim().isEmpty()) { - return; - } - loginButton.fire(); - }); - - dialog.showAndWait(); + try { + GUITools.showPane("/layout/dialogs/login_mojang.fxml", Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.LOGIN_DIALOG_TITLE)); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/main/GUITools.java b/src/main/java/de/bixilon/minosoft/gui/main/GUITools.java index fa670facc..e8e9d7f95 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/GUITools.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/GUITools.java @@ -19,12 +19,16 @@ import de.bixilon.minosoft.data.mappings.versions.Versions; import de.bixilon.minosoft.logging.LogLevels; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.layout.Pane; +import javafx.stage.Modality; import javafx.stage.Stage; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.util.Arrays; import java.util.Base64; @@ -82,4 +86,25 @@ public class GUITools { initializeScene(pane.getScene()); return pane; } + + public static T showPane(String fxmlPath, Modality modality, String title) throws IOException { + FXMLLoader loader = new FXMLLoader(GUITools.class.getResource(fxmlPath)); + Parent root = loader.load(); + Stage stage = new Stage(); + stage.initModality(modality); + double width = 600; + double height = 400; + if (root instanceof Pane pane) { + width = pane.getPrefWidth(); + height = pane.getPrefHeight(); + } + Scene scene = new Scene(root, width, height); + stage.setScene(scene); + + stage.setTitle(title); + initializeScene(scene); + + stage.show(); + return loader.getController(); + } } diff --git a/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java b/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java index 4af4a3e25..71a56779d 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/ServerListCell.java @@ -37,8 +37,6 @@ import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; @@ -48,7 +46,6 @@ import javafx.scene.paint.Color; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; import javafx.stage.Modality; -import javafx.stage.Stage; import java.io.IOException; import java.net.URL; @@ -418,15 +415,8 @@ public class ServerListCell extends ListCell implements Initializable { public void manageSessions() { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/layout/sessions.fxml")); - Parent parent = loader.load(); - ((SessionsWindow) loader.getController()).setServer(this.server); - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setTitle(LocaleManager.translate(Strings.SESSIONS_DIALOG_TITLE, this.server.getName())); - stage.setScene(new Scene(parent)); - GUITools.initializeScene(stage.getScene()); - stage.show(); + SessionsWindow sessionsWindow = GUITools.showPane("/layout/dialogs/login_mojang.fxml", Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.SESSIONS_DIALOG_TITLE, this.server.getName())); + sessionsWindow.setServer(this.server); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/de/bixilon/minosoft/gui/main/dialogs/MojangLoginController.java b/src/main/java/de/bixilon/minosoft/gui/main/dialogs/MojangLoginController.java new file mode 100644 index 000000000..aaa3726a9 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/main/dialogs/MojangLoginController.java @@ -0,0 +1,144 @@ +/* + * Minosoft + * Copyright (C) 2020 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.main.dialogs; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXPasswordField; +import com.jfoenix.controls.JFXTextField; +import de.bixilon.minosoft.Minosoft; +import de.bixilon.minosoft.data.locale.LocaleManager; +import de.bixilon.minosoft.data.locale.Strings; +import de.bixilon.minosoft.gui.main.AccountListCell; +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.util.mojang.api.MojangAccount; +import de.bixilon.minosoft.util.mojang.api.MojangAccountAuthenticationAttempt; +import de.bixilon.minosoft.util.mojang.api.MojangAuthentication; +import javafx.application.Platform; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.ResourceBundle; + +public class MojangLoginController implements Initializable { + public HBox hBox; + public Label header; + public Label emailLabel; + public JFXTextField email; + public Label passwordLabel; + public JFXPasswordField password; + public JFXButton loginButton; + public Label errorMessage; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + // translate + + this.header.setText(LocaleManager.translate(Strings.LOGIN_DIALOG_HEADER)); + this.emailLabel.setText(LocaleManager.translate(Strings.EMAIL)); + this.passwordLabel.setText(LocaleManager.translate(Strings.PASSWORD)); + this.loginButton.setText(LocaleManager.translate(Strings.BUTTON_LOGIN)); + + + this.email.textProperty().addListener(this::checkData); + this.password.textProperty().addListener(this::checkData); + + + this.hBox.setOnKeyReleased(keyEvent -> { + if (keyEvent.getCode() == KeyCode.ENTER) { + if (this.loginButton.isDisable()) { + return; + } + this.loginButton.fire(); + return; + } + if (keyEvent.getCode() == KeyCode.ESCAPE) { + close(); + } + }); + } + + public void login(ActionEvent event) { + event.consume(); + this.email.setDisable(true); + this.password.setDisable(true); + this.loginButton.setDisable(true); + this.errorMessage.setVisible(false); + + + new Thread(() -> { // ToDo: recycle thread + MojangAccountAuthenticationAttempt attempt = MojangAuthentication.login(this.email.getText(), this.password.getText()); + if (attempt.succeeded()) { + // login okay + final MojangAccount account = attempt.getAccount(); + if (Minosoft.getConfig().getAccountList().containsValue(account)) { + // account already present + // replace access token + MojangAccount existingAccount = Minosoft.getConfig().getAccountList().get(account.getUserId()); + existingAccount.setAccessToken(attempt.getAccount().getAccessToken()); + existingAccount.saveToConfig(); + Platform.runLater(() -> { + this.errorMessage.setText(LocaleManager.translate(Strings.LOGIN_ACCOUNT_ALREADY_PRESENT)); + this.errorMessage.setVisible(true); + this.email.setDisable(false); + this.password.setDisable(false); + this.loginButton.setDisable(true); + }); + return; + } + Minosoft.getConfig().putMojangAccount(account); + account.saveToConfig(); + Log.info(String.format("Added and saved account (playerName=%s, email=%s, uuid=%s)", account.getPlayerName(), account.getMojangUserName(), account.getUUID())); + Platform.runLater(() -> { + AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().add(account); + close(); + }); + if (Minosoft.getSelectedAccount() == null) { + // select account + Minosoft.selectAccount(account); + } + return; + } + Platform.runLater(() -> { + this.errorMessage.setText(attempt.getError()); + this.errorMessage.setVisible(true); + this.email.setDisable(false); + this.password.setDisable(false); + this.loginButton.setDisable(true); + }); + }, "AccountLoginThread").start(); + + } + + private void checkData(ObservableValue observableValue, String oldValue, String newValue) { + if (newValue.isEmpty()) { + this.loginButton.setDisable(true); + return; + } + this.loginButton.setDisable(this.email.getText().isBlank() || this.password.getText().isBlank()); + } + + public void close() { + getStage().close(); + } + + public Stage getStage() { + return ((Stage) this.hBox.getScene().getWindow()); + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAccount.java b/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAccount.java index 5f0a0f2d8..e7fe5761a 100644 --- a/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAccount.java +++ b/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAccount.java @@ -26,6 +26,7 @@ public class MojangAccount { final String mojangUserName; String accessToken; RefreshStates lastRefreshStatus; + private boolean needsRefresh = true; public MojangAccount(String username, JsonObject json) { this.accessToken = json.get("accessToken").getAsString(); @@ -137,6 +138,15 @@ public class MojangAccount { return account.getUserId().equals(getUserId()); } + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + this.needsRefresh = false; + } + + public boolean needsRefresh() { + return this.needsRefresh; + } + public enum RefreshStates { SUCCESSFUL, ERROR, // account not valid anymore diff --git a/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAuthentication.java b/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAuthentication.java index 40e465441..7e16f6d93 100644 --- a/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAuthentication.java +++ b/src/main/java/de/bixilon/minosoft/util/mojang/api/MojangAuthentication.java @@ -56,6 +56,7 @@ public final class MojangAuthentication { return new MojangAccountAuthenticationAttempt(new MojangAccount(username, jsonResponse)); } + public static void joinServer(MojangAccount account, String serverId) { if (StaticConfiguration.SKIP_MOJANG_AUTHENTICATION) { return; diff --git a/src/main/resources/assets/locale/de_DE.json b/src/main/resources/assets/locale/de_DE.json index e9fca66b9..45a42cf40 100644 --- a/src/main/resources/assets/locale/de_DE.json +++ b/src/main/resources/assets/locale/de_DE.json @@ -54,8 +54,9 @@ "SETTINGS_GENERAL": "Allgemein", "SETTINGS_GENERAL_LOG_LEVEL": "Log Level", "SETTINGS_DOWNLOAD": "Download", - "LOGIN_DIALOG_TITLE": "Anmelden - Minosoft", - "LOGIN_DIALOG_HEADER": "Bitte gib deine Minecraft Zugangsdaten ein dich anzumelden", + "LOGIN_DIALOG_TITLE": "Anmelden (Mojang) - Minosoft", + "LOGIN_DIALOG_HEADER": "Bitte gib deine Mojang Zugangsdaten ein dich anzumelden", + "LOGIN_ACCOUNT_ALREADY_PRESENT": "Account existiert bereits!", "ERROR": "Fehler", "MINOSOFT_STILL_STARTING_TITLE": "Bitte warten", "MINOSOFT_STILL_STARTING_HEADER": "Minosoft muss noch ein paar Dinge erledigen, bevor du es verwenden kannst.\nBitte warte noch ein paar Sekunden...", diff --git a/src/main/resources/assets/locale/en_US.json b/src/main/resources/assets/locale/en_US.json index dd6fab066..63cbaf02a 100644 --- a/src/main/resources/assets/locale/en_US.json +++ b/src/main/resources/assets/locale/en_US.json @@ -55,8 +55,9 @@ "SETTINGS_GENERAL": "General", "SETTINGS_GENERAL_LOG_LEVEL": "Log level", "SETTINGS_DOWNLOAD": "Download", - "LOGIN_DIALOG_TITLE": "Login - Minosoft", + "LOGIN_DIALOG_TITLE": "Login (Mojang) - Minosoft", "LOGIN_DIALOG_HEADER": "Please login to your mojang account", + "LOGIN_ACCOUNT_ALREADY_PRESENT": "Account is already present!", "ERROR": "Error", "MINOSOFT_STILL_STARTING_TITLE": "Please wait", "MINOSOFT_STILL_STARTING_HEADER": "Minosoft is still starting up...", diff --git a/src/main/resources/layout/dialogs/login_mojang.fxml b/src/main/resources/layout/dialogs/login_mojang.fxml new file mode 100644 index 000000000..0c038eb11 --- /dev/null +++ b/src/main/resources/layout/dialogs/login_mojang.fxml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/layout/main.fxml b/src/main/resources/layout/main.fxml index c61c5243d..7fabe2411 100644 --- a/src/main/resources/layout/main.fxml +++ b/src/main/resources/layout/main.fxml @@ -37,7 +37,7 @@ - + diff --git a/src/main/resources/layout/style.css b/src/main/resources/layout/style.css index 2371fd621..6f8c6c31b 100644 --- a/src/main/resources/layout/style.css +++ b/src/main/resources/layout/style.css @@ -201,3 +201,7 @@ .obfuscated { -fx-font-family: "Hack"; } + +.error { + -fx-text-fill: red; +}