mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-16 19:05:02 -04:00
Improvements, WIP microsoft accounts (1): multiple login endpoints
This commit is contained in:
parent
569b9f4ba4
commit
9adb18681a
@ -18,6 +18,7 @@ import com.jfoenix.controls.JFXDialogLayout;
|
||||
import de.bixilon.minosoft.config.Configuration;
|
||||
import de.bixilon.minosoft.config.ConfigurationPaths;
|
||||
import de.bixilon.minosoft.config.StaticConfiguration;
|
||||
import de.bixilon.minosoft.data.accounts.Account;
|
||||
import de.bixilon.minosoft.data.assets.AssetsManager;
|
||||
import de.bixilon.minosoft.data.locale.LocaleManager;
|
||||
import de.bixilon.minosoft.data.locale.minecraft.MinecraftLocaleManager;
|
||||
@ -35,7 +36,6 @@ import de.bixilon.minosoft.protocol.protocol.LANServerListener;
|
||||
import de.bixilon.minosoft.util.CountUpAndDownLatch;
|
||||
import de.bixilon.minosoft.util.MinosoftCommandLineArguments;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAccount;
|
||||
import de.bixilon.minosoft.util.task.AsyncTaskWorker;
|
||||
import de.bixilon.minosoft.util.task.Task;
|
||||
import de.bixilon.minosoft.util.task.TaskImportance;
|
||||
@ -51,7 +51,6 @@ import java.util.UUID;
|
||||
public final class Minosoft {
|
||||
public static final HashSet<EventManager> EVENT_MANAGERS = new HashSet<>();
|
||||
private static final CountUpAndDownLatch START_STATUS_LATCH = new CountUpAndDownLatch(1);
|
||||
public static MojangAccount selectedAccount;
|
||||
public static Configuration config;
|
||||
|
||||
public static void main(String[] args) {
|
||||
@ -136,7 +135,7 @@ public final class Minosoft {
|
||||
taskWorker.addTask(new Task(progress -> {
|
||||
Log.debug("Refreshing account token...");
|
||||
checkClientToken();
|
||||
selectAccount(config.getAccountList().get(config.getString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED)));
|
||||
selectAccount(config.getSccounts().get(config.getString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED)));
|
||||
}, "Token refresh", "Refresh selected account token", Priorities.LOW, TaskImportance.OPTIONAL, "Configuration"));
|
||||
|
||||
taskWorker.addTask(new Task(progress -> {
|
||||
@ -191,37 +190,31 @@ public final class Minosoft {
|
||||
}
|
||||
}
|
||||
|
||||
public static void selectAccount(MojangAccount account) {
|
||||
public static void selectAccount(Account account) {
|
||||
if (account == null) {
|
||||
selectedAccount = null;
|
||||
config.putString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED, "");
|
||||
config.saveToFile();
|
||||
return;
|
||||
}
|
||||
if (account.needsRefresh()) {
|
||||
MojangAccount.RefreshStates refreshState = account.refreshToken();
|
||||
if (refreshState == MojangAccount.RefreshStates.ERROR) {
|
||||
account.delete();
|
||||
if (!account.select()) {
|
||||
account.logout();
|
||||
AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().remove(account);
|
||||
selectedAccount = null;
|
||||
config.removeAccount(account);
|
||||
config.saveToFile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
config.putString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED, account.getUserId());
|
||||
config.putAccount(account);
|
||||
config.selectAccount(account);
|
||||
if (Launcher.getMainWindow() != null) {
|
||||
Launcher.getMainWindow().selectAccount(selectedAccount);
|
||||
Launcher.getMainWindow().selectAccount(account);
|
||||
}
|
||||
account.saveToConfig();
|
||||
selectedAccount = account;
|
||||
config.saveToFile();
|
||||
}
|
||||
|
||||
public static Configuration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public static MojangAccount getSelectedAccount() {
|
||||
return selectedAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until all critical components are started
|
||||
|
@ -15,10 +15,12 @@ package de.bixilon.minosoft.config;
|
||||
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.gson.*;
|
||||
import de.bixilon.minosoft.data.accounts.Account;
|
||||
import de.bixilon.minosoft.data.accounts.MojangAccount;
|
||||
import de.bixilon.minosoft.data.accounts.OfflineAccount;
|
||||
import de.bixilon.minosoft.gui.main.Server;
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAccount;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
@ -30,7 +32,7 @@ public class Configuration {
|
||||
public static final int LATEST_CONFIG_VERSION = 1;
|
||||
private static final JsonObject DEFAULT_CONFIGURATION = JsonParser.parseReader(new InputStreamReader(Configuration.class.getResourceAsStream("/config/" + StaticConfiguration.CONFIG_FILENAME))).getAsJsonObject();
|
||||
private final HashMap<ConfigurationPaths.ConfigurationPath, Object> config = new HashMap<>();
|
||||
private final HashBiMap<String, MojangAccount> accountList = HashBiMap.create();
|
||||
private final HashBiMap<String, Account> accountList = HashBiMap.create();
|
||||
private final HashBiMap<Integer, Server> serverList = HashBiMap.create();
|
||||
private final Object lock = new Object();
|
||||
|
||||
@ -71,8 +73,13 @@ public class Configuration {
|
||||
|
||||
// accounts
|
||||
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject("accounts").getAsJsonObject("entries").entrySet()) {
|
||||
MojangAccount account = MojangAccount.deserialize(entry.getValue().getAsJsonObject());
|
||||
this.accountList.put(account.getUserId(), account);
|
||||
JsonObject data = entry.getValue().getAsJsonObject();
|
||||
Account account = switch (data.get("type").getAsString()) {
|
||||
case "mojang" -> MojangAccount.deserialize(data);
|
||||
case "offline" -> OfflineAccount.deserialize(data);
|
||||
default -> throw new IllegalStateException("Unexpected value: " + data.get("type").getAsString());
|
||||
};
|
||||
this.accountList.put(account.getId(), account);
|
||||
}
|
||||
|
||||
final File finalFile = file;
|
||||
@ -106,7 +113,7 @@ public class Configuration {
|
||||
|
||||
// servers
|
||||
JsonObject serversEntriesJson = jsonObject.getAsJsonObject("accounts").getAsJsonObject("entries");
|
||||
for (Map.Entry<String, MojangAccount> entry : this.accountList.entrySet()) {
|
||||
for (Map.Entry<String, Account> entry : this.accountList.entrySet()) {
|
||||
serversEntriesJson.add(entry.getKey(), entry.getValue().serialize());
|
||||
}
|
||||
|
||||
@ -159,8 +166,8 @@ public class Configuration {
|
||||
this.config.put(path, value);
|
||||
}
|
||||
|
||||
public void putMojangAccount(MojangAccount account) {
|
||||
this.accountList.put(account.getUserId(), account);
|
||||
public void putAccount(Account account) {
|
||||
this.accountList.put(account.getId(), account);
|
||||
}
|
||||
|
||||
public void putServer(Server server) {
|
||||
@ -177,15 +184,26 @@ public class Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAccount(MojangAccount account) {
|
||||
this.accountList.remove(account.getUserId());
|
||||
public Account getSelectedAccount() {
|
||||
return this.accountList.get(getString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED));
|
||||
}
|
||||
|
||||
public void selectAccount(Account account) {
|
||||
putString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED, account.getId());
|
||||
}
|
||||
|
||||
public void removeAccount(Account account) {
|
||||
this.accountList.remove(account.getId());
|
||||
if (getString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED).equals(account.getId())) {
|
||||
putString(ConfigurationPaths.StringPaths.ACCOUNT_SELECTED, "");
|
||||
}
|
||||
}
|
||||
|
||||
public HashBiMap<Integer, Server> getServerList() {
|
||||
return this.serverList;
|
||||
}
|
||||
|
||||
public HashBiMap<String, MojangAccount> getAccountList() {
|
||||
public HashBiMap<String, Account> getSccounts() {
|
||||
return this.accountList;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
package de.bixilon.minosoft.data;
|
||||
|
||||
import de.bixilon.minosoft.data.accounts.Account;
|
||||
import de.bixilon.minosoft.data.entities.entities.player.PlayerEntity;
|
||||
import de.bixilon.minosoft.data.inventory.Inventory;
|
||||
import de.bixilon.minosoft.data.inventory.InventoryProperties;
|
||||
@ -23,7 +24,6 @@ import de.bixilon.minosoft.data.scoreboard.ScoreboardManager;
|
||||
import de.bixilon.minosoft.data.text.ChatComponent;
|
||||
import de.bixilon.minosoft.data.world.BlockPosition;
|
||||
import de.bixilon.minosoft.data.world.World;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAccount;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
@ -32,7 +32,7 @@ import static de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.PLAYER_IN
|
||||
|
||||
public class Player {
|
||||
public final HashMap<UUID, PlayerListItem> playerList = new HashMap<>();
|
||||
final MojangAccount account;
|
||||
final Account account;
|
||||
final ScoreboardManager scoreboardManager = new ScoreboardManager();
|
||||
final World world = new World();
|
||||
final HashMap<Integer, Inventory> inventories = new HashMap<>();
|
||||
@ -50,21 +50,21 @@ public class Player {
|
||||
ChatComponent tabHeader;
|
||||
ChatComponent tabFooter;
|
||||
|
||||
public Player(MojangAccount account) {
|
||||
public Player(Account account) {
|
||||
this.account = account;
|
||||
// create our own inventory without any properties
|
||||
this.inventories.put(PLAYER_INVENTORY_ID, new Inventory(null));
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return this.account.getPlayerName();
|
||||
return this.account.getUsername();
|
||||
}
|
||||
|
||||
public UUID getPlayerUUID() {
|
||||
return this.account.getUUID();
|
||||
}
|
||||
|
||||
public MojangAccount getAccount() {
|
||||
public Account getAccount() {
|
||||
return this.account;
|
||||
}
|
||||
|
||||
|
68
src/main/java/de/bixilon/minosoft/data/accounts/Account.java
Normal file
68
src/main/java/de/bixilon/minosoft/data/accounts/Account.java
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.accounts;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class Account {
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
|
||||
protected Account(String username, UUID uuid) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public abstract JsonObject serialize();
|
||||
|
||||
public abstract void join(String serverId) throws MojangJoinServerErrorException, NoNetworkConnectionException;
|
||||
|
||||
public abstract boolean select();
|
||||
|
||||
public abstract void logout();
|
||||
|
||||
public abstract String getId();
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (super.equals(obj)) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (hashCode() != obj.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
Account account = (Account) obj;
|
||||
return getId().equals(account.getId());
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.accounts;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAuthentication;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.AuthenticationException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MojangAccount extends Account {
|
||||
private final String id;
|
||||
private final String email;
|
||||
private String accessToken;
|
||||
private RefreshStates lastRefreshStatus;
|
||||
private boolean needsRefresh = true;
|
||||
|
||||
public MojangAccount(String username, JsonObject json) {
|
||||
super(json.getAsJsonObject("selectedProfile").get("name").getAsString(), Util.getUUIDFromString(json.getAsJsonObject("selectedProfile").get("id").getAsString()));
|
||||
this.accessToken = json.get("accessToken").getAsString();
|
||||
this.id = json.getAsJsonObject("user").get("id").getAsString();
|
||||
this.email = username;
|
||||
}
|
||||
|
||||
public MojangAccount(String accessToken, String id, UUID uuid, String username, String email) {
|
||||
super(username, uuid);
|
||||
this.accessToken = accessToken;
|
||||
this.id = id;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public static MojangAccount deserialize(JsonObject json) {
|
||||
return new MojangAccount(json.get("accessToken").getAsString(), json.get("id").getAsString(), Util.getUUIDFromString(json.get("uuid").getAsString()), json.get("username").getAsString(), json.get("email").getAsString());
|
||||
}
|
||||
|
||||
public JsonObject serialize() {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("id", this.id);
|
||||
json.addProperty("accessToken", this.accessToken);
|
||||
json.addProperty("uuid", getUUID().toString());
|
||||
json.addProperty("username", getUsername());
|
||||
json.addProperty("email", this.email);
|
||||
json.addProperty("type", "mojang");
|
||||
return json;
|
||||
}
|
||||
|
||||
public void join(String serverId) throws MojangJoinServerErrorException, NoNetworkConnectionException {
|
||||
MojangAuthentication.joinServer(this, serverId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean select() {
|
||||
if (this.needsRefresh) {
|
||||
return refreshToken() != RefreshStates.ERROR;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public RefreshStates refreshToken() {
|
||||
try {
|
||||
this.accessToken = MojangAuthentication.refresh(this.accessToken);
|
||||
this.lastRefreshStatus = RefreshStates.SUCCESSFUL;
|
||||
} catch (NoNetworkConnectionException e) {
|
||||
e.printStackTrace();
|
||||
this.lastRefreshStatus = RefreshStates.FAILED;
|
||||
} catch (AuthenticationException e) {
|
||||
e.printStackTrace();
|
||||
this.lastRefreshStatus = RefreshStates.ERROR;
|
||||
}
|
||||
return this.lastRefreshStatus;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
this.needsRefresh = false;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
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() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
public boolean needsRefresh() {
|
||||
return this.needsRefresh;
|
||||
}
|
||||
|
||||
public void setNeedRefresh(boolean needsRefresh) {
|
||||
this.needsRefresh = needsRefresh;
|
||||
}
|
||||
|
||||
public enum RefreshStates {
|
||||
SUCCESSFUL,
|
||||
ERROR, // account not valid anymore
|
||||
FAILED // error occurred while checking -> Unknown state
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
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) {
|
||||
return new OfflineAccount(json.get("username").getAsString(), Util.getUUIDFromString(json.get("uuid").getAsString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serialize() {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("username", getUsername());
|
||||
json.addProperty("uuid", getUUID().toString());
|
||||
json.addProperty("type", "offline");
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void join(String serverId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean select() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return getUsername();
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ public class AssetsManager {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void downloadAllAssets(CountUpAndDownLatch latch) throws IOException {
|
||||
public static void downloadAllAssets(CountUpAndDownLatch latch) throws IOException, InterruptedException {
|
||||
if (!ASSETS_MAP.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@ -184,7 +184,7 @@ public class AssetsManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void generateJarAssets() throws IOException {
|
||||
public static void generateJarAssets() throws IOException, InterruptedException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
Log.verbose("Generating client.jar assets...");
|
||||
if (verifyAssetHash(ASSETS_CLIENT_JAR_HASH)) {
|
||||
|
@ -80,7 +80,6 @@ public enum Strings {
|
||||
|
||||
LOGIN_DIALOG_TITLE,
|
||||
LOGIN_DIALOG_HEADER,
|
||||
LOGIN_ACCOUNT_ALREADY_PRESENT,
|
||||
|
||||
MINOSOFT_STILL_STARTING_TITLE,
|
||||
MINOSOFT_STILL_STARTING_HEADER,
|
||||
|
@ -14,10 +14,10 @@
|
||||
package de.bixilon.minosoft.gui.main;
|
||||
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.data.accounts.Account;
|
||||
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 javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
@ -28,8 +28,8 @@ import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class AccountListCell extends ListCell<MojangAccount> implements Initializable {
|
||||
public static final ListView<MojangAccount> MOJANG_ACCOUNT_LIST_VIEW = new ListView<>();
|
||||
public class AccountListCell extends ListCell<Account> implements Initializable {
|
||||
public static final ListView<Account> MOJANG_ACCOUNT_LIST_VIEW = new ListView<>();
|
||||
|
||||
public MenuButton optionsMenu;
|
||||
public Label playerName;
|
||||
@ -38,7 +38,7 @@ public class AccountListCell extends ListCell<MojangAccount> implements Initiali
|
||||
public MenuItem optionsDelete;
|
||||
public AnchorPane root;
|
||||
|
||||
private MojangAccount account;
|
||||
private Account account;
|
||||
|
||||
public static AccountListCell newInstance() {
|
||||
FXMLLoader loader = new FXMLLoader(AccountListCell.class.getResource("/layout/cells/account.fxml"));
|
||||
@ -67,7 +67,7 @@ public class AccountListCell extends ListCell<MojangAccount> implements Initiali
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(MojangAccount account, boolean empty) {
|
||||
protected void updateItem(Account account, boolean empty) {
|
||||
super.updateItem(account, empty);
|
||||
|
||||
this.root.setVisible(!empty);
|
||||
@ -85,9 +85,9 @@ public class AccountListCell extends ListCell<MojangAccount> implements Initiali
|
||||
resetCell();
|
||||
|
||||
this.account = account;
|
||||
this.playerName.setText(account.getPlayerName());
|
||||
this.email.setText(account.getMojangUserName());
|
||||
if (Minosoft.getSelectedAccount() == account) {
|
||||
this.playerName.setText(account.getUsername());
|
||||
// this.email.setText(account.getEmail());
|
||||
if (Minosoft.getConfig().getSelectedAccount() == account) {
|
||||
setStyle("-fx-background-color: darkseagreen;");
|
||||
this.optionsSelect.setDisable(true);
|
||||
}
|
||||
@ -99,17 +99,19 @@ public class AccountListCell extends ListCell<MojangAccount> implements Initiali
|
||||
this.optionsSelect.setDisable(false);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.account.delete();
|
||||
if (Minosoft.getSelectedAccount() == this.account) {
|
||||
if (Minosoft.getConfig().getAccountList().isEmpty()) {
|
||||
public void logout() {
|
||||
this.account.logout();
|
||||
Minosoft.getConfig().removeAccount(this.account);
|
||||
Minosoft.getConfig().saveToFile();
|
||||
if (Minosoft.getConfig().getSelectedAccount() == this.account) {
|
||||
if (Minosoft.getConfig().getSccounts().isEmpty()) {
|
||||
Minosoft.selectAccount(null);
|
||||
} else {
|
||||
Minosoft.selectAccount(Minosoft.getConfig().getAccountList().values().iterator().next());
|
||||
Minosoft.selectAccount(Minosoft.getConfig().getSccounts().values().iterator().next());
|
||||
}
|
||||
MOJANG_ACCOUNT_LIST_VIEW.refresh();
|
||||
}
|
||||
Log.info(String.format("Deleted account (email=\"%s\", playerName=\"%s\")", this.account.getMojangUserName(), this.account.getPlayerName()));
|
||||
Log.info(String.format("Deleted account (id=%s, username=%s)", this.account.getId(), this.account.getUsername()));
|
||||
MOJANG_ACCOUNT_LIST_VIEW.getItems().remove(this.account);
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,9 @@
|
||||
package de.bixilon.minosoft.gui.main;
|
||||
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.data.accounts.Account;
|
||||
import de.bixilon.minosoft.data.locale.LocaleManager;
|
||||
import de.bixilon.minosoft.data.locale.Strings;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAccount;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
@ -30,7 +30,6 @@ import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class AccountWindow implements Initializable {
|
||||
|
||||
public BorderPane accountPane;
|
||||
public MenuItem menuAddAccount;
|
||||
|
||||
@ -38,7 +37,7 @@ public class AccountWindow implements Initializable {
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.setCellFactory((lv) -> AccountListCell.newInstance());
|
||||
|
||||
ObservableList<MojangAccount> accounts = FXCollections.observableArrayList(Minosoft.getConfig().getAccountList().values());
|
||||
ObservableList<Account> accounts = FXCollections.observableArrayList(Minosoft.getConfig().getSccounts().values());
|
||||
AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.setItems(accounts);
|
||||
this.accountPane.setCenter(AccountListCell.MOJANG_ACCOUNT_LIST_VIEW);
|
||||
|
||||
|
@ -92,7 +92,7 @@ public class Launcher {
|
||||
|
||||
|
||||
stage.show();
|
||||
if (Minosoft.getSelectedAccount() == null) {
|
||||
if (Minosoft.getConfig().getSelectedAccount() == null) {
|
||||
MainWindow.manageAccounts();
|
||||
}
|
||||
latch.countDown();
|
||||
|
@ -19,6 +19,7 @@ import com.jfoenix.controls.JFXDialogLayout;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.validation.RequiredFieldValidator;
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.data.accounts.Account;
|
||||
import de.bixilon.minosoft.data.locale.LocaleManager;
|
||||
import de.bixilon.minosoft.data.locale.Strings;
|
||||
import de.bixilon.minosoft.data.mappings.versions.Versions;
|
||||
@ -26,7 +27,6 @@ import de.bixilon.minosoft.data.text.BaseComponent;
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.protocol.protocol.LANServerListener;
|
||||
import de.bixilon.minosoft.util.DNSUtil;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAccount;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
@ -71,7 +71,7 @@ public class MainWindow implements Initializable {
|
||||
GUITools.initializeScene(stage.getScene());
|
||||
Platform.setImplicitExit(false);
|
||||
stage.setOnCloseRequest(event -> {
|
||||
if (Minosoft.getSelectedAccount() == null) {
|
||||
if (Minosoft.getConfig().getSelectedAccount() == null) {
|
||||
event.consume();
|
||||
JFXAlert<?> alert = new JFXAlert<>();
|
||||
GUITools.initializePane(alert.getDialogPane());
|
||||
@ -212,10 +212,22 @@ public class MainWindow implements Initializable {
|
||||
dialog.showAndWait();
|
||||
}
|
||||
|
||||
public void selectAccount(MojangAccount account) {
|
||||
public void selectAccount(Account account) {
|
||||
Runnable runnable = () -> {
|
||||
if (account != null) {
|
||||
MainWindow.this.menuAccount.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_ACCOUNTS_SELECTED, account.getUsername()));
|
||||
} else {
|
||||
MainWindow.this.menuAccount.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_ACCOUNTS));
|
||||
}
|
||||
};
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
Platform.runLater(runnable);
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
if (account != null) {
|
||||
this.menuAccount.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_ACCOUNTS_SELECTED, account.getPlayerName()));
|
||||
this.menuAccount.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_ACCOUNTS_SELECTED, account.getUsername()));
|
||||
} else {
|
||||
this.menuAccount.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_ACCOUNTS));
|
||||
}
|
||||
@ -235,7 +247,7 @@ public class MainWindow implements Initializable {
|
||||
this.menuHelp.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_HELP));
|
||||
this.menuHelpAbout.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_HELP_ABOUT));
|
||||
this.menuAccountManage.setText(LocaleManager.translate(Strings.MAIN_WINDOW_MENU_SERVERS_ACCOUNTS_MANAGE));
|
||||
selectAccount(Minosoft.getSelectedAccount());
|
||||
selectAccount(Minosoft.getConfig().getSelectedAccount());
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -281,7 +281,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
|
||||
if (!this.canConnect || this.server.getLastPing() == null) {
|
||||
return;
|
||||
}
|
||||
Connection connection = new Connection(Connection.lastConnectionId++, this.server.getAddress(), new Player(Minosoft.getSelectedAccount()));
|
||||
Connection connection = new Connection(Connection.lastConnectionId++, this.server.getAddress(), new Player(Minosoft.getConfig().getSelectedAccount()));
|
||||
Version version;
|
||||
if (this.server.getDesiredVersionId() == ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID) {
|
||||
version = this.server.getLastPing().getVersion();
|
||||
@ -317,7 +317,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
|
||||
}
|
||||
}
|
||||
|
||||
this.optionsConnect.setDisable(Minosoft.getSelectedAccount() == connection.getPlayer().getAccount());
|
||||
this.optionsConnect.setDisable(Minosoft.getConfig().getSelectedAccount() == connection.getPlayer().getAccount());
|
||||
getStyleClass().add("list-cell-connected");
|
||||
this.optionsSessions.setDisable(false);
|
||||
});
|
||||
|
@ -85,7 +85,7 @@ public class SessionListCell extends ListCell<Connection> implements Initializab
|
||||
this.connection = connection;
|
||||
connection.registerEvent(new EventInvokerCallback<>(ConnectionStateChangeEvent.class, this::handleConnectionCallback));
|
||||
this.connectionId.setText(String.format("#%d", connection.getConnectionId()));
|
||||
this.account.setText(connection.getPlayer().getAccount().getPlayerName());
|
||||
this.account.setText(connection.getPlayer().getAccount().getUsername());
|
||||
}
|
||||
|
||||
private void handleConnectionCallback(ConnectionStateChangeEvent event) {
|
||||
|
@ -17,13 +17,14 @@ 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.accounts.MojangAccount;
|
||||
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 de.bixilon.minosoft.util.mojang.api.exceptions.AuthenticationException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.ActionEvent;
|
||||
@ -49,7 +50,6 @@ public class MojangLoginController implements Initializable {
|
||||
@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));
|
||||
@ -83,45 +83,31 @@ public class MojangLoginController implements Initializable {
|
||||
|
||||
|
||||
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);
|
||||
try {
|
||||
MojangAccount account = MojangAuthentication.login(this.email.getText(), this.password.getText());
|
||||
|
||||
account.setNeedRefresh(false);
|
||||
Minosoft.getConfig().putAccount(account);
|
||||
account.saveToConfig();
|
||||
Log.info(String.format("Added and saved account (playerName=%s, email=%s, uuid=%s)", account.getPlayerName(), account.getMojangUserName(), account.getUUID()));
|
||||
Log.info(String.format("Added and saved account (username=%s, email=%s, uuid=%s)", account.getUsername(), account.getEmail(), account.getUUID()));
|
||||
Platform.runLater(() -> {
|
||||
AccountListCell.MOJANG_ACCOUNT_LIST_VIEW.getItems().add(account);
|
||||
close();
|
||||
});
|
||||
if (Minosoft.getSelectedAccount() == null) {
|
||||
if (Minosoft.getConfig().getSelectedAccount() == null) {
|
||||
// select account
|
||||
Minosoft.selectAccount(account);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (AuthenticationException | NoNetworkConnectionException e) {
|
||||
e.printStackTrace();
|
||||
Platform.runLater(() -> {
|
||||
this.errorMessage.setText(attempt.getError());
|
||||
this.errorMessage.setText(e.getMessage());
|
||||
this.errorMessage.setVisible(true);
|
||||
this.email.setDisable(false);
|
||||
this.password.setDisable(false);
|
||||
this.loginButton.setDisable(true);
|
||||
});
|
||||
}
|
||||
}, "AccountLoginThread").start();
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.protocol.exceptions.PacketNotImplementedException;
|
||||
import de.bixilon.minosoft.protocol.exceptions.PacketParseException;
|
||||
import de.bixilon.minosoft.protocol.exceptions.UnknownPacketException;
|
||||
import de.bixilon.minosoft.protocol.network.socket.NonBlockingSocketNetwork;
|
||||
import de.bixilon.minosoft.protocol.network.socket.BlockingSocketNetwork;
|
||||
import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
|
||||
import de.bixilon.minosoft.protocol.packets.ServerboundPacket;
|
||||
import de.bixilon.minosoft.protocol.packets.clientbound.interfaces.CompressionThresholdChange;
|
||||
@ -36,7 +36,7 @@ public abstract class Network {
|
||||
}
|
||||
|
||||
public static Network getNetworkInstance(Connection connection) {
|
||||
return new NonBlockingSocketNetwork(connection);
|
||||
return new BlockingSocketNetwork(connection);
|
||||
}
|
||||
|
||||
public abstract void connect(ServerAddress address);
|
||||
|
@ -40,7 +40,6 @@ import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
@Deprecated
|
||||
public class BlockingSocketNetwork extends Network {
|
||||
private final LinkedBlockingQueue<ServerboundPacket> queue = new LinkedBlockingQueue<>();
|
||||
private Thread socketReceiveThread;
|
||||
|
@ -19,6 +19,8 @@ import de.bixilon.minosoft.protocol.packets.ClientboundPacket;
|
||||
import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse;
|
||||
import de.bixilon.minosoft.protocol.protocol.CryptManager;
|
||||
import de.bixilon.minosoft.protocol.protocol.InByteBuffer;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.math.BigInteger;
|
||||
@ -43,7 +45,13 @@ public class PacketEncryptionRequest extends ClientboundPacket {
|
||||
SecretKey secretKey = CryptManager.createNewSharedKey();
|
||||
PublicKey publicKey = CryptManager.decodePublicKey(getPublicKey());
|
||||
String serverHash = new BigInteger(CryptManager.getServerHash(getServerId(), publicKey, secretKey)).toString(16);
|
||||
try {
|
||||
connection.getPlayer().getAccount().join(serverHash);
|
||||
} catch (MojangJoinServerErrorException | NoNetworkConnectionException e) {
|
||||
e.printStackTrace();
|
||||
connection.disconnect();
|
||||
return;
|
||||
}
|
||||
connection.sendPacket(new PacketEncryptionResponse(secretKey, getVerifyToken(), publicKey));
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,6 @@ package de.bixilon.minosoft.util;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.logging.LogLevels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
@ -27,32 +25,22 @@ import java.net.http.HttpResponse;
|
||||
|
||||
public final class HTTP {
|
||||
|
||||
public static HttpResponse<String> postJson(String url, JsonObject json) {
|
||||
public static HttpResponse<String> postJson(String url, JsonObject json) throws IOException, InterruptedException {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).POST(HttpRequest.BodyPublishers.ofString(json.toString())).header("Content-Type", "application/json").build();
|
||||
try {
|
||||
return client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
} catch (Exception e) {
|
||||
Log.printException(e, LogLevels.DEBUG);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HttpResponse<String> get(String url) {
|
||||
public static HttpResponse<String> get(String url) throws IOException, InterruptedException {
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
|
||||
try {
|
||||
return client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JsonElement getJson(String url) {
|
||||
public static JsonElement getJson(String url) throws IOException, InterruptedException {
|
||||
HttpResponse<String> response = get(url);
|
||||
if (response == null || response.statusCode() != 200) {
|
||||
return null;
|
||||
if (response.statusCode() != 200) {
|
||||
throw new IOException();
|
||||
}
|
||||
return JsonParser.parseString(response.body());
|
||||
}
|
||||
|
@ -1,156 +0,0 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util.mojang.api;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MojangAccount {
|
||||
final String userId;
|
||||
final UUID uuid;
|
||||
final String playerName;
|
||||
final String mojangUserName;
|
||||
String accessToken;
|
||||
RefreshStates lastRefreshStatus;
|
||||
private boolean needsRefresh = true;
|
||||
|
||||
public MojangAccount(String username, JsonObject json) {
|
||||
this.accessToken = json.get("accessToken").getAsString();
|
||||
JsonObject profile = json.get("selectedProfile").getAsJsonObject();
|
||||
this.uuid = Util.getUUIDFromString(profile.get("id").getAsString());
|
||||
this.playerName = profile.get("name").getAsString();
|
||||
|
||||
JsonObject mojang = json.get("user").getAsJsonObject();
|
||||
this.userId = mojang.get("id").getAsString();
|
||||
this.mojangUserName = username;
|
||||
}
|
||||
|
||||
public MojangAccount(String accessToken, String userId, UUID uuid, String playerName, String mojangUserName) {
|
||||
this.accessToken = accessToken;
|
||||
this.userId = userId;
|
||||
this.uuid = uuid;
|
||||
this.playerName = playerName;
|
||||
this.mojangUserName = mojangUserName;
|
||||
}
|
||||
|
||||
public static MojangAccount deserialize(JsonObject json) {
|
||||
return new MojangAccount(json.get("accessToken").getAsString(), json.get("userId").getAsString(), Util.getUUIDFromString(json.get("uuid").getAsString()), json.get("playerName").getAsString(), json.get("userName").getAsString());
|
||||
}
|
||||
|
||||
public JsonObject serialize() {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("accessToken", this.accessToken);
|
||||
json.addProperty("userId", this.userId);
|
||||
json.addProperty("uuid", this.uuid.toString());
|
||||
json.addProperty("playerName", this.playerName);
|
||||
json.addProperty("userName", this.mojangUserName);
|
||||
return json;
|
||||
}
|
||||
|
||||
public void join(String serverId) {
|
||||
MojangAuthentication.joinServer(this, serverId);
|
||||
}
|
||||
|
||||
public RefreshStates refreshToken() {
|
||||
if (this.lastRefreshStatus != null) {
|
||||
return this.lastRefreshStatus;
|
||||
}
|
||||
String accessToken = MojangAuthentication.refresh(this.accessToken);
|
||||
if (accessToken == null) {
|
||||
this.lastRefreshStatus = RefreshStates.FAILED;
|
||||
return this.lastRefreshStatus;
|
||||
}
|
||||
if (accessToken.isBlank()) {
|
||||
this.lastRefreshStatus = RefreshStates.ERROR;
|
||||
return this.lastRefreshStatus;
|
||||
}
|
||||
this.accessToken = accessToken;
|
||||
this.lastRefreshStatus = RefreshStates.SUCCESSFUL;
|
||||
return this.lastRefreshStatus;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return this.playerName;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
public String getMojangUserName() {
|
||||
return this.mojangUserName;
|
||||
}
|
||||
|
||||
public void saveToConfig() {
|
||||
Minosoft.getConfig().putMojangAccount(this);
|
||||
Minosoft.getConfig().saveToFile();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
Minosoft.getConfig().removeAccount(this);
|
||||
Minosoft.getConfig().saveToFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getUserId();
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.userId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (super.equals(obj)) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (hashCode() != obj.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
MojangAccount account = (MojangAccount) obj;
|
||||
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
|
||||
FAILED // error occurred while checking -> Unknown state
|
||||
}
|
||||
|
||||
}
|
@ -18,19 +18,25 @@ import com.google.gson.JsonParser;
|
||||
import de.bixilon.minosoft.Minosoft;
|
||||
import de.bixilon.minosoft.config.ConfigurationPaths;
|
||||
import de.bixilon.minosoft.config.StaticConfiguration;
|
||||
import de.bixilon.minosoft.data.accounts.MojangAccount;
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.logging.LogLevels;
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||
import de.bixilon.minosoft.util.HTTP;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.AuthenticationException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.MojangJoinServerErrorException;
|
||||
import de.bixilon.minosoft.util.mojang.api.exceptions.NoNetworkConnectionException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
public final class MojangAuthentication {
|
||||
|
||||
public static MojangAccountAuthenticationAttempt login(String username, String password) {
|
||||
public static MojangAccount login(String username, String password) throws AuthenticationException, NoNetworkConnectionException {
|
||||
return login(Minosoft.getConfig().getString(ConfigurationPaths.StringPaths.CLIENT_TOKEN), username, password);
|
||||
}
|
||||
|
||||
public static MojangAccountAuthenticationAttempt login(String clientToken, String username, String password) {
|
||||
public static MojangAccount login(String clientToken, String username, String password) throws NoNetworkConnectionException, AuthenticationException {
|
||||
JsonObject agent = new JsonObject();
|
||||
agent.addProperty("name", "Minecraft");
|
||||
agent.addProperty("version", 1);
|
||||
@ -42,22 +48,28 @@ public final class MojangAuthentication {
|
||||
payload.addProperty("clientToken", clientToken);
|
||||
payload.addProperty("requestUser", true);
|
||||
|
||||
HttpResponse<String> response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_LOGIN, payload);
|
||||
HttpResponse<String> response;
|
||||
try {
|
||||
response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_LOGIN, payload);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.printException(e, LogLevels.DEBUG);
|
||||
throw new NoNetworkConnectionException(e);
|
||||
}
|
||||
if (response == null) {
|
||||
Log.mojang(String.format("Failed to login with username %s", username));
|
||||
return new MojangAccountAuthenticationAttempt("Unknown error, check your Internet connection");
|
||||
throw new NoNetworkConnectionException("Unknown error, check your Internet connection");
|
||||
}
|
||||
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||
if (response.statusCode() != 200) {
|
||||
Log.mojang(String.format("Failed to login with error code %d: %s", response.statusCode(), jsonResponse.get("errorMessage").getAsString()));
|
||||
return new MojangAccountAuthenticationAttempt(jsonResponse.get("errorMessage").getAsString());
|
||||
throw new AuthenticationException(jsonResponse.get("errorMessage").getAsString());
|
||||
}
|
||||
// now it is okay
|
||||
return new MojangAccountAuthenticationAttempt(new MojangAccount(username, jsonResponse));
|
||||
return new MojangAccount(username, jsonResponse);
|
||||
}
|
||||
|
||||
|
||||
public static void joinServer(MojangAccount account, String serverId) {
|
||||
public static void joinServer(MojangAccount account, String serverId) throws NoNetworkConnectionException, MojangJoinServerErrorException {
|
||||
if (StaticConfiguration.SKIP_MOJANG_AUTHENTICATION) {
|
||||
return;
|
||||
}
|
||||
@ -67,26 +79,31 @@ public final class MojangAuthentication {
|
||||
payload.addProperty("selectedProfile", account.getUUID().toString().replace("-", ""));
|
||||
payload.addProperty("serverId", serverId);
|
||||
|
||||
HttpResponse<String> response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_JOIN, payload);
|
||||
HttpResponse<String> response;
|
||||
try {
|
||||
response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_JOIN, payload);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new NoNetworkConnectionException(e);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Log.mojang(String.format("Failed to join server: %s", serverId));
|
||||
return;
|
||||
throw new MojangJoinServerErrorException();
|
||||
}
|
||||
if (response.statusCode() != 204) {
|
||||
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||
Log.mojang(String.format("Failed to join server with error code %d: %s", response.statusCode(), jsonResponse.has("errorMessage") ? jsonResponse.get("errorMessage").getAsString() : "null"));
|
||||
return;
|
||||
throw new MojangJoinServerErrorException(jsonResponse.get("errorMessage").getAsString());
|
||||
}
|
||||
// joined
|
||||
Log.mojang("Joined server successfully");
|
||||
}
|
||||
|
||||
public static String refresh(String accessToken) {
|
||||
public static String refresh(String accessToken) throws NoNetworkConnectionException, AuthenticationException {
|
||||
return refresh(Minosoft.getConfig().getString(ConfigurationPaths.StringPaths.CLIENT_TOKEN), accessToken);
|
||||
}
|
||||
|
||||
public static String refresh(String clientToken, String accessToken) {
|
||||
public static String refresh(String clientToken, String accessToken) throws NoNetworkConnectionException, AuthenticationException {
|
||||
if (StaticConfiguration.SKIP_MOJANG_AUTHENTICATION) {
|
||||
return clientToken;
|
||||
}
|
||||
@ -97,18 +114,18 @@ public final class MojangAuthentication {
|
||||
HttpResponse<String> response;
|
||||
try {
|
||||
response = HTTP.postJson(ProtocolDefinition.MOJANG_URL_REFRESH, payload);
|
||||
} catch (Exception e) {
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.mojang(String.format("Could not connect to mojang server: %s", e.getCause().toString()));
|
||||
return null;
|
||||
throw new NoNetworkConnectionException(e);
|
||||
}
|
||||
if (response == null) {
|
||||
Log.mojang("Failed to refresh session");
|
||||
return null;
|
||||
throw new NoNetworkConnectionException();
|
||||
}
|
||||
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
|
||||
if (response.statusCode() != 200) {
|
||||
Log.mojang(String.format("Failed to refresh session with error code %d: %s", response.statusCode(), jsonResponse.get("errorMessage").getAsString()));
|
||||
return "";
|
||||
throw new AuthenticationException(jsonResponse.get("errorMessage").getAsString());
|
||||
}
|
||||
// now it is okay
|
||||
Log.mojang("Refreshed 1 session token");
|
||||
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util.mojang.api;
|
||||
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
|
||||
import de.bixilon.minosoft.util.HTTP;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class MojangBlockedServers {
|
||||
|
||||
public static ArrayList<String> getBlockedServers() {
|
||||
HttpResponse<String> response = HTTP.get(ProtocolDefinition.MOJANG_URL_BLOCKED_SERVERS);
|
||||
if (response == null) {
|
||||
Log.mojang("Failed to fetch blocked servers");
|
||||
return null;
|
||||
}
|
||||
if (response.statusCode() != 200) {
|
||||
Log.mojang(String.format("Failed to fetch blocked server error code %d", response.statusCode()));
|
||||
return null;
|
||||
}
|
||||
// now it is hopefully okay
|
||||
return new ArrayList<>(Arrays.asList(response.body().split("\n")));
|
||||
}
|
||||
|
||||
public boolean isServerBlocked(List<String> list, String hostname) {
|
||||
for (String hash : list) {
|
||||
if (hash.equals(Util.sha1(hostname))) {
|
||||
return true;
|
||||
}
|
||||
// ToDo: check subdomains and ip addresses
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util.mojang.api.exceptions;
|
||||
|
||||
public class AuthenticationException extends Exception {
|
||||
|
||||
public AuthenticationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util.mojang.api.exceptions;
|
||||
|
||||
public class MojangJoinServerErrorException extends Exception {
|
||||
|
||||
public MojangJoinServerErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MojangJoinServerErrorException() {
|
||||
}
|
||||
}
|
@ -11,31 +11,19 @@
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.util.mojang.api;
|
||||
package de.bixilon.minosoft.util.mojang.api.exceptions;
|
||||
|
||||
public class MojangAccountAuthenticationAttempt {
|
||||
final MojangAccount account;
|
||||
final String error;
|
||||
public class NoNetworkConnectionException extends Exception {
|
||||
|
||||
public MojangAccountAuthenticationAttempt(MojangAccount account) {
|
||||
this.account = account;
|
||||
this.error = null;
|
||||
public NoNetworkConnectionException(Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public MojangAccountAuthenticationAttempt(String error) {
|
||||
this.account = null;
|
||||
this.error = error;
|
||||
public NoNetworkConnectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return this.error;
|
||||
}
|
||||
public NoNetworkConnectionException() {
|
||||
|
||||
public MojangAccount getAccount() {
|
||||
return this.account;
|
||||
}
|
||||
|
||||
public boolean succeeded() {
|
||||
return this.account != null;
|
||||
}
|
||||
}
|
@ -56,7 +56,6 @@
|
||||
"SETTINGS_DOWNLOAD": "Download",
|
||||
"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...",
|
||||
|
@ -57,7 +57,6 @@
|
||||
"SETTINGS_DOWNLOAD": "Download",
|
||||
"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...",
|
||||
|
@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
<!--
|
||||
~ Minosoft
|
||||
~ Copyright (C) 2020 Moritz Zwerger
|
||||
@ -14,6 +11,9 @@
|
||||
~
|
||||
~ This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
-->
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:id="root" maxHeight="-Infinity" maxWidth="500.0" minHeight="-Infinity" minWidth="500.0" onMouseClicked="#clicked" prefWidth="500.0" fx:controller="de.bixilon.minosoft.gui.main.AccountListCell">
|
||||
<Label fx:id="playerName" layoutX="111.0" layoutY="14.0" maxWidth="200.0" minWidth="10.0" text="#Player name#" underline="true" AnchorPane.leftAnchor="5.0" AnchorPane.topAnchor="5.0">
|
||||
<font>
|
||||
@ -23,7 +23,7 @@
|
||||
<MenuButton fx:id="optionsMenu" layoutX="389.0" layoutY="81.0" mnemonicParsing="false" text="⋮" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0">
|
||||
<items>
|
||||
<MenuItem fx:id="optionsSelect" mnemonicParsing="false" onAction="#select" text="-Select-" />
|
||||
<MenuItem fx:id="optionsDelete" mnemonicParsing="false" onAction="#delete" style="-fx-text-fill: red" text="-Delete-" />
|
||||
<MenuItem fx:id="optionsDelete" mnemonicParsing="false" onAction="#logout" style="-fx-text-fill: red" text="-Delete-"/>
|
||||
</items>
|
||||
</MenuButton>
|
||||
<Label fx:id="email" layoutX="121.0" layoutY="24.0" maxWidth="200.0" minWidth="10.0" text="#Email#" AnchorPane.leftAnchor="5.0" AnchorPane.topAnchor="30.0" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user