Authlib Injector support

This commit is contained in:
huanghongxun 2018-02-17 23:28:05 +08:00
parent 83bc6748a3
commit 24ec0adacf
33 changed files with 716 additions and 137 deletions

View File

@ -45,7 +45,7 @@ public final class AccountHelper {
public static final AccountHelper INSTANCE = new AccountHelper();
private AccountHelper() {}
public static final File SKIN_DIR = new File(Main.APPDATA, "skins");
public static final File SKIN_DIR = new File(Main.HMCL_DIRECTORY, "skins");
public static void loadSkins() {
loadSkins(Proxy.NO_PROXY);

View File

@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.setting;
import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
@ -24,12 +25,12 @@ import org.jackhuang.hmcl.auth.OfflineAccount;
import org.jackhuang.hmcl.auth.OfflineAccountFactory;
import org.jackhuang.hmcl.auth.yggdrasil.*;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@ -49,6 +50,8 @@ public final class Accounts {
new Pair<>(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector))
);
private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>();
public static String getAccountType(Account account) {
if (account instanceof OfflineAccount) return OFFLINE_ACCOUNT_KEY;
else if (account instanceof AuthlibInjectorAccount) return AUTHLIB_INJECTOR_ACCOUNT_KEY;
@ -87,11 +90,29 @@ public final class Accounts {
AuthlibInjectorBuildInfo buildInfo = AuthlibInjectorBuildInfo.requestBuildInfo();
File jar = new File(Main.HMCL_DIRECTORY, "authlib-injector.jar");
File local = new File(Main.HMCL_DIRECTORY, "authlib-injector.txt");
int buildNumber = Integer.parseInt(FileUtils.readText(local));
int buildNumber = 0;
try {
buildNumber = Integer.parseInt(FileUtils.readText(local));
} catch (IOException | NumberFormatException ignore) {
}
if (buildNumber < buildInfo.getBuildNumber()) {
new FileDownloadTask(new URL(buildInfo.getUrl()), jar).run();
FileUtils.writeText(local, String.valueOf(buildInfo.getBuildNumber()));
}
return jar.getAbsolutePath();
}
public static String getAuthlibInjectorServerName(String serverIp) {
if (AUTHLIB_INJECTOR_SERVER_NAMES.containsKey(serverIp))
return AUTHLIB_INJECTOR_SERVER_NAMES.get(serverIp);
else {
try {
AuthlibInjectorServerResponse response = Constants.GSON.fromJson(NetworkUtils.doGet(NetworkUtils.toURL(serverIp)), AuthlibInjectorServerResponse.class);
AUTHLIB_INJECTOR_SERVER_NAMES.put(serverIp, response.getMeta().getServerName());
return response.getMeta().getServerName();
} catch (JsonParseException | IOException | NullPointerException e) {
return null;
}
}
}
}

View File

@ -19,15 +19,11 @@ package org.jackhuang.hmcl.setting;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorBuildInfo;
import org.jackhuang.hmcl.util.JavaVersion;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.*;
public final class Config {
final class Config {
@SerializedName("last")
private String selectedProfile = "";
@ -92,8 +88,8 @@ public final class Config {
@SerializedName("logLines")
private int logLines = 100;
@SerializedName("authlibInjectorServerURL")
private String authlibInjectorServerURL = AuthlibInjectorBuildInfo.UPDATE_URL;
@SerializedName("authlibInjectorServerURLs")
private Set<String> authlibInjectorServerURLs = new HashSet<>();
public String getSelectedProfile() {
return selectedProfile;
@ -283,17 +279,12 @@ public final class Config {
this.logLines = logLines;
}
public String getAuthlibInjectorServerURL() {
return authlibInjectorServerURL;
public Set<String> getAuthlibInjectorServerURLs() {
return authlibInjectorServerURLs;
}
/**
* Will not invoke Settings.INSTANCE.save()
* @param authlibInjectorServerURL new server url.
*/
public void setAuthlibInjectorServerURL(String authlibInjectorServerURL) {
this.authlibInjectorServerURL = authlibInjectorServerURL;
// Do not invoke Settings.INSTANCE.save()
// Because we want users set it theirself.
public void setAuthlibInjectorServerURLs(Set<String> authlibInjectorServerURLs) {
this.authlibInjectorServerURLs = authlibInjectorServerURLs;
Settings.INSTANCE.save();
}
}

View File

@ -28,6 +28,7 @@ import javafx.scene.text.Font;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorBuildInfo;
import org.jackhuang.hmcl.download.BMCLAPIDownloadProvider;
import org.jackhuang.hmcl.download.DownloadProvider;
@ -87,10 +88,12 @@ public class Settings {
accounts.put(Accounts.getAccountId(account), account);
}
save();
checkAuthlibInjectorServerURLs();
checkAuthlibInjectorAccounts();
checkProfileMap();
save();
for (Map.Entry<String, Profile> entry2 : getProfileMap().entrySet()) {
entry2.getValue().setName(entry2.getKey());
entry2.getValue().nameProperty().setChangedListener(this::profileNameChanged);
@ -104,13 +107,6 @@ public class Settings {
});
loadProxy();
try {
new URL(SETTINGS.getAuthlibInjectorServerURL());
} catch (MalformedURLException ex) {
Logging.LOG.log(Level.SEVERE, "Authlib injector server URL is malformed, using official update url.", ex);
SETTINGS.setAuthlibInjectorServerURL(AuthlibInjectorBuildInfo.UPDATE_URL);
}
}
private Config initSettings() {
@ -280,8 +276,39 @@ public class Settings {
SETTINGS.setLogLines(logLines);
}
public String getAuthlibInjectorServerURL() {
return SETTINGS.getAuthlibInjectorServerURL();
public Set<String> getAuthlibInjectorServerURLs() {
return SETTINGS.getAuthlibInjectorServerURLs();
}
public void removeAuthlibInjectorServerURL(String serverURL) {
checkAuthlibInjectorServerURLs();
SETTINGS.getAuthlibInjectorServerURLs().remove(serverURL);
checkAuthlibInjectorAccounts();
save();
}
public void addAuthlibInjectorServerURL(String serverURL) {
checkAuthlibInjectorServerURLs();
SETTINGS.getAuthlibInjectorServerURLs().add(serverURL);
save();
}
private void checkAuthlibInjectorServerURLs() {
if (SETTINGS.getAuthlibInjectorServerURLs() == null)
SETTINGS.setAuthlibInjectorServerURLs(new HashSet<>());
}
private void checkAuthlibInjectorAccounts() {
for (Account account : getAccounts()) {
if (account instanceof AuthlibInjectorAccount) {
AuthlibInjectorAccount injectorAccount = (AuthlibInjectorAccount) account;
if (!SETTINGS.getAuthlibInjectorServerURLs().contains(injectorAccount.getServerBaseURL()))
deleteAccount(account);
}
}
}
public DownloadProvider getDownloadProvider() {
@ -490,6 +517,8 @@ public class Settings {
getProfileMap().put(ver.getName(), ver);
ver.nameProperty().setChangedListener(this::profileNameChanged);
save();
}
public void deleteProfile(Profile profile) {

View File

@ -36,11 +36,13 @@ import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.OfflineAccount;
import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.game.AccountHelper;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
public final class AccountItem extends StackPane {
@ -56,6 +58,7 @@ public final class AccountItem extends StackPane {
@FXML private Label lblUser;
@FXML private Label lblType;
@FXML private Label lblEmail;
@FXML private Label lblServer;
@FXML private Label lblCurrentAccount;
@FXML private JFXRadioButton chkSelected;
@FXML private JFXProgressBar pgsSkin;
@ -73,12 +76,8 @@ public final class AccountItem extends StackPane {
btnDelete.setGraphic(SVG.delete("black", 15, 15));
btnRefresh.setGraphic(SVG.refresh("black", 15, 15));
// create content
String headerColor = getDefaultColor(i % 12);
header.setStyle("-fx-background-radius: 2 2 0 0; -fx-background-color: " + headerColor);
// create image view
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 32.0, header.boundsInParentProperty(), icon.heightProperty()));
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
chkSelected.getProperties().put("account", account);
setSelected(Settings.INSTANCE.getSelectedAccount() == account);
@ -87,6 +86,13 @@ public final class AccountItem extends StackPane {
lblType.setText(AccountsPage.accountType(account));
lblEmail.setText(account.getUsername());
if (account instanceof AuthlibInjectorAccount) {
Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(((AuthlibInjectorAccount) account).getServerBaseURL()))
.subscribe(Schedulers.javafx(), variables -> {
lblServer.setText(variables.get("serverName"));
});
}
if (account instanceof YggdrasilAccount) {
btnRefresh.setOnMouseClicked(e -> {
pgsSkin.setVisible(true);
@ -113,25 +119,6 @@ public final class AccountItem extends StackPane {
FXUtils.limitSize(portraitView, 32, 32);
}
private String getDefaultColor(int i) {
switch (i) {
case 0: return "#8F3F7E";
case 1: return "#B5305F";
case 2: return "#CE584A";
case 3: return "#DB8D5C";
case 4: return "#DA854E";
case 5: return "#E9AB44";
case 6: return "#FEE435";
case 7: return "#99C286";
case 8: return "#01A05E";
case 9: return "#4A8895";
case 10: return "#16669B";
case 11: return "#2F65A5";
case 12: return "#4E6A9C";
default: return "#FFFFFF";
}
}
public Account getAccount() {
return account;
}

View File

@ -23,7 +23,6 @@ import com.jfoenix.controls.JFXProgressBar;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
@ -34,8 +33,8 @@ import org.jackhuang.hmcl.task.Task;
import java.util.function.Consumer;
public class YggdrasilAccountLoginPane extends StackPane {
private final YggdrasilAccount oldAccount;
public class AccountLoginPane extends StackPane {
private final Account oldAccount;
private final Consumer<AuthInfo> success;
private final Runnable failed;
@ -46,12 +45,12 @@ public class YggdrasilAccountLoginPane extends StackPane {
@FXML private JFXProgressBar progressBar;
private JFXDialog dialog;
public YggdrasilAccountLoginPane(YggdrasilAccount oldAccount, Consumer<AuthInfo> success, Runnable failed) {
public AccountLoginPane(Account oldAccount, Consumer<AuthInfo> success, Runnable failed) {
this.oldAccount = oldAccount;
this.success = success;
this.failed = failed;
FXUtils.loadFXML(this, "/assets/fxml/yggdrasil-account-login.fxml");
FXUtils.loadFXML(this, "/assets/fxml/account-login.fxml");
lblUsername.setText(oldAccount.getUsername());
txtPassword.setOnAction(e -> onAccept());
@ -59,14 +58,12 @@ public class YggdrasilAccountLoginPane extends StackPane {
@FXML
private void onAccept() {
String username = oldAccount.getUsername();
String password = txtPassword.getText();
progressBar.setVisible(true);
lblCreationWarning.setText("");
Task.ofResult("login", () -> {
try {
Account account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password);
return account.logIn(new SpecificCharacterSelector(Accounts.getCurrentCharacter(oldAccount)), Settings.INSTANCE.getProxy());
return oldAccount.logInWithPassword(new SpecificCharacterSelector(Accounts.getCurrentCharacter(oldAccount)), password, Settings.INSTANCE.getProxy());
} catch (Exception e) {
return e;
}

View File

@ -25,20 +25,17 @@ import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.util.Callback;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.*;
import org.jackhuang.hmcl.auth.InvalidCredentialsException;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
import org.jackhuang.hmcl.game.AccountHelper;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings;
@ -48,10 +45,14 @@ import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.IconedItem;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.ReflectionHelper;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
public final class AccountsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account"));
@ -64,7 +65,10 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
@FXML private JFXPasswordField txtPassword;
@FXML private Label lblCreationWarning;
@FXML private JFXComboBox<String> cboType;
@FXML private JFXComboBox<TwoLineListItem> cboServers;
@FXML private JFXProgressBar progressBar;
@FXML private Label lblAddInjectorServer;
@FXML private Hyperlink linkAddInjectorServer;
{
FXUtils.loadFXML(this, "/assets/fxml/account.fxml");
@ -74,12 +78,19 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
FXUtils.smoothScrolling(scrollPane);
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"));
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"), Main.i18n("account.methods.authlib_injector"));
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
txtPassword.setVisible(newValue.intValue() != 0);
cboServers.setVisible(newValue.intValue() == 2);
linkAddInjectorServer.setVisible(newValue.intValue() == 2);
lblAddInjectorServer.setVisible(newValue.intValue() == 2);
});
cboType.getSelectionModel().select(0);
// These two lines can eliminate black, don't know why.
cboServers.getItems().setAll(new TwoLineListItem("", ""));
cboServers.getSelectionModel().select(0);
txtPassword.setOnAction(e -> onCreationAccept());
txtUsername.setOnAction(e -> onCreationAccept());
txtUsername.getValidators().add(new Validator(Main.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
@ -91,6 +102,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
});
loadAccounts();
loadServers();
if (Settings.INSTANCE.getAccounts().isEmpty())
addNewAccount();
@ -114,6 +126,17 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
});
}
public void loadServers() {
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.map(serverURL -> new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL))
.collect(Collectors.toList()))
.subscribe(Task.of(Schedulers.javafx(), variables -> {
cboServers.getItems().setAll(variables.<Collection<TwoLineListItem>>get("list"));
if (!cboServers.getItems().isEmpty())
cboServers.getSelectionModel().select(0);
}));
}
private Node buildNode(int i, Account account, ToggleGroup group) {
AccountItem item = new AccountItem(i, account, group);
item.setOnDeleteButtonMouseClicked(e -> {
@ -130,6 +153,11 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
dialog.show();
}
@FXML
private void onAddInjecterServer() {
Controllers.navigate(Controllers.getServersPage());
}
@FXML
private void onCreationAccept() {
int type = cboType.getSelectionModel().getSelectedIndex();
@ -141,8 +169,9 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
try {
Account account;
switch (type) {
case 0: account = OfflineAccountFactory.INSTANCE.fromUsername(username); break;
case 1: account = YggdrasilAccountFactory.INSTANCE.fromUsername(username, password); break;
case 0: account = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY).fromUsername(username); break;
case 1: account = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY).fromUsername(username, password); break;
case 2: account = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY).fromUsername(username, password, cboServers.getSelectionModel().getSelectedItem().getSubtitle()); break;
default: throw new Error();
}
@ -203,6 +232,7 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
public static String accountType(Account account) {
if (account instanceof OfflineAccount) return Main.i18n("account.methods.offline");
else if (account instanceof AuthlibInjectorAccount) return Main.i18n("account.methods.authlib_injector");
else if (account instanceof YggdrasilAccount) return Main.i18n("account.methods.yggdrasil");
else throw new Error(Main.i18n("account.methods.no_method") + ": " + account);
}

View File

@ -0,0 +1,65 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.effects.JFXDepthManager;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorServerInfo;
import org.jackhuang.hmcl.mod.ModInfo;
import java.util.function.Consumer;
public final class AuthlibInjectorServerItem extends BorderPane {
private final AuthlibInjectorServerInfo info;
private final Label lblServerName = new Label();
private final Label lblServerIp = new Label();
public AuthlibInjectorServerItem(AuthlibInjectorServerInfo info, Consumer<AuthlibInjectorServerItem> deleteCallback) {
this.info = info;
lblServerName.setStyle("-fx-font-size: 15;");
lblServerIp.setStyle("-fx-font-size: 10;");
VBox center = new VBox();
BorderPane.setAlignment(center, Pos.CENTER);
center.getChildren().addAll(lblServerName, lblServerIp);
setCenter(center);
JFXButton right = new JFXButton();
right.setOnMouseClicked(e -> deleteCallback.accept(this));
right.getStyleClass().add("toggle-icon4");
BorderPane.setAlignment(right, Pos.CENTER);
right.setGraphic(SVG.close("black", 15, 15));
setRight(right);
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
lblServerName.setText(info.getServerName());
lblServerIp.setText(info.getServerIp());
}
public AuthlibInjectorServerInfo getInfo() {
return info;
}
}

View File

@ -0,0 +1,150 @@
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.yggdrasil.AuthlibInjectorServerInfo;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Main.i18n("account.injector.server"));
@FXML private ScrollPane scrollPane;
@FXML private StackPane addServerContainer;
@FXML private Label lblServerIp;
@FXML private Label lblServerName;
@FXML private Label lblCreationWarning;
@FXML private VBox listPane;
@FXML private JFXTextField txtServerIp;
@FXML private JFXDialogLayout addServerPane;
@FXML private JFXDialogLayout confirmServerPane;
@FXML private JFXDialog dialog;
@FXML private StackPane contentPane;
@FXML private JFXSpinner spinner;
@FXML private JFXProgressBar progressBar;
@FXML private JFXButton btnAddNext;
private TransitionHandler transitionHandler;
{
FXUtils.loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml");
FXUtils.smoothScrolling(scrollPane);
transitionHandler = new TransitionHandler(addServerContainer);
getChildren().remove(dialog);
dialog.setDialogContainer(this);
txtServerIp.textProperty().addListener((a, b, newValue) -> {
btnAddNext.setDisable(!txtServerIp.validate());
});
loading();
}
private void removeServer(AuthlibInjectorServerItem item) {
Settings.INSTANCE.removeAuthlibInjectorServerURL(item.getInfo().getServerIp());
loading();
}
private void loading() {
getChildren().remove(contentPane);
spinner.setVisible(true);
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.map(serverURL -> new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer))
.collect(Collectors.toList()))
.subscribe(Task.of(Schedulers.javafx(), variables -> {
listPane.getChildren().setAll(variables.<Collection<? extends Node>>get("list"));
loadingCompleted();
}));
}
private void loadingCompleted() {
getChildren().add(contentPane);
spinner.setVisible(false);
if (Settings.INSTANCE.getAuthlibInjectorServerURLs().isEmpty())
onAdd();
}
@FXML
private void onAdd() {
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
txtServerIp.setText("");
addServerPane.setDisable(false);
progressBar.setVisible(false);
dialog.show();
}
@FXML
private void onAddCancel() {
dialog.close();
}
@FXML
private void onAddNext() {
progressBar.setVisible(true);
addServerPane.setDisable(true);
Task.ofResult("serverName", () -> Objects.requireNonNull(Accounts.getAuthlibInjectorServerName(txtServerIp.getText())))
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
progressBar.setVisible(false);
addServerPane.setDisable(false);
if (isDependentsSucceeded) {
lblServerName.setText(variables.get("serverName"));
lblServerIp.setText(txtServerIp.getText());
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
} else
lblCreationWarning.setText(variables.<Exception>get("lastException").getLocalizedMessage());
}).start();
}
@FXML
private void onAddPrev() {
transitionHandler.setContent(addServerPane, ContainerAnimations.SWIPE_RIGHT.getAnimationProducer());
}
@FXML
private void onAddFinish() {
String ip = txtServerIp.getText();
if (!ip.endsWith("/"))
ip += "/";
Settings.INSTANCE.addAuthlibInjectorServerURL(ip);
loading();
dialog.close();
}
public String getTitle() {
return title.get();
}
@Override
public StringProperty titleProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
}

View File

@ -42,6 +42,7 @@ public final class Controllers {
private static MainPage mainPage = new MainPage();
private static SettingsPage settingsPage = null;
private static VersionPage versionPage = null;
private static AuthlibInjectorServersPage serversPage = null;
private static LeftPaneController leftPaneController;
private static Decorator decorator;
@ -67,6 +68,13 @@ public final class Controllers {
return versionPage;
}
// FXThread
public static AuthlibInjectorServersPage getServersPage() {
if (serversPage == null)
serversPage = new AuthlibInjectorServersPage();
return serversPage;
}
public static Decorator getDecorator() {
return decorator;
}

View File

@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.concurrency.JFXUtilities;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.MultiCharacterSelector;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
@ -35,7 +36,7 @@ public final class DialogController {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<AuthInfo> res = new AtomicReference<>(null);
JFXUtilities.runInFX(() -> {
YggdrasilAccountLoginPane pane = new YggdrasilAccountLoginPane((YggdrasilAccount) account, it -> {
AccountLoginPane pane = new AccountLoginPane(account, it -> {
res.set(it);
latch.countDown();
Controllers.closeDialog();

View File

@ -29,7 +29,7 @@ import org.jackhuang.hmcl.mod.ModInfo;
import java.util.function.Consumer;
public final class ModItem extends BorderPane {
public final class ModItem extends BorderPane {
private final Label lblModFileName = new Label();
private final Label lblModAuthor = new Label();
private final JFXCheckBox chkEnabled = new JFXCheckBox();

View File

@ -0,0 +1,45 @@
package org.jackhuang.hmcl.ui;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.util.Pair;
public class TwoLineListItem extends StackPane {
private final Label lblTitle = new Label();
private final Label lblSubtitle = new Label();
private final String title;
private final String subtitle;
public TwoLineListItem(Pair<String, String> pair) {
this(pair.getKey(), pair.getValue());
}
public TwoLineListItem(String title, String subtitle) {
lblTitle.setStyle("-fx-font-size: 15;");
lblSubtitle.setStyle("-fx-font-size: 10; -fx-text-fill: gray;");
this.title = title;
this.subtitle = subtitle;
lblTitle.setText(title);
lblSubtitle.setText(subtitle);
VBox vbox = new VBox();
vbox.getChildren().setAll(lblTitle, lblSubtitle);
getChildren().setAll(vbox);
}
public String getTitle() {
return title;
}
public String getSubtitle() {
return subtitle;
}
@Override
public String toString() {
return getTitle();
}
}

View File

@ -0,0 +1,42 @@
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.validation.base.ValidatorBase;
import javafx.beans.NamedArg;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TextInputControl;
import java.net.MalformedURLException;
import java.net.URL;
public class URLValidator extends ValidatorBase {
private final ObservableList<String> protocols = FXCollections.observableArrayList();
public URLValidator() {
super();
}
public URLValidator(@NamedArg("message") String message) {
super(message);
}
public ObservableList<String> getProtocols() {
return protocols;
}
@Override
protected void eval() {
if (srcControl.get() instanceof TextInputControl) {
try {
URL url = new URL(((TextInputControl) srcControl.get()).getText());
if (protocols.isEmpty())
hasErrors.set(false);
else
hasErrors.set(!protocols.contains(url.getProtocol()));
} catch (MalformedURLException e) {
hasErrors.set(true);
}
}
}
}

View File

@ -13,21 +13,16 @@
xmlns:fx="http://javafx.com/fxml"
type="StackPane" FXUtils.limitWidth="160" FXUtils.limitHeight="156">
<VBox fx:id="content">
<StackPane fx:id="header" VBox.vgrow="ALWAYS">
<BorderPane>
<top>
<HBox alignment="CENTER_RIGHT">
<JFXProgressBar fx:id="pgsSkin" visible="false" />
</HBox>
</top>
<center>
<VBox style="-fx-padding: 20 20 0 20;">
<Label fx:id="lblUser" style="-fx-font-size: 20;" wrapText="true" textAlignment="JUSTIFY" />
<Label fx:id="lblEmail" style="-fx-font-size: 12;" />
<Label fx:id="lblType" style="-fx-font-size: 12;" />
</VBox>
</center>
</BorderPane>
<StackPane fx:id="header" VBox.vgrow="ALWAYS" style="-fx-background-radius: 2 2 0 0; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;">
<HBox StackPane.alignment="TOP_CENTER" alignment="CENTER_RIGHT">
<JFXProgressBar fx:id="pgsSkin" visible="false" />
</HBox>
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblUser" style="-fx-font-size: 15;" wrapText="true" textAlignment="JUSTIFY" />
<Label fx:id="lblEmail" style="-fx-font-size: 11; -fx-text-fill: gray;" />
<Label fx:id="lblServer" style="-fx-font-size: 11; -fx-text-fill: gray;" />
<Label fx:id="lblType" style="-fx-font-size: 11; -fx-text-fill: gray;" />
</VBox>
</StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40">
<BorderPane>

View File

@ -7,6 +7,8 @@
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<?import javafx.scene.control.Hyperlink?>
<?import java.lang.String?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
@ -31,20 +33,28 @@
<body>
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
<columnConstraints>
<ColumnConstraints maxWidth="100" />
<ColumnConstraints maxWidth="70" minWidth="70"/>
<ColumnConstraints />
<ColumnConstraints minWidth="140" />
</columnConstraints>
<Label text="%account.methods" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" GridPane.columnSpan="2" />
<JFXTextField fx:id="txtUsername" promptText="%account.username" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<Label fx:id="lblAddInjectorServer" text="%account.injector.server" GridPane.halignment="RIGHT" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<JFXComboBox fx:id="cboServers" maxHeight="25" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Hyperlink fx:id="linkAddInjectorServer" text="%account.injector.add" onMouseClicked="#onAddInjecterServer" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<JFXTextField fx:id="txtUsername" promptText="%account.username" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="3" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
</validators>
</JFXTextField>
<JFXPasswordField fx:id="txtPassword" promptText="%account.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<JFXPasswordField fx:id="txtPassword" promptText="%account.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="3" GridPane.columnSpan="3" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import com.jfoenix.controls.JFXSpinner?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import com.jfoenix.controls.JFXDialog?>
<?import com.jfoenix.controls.JFXDialogLayout?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import com.jfoenix.controls.JFXTextField?>
<?import org.jackhuang.hmcl.ui.construct.URLValidator?>
<?import java.lang.String?>
<?import com.jfoenix.controls.JFXProgressBar?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
<StackPane fx:id="contentPane">
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
<VBox fx:id="listPane" spacing="10" style="-fx-padding: 20 20 70 20;">
</VBox>
</ScrollPane>
<VBox style="-fx-padding: 15;" spacing="15" pickOnBounds="false" alignment="BOTTOM_RIGHT">
<JFXButton prefWidth="40" prefHeight="40" buttonType="RAISED" onMouseClicked="#onAdd" styleClass="jfx-button-raised-round">
<graphic>
<fx:include source="/assets/svg/plus.fxml"/>
</graphic>
</JFXButton>
</VBox>
</StackPane>
<JFXDialog fx:id="dialog">
<StackPane>
<StackPane fx:id="addServerContainer">
<JFXDialogLayout fx:id="addServerPane">
<heading>
<Label text="%account.injector.add" />
</heading>
<body>
<JFXTextField fx:id="txtServerIp" promptText="%account.injector.server_ip" labelFloat="true">
<validators>
<URLValidator message="%input.url">
<protocols>
<String fx:value="http" />
<String fx:value="https" />
</protocols>
</URLValidator>
</validators>
</JFXTextField>
</body>
<actions>
<Label fx:id="lblCreationWarning" />
<JFXButton onMouseClicked="#onAddCancel" text="%button.ok" styleClass="dialog-cancel" />
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
</actions>
</JFXDialogLayout>
<JFXDialogLayout fx:id="confirmServerPane">
<heading>
<Label text="%account.injector.add" />
</heading>
<body>
<GridPane vgap="15" hgap="15" style="-fx-padding: 15 0 0 0;">
<columnConstraints>
<ColumnConstraints maxWidth="100" />
<ColumnConstraints />
</columnConstraints>
<Label text="%account.injector.server_ip" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<Label text="%account.injector.server_name" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label fx:id="lblServerIp" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<Label fx:id="lblServerName" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="%account.injector.http" style="-fx-text-fill: red;" GridPane.rowIndex="2" GridPane.columnSpan="2" GridPane.columnIndex="0" />
</GridPane>
</body>
<actions>
<JFXButton onMouseClicked="#onAddPrev" text="%wizard.prev" styleClass="dialog-cancel" />
<JFXButton onMouseClicked="#onAddCancel" text="%button.cancel" styleClass="dialog-cancel" />
<JFXButton onMouseClicked="#onAddFinish" text="%wizard.finish" styleClass="dialog-accept" />
</actions>
</JFXDialogLayout>
</StackPane>
<JFXProgressBar fx:id="progressBar" visible="false" StackPane.alignment="TOP_CENTER" />
</StackPane>
</JFXDialog>
</fx:root>

View File

@ -61,7 +61,7 @@
<bottom>
<HBox alignment="CENTER_RIGHT">
<JFXButton fx:id="btnNext" onMouseClicked="#onNext" prefWidth="100" prefHeight="40" buttonType="RAISED"
text="%wizard.next_>" styleClass="jfx-button-raised"/>
text="%wizard.next" styleClass="jfx-button-raised"/>
</HBox>
</bottom>
</BorderPane>

View File

@ -12,19 +12,11 @@
type="StackPane" pickOnBounds="false" FXUtils.limitWidth="160" FXUtils.limitHeight="156">
<VBox fx:id="content" pickOnBounds="false">
<StackPane fx:id="header" VBox.vgrow="ALWAYS" pickOnBounds="false" style="-fx-background-radius: 2 2 0 0; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;">
<BorderPane>
<top>
<HBox alignment="CENTER_RIGHT">
</HBox>
</top>
<center>
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblVersionName" style="-fx-font-size: 15;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblGameVersion" style="-fx-font-size: 10;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 10; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
</VBox>
</center>
</BorderPane>
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblVersionName" style="-fx-font-size: 15;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblGameVersion" style="-fx-font-size: 11;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 11; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
</VBox>
</StackPane>
<StackPane fx:id="body" style="-fx-background-radius: 0 0 2 2; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;" minHeight="40" pickOnBounds="false">
<BorderPane>

View File

@ -38,7 +38,13 @@ account.failed.invalid_credentials=Incorrect password. Or forbidden to log in te
account.failed.invalid_password=Invalid password
account.failed.invalid_token=Please log out and re-input your password to log in.
account.failed.no_charactor=No character in this account.
account.injector.add=Add new authentication server
account.injector.http=Warning: This server is using HTTP, which will cause your password be transmitted in clear text.
account.injector.server=Auth Server
account.injector.server_ip=Server URL
account.injector.server_name=Server Name
account.methods=Login Type
account.methods.authlib_injector=authlib-injector
account.methods.no_method=No login method
account.methods.offline=Offline
account.methods.yggdrasil=Mojang
@ -127,6 +133,7 @@ folder.screenshots=Screenshots
input.email=The username must be an e-mail.
input.number=Must be a number.
input.not_empty=Input Requrired!
input.url=Must be a valid URL.
install=Install New Game
install.failed=Failed to install
@ -406,11 +413,11 @@ version.manage.rename.message=Please enter the new name
version.settings=Settings
version.update=Update
wizard.<_prev=< Prev
wizard.prev=< Prev
wizard.close=Close
wizard.failed=Failed
wizard.finish=Finish
wizard.help=Help
wizard.next_>=Next >
wizard.next=Next >
wizard.steps=Steps
wizard.summary=Summary

View File

@ -38,7 +38,13 @@ account.failed.invalid_credentials=您的用户名或密码错误,或者登录
account.failed.invalid_password=无效的密码
account.failed.invalid_token=请尝试登出并重新输入密码登录
account.failed.no_charactor=该帐号没有角色
account.injector.add=添加登录认证服务器
account.injector.http=警告此服务器使用不安全的http协议您的密码在登录时会被明文传输。
account.injector.server=认证服务器
account.injector.server_ip=服务器地址
account.injector.server_name=服务器名称
account.methods=登录方式
account.methods.authlib_injector=authlib-injector 登录
account.methods.no_method=没有登入方式...
account.methods.offline=离线模式
account.methods.yggdrasil=正版登录
@ -127,6 +133,7 @@ folder.screenshots=截图文件夹
input.email=用户名必须是邮箱
input.number=必须是数字
input.not_empty=必填项
input.url=必须是合法的链接
install=添加游戏
install.failed=安装失败
@ -406,11 +413,11 @@ version.manage.rename.message=请输入要改成的名字
version.settings=游戏设置
version.update=更新
wizard.<_prev=< 上一步
wizard.prev=< 上一步
wizard.close=关闭
wizard.failed=失败
wizard.finish=完成
wizard.help=帮助
wizard.next_>=下一步 >
wizard.next=下一步 >
wizard.steps=步骤
wizard.summary=概要

View File

@ -57,6 +57,12 @@ public abstract class Account {
*/
public abstract AuthInfo logIn(MultiCharacterSelector selector, Proxy proxy) throws AuthenticationException;
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password) throws AuthenticationException {
return logInWithPassword(selector, password, Proxy.NO_PROXY);
}
public abstract AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException;
public abstract boolean canPlayOffline();
/**

View File

@ -64,6 +64,11 @@ public class OfflineAccount extends Account {
return new AuthInfo(username, uuid, uuid);
}
@Override
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException {
return logIn(selector, proxy);
}
@Override
public void logOut() {
// Offline account need not log out.

View File

@ -32,7 +32,7 @@ public class OfflineAccountFactory extends AccountFactory<OfflineAccount> {
}
@Override
public OfflineAccount fromUsername(String username, String password) {
public OfflineAccount fromUsername(String username, String password, Object additionalData) {
return new OfflineAccount(username, getUUIDFromUserName(username));
}

View File

@ -29,13 +29,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
// Authlib Injector recommends launchers to pre-fetch the server basic information before launched the game to save time.
GetTask getTask = new GetTask(NetworkUtils.toURL(serverBaseURL));
AtomicBoolean flag = new AtomicBoolean(true);
Thread thread = Lang.thread(() -> {
try {
getTask.run();
} catch (Exception ignore) {
flag.set(false);
}
});
Thread thread = Lang.thread(() -> flag.set(getTask.test()));
AuthInfo info = super.logIn(selector, proxy);
try {
@ -53,6 +47,29 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
}
}
@Override
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException {
// Authlib Injector recommends launchers to pre-fetch the server basic information before launched the game to save time.
GetTask getTask = new GetTask(NetworkUtils.toURL(serverBaseURL));
AtomicBoolean flag = new AtomicBoolean(true);
Thread thread = Lang.thread(() -> flag.set(getTask.test()));
AuthInfo info = super.logInWithPassword(selector, password, proxy);
try {
thread.join();
String arg = "-javaagent:" + injectorJarPath.get() + "=" + serverBaseURL;
Arguments arguments = Arguments.addJVMArguments(null, arg);
//if (flag.get())
// arguments = Arguments.addJVMArguments(arguments, "-Dorg.to2mbn.authlibinjector.config.prefetched=" + getTask.getResult());
return info.setArguments(arguments);
} catch (Exception e) {
throw new AuthenticationException("Unable to get authlib injector jar path", e);
}
}
@Override
public Map<Object, Object> toStorageImpl() {
Map<Object, Object> map = super.toStorageImpl();

View File

@ -0,0 +1,19 @@
package org.jackhuang.hmcl.auth.yggdrasil;
public class AuthlibInjectorServerInfo {
private final String serverIp;
private final String serverName;
public AuthlibInjectorServerInfo(String serverIp, String serverName) {
this.serverIp = serverIp;
this.serverName = serverName;
}
public String getServerIp() {
return serverIp;
}
public String getServerName() {
return serverName;
}
}

View File

@ -0,0 +1,34 @@
package org.jackhuang.hmcl.auth.yggdrasil;
public class AuthlibInjectorServerResponse {
private final Meta meta;
public AuthlibInjectorServerResponse() {
this(new Meta());
}
public AuthlibInjectorServerResponse(Meta meta) {
this.meta = meta;
}
public Meta getMeta() {
return meta;
}
public static class Meta {
private final String serverName;
public Meta() {
this("");
}
public Meta(String serverName) {
this.serverName = serverName;
}
public String getServerName() {
return serverName;
}
}
}

View File

@ -132,24 +132,47 @@ public class YggdrasilAccount extends Account {
}
}
@Override
public AuthInfo logInWithPassword(MultiCharacterSelector selector, String password, Proxy proxy) throws AuthenticationException {
logInWithPassword0(password, proxy);
if (!isLoggedIn())
throw new AuthenticationException("Wrong password for account " + username);
if (selectedProfile == null) {
if (profiles == null || profiles.length <= 0)
throw new NoCharacterException(this);
selectedProfile = selector.select(this, Arrays.asList(profiles));
}
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
}
private void logIn0(Proxy proxy) throws AuthenticationException {
if (StringUtils.isNotBlank(accessToken)) {
if (StringUtils.isBlank(userId))
if (StringUtils.isNotBlank(username))
userId = username;
else
throw new AuthenticationException("Invalid uuid and username");
if (checkTokenValidity(proxy)) {
isOnline = true;
return;
}
logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken), proxy);
logInWithToken(proxy);
} else if (StringUtils.isNotBlank(password))
logIn1(routeAuthenticate, new AuthenticationRequest(username, password, clientToken), proxy);
logInWithPassword0(password, proxy);
else
throw new AuthenticationException("Password cannot be blank");
}
private void logInWithToken(Proxy proxy) throws AuthenticationException {
if (StringUtils.isBlank(userId))
if (StringUtils.isNotBlank(username))
userId = username;
else
throw new AuthenticationException("Invalid uuid and username");
if (checkTokenValidity(proxy)) {
isOnline = true;
return;
}
logIn1(routeRefresh, new RefreshRequest(accessToken, clientToken), proxy);
}
public void logInWithPassword0(String password, Proxy proxy) throws AuthenticationException {
logIn1(routeAuthenticate, new AuthenticationRequest(username, password, clientToken), proxy);
}
private void logIn1(URL url, Object input, Proxy proxy) throws AuthenticationException {
AuthenticationResponse response = makeRequest(url, input, proxy)
.orElseThrow(() -> new AuthenticationException("Server response empty"));

View File

@ -138,10 +138,16 @@ public class DefaultLauncher extends Launcher {
configuration.put("${assets_root}", gameAssets.getAbsolutePath());
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getJvm).orElseGet(this::getDefaultJVMArguments), configuration));
if (authInfo.getArguments() != null && authInfo.getArguments().getJvm() != null && !authInfo.getArguments().getJvm().isEmpty())
res.addAll(Arguments.parseArguments(authInfo.getArguments().getJvm(), configuration));
res.add(version.getMainClass());
Map<String, Boolean> features = getFeatures();
res.addAll(Arguments.parseArguments(version.getArguments().map(Arguments::getGame).orElseGet(this::getDefaultGameArguments), configuration, features));
if (authInfo.getArguments() != null && authInfo.getArguments().getGame() != null && !authInfo.getArguments().getGame().isEmpty())
res.addAll(Arguments.parseArguments(authInfo.getArguments().getGame(), configuration, features));
res.addAll(Arguments.parseStringArguments(version.getMinecraftArguments().map(StringUtils::tokenize).orElseGet(LinkedList::new), configuration));
// Optional Minecraft arguments

View File

@ -192,6 +192,7 @@ public final class TaskExecutor {
// do nothing
} catch (Exception e) {
lastException = e;
variables.set("lastException", e);
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e));

View File

@ -35,15 +35,15 @@ public final class AutoTypingMap<K> {
this.impl = impl;
}
public <V> V get(K key) {
public synchronized <V> V get(K key) {
return (V) impl.get(key);
}
public <V> Optional<V> getOptional(K key) {
public synchronized <V> Optional<V> getOptional(K key) {
return Optional.ofNullable(get(key));
}
public void set(K key, Object value) {
public synchronized void set(K key, Object value) {
if (value != null)
impl.put(key, value);
}

View File

@ -93,7 +93,7 @@ public final class NetworkUtils {
public static HttpURLConnection createConnection(URL url, Proxy proxy) throws IOException {
initHttps();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setConnectTimeout(15000);