Merge pull request #351 from yushijinhun/refactor-yggdrasil

authlib-injector 部分重构
This commit is contained in:
huanghongxun
2018-06-17 10:34:47 +08:00
committed by GitHub
18 changed files with 274 additions and 210 deletions

View File

@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorBuildInfo; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorBuildInfo;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerResponse; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerResponse;
import org.jackhuang.hmcl.auth.offline.OfflineAccount; import org.jackhuang.hmcl.auth.offline.OfflineAccount;
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory; import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
@@ -30,8 +31,6 @@ import org.jackhuang.hmcl.auth.yggdrasil.MojangYggdrasilProvider;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.NetworkUtils; import org.jackhuang.hmcl.util.NetworkUtils;
@@ -41,8 +40,10 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import static org.jackhuang.hmcl.util.Lang.mapOf; import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.Pair.pair;
/** /**
@@ -58,7 +59,7 @@ public final class Accounts {
public static final Map<String, AccountFactory<?>> ACCOUNT_FACTORY = mapOf( public static final Map<String, AccountFactory<?>> ACCOUNT_FACTORY = mapOf(
pair(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE), pair(OFFLINE_ACCOUNT_KEY, OfflineAccountFactory.INSTANCE),
pair(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE)), pair(YGGDRASIL_ACCOUNT_KEY, new YggdrasilAccountFactory(MojangYggdrasilProvider.INSTANCE)),
pair(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector)) pair(AUTHLIB_INJECTOR_ACCOUNT_KEY, new AuthlibInjectorAccountFactory(Accounts::downloadAuthlibInjector, Accounts::getOrCreateAuthlibInjectorServer))
); );
private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>(); private static final Map<String, String> AUTHLIB_INJECTOR_SERVER_NAMES = new HashMap<>();
@@ -104,7 +105,23 @@ public final class Accounts {
} }
} }
public static TaskResult<String> getAuthlibInjectorServerNameAsync(AuthlibInjectorAccount account) { private static AuthlibInjectorServer getOrCreateAuthlibInjectorServer(String url) {
return Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(account.getServerBaseURL())); return Settings.SETTINGS.authlibInjectorServers.stream()
.filter(server -> url.equals(server.getUrl()))
.findFirst()
.orElseGet(() -> {
// this usually happens when migrating data from an older version
String name;
try {
name = Accounts.getAuthlibInjectorServerName(url);
LOG.info("Migrated authlib injector server [" + url + "], name=[" + name + "]");
} catch (Exception e) {
name = url;
LOG.log(Level.WARNING, "Failed to migrate authlib injector server [" + url + "]", e);
}
AuthlibInjectorServer server = new AuthlibInjectorServer(url, name);
Settings.SETTINGS.authlibInjectorServers.add(server);
return server;
});
} }
} }

View File

@@ -17,11 +17,12 @@
*/ */
package org.jackhuang.hmcl.setting; package org.jackhuang.hmcl.setting;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
@@ -35,7 +36,6 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.collections.ObservableMap; import javafx.collections.ObservableMap;
import javafx.collections.ObservableSet;
public final class Config implements Cloneable { public final class Config implements Cloneable {
@@ -99,8 +99,7 @@ public final class Config implements Cloneable {
@SerializedName("logLines") @SerializedName("logLines")
public final IntegerProperty logLines = new SimpleIntegerProperty(100); public final IntegerProperty logLines = new SimpleIntegerProperty(100);
@SerializedName("authlibInjectorServerURLs") public final ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
public final ObservableSet<String> authlibInjectorServerURLs = FXCollections.observableSet(new HashSet<>());
@Override @Override
public Config clone() { public Config clone() {

View File

@@ -20,14 +20,18 @@ package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Launcher;
public final class Profiles { public final class Profiles {
public static final String DEFAULT_PROFILE = "Default";
public static final String HOME_PROFILE = "Home";
private Profiles() { private Profiles() {
} }
public static String getProfileDisplayName(Profile profile) { public static String getProfileDisplayName(Profile profile) {
switch (profile.getName()) { switch (profile.getName()) {
case Settings.DEFAULT_PROFILE: case Profiles.DEFAULT_PROFILE:
return Launcher.i18n("profile.default"); return Launcher.i18n("profile.default");
case Settings.HOME_PROFILE: case Profiles.HOME_PROFILE:
return Launcher.i18n("profile.home"); return Launcher.i18n("profile.home");
default: default:
return profile.getName(); return profile.getName();

View File

@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.setting;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@@ -35,6 +36,7 @@ import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AccountFactory; import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.event.*; import org.jackhuang.hmcl.event.*;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
@@ -51,7 +53,10 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.Logging.LOG;
public class Settings { public class Settings {
public static final Gson GSON = new GsonBuilder() public static final Gson GSON = new GsonBuilder()
@@ -65,15 +70,12 @@ public class Settings {
.setPrettyPrinting() .setPrettyPrinting()
.create(); .create();
public static final String DEFAULT_PROFILE = "Default";
public static final String HOME_PROFILE = "Home";
public static final String SETTINGS_FILE_NAME = "hmcl.json"; public static final String SETTINGS_FILE_NAME = "hmcl.json";
public static final File SETTINGS_FILE = new File(SETTINGS_FILE_NAME).getAbsoluteFile(); public static final File SETTINGS_FILE = new File(SETTINGS_FILE_NAME).getAbsoluteFile();
public static final Settings INSTANCE = new Settings(); public static final Config SETTINGS = initSettings();
private final Config SETTINGS = initSettings(); public static final Settings INSTANCE = new Settings();
private final Map<String, Account> accounts = new ConcurrentHashMap<>(); private final Map<String, Account> accounts = new ConcurrentHashMap<>();
@@ -84,7 +86,7 @@ public class Settings {
Map<Object, Object> settings = iterator.next(); Map<Object, Object> settings = iterator.next();
AccountFactory<?> factory = Accounts.ACCOUNT_FACTORY.get(tryCast(settings.get("type"), String.class).orElse("")); AccountFactory<?> factory = Accounts.ACCOUNT_FACTORY.get(tryCast(settings.get("type"), String.class).orElse(""));
if (factory == null) { if (factory == null) {
// unrecognized account type, so remove it. LOG.warning("Unrecognized account type, removing: " + settings);
iterator.remove(); iterator.remove();
continue; continue;
} }
@@ -93,7 +95,7 @@ public class Settings {
try { try {
account = factory.fromStorage(settings, getProxy()); account = factory.fromStorage(settings, getProxy());
} catch (Exception e) { } catch (Exception e) {
// storage is malformed, delete. LOG.log(Level.WARNING, "Malformed account storage, removing: " + settings, e);
iterator.remove(); iterator.remove();
continue; continue;
} }
@@ -101,7 +103,8 @@ public class Settings {
accounts.put(Accounts.getAccountId(account), account); accounts.put(Accounts.getAccountId(account), account);
} }
checkAuthlibInjectorAccounts(); SETTINGS.authlibInjectorServers.addListener(onInvalidating(this::removeDanglingAuthlibInjectorAccounts));
checkProfileMap(); checkProfileMap();
save(); save();
@@ -115,7 +118,7 @@ public class Settings {
Lang.ignoringException(() -> Runtime.getRuntime().addShutdownHook(new Thread(this::save))); Lang.ignoringException(() -> Runtime.getRuntime().addShutdownHook(new Thread(this::save)));
} }
private Config initSettings() { private static Config initSettings() {
Config c = new Config(); Config c = new Config();
if (SETTINGS_FILE.exists()) if (SETTINGS_FILE.exists())
try { try {
@@ -305,30 +308,17 @@ public class Settings {
* AUTHLIB INJECTORS * * AUTHLIB INJECTORS *
****************************************/ ****************************************/
public Set<String> getAuthlibInjectorServerURLs() { /**
return SETTINGS.authlibInjectorServerURLs; * After an {@link AuthlibInjectorServer} is removed, the associated accounts should also be removed.
} * This method performs a check and removes the dangling accounts.
* Don't call this before {@link #migrateAuthlibInjectorServers()} is called, otherwise old data would be lost.
public void removeAuthlibInjectorServerURL(String serverURL) { */
SETTINGS.authlibInjectorServerURLs.remove(serverURL); private void removeDanglingAuthlibInjectorAccounts() {
accounts.values().stream()
checkAuthlibInjectorAccounts(); .filter(AuthlibInjectorAccount.class::isInstance)
save(); .filter(it -> !SETTINGS.authlibInjectorServers.contains(((AuthlibInjectorAccount) it).getServer()))
} .collect(toList())
.forEach(this::deleteAccount);
public void addAuthlibInjectorServerURL(String serverURL) {
SETTINGS.authlibInjectorServerURLs.add(serverURL);
save();
}
private void checkAuthlibInjectorAccounts() {
for (Account account : getAccounts()) {
if (account instanceof AuthlibInjectorAccount) {
AuthlibInjectorAccount injectorAccount = (AuthlibInjectorAccount) account;
if (!SETTINGS.authlibInjectorServerURLs.contains(injectorAccount.getServerBaseURL()))
deleteAccount(account);
}
}
} }
/**************************************** /****************************************
@@ -561,8 +551,8 @@ public class Settings {
private void checkProfileMap() { private void checkProfileMap() {
if (getProfileMap().isEmpty()) { if (getProfileMap().isEmpty()) {
getProfileMap().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE)); getProfileMap().put(Profiles.DEFAULT_PROFILE, new Profile(Profiles.DEFAULT_PROFILE));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY)); getProfileMap().put(Profiles.HOME_PROFILE, new Profile(Profiles.HOME_PROFILE, Launcher.MINECRAFT_DIRECTORY));
} }
} }

View File

@@ -34,7 +34,6 @@ import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.offline.OfflineAccount; import org.jackhuang.hmcl.auth.offline.OfflineAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.game.AccountHelper; import org.jackhuang.hmcl.game.AccountHelper;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
@@ -80,8 +79,7 @@ public class AccountPage extends StackPane implements DecoratorPage {
FXUtils.setLimitWidth(this, 300); FXUtils.setLimitWidth(this, 300);
if (account instanceof AuthlibInjectorAccount) { if (account instanceof AuthlibInjectorAccount) {
Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account) lblServer.setText(((AuthlibInjectorAccount) account).getServer().getName());
.subscribe(Schedulers.javafx(), variables -> lblServer.setText(variables.get("serverName")));
FXUtils.setLimitHeight(this, 182); FXUtils.setLimitHeight(this, 182);
} else { } else {
componentList.removeChildren(paneServer); componentList.removeChildren(paneServer);

View File

@@ -33,6 +33,7 @@ import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.*; import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.auth.offline.OfflineAccount; import org.jackhuang.hmcl.auth.offline.OfflineAccount;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile; import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
@@ -46,16 +47,17 @@ import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.ui.construct.AdvancedListBox; import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
import org.jackhuang.hmcl.ui.construct.IconedItem; import org.jackhuang.hmcl.ui.construct.IconedItem;
import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.Logging;
import java.util.Collection; import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory;
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AddAccountPane extends StackPane { public class AddAccountPane extends StackPane {
@@ -64,7 +66,7 @@ public class AddAccountPane extends StackPane {
@FXML private Label lblCreationWarning; @FXML private Label lblCreationWarning;
@FXML private Label lblPassword; @FXML private Label lblPassword;
@FXML private JFXComboBox<String> cboType; @FXML private JFXComboBox<String> cboType;
@FXML private JFXComboBox<TwoLineListItem> cboServers; @FXML private JFXComboBox<AuthlibInjectorServer> cboServers;
@FXML private Label lblInjectorServer; @FXML private Label lblInjectorServer;
@FXML private Hyperlink linkManageInjectorServers; @FXML private Hyperlink linkManageInjectorServers;
@FXML private JFXDialogLayout layout; @FXML private JFXDialogLayout layout;
@@ -82,7 +84,13 @@ public class AddAccountPane extends StackPane {
transitionHandler = new TransitionHandler(acceptPane); transitionHandler = new TransitionHandler(acceptPane);
acceptPane.getChildren().setAll(btnAccept); acceptPane.getChildren().setAll(btnAccept);
loadServers(); cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
cboServers.setItems(Settings.INSTANCE.SETTINGS.authlibInjectorServers);
// workaround: otherwise the combox will be black
if (!cboServers.getItems().isEmpty())
cboServers.getSelectionModel().select(0);
cboType.getItems().setAll(Launcher.i18n("account.methods.offline"), Launcher.i18n("account.methods.yggdrasil"), Launcher.i18n("account.methods.authlib_injector")); cboType.getItems().setAll(Launcher.i18n("account.methods.offline"), Launcher.i18n("account.methods.yggdrasil"), Launcher.i18n("account.methods.authlib_injector"));
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> { cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
@@ -95,10 +103,6 @@ public class AddAccountPane extends StackPane {
}); });
cboType.getSelectionModel().select(0); 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()); txtPassword.setOnAction(e -> onCreationAccept());
txtUsername.setOnAction(e -> onCreationAccept()); txtUsername.setOnAction(e -> onCreationAccept());
txtUsername.getValidators().add(new Validator(Launcher.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@"))); txtUsername.getValidators().add(new Validator(Launcher.i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
@@ -111,24 +115,6 @@ public class AddAccountPane extends StackPane {
btnAccept.setDisable(!txtUsername.validate() || (cboType.getSelectionModel().getSelectedIndex() != 0 && !txtPassword.validate())); btnAccept.setDisable(!txtUsername.validate() || (cboType.getSelectionModel().getSelectedIndex() != 0 && !txtPassword.validate()));
} }
private void loadServers() {
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream()
.flatMap(serverURL -> {
try {
return Stream.of(new TwoLineListItem(Accounts.getAuthlibInjectorServerName(serverURL), serverURL));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Authlib-injector server root " + serverURL + " cannot be recognized.", e);
return Stream.empty();
}
})
.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 void showSpinner() { private void showSpinner() {
transitionHandler.setContent(spinnerAccept, ContainerAnimations.FADE.getAnimationProducer()); transitionHandler.setContent(spinnerAccept, ContainerAnimations.FADE.getAnimationProducer());
} }
@@ -139,34 +125,51 @@ public class AddAccountPane extends StackPane {
@FXML @FXML
private void onCreationAccept() { private void onCreationAccept() {
int type = cboType.getSelectionModel().getSelectedIndex();
String username = txtUsername.getText(); String username = txtUsername.getText();
String password = txtPassword.getText(); String password = txtPassword.getText();
String apiRoot = Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem()).map(TwoLineListItem::getSubtitle).orElse(null); Object addtionalData;
int type = cboType.getSelectionModel().getSelectedIndex();
AccountFactory<?> factory;
switch (type) {
case 0:
factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY);
addtionalData = null;
break;
case 1:
factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY);
addtionalData = null;
break;
case 2:
factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY);
Optional<AuthlibInjectorServer> server = Optional.ofNullable(cboServers.getSelectionModel().getSelectedItem());
if (server.isPresent()) {
addtionalData = server.get();
} else {
lblCreationWarning.setText(Launcher.i18n("account.failed.no_selected_server"));
return;
}
break;
default:
throw new Error();
}
showSpinner(); showSpinner();
lblCreationWarning.setText(""); lblCreationWarning.setText("");
Task.ofResult("create_account", () -> {
AccountFactory<?> factory;
switch (type) {
case 0: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.OFFLINE_ACCOUNT_KEY); break;
case 1: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.YGGDRASIL_ACCOUNT_KEY); break;
case 2: factory = Accounts.ACCOUNT_FACTORY.get(Accounts.AUTHLIB_INJECTOR_ACCOUNT_KEY); break;
default: throw new Error();
}
return factory.create(new Selector(), username, password, apiRoot, Settings.INSTANCE.getProxy()); Task.ofResult("create_account", () -> factory.create(new Selector(), username, password, addtionalData, Settings.INSTANCE.getProxy()))
}).finalized(Schedulers.javafx(), variables -> { .finalized(Schedulers.javafx(), variables -> {
Settings.INSTANCE.addAccount(variables.get("create_account")); Settings.INSTANCE.addAccount(variables.get("create_account"));
hideSpinner(); hideSpinner();
finalization.accept(this); finalization.accept(this);
}, exception -> { }, exception -> {
if (exception instanceof NoSelectedCharacterException) { if (exception instanceof NoSelectedCharacterException) {
finalization.accept(this); finalization.accept(this);
} else { } else {
lblCreationWarning.setText(accountException(exception)); lblCreationWarning.setText(accountException(exception));
} }
hideSpinner(); hideSpinner();
}).start(); }).start();
} }
@FXML @FXML
@@ -224,13 +227,13 @@ public class AddAccountPane extends StackPane {
image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, 4, Settings.INSTANCE.getProxy()); image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, 4, Settings.INSTANCE.getProxy());
} catch (Exception e) { } catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Failed to get skin for " + profile.getName(), e); Logging.LOG.log(Level.WARNING, "Failed to get skin for " + profile.getName(), e);
image = FXUtils.DEFAULT_ICON; image = null;
} }
ImageView portraitView = new ImageView(); ImageView portraitView = new ImageView();
portraitView.setSmooth(false); portraitView.setSmooth(false);
if (image == FXUtils.DEFAULT_ICON) if (image == null) {
portraitView.setImage(FXUtils.DEFAULT_ICON); portraitView.setImage(Constants.DEFAULT_ICON.get());
else { } else {
portraitView.setImage(image); portraitView.setImage(image);
portraitView.setViewport(AccountHelper.getViewport(4)); portraitView.setViewport(AccountHelper.getViewport(4));
} }

View File

@@ -23,19 +23,19 @@ import javafx.geometry.Pos;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.setting.Theme;
import java.util.function.Consumer; import java.util.function.Consumer;
public final class AuthlibInjectorServerItem extends BorderPane { public final class AuthlibInjectorServerItem extends BorderPane {
private final AuthlibInjectorServerInfo info; private final AuthlibInjectorServer server;
private final Label lblServerName = new Label(); private final Label lblServerName = new Label();
private final Label lblServerIp = new Label(); private final Label lblServerIp = new Label();
public AuthlibInjectorServerItem(AuthlibInjectorServerInfo info, Consumer<AuthlibInjectorServerItem> deleteCallback) { public AuthlibInjectorServerItem(AuthlibInjectorServer server, Consumer<AuthlibInjectorServerItem> deleteCallback) {
this.info = info; this.server = server;
lblServerName.setStyle("-fx-font-size: 15;"); lblServerName.setStyle("-fx-font-size: 15;");
lblServerIp.setStyle("-fx-font-size: 10;"); lblServerIp.setStyle("-fx-font-size: 10;");
@@ -54,11 +54,11 @@ public final class AuthlibInjectorServerItem extends BorderPane {
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;"); setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1); JFXDepthManager.setDepth(this, 1);
lblServerName.setText(info.getServerName()); lblServerName.setText(server.getName());
lblServerIp.setText(info.getServerIp()); lblServerIp.setText(server.getUrl());
} }
public AuthlibInjectorServerInfo getInfo() { public AuthlibInjectorServer getServer() {
return info; return server;
} }
} }

View File

@@ -4,13 +4,12 @@ import com.jfoenix.controls.*;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServerInfo; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Schedulers;
@@ -18,35 +17,32 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations; import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionHandler; import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage; import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.NetworkUtils; import org.jackhuang.hmcl.util.NetworkUtils;
import java.util.Collection; import static java.util.stream.Collectors.toList;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage { public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account.injector.server")); private final StringProperty title = new SimpleStringProperty(this, "title", Launcher.i18n("account.injector.server"));
@FXML private ScrollPane scrollPane; @FXML private ScrollPane scrollPane;
@FXML private StackPane addServerContainer; @FXML private StackPane addServerContainer;
@FXML private Label lblServerIp; @FXML private Label lblServerUrl;
@FXML private Label lblServerName; @FXML private Label lblServerName;
@FXML private Label lblCreationWarning; @FXML private Label lblCreationWarning;
@FXML private Label lblServerWarning; @FXML private Label lblServerWarning;
@FXML private VBox listPane; @FXML private VBox listPane;
@FXML private JFXTextField txtServerIp; @FXML private JFXTextField txtServerUrl;
@FXML private JFXDialogLayout addServerPane; @FXML private JFXDialogLayout addServerPane;
@FXML private JFXDialogLayout confirmServerPane; @FXML private JFXDialogLayout confirmServerPane;
@FXML private JFXDialog dialog; @FXML private JFXDialog dialog;
@FXML private StackPane contentPane; @FXML private StackPane contentPane;
@FXML private JFXSpinner spinner;
@FXML private JFXProgressBar progressBar; @FXML private JFXProgressBar progressBar;
@FXML private JFXButton btnAddNext; @FXML private JFXButton btnAddNext;
private final TransitionHandler transitionHandler; private final TransitionHandler transitionHandler;
private AuthlibInjectorServer serverBeingAdded;
{ {
FXUtils.loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml"); FXUtils.loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml");
FXUtils.smoothScrolling(scrollPane); FXUtils.smoothScrolling(scrollPane);
@@ -55,49 +51,33 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
getChildren().remove(dialog); getChildren().remove(dialog);
dialog.setDialogContainer(this); dialog.setDialogContainer(this);
txtServerIp.textProperty().addListener((a, b, newValue) -> txtServerUrl.textProperty().addListener((a, b, newValue) ->
btnAddNext.setDisable(!txtServerIp.validate())); btnAddNext.setDisable(!txtServerUrl.validate()));
loading(); reload();
} }
private void removeServer(AuthlibInjectorServerItem item) { private void removeServer(AuthlibInjectorServerItem item) {
Settings.INSTANCE.removeAuthlibInjectorServerURL(item.getInfo().getServerIp()); Settings.INSTANCE.SETTINGS.authlibInjectorServers.remove(item.getServer());
loading(); reload();
} }
private void loading() { private void reload() {
getChildren().remove(contentPane); listPane.getChildren().setAll(
spinner.setVisible(true); Settings.INSTANCE.SETTINGS.authlibInjectorServers.stream()
.map(server -> new AuthlibInjectorServerItem(server, this::removeServer))
Task.ofResult("list", () -> Settings.INSTANCE.getAuthlibInjectorServerURLs().parallelStream() .collect(toList()));
.flatMap(serverURL -> { if (Settings.INSTANCE.SETTINGS.authlibInjectorServers.isEmpty()) {
try {
return Stream.of(new AuthlibInjectorServerItem(new AuthlibInjectorServerInfo(serverURL, Accounts.getAuthlibInjectorServerName(serverURL)), this::removeServer));
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Authlib-injector server root " + serverURL + " cannot be recognized.", e);
return Stream.empty();
}
})
.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(); onAdd();
}
} }
@FXML @FXML
private void onAdd() { private void onAdd() {
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer()); transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
txtServerIp.setText(""); txtServerUrl.setText("");
txtServerUrl.resetValidation();
lblCreationWarning.setText("");
addServerPane.setDisable(false); addServerPane.setDisable(false);
progressBar.setVisible(false); progressBar.setVisible(false);
dialog.show(); dialog.show();
@@ -110,26 +90,28 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
@FXML @FXML
private void onAddNext() { private void onAddNext() {
String serverIp = txtServerIp.getText(); String url = fixInputUrl(txtServerUrl.getText());
progressBar.setVisible(true); progressBar.setVisible(true);
addServerPane.setDisable(true); addServerPane.setDisable(true);
Task.ofResult("serverName", () -> Accounts.getAuthlibInjectorServerName(serverIp)) Task.of(() -> {
.finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> { serverBeingAdded = new AuthlibInjectorServer(url, Accounts.getAuthlibInjectorServerName(url));
progressBar.setVisible(false); }).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
addServerPane.setDisable(false); progressBar.setVisible(false);
addServerPane.setDisable(false);
if (isDependentsSucceeded) { if (isDependentsSucceeded) {
lblServerName.setText(variables.get("serverName")); lblServerName.setText(serverBeingAdded.getName());
lblServerIp.setText(txtServerIp.getText()); lblServerUrl.setText(serverBeingAdded.getUrl());
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverIp).getProtocol())); lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverBeingAdded.getUrl()).getProtocol()));
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
} else
lblCreationWarning.setText(variables.<Exception>get("lastException").getLocalizedMessage());
}).start();
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
} else {
lblCreationWarning.setText(variables.<Exception>get("lastException").getLocalizedMessage());
}
}).start();
} }
@@ -140,11 +122,10 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
@FXML @FXML
private void onAddFinish() { private void onAddFinish() {
String ip = txtServerIp.getText(); if (!Settings.INSTANCE.SETTINGS.authlibInjectorServers.contains(serverBeingAdded)) {
if (!ip.endsWith("/")) Settings.INSTANCE.SETTINGS.authlibInjectorServers.add(serverBeingAdded);
ip += "/"; }
Settings.INSTANCE.addAuthlibInjectorServerURL(ip); reload();
loading();
dialog.close(); dialog.close();
} }
@@ -160,4 +141,11 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
public void setTitle(String title) { public void setTitle(String title) {
this.title.set(title); this.title.set(title);
} }
private String fixInputUrl(String url) {
if (!url.endsWith("/")) {
url += "/";
}
return url;
}
} }

View File

@@ -23,6 +23,7 @@ import javafx.animation.Interpolator;
import javafx.animation.KeyFrame; import javafx.animation.KeyFrame;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@@ -30,18 +31,22 @@ import javafx.beans.value.WeakChangeListener;
import javafx.event.EventHandler; import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.control.ScrollBar; import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent; import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle; import javafx.scene.shape.Rectangle;
import javafx.util.Callback;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.StringConverter;
import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Launcher;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.*;
import java.io.File; import java.io.File;
@@ -52,6 +57,7 @@ import java.net.URI;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
@@ -68,6 +74,10 @@ public final class FXUtils {
} }
} }
public static InvalidationListener onInvalidating(Runnable action) {
return arg -> action.run();
}
public static <T> void onChange(ObservableValue<T> value, Consumer<T> consumer) { public static <T> void onChange(ObservableValue<T> value, Consumer<T> consumer) {
value.addListener((a, b, c) -> consumer.accept(c)); value.addListener((a, b, c) -> consumer.accept(c));
} }
@@ -402,7 +412,32 @@ public final class FXUtils {
} }
} }
public static final Image DEFAULT_ICON = new Image("/assets/img/icon.png"); public static <T> StringConverter<T> stringConverter(Function<T, String> func) {
return new StringConverter<T>() {
@Override
public String toString(T object) {
return object == null ? "" : func.apply(object);
}
@Override
public T fromString(String string) {
throw new UnsupportedOperationException();
}
};
}
public static <T> Callback<ListView<T>, ListCell<T>> jfxListCellFactory(Function<T, Node> graphicBuilder) {
return view -> new JFXListCell<T>() {
@Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setGraphic(graphicBuilder.apply(item));
}
}
};
}
public static final Interpolator SINE = new Interpolator() { public static final Interpolator SINE = new Interpolator() {
@Override @Override

View File

@@ -186,9 +186,9 @@ public final class LeftPaneController {
} else } else
item.setImage(AccountHelper.getDefaultSkin(account.getUUID(), 4), AccountHelper.getViewport(4)); item.setImage(AccountHelper.getDefaultSkin(account.getUUID(), 4), AccountHelper.getViewport(4));
if (account instanceof AuthlibInjectorAccount) if (account instanceof AuthlibInjectorAccount) {
Accounts.getAuthlibInjectorServerNameAsync((AuthlibInjectorAccount) account) FXUtils.installTooltip(ripplerContainer, 500, 5000, 0, new Tooltip(((AuthlibInjectorAccount) account).getServer().getName()));
.subscribe(Schedulers.javafx(), variables -> FXUtils.installTooltip(ripplerContainer, 500, 5000, 0, new Tooltip(variables.get("serverName")))); }
if (selectedAccount == account) if (selectedAccount == account)
ripplerContainer.setSelected(true); ripplerContainer.setSelected(true);

View File

@@ -264,7 +264,7 @@ public final class VersionSettingsController {
if (iconFile.exists()) if (iconFile.exists())
iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath())); iconPickerItem.setImage(new Image("file:" + iconFile.getAbsolutePath()));
else else
iconPickerItem.setImage(FXUtils.DEFAULT_ICON); iconPickerItem.setImage(Constants.DEFAULT_ICON.get());
FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32); FXUtils.limitSize(iconPickerItem.getImageView(), 32, 32);
} }
} }

View File

@@ -8,7 +8,6 @@
<fx:root xmlns="http://javafx.com/javafx" <fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml" xmlns:fx="http://javafx.com/fxml"
type="StackPane"> type="StackPane">
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
<StackPane fx:id="contentPane"> <StackPane fx:id="contentPane">
<ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true"> <ScrollPane fx:id="scrollPane" fitToWidth="true" fitToHeight="true">
<VBox fx:id="listPane" spacing="10" style="-fx-padding: 20 20 70 20;"> <VBox fx:id="listPane" spacing="10" style="-fx-padding: 20 20 70 20;">
@@ -32,7 +31,7 @@
<Label text="%account.injector.add" /> <Label text="%account.injector.add" />
</heading> </heading>
<body> <body>
<JFXTextField fx:id="txtServerIp" promptText="%account.injector.server_ip"> <JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url">
<validators> <validators>
<URLValidator message="%input.url"> <URLValidator message="%input.url">
</URLValidator> </URLValidator>
@@ -62,10 +61,10 @@
<ColumnConstraints maxWidth="100" /> <ColumnConstraints maxWidth="100" />
<ColumnConstraints /> <ColumnConstraints />
</columnConstraints> </columnConstraints>
<Label text="%account.injector.server_ip" GridPane.columnIndex="0" GridPane.rowIndex="0" /> <Label text="%account.injector.server_url" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<Label text="%account.injector.server_name" GridPane.columnIndex="0" GridPane.rowIndex="1" /> <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="lblServerUrl" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<Label fx:id="lblServerName" GridPane.columnIndex="1" GridPane.rowIndex="1" /> <Label fx:id="lblServerName" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label fx:id="lblServerWarning" text="%account.injector.http" style="-fx-text-fill: red;" GridPane.rowIndex="2" GridPane.columnSpan="2" GridPane.columnIndex="0" /> <Label fx:id="lblServerWarning" text="%account.injector.http" style="-fx-text-fill: red;" GridPane.rowIndex="2" GridPane.columnSpan="2" GridPane.columnIndex="0" />

View File

@@ -39,11 +39,12 @@ account.failed.invalid_credentials=Incorrect password. Or forbidden to log in te
account.failed.invalid_password=Invalid password account.failed.invalid_password=Invalid password
account.failed.invalid_token=Please log out and re-input your password to log in. 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.failed.no_charactor=No character in this account.
account.failed.no_selected_server=No authentication server is selected.
account.injector.add=Add authentication server account.injector.add=Add authentication server
account.injector.manage=Manage authentication servers account.injector.manage=Manage authentication servers
account.injector.http=Warning: This server is using HTTP, which will cause your password be transmitted in clear text. 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=Auth Server
account.injector.server_ip=Server URL account.injector.server_url=Server URL
account.injector.server_name=Server Name account.injector.server_name=Server Name
account.methods=Login Type account.methods=Login Type
account.methods.authlib_injector=authlib-injector account.methods.authlib_injector=authlib-injector

View File

@@ -39,11 +39,12 @@ account.failed.invalid_credentials=您的用户名或密码错误,或者登录
account.failed.invalid_password=无效的密码 account.failed.invalid_password=无效的密码
account.failed.invalid_token=请尝试登出并重新输入密码登录 account.failed.invalid_token=请尝试登出并重新输入密码登录
account.failed.no_charactor=该帐号没有角色 account.failed.no_charactor=该帐号没有角色
account.failed.no_selected_server=未选择认证服务器
account.injector.add=添加认证服务器 account.injector.add=添加认证服务器
account.injector.manage=管理认证服务器 account.injector.manage=管理认证服务器
account.injector.http=警告此服务器使用不安全的http协议您的密码在登录时会被明文传输。 account.injector.http=警告此服务器使用不安全的http协议您的密码在登录时会被明文传输。
account.injector.server=认证服务器 account.injector.server=认证服务器
account.injector.server_ip=服务器地址 account.injector.server_url=服务器地址
account.injector.server_name=服务器名称 account.injector.server_name=服务器名称
account.methods=登录方式 account.methods=登录方式
account.methods.authlib_injector=authlib-injector 登录 account.methods.authlib_injector=authlib-injector 登录

View File

@@ -37,14 +37,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
public class AuthlibInjectorAccount extends YggdrasilAccount { public class AuthlibInjectorAccount extends YggdrasilAccount {
private final String serverBaseURL; private final AuthlibInjectorServer server;
private final ExceptionalSupplier<String, ?> injectorJarPath; private final ExceptionalSupplier<String, ?> injectorJarPath;
protected AuthlibInjectorAccount(YggdrasilService service, String serverBaseURL, ExceptionalSupplier<String, ?> injectorJarPath, String username, UUID characterUUID, YggdrasilSession session) { protected AuthlibInjectorAccount(YggdrasilService service, AuthlibInjectorServer server, ExceptionalSupplier<String, ?> injectorJarPath, String username, UUID characterUUID, YggdrasilSession session) {
super(service, username, characterUUID, session); super(service, username, characterUUID, session);
this.injectorJarPath = injectorJarPath; this.injectorJarPath = injectorJarPath;
this.serverBaseURL = serverBaseURL; this.server = server;
} }
@Override @Override
@@ -59,7 +59,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
private AuthInfo inject(ExceptionalSupplier<AuthInfo, AuthenticationException> supplier) throws AuthenticationException { private AuthInfo inject(ExceptionalSupplier<AuthInfo, AuthenticationException> supplier) throws AuthenticationException {
// Authlib Injector recommends launchers to pre-fetch the server basic information before launched the game to save time. // 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)); GetTask getTask = new GetTask(NetworkUtils.toURL(server.getUrl()));
AtomicBoolean flag = new AtomicBoolean(true); AtomicBoolean flag = new AtomicBoolean(true);
Thread thread = Lang.thread(() -> flag.set(getTask.test())); Thread thread = Lang.thread(() -> flag.set(getTask.test()));
@@ -67,7 +67,7 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
try { try {
thread.join(); thread.join();
Arguments arguments = new Arguments().addJVMArguments("-javaagent:" + injectorJarPath.get() + "=" + serverBaseURL); Arguments arguments = new Arguments().addJVMArguments("-javaagent:" + injectorJarPath.get() + "=" + server.getUrl());
if (flag.get()) if (flag.get())
arguments = arguments.addJVMArguments("-Dorg.to2mbn.authlibinjector.config.prefetched=" + new String(Base64.getEncoder().encode(getTask.getResult().getBytes()), UTF_8)); arguments = arguments.addJVMArguments("-Dorg.to2mbn.authlibinjector.config.prefetched=" + new String(Base64.getEncoder().encode(getTask.getResult().getBytes()), UTF_8));
@@ -81,12 +81,12 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
@Override @Override
public Map<Object, Object> toStorage() { public Map<Object, Object> toStorage() {
Map<Object, Object> map = super.toStorage(); Map<Object, Object> map = super.toStorage();
map.put("serverBaseURL", serverBaseURL); map.put("serverBaseURL", server.getUrl());
return map; return map;
} }
public String getServerBaseURL() { public AuthlibInjectorServer getServer() {
return serverBaseURL; return server;
} }
} }

View File

@@ -6,33 +6,37 @@ import org.jackhuang.hmcl.auth.CharacterSelector;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession; import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
import org.jackhuang.hmcl.util.ExceptionalSupplier; import org.jackhuang.hmcl.util.ExceptionalSupplier;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.net.Proxy; import java.net.Proxy;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.Lang.tryCast;
public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjectorAccount> { public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjectorAccount> {
private final ExceptionalSupplier<String, ?> injectorJarPathSupplier; private final ExceptionalSupplier<String, ?> injectorJarPathSupplier;
private Function<String, AuthlibInjectorServer> serverLookup;
public AuthlibInjectorAccountFactory(ExceptionalSupplier<String, ?> injectorJarPathSupplier) { /**
* @param serverLookup a function that looks up {@link AuthlibInjectorServer} by url
*/
public AuthlibInjectorAccountFactory(ExceptionalSupplier<String, ?> injectorJarPathSupplier, Function<String, AuthlibInjectorServer> serverLookup) {
this.injectorJarPathSupplier = injectorJarPathSupplier; this.injectorJarPathSupplier = injectorJarPathSupplier;
this.serverLookup = serverLookup;
} }
@Override @Override
public AuthlibInjectorAccount create(CharacterSelector selector, String username, String password, Object serverBaseURL, Proxy proxy) throws AuthenticationException { public AuthlibInjectorAccount create(CharacterSelector selector, String username, String password, Object additionalData, Proxy proxy) throws AuthenticationException {
Objects.requireNonNull(selector); Objects.requireNonNull(selector);
Objects.requireNonNull(username); Objects.requireNonNull(username);
Objects.requireNonNull(password); Objects.requireNonNull(password);
Objects.requireNonNull(proxy); Objects.requireNonNull(proxy);
if (!(serverBaseURL instanceof String) || !NetworkUtils.isURL((String) serverBaseURL)) AuthlibInjectorServer server = (AuthlibInjectorServer) additionalData;
throw new IllegalArgumentException("Additional data should be server base url string for authlib injector accounts.");
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider((String) serverBaseURL), proxy), AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl()), proxy),
(String) serverBaseURL, injectorJarPathSupplier, username, null, null); server, injectorJarPathSupplier, username, null, null);
account.logInWithPassword(password, selector); account.logInWithPassword(password, selector);
return account; return account;
} }
@@ -49,7 +53,9 @@ public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjecto
String apiRoot = tryCast(storage.get("serverBaseURL"), String.class) String apiRoot = tryCast(storage.get("serverBaseURL"), String.class)
.orElseThrow(() -> new IllegalArgumentException("storage does not have API root.")); .orElseThrow(() -> new IllegalArgumentException("storage does not have API root."));
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(apiRoot), proxy), AuthlibInjectorServer server = serverLookup.apply(apiRoot);
apiRoot, injectorJarPathSupplier, username, session.getSelectedProfile().getId(), session);
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl()), proxy),
server, injectorJarPathSupplier, username, session.getSelectedProfile().getId(), session);
} }
} }

View File

@@ -17,20 +17,35 @@
*/ */
package org.jackhuang.hmcl.auth.authlibinjector; package org.jackhuang.hmcl.auth.authlibinjector;
public class AuthlibInjectorServerInfo { public class AuthlibInjectorServer {
private final String serverIp; private String url;
private final String serverName; private String name;
public AuthlibInjectorServerInfo(String serverIp, String serverName) { public AuthlibInjectorServer(String url, String name) {
this.serverIp = serverIp; this.url = url;
this.serverName = serverName; this.name = name;
} }
public String getServerIp() { public String getUrl() {
return serverIp; return url;
} }
public String getServerName() { public String getName() {
return serverName; return name;
}
@Override
public int hashCode() {
return url.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof AuthlibInjectorServer))
return false;
AuthlibInjectorServer another = (AuthlibInjectorServer) obj;
return this.url.equals(another.url);
} }
} }

View File

@@ -19,6 +19,11 @@ package org.jackhuang.hmcl.util;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.game.Argument; import org.jackhuang.hmcl.game.Argument;
import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.RuledArgument; import org.jackhuang.hmcl.game.RuledArgument;
@@ -65,6 +70,9 @@ public final class Constants {
javafx.application.Platform.runLater(s); javafx.application.Platform.runLater(s);
}; };
// lazy loading
public static final ObjectBinding<Image> DEFAULT_ICON = Bindings.createObjectBinding(() -> new Image("/assets/img/icon.png"));
public static final Gson GSON = new GsonBuilder() public static final Gson GSON = new GsonBuilder()
.enableComplexMapKeySerialization() .enableComplexMapKeySerialization()
.setPrettyPrinting() .setPrettyPrinting()