diff --git a/src/main/java/de/bixilon/minosoft/Minosoft.java b/src/main/java/de/bixilon/minosoft/Minosoft.java index aaac11079..6c7f196d6 100644 --- a/src/main/java/de/bixilon/minosoft/Minosoft.java +++ b/src/main/java/de/bixilon/minosoft/Minosoft.java @@ -135,7 +135,7 @@ public final class Minosoft { taskWorker.addTask(new Task(progress -> { Log.debug("Refreshing account token..."); checkClientToken(); - selectAccount(config.getSccounts().get(config.getString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED))); + selectAccount(config.getAccounts().get(config.getString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED))); }, "Token refresh", "Refresh selected account token", Priorities.LOW, TaskImportance.OPTIONAL, "Configuration")); taskWorker.addTask(new Task(progress -> { diff --git a/src/main/java/de/bixilon/minosoft/config/Configuration.java b/src/main/java/de/bixilon/minosoft/config/Configuration.java index 17eea5b5d..30a681cc5 100644 --- a/src/main/java/de/bixilon/minosoft/config/Configuration.java +++ b/src/main/java/de/bixilon/minosoft/config/Configuration.java @@ -203,7 +203,7 @@ public class Configuration { return this.serverList; } - public HashBiMap getSccounts() { + public HashBiMap getAccounts() { return this.accountList; } diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/Account.java b/src/main/java/de/bixilon/minosoft/data/accounts/Account.java index 5d74fec4f..4d4334fae 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/Account.java +++ b/src/main/java/de/bixilon/minosoft/data/accounts/Account.java @@ -14,6 +14,7 @@ package de.bixilon.minosoft.data.accounts; import com.google.gson.JsonObject; +import de.bixilon.minosoft.Minosoft; import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException; import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException; @@ -65,4 +66,9 @@ public abstract class Account { Account account = (Account) obj; return getId().equals(account.getId()); } + + public void saveToConfig() { + Minosoft.getConfig().putAccount(this); + Minosoft.getConfig().saveToFile(); + } } diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/MojangAccount.java b/src/main/java/de/bixilon/minosoft/data/accounts/MojangAccount.java index feceea552..ce36cb205 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/MojangAccount.java +++ b/src/main/java/de/bixilon/minosoft/data/accounts/MojangAccount.java @@ -73,6 +73,8 @@ public class MojangAccount extends Account { @Override public void logout() { + Minosoft.getConfig().removeAccount(this); + Minosoft.getConfig().saveToFile(); } @Override @@ -107,15 +109,6 @@ public class MojangAccount extends Account { return this.email; } - public void saveToConfig() { - Minosoft.getConfig().putAccount(this); - Minosoft.getConfig().saveToFile(); - } - - public void delete() { - Minosoft.getConfig().removeAccount(this); - Minosoft.getConfig().saveToFile(); - } @Override public String toString() { diff --git a/src/main/java/de/bixilon/minosoft/data/accounts/OfflineAccount.java b/src/main/java/de/bixilon/minosoft/data/accounts/OfflineAccount.java index 2b0130186..490f79ac4 100644 --- a/src/main/java/de/bixilon/minosoft/data/accounts/OfflineAccount.java +++ b/src/main/java/de/bixilon/minosoft/data/accounts/OfflineAccount.java @@ -14,28 +14,18 @@ package de.bixilon.minosoft.data.accounts; import com.google.gson.JsonObject; -import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition; import de.bixilon.minosoft.util.Util; import java.nio.charset.StandardCharsets; import java.util.UUID; -import java.util.regex.Matcher; public class OfflineAccount extends Account { public OfflineAccount(String username) { super(username, UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8))); - Matcher matcher = ProtocolDefinition.MINECRAFT_NAME_VALIDATOR.matcher(username); - if (!matcher.find() || !matcher.group().equals(username)) { - throw new IllegalArgumentException(String.format("%s is not valid Minecraft username", username)); - } } public OfflineAccount(String username, UUID uuid) { super(username, uuid); - Matcher matcher = ProtocolDefinition.MINECRAFT_NAME_VALIDATOR.matcher(username); - if (!matcher.find() || !matcher.group().equals(username)) { - throw new IllegalArgumentException(String.format("%s is not valid Minecraft username", username)); - } } public static OfflineAccount deserialize(JsonObject json) { @@ -66,6 +56,6 @@ public class OfflineAccount extends Account { @Override public String getId() { - return getUsername(); + return getUsername() + ":" + getUUID().toString(); } } 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..03b8a466d 100644 --- a/src/main/java/de/bixilon/minosoft/data/locale/Strings.java +++ b/src/main/java/de/bixilon/minosoft/data/locale/Strings.java @@ -52,7 +52,8 @@ public enum Strings { SERVER_ACTION_DELETE, SESSIONS_ACTION_DISCONNECT, - ACCOUNT_MODAL_MENU_ADD_ACCOUNT, + ACCOUNT_MODAL_MENU_ADD_MOJANG_ACCOUNT, + ACCOUNT_MODAL_MENU_ADD_OFFLINE_ACCOUNT, MAIN_WINDOW_TITLE, MAIN_WINDOW_MENU_FILE, MAIN_WINDOW_MENU_FILE_PREFERENCES, @@ -78,8 +79,14 @@ public enum Strings { MANAGE_ACCOUNTS_NO_ACCOUNT_ERROR_ERROR, MANAGE_ACCOUNTS_NO_ACCOUNT_ERROR, - LOGIN_DIALOG_TITLE, - LOGIN_DIALOG_HEADER, + LOGIN_MOJANG_DIALOG_TITLE, + LOGIN_MOJANG_DIALOG_HEADER, + + LOGIN_OFFLINE_DIALOG_TITLE, + LOGIN_OFFLINE_DIALOG_HEADER, + LOGIN_OFFLINE_USERNAME, + LOGIN_OFFLINE_UUID, + LOGIN_OFFLINE_ADD_BUTTON, MINOSOFT_STILL_STARTING_TITLE, MINOSOFT_STILL_STARTING_HEADER, diff --git a/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java b/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java index 76eff9d5d..c9bcc66fa 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/AccountListCell.java @@ -104,10 +104,10 @@ public class AccountListCell extends ListCell implements Initializable Minosoft.getConfig().removeAccount(this.account); Minosoft.getConfig().saveToFile(); if (Minosoft.getConfig().getSelectedAccount() == this.account) { - if (Minosoft.getConfig().getSccounts().isEmpty()) { + if (Minosoft.getConfig().getAccounts().isEmpty()) { Minosoft.selectAccount(null); } else { - Minosoft.selectAccount(Minosoft.getConfig().getSccounts().values().iterator().next()); + Minosoft.selectAccount(Minosoft.getConfig().getAccounts().values().iterator().next()); } MOJANG_ACCOUNT_LIST_VIEW.refresh(); } 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 76ab371e0..ea0fcc903 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/AccountWindow.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/AccountWindow.java @@ -19,7 +19,6 @@ import de.bixilon.minosoft.data.locale.LocaleManager; import de.bixilon.minosoft.data.locale.Strings; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.MenuItem; import javafx.scene.layout.BorderPane; @@ -31,23 +30,33 @@ import java.util.ResourceBundle; public class AccountWindow implements Initializable { public BorderPane accountPane; - public MenuItem menuAddAccount; + public MenuItem menuAddMojangAccount; + public MenuItem menuAddOfflineAccount; @Override public void initialize(URL url, ResourceBundle resourceBundle) { AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.setCellFactory((lv) -> AccountListCell.newInstance()); - ObservableList accounts = FXCollections.observableArrayList(Minosoft.getConfig().getSccounts().values()); + ObservableList accounts = FXCollections.observableArrayList(Minosoft.getConfig().getAccounts().values()); AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.setItems(accounts); this.accountPane.setCenter(AccountListCell.MOJANG_ACCOUNT_LIST_VIEW); - this.menuAddAccount.setText(LocaleManager.translate(Strings.ACCOUNT_MODAL_MENU_ADD_ACCOUNT)); + this.menuAddMojangAccount.setText(LocaleManager.translate(Strings.ACCOUNT_MODAL_MENU_ADD_MOJANG_ACCOUNT)); + this.menuAddOfflineAccount.setText(LocaleManager.translate(Strings.ACCOUNT_MODAL_MENU_ADD_OFFLINE_ACCOUNT)); } - @FXML - public void addAccount() { + public void addMojangAccount() { try { - GUITools.showPane("/layout/dialogs/login_mojang.fxml", Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.LOGIN_DIALOG_TITLE)); + GUITools.showPane("/layout/dialogs/login_mojang.fxml", Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.LOGIN_MOJANG_DIALOG_TITLE)); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + public void addOfflineAccount() { + try { + GUITools.showPane("/layout/dialogs/login_offline.fxml", Modality.APPLICATION_MODAL, LocaleManager.translate(Strings.LOGIN_OFFLINE_DIALOG_TITLE)); } catch (IOException e) { e.printStackTrace(); System.exit(1); 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 index 9835a2aac..7d2cbf446 100644 --- a/src/main/java/de/bixilon/minosoft/gui/main/dialogs/MojangLoginController.java +++ b/src/main/java/de/bixilon/minosoft/gui/main/dialogs/MojangLoginController.java @@ -50,7 +50,7 @@ public class MojangLoginController implements Initializable { @Override public void initialize(URL url, ResourceBundle resourceBundle) { // translate - this.header.setText(LocaleManager.translate(Strings.LOGIN_DIALOG_HEADER)); + this.header.setText(LocaleManager.translate(Strings.LOGIN_MOJANG_DIALOG_HEADER)); this.emailLabel.setText(LocaleManager.translate(Strings.EMAIL)); this.passwordLabel.setText(LocaleManager.translate(Strings.PASSWORD)); this.loginButton.setText(LocaleManager.translate(Strings.BUTTON_LOGIN)); @@ -89,7 +89,7 @@ public class MojangLoginController implements Initializable { account.setNeedRefresh(false); Minosoft.getConfig().putAccount(account); account.saveToConfig(); - Log.info(String.format("Added and saved account (username=%s, email=%s, uuid=%s)", account.getUsername(), account.getEmail(), account.getUUID())); + Log.info(String.format("Added and saved account (type=mojang, username=%s, email=%s, uuid=%s)", account.getUsername(), account.getEmail(), account.getUUID())); Platform.runLater(() -> { AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().add(account); close(); diff --git a/src/main/java/de/bixilon/minosoft/gui/main/dialogs/OfflineLoginController.java b/src/main/java/de/bixilon/minosoft/gui/main/dialogs/OfflineLoginController.java new file mode 100644 index 000000000..fdedb2539 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/main/dialogs/OfflineLoginController.java @@ -0,0 +1,122 @@ +/* + * 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.JFXTextField; +import de.bixilon.minosoft.Minosoft; +import de.bixilon.minosoft.data.accounts.OfflineAccount; +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.protocol.protocol.ProtocolDefinition; +import de.bixilon.minosoft.util.Util; +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; +import java.util.regex.Matcher; + +public class OfflineLoginController implements Initializable { + public HBox hBox; + public Label header; + public Label usernameLabel; + public JFXTextField username; + public Label uuidLabel; + public JFXTextField uuid; + public JFXButton addButton; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + // translate + this.header.setText(LocaleManager.translate(Strings.LOGIN_OFFLINE_DIALOG_HEADER)); + this.usernameLabel.setText(LocaleManager.translate(Strings.LOGIN_OFFLINE_USERNAME)); + this.uuidLabel.setText(LocaleManager.translate(Strings.LOGIN_OFFLINE_UUID)); + this.addButton.setText(LocaleManager.translate(Strings.LOGIN_OFFLINE_ADD_BUTTON)); + + + this.username.textProperty().addListener(this::checkData); + this.uuid.textProperty().addListener(this::checkData); + + + this.hBox.setOnKeyReleased(keyEvent -> { + if (keyEvent.getCode() == KeyCode.ENTER) { + if (this.addButton.isDisable()) { + return; + } + this.addButton.fire(); + return; + } + if (keyEvent.getCode() == KeyCode.ESCAPE) { + close(); + } + }); + } + + public void add(ActionEvent event) { + event.consume(); + OfflineAccount account; + if (this.uuid.getText().isBlank()) { + account = new OfflineAccount(this.username.getText()); + } else { + account = new OfflineAccount(this.username.getText(), Util.getUUIDFromString(this.uuid.getText())); + } + + Minosoft.getConfig().putAccount(account); + account.saveToConfig(); + Log.info(String.format("Added and saved account (type=offline, username=%s, uuid=%s)", account.getUsername(), account.getUUID())); + Platform.runLater(() -> { + AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().add(account); + close(); + }); + if (Minosoft.getConfig().getSelectedAccount() == null) { + // select account + Minosoft.selectAccount(account); + } + + } + + private void checkData(ObservableValue observableValue, String oldValue, String newValue) { + Matcher matcher = ProtocolDefinition.MINECRAFT_NAME_VALIDATOR.matcher(this.username.getText()); + if (!matcher.find() || !matcher.group().equals(this.username.getText())) { + this.addButton.setDisable(true); + return; + } + if (!this.uuid.getText().isBlank()) { + try { + Util.getUUIDFromString(this.uuid.getText()); + } catch (IllegalArgumentException e) { + this.addButton.setDisable(true); + return; + } + } + this.addButton.setDisable(false); + } + + public void close() { + getStage().close(); + } + + public Stage getStage() { + return ((Stage) this.hBox.getScene().getWindow()); + } +} diff --git a/src/main/java/de/bixilon/minosoft/util/Util.java b/src/main/java/de/bixilon/minosoft/util/Util.java index 6a4da3f76..0a5de4c72 100644 --- a/src/main/java/de/bixilon/minosoft/util/Util.java +++ b/src/main/java/de/bixilon/minosoft/util/Util.java @@ -42,6 +42,7 @@ public final class Util { private static final Random THREAD_LOCAL_RANDOM = ThreadLocalRandom.current(); public static UUID getUUIDFromString(String uuid) { + uuid = uuid.trim(); if (uuid.length() == 36) { return UUID.fromString(uuid); } diff --git a/src/main/resources/assets/locale/de_DE.json b/src/main/resources/assets/locale/de_DE.json index 56e6a846f..801d6ff23 100644 --- a/src/main/resources/assets/locale/de_DE.json +++ b/src/main/resources/assets/locale/de_DE.json @@ -35,7 +35,8 @@ "SERVER_ACTION_SESSIONS": "Verbindungen", "SERVER_ACTION_DELETE": "Löschen", "SESSIONS_ACTION_DISCONNECT": "Trennen", - "ACCOUNT_MODAL_MENU_ADD_ACCOUNT": "Hinzufügen", + "ACCOUNT_MODAL_MENU_ADD_MOJANG_ACCOUNT": "Anmelden (Mojang)", + "ACCOUNT_MODAL_MENU_ADD_OFFLINE_ACCOUNT": "Hinzufügen (Offline)", "MAIN_WINDOW_TITLE": "Minosoft", "MAIN_WINDOW_MENU_FILE": "_Datei", "MAIN_WINDOW_MENU_FILE_PREFERENCES": "Einstellungen", @@ -54,8 +55,13 @@ "SETTINGS_GENERAL": "Allgemein", "SETTINGS_GENERAL_LOG_LEVEL": "Log Level", "SETTINGS_DOWNLOAD": "Download", - "LOGIN_DIALOG_TITLE": "Anmelden (Mojang) - Minosoft", - "LOGIN_DIALOG_HEADER": "Bitte gib deine Mojang Zugangsdaten ein dich anzumelden", + "LOGIN_MOJANG_DIALOG_TITLE": "Anmelden (Mojang) - Minosoft", + "LOGIN_MOJANG_DIALOG_HEADER": "Bitte gib deine Mojang Zugangsdaten ein dich anzumelden", + "LOGIN_OFFLINE_DIALOG_TITLE": "Anmelden (Offline) - Minosoft", + "LOGIN_OFFLINE_DIALOG_HEADER": "Bitte gib einen Benutzernamen ein (und eine UUID)", + "LOGIN_OFFLINE_USERNAME": "Benutzername", + "LOGIN_OFFLINE_UUID": "(UUID)", + "LOGIN_OFFLINE_ADD_BUTTON": "Hinzufügen", "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 1a6d4ed24..7637a5711 100644 --- a/src/main/resources/assets/locale/en_US.json +++ b/src/main/resources/assets/locale/en_US.json @@ -36,7 +36,8 @@ "SERVER_ACTION_SESSIONS": "Sessions", "SERVER_ACTION_DELETE": "Delete", "SESSIONS_ACTION_DISCONNECT": "Disconnect", - "ACCOUNT_MODAL_MENU_ADD_ACCOUNT": "Add", + "ACCOUNT_MODAL_MENU_ADD_MOJANG_ACCOUNT": "Login (Mojang)", + "ACCOUNT_MODAL_MENU_ADD_OFFLINE_ACCOUNT": "Add (Offline)", "MAIN_WINDOW_TITLE": "Minosoft", "MAIN_WINDOW_MENU_FILE": "_File", "MAIN_WINDOW_MENU_FILE_PREFERENCES": "Preferences", @@ -55,8 +56,13 @@ "SETTINGS_GENERAL": "General", "SETTINGS_GENERAL_LOG_LEVEL": "Log level", "SETTINGS_DOWNLOAD": "Download", - "LOGIN_DIALOG_TITLE": "Login (Mojang) - Minosoft", - "LOGIN_DIALOG_HEADER": "Please login to your mojang account", + "LOGIN_MOJANG_DIALOG_TITLE": "Login (Mojang) - Minosoft", + "LOGIN_MOJANG_DIALOG_HEADER": "Please login to your mojang account", + "LOGIN_OFFLINE_DIALOG_TITLE": "Login (Offline) - Minosoft", + "LOGIN_OFFLINE_DIALOG_HEADER": "Please enter a username (and a UUID)", + "LOGIN_OFFLINE_USERNAME": "Username", + "LOGIN_OFFLINE_UUID": "(UUID)", + "LOGIN_OFFLINE_ADD_BUTTON": "Add", "ERROR": "Error", "MINOSOFT_STILL_STARTING_TITLE": "Please wait", "MINOSOFT_STILL_STARTING_HEADER": "Minosoft is still starting up...", diff --git a/src/main/resources/layout/accounts.fxml b/src/main/resources/layout/accounts.fxml index dec4b3b4b..e99f20d25 100644 --- a/src/main/resources/layout/accounts.fxml +++ b/src/main/resources/layout/accounts.fxml @@ -11,11 +11,13 @@ ~ ~ This software is not affiliated with Mojang AB, the original developer of Minecraft. --> - + + - + + diff --git a/src/main/resources/layout/dialogs/login_offline.fxml b/src/main/resources/layout/dialogs/login_offline.fxml new file mode 100644 index 000000000..f26347eb6 --- /dev/null +++ b/src/main/resources/layout/dialogs/login_offline.fxml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +