Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dad8d452f | ||
|
|
07f8294876 | ||
|
|
7185cc410b | ||
|
|
2d985af60d | ||
|
|
bee2e64482 | ||
|
|
d5dd43c0c1 | ||
|
|
09470eeb8a | ||
|
|
094249a6fa | ||
|
|
6b5302907a | ||
|
|
adc5b08765 | ||
|
|
0150e2a8d7 | ||
|
|
45eaa0c60f | ||
|
|
66ed683196 | ||
|
|
aa4239721e | ||
|
|
5b0b926cbd | ||
|
|
929ce398eb | ||
|
|
b3fdbe021d | ||
|
|
423bd68ce8 | ||
|
|
9bffc68c70 | ||
|
|
b7a5a60091 | ||
|
|
333b11c19f | ||
|
|
fbd4d1b772 | ||
|
|
06a58514b3 | ||
|
|
80815fc3a0 | ||
|
|
b97c277a3f | ||
|
|
310d693e2b | ||
|
|
c1596f6a11 | ||
|
|
76a04343d9 | ||
|
|
25261c99f1 | ||
|
|
d0b994fa9d | ||
|
|
f259601397 | ||
|
|
87904109ca | ||
|
|
785790ccc4 | ||
|
|
759921d00e | ||
|
|
e9db183d54 | ||
|
|
a853374a88 |
No files matched your search
@@ -37,6 +37,7 @@ import org.jackhuang.hmcl.task.*;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.DialogController;
|
||||
import org.jackhuang.hmcl.ui.LogWindow;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
@@ -148,11 +149,11 @@ public final class LauncherHelper {
|
||||
else
|
||||
launchingStepsPane.setCancel(it -> {
|
||||
process.stop();
|
||||
Controllers.closeDialog(it);
|
||||
it.fireEvent(new DialogCloseEvent());
|
||||
});
|
||||
} else
|
||||
Platform.runLater(() -> {
|
||||
Controllers.closeDialog(launchingStepsPane);
|
||||
launchingStepsPane.fireEvent(new DialogCloseEvent());
|
||||
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
||||
});
|
||||
|
||||
@@ -177,7 +178,7 @@ public final class LauncherHelper {
|
||||
// Check if the application has stopped
|
||||
// because onStop will be invoked if tasks fail when the executor service shut down.
|
||||
if (!Controllers.isStopped()) {
|
||||
Controllers.closeDialog(launchingStepsPane);
|
||||
launchingStepsPane.fireEvent(new DialogCloseEvent());
|
||||
Exception ex = executor.getLastException();
|
||||
if (ex != null) {
|
||||
String message;
|
||||
@@ -267,8 +268,9 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
public void emitStatus(LoadingState state) {
|
||||
if (state == LoadingState.DONE)
|
||||
Controllers.closeDialog(launchingStepsPane);
|
||||
if (state == LoadingState.DONE) {
|
||||
launchingStepsPane.fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
|
||||
launchingStepsPane.setTitle(state.getLocalizedMessage());
|
||||
launchingStepsPane.setSubtitle((state.ordinal() + 1) + " / " + LoadingState.values().length);
|
||||
|
||||
@@ -71,7 +71,7 @@ public final class Accounts {
|
||||
}
|
||||
|
||||
private static AuthlibInjectorServer getOrCreateAuthlibInjectorServer(String url) {
|
||||
return Settings.SETTINGS.authlibInjectorServers.stream()
|
||||
return ConfigHolder.CONFIG.authlibInjectorServers.stream()
|
||||
.filter(server -> url.equals(server.getUrl()))
|
||||
.findFirst()
|
||||
.orElseGet(() -> {
|
||||
@@ -85,7 +85,7 @@ public final class Accounts {
|
||||
LOG.log(Level.WARNING, "Failed to migrate authlib injector server " + url, e);
|
||||
}
|
||||
|
||||
Settings.SETTINGS.authlibInjectorServers.add(server);
|
||||
ConfigHolder.CONFIG.authlibInjectorServers.add(server);
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,12 +17,21 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.hildan.fxgson.creators.ObservableListCreator;
|
||||
import org.hildan.fxgson.creators.ObservableMapCreator;
|
||||
import org.hildan.fxgson.creators.ObservableSetCreator;
|
||||
import org.hildan.fxgson.factories.JavaFxPropertyTypeAdapterFactory;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.util.FileTypeAdapter;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
@@ -36,9 +45,25 @@ import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
import javafx.collections.ObservableSet;
|
||||
|
||||
public final class Config implements Cloneable {
|
||||
|
||||
private static final Gson CONFIG_GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting.class, VersionSetting.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(Profile.class, Profile.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapter(ObservableList.class, new ObservableListCreator())
|
||||
.registerTypeAdapter(ObservableSet.class, new ObservableSetCreator())
|
||||
.registerTypeAdapter(ObservableMap.class, new ObservableMapCreator())
|
||||
.registerTypeAdapterFactory(new JavaFxPropertyTypeAdapterFactory(true, true))
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
public static Config fromJson(String json) throws JsonParseException {
|
||||
return CONFIG_GSON.fromJson(json, Config.class);
|
||||
}
|
||||
|
||||
@SerializedName("last")
|
||||
public final StringProperty selectedProfile = new SimpleStringProperty("");
|
||||
|
||||
@@ -104,8 +129,12 @@ public final class Config implements Cloneable {
|
||||
|
||||
public final ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
|
||||
|
||||
public String toJson() {
|
||||
return CONFIG_GSON.toJson(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Config clone() {
|
||||
return Settings.GSON.fromJson(Settings.GSON.toJson(this), Config.class);
|
||||
return fromJson(this.toJson());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.setting;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
public final class ConfigHolder {
|
||||
|
||||
private ConfigHolder() {}
|
||||
|
||||
public static final String CONFIG_FILENAME = "hmcl.json";
|
||||
public static final Path CONFIG_PATH = Paths.get(CONFIG_FILENAME).toAbsolutePath();
|
||||
public static final Config CONFIG = initSettings();
|
||||
|
||||
private static Config initSettings() {
|
||||
Config config = new Config();
|
||||
if (Files.exists(CONFIG_PATH)) {
|
||||
try {
|
||||
Config deserialized = Config.fromJson(new String(Files.readAllBytes(CONFIG_PATH), UTF_8));
|
||||
if (deserialized == null) {
|
||||
LOG.finer("Settings file is empty, use the default settings.");
|
||||
} else {
|
||||
config = deserialized;
|
||||
}
|
||||
LOG.finest("Initialized settings.");
|
||||
} catch (IOException | JsonParseException e) {
|
||||
LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
static void saveConfig(Config config) {
|
||||
try {
|
||||
Files.write(CONFIG_PATH, config.toJson().getBytes(UTF_8));
|
||||
} catch (IOException ex) {
|
||||
LOG.log(Level.SEVERE, "Failed to save config", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,21 +17,11 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.scene.text.Font;
|
||||
|
||||
import org.hildan.fxgson.creators.ObservableListCreator;
|
||||
import org.hildan.fxgson.creators.ObservableMapCreator;
|
||||
import org.hildan.fxgson.creators.ObservableSetCreator;
|
||||
import org.hildan.fxgson.factories.JavaFxPropertyTypeAdapterFactory;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
@@ -43,8 +33,6 @@ import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import org.jackhuang.hmcl.util.i18n.Locales;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
@@ -60,21 +48,6 @@ import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||
|
||||
public class Settings {
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(VersionSetting.class, VersionSetting.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(Profile.class, Profile.Serializer.INSTANCE)
|
||||
.registerTypeAdapter(File.class, FileTypeAdapter.INSTANCE)
|
||||
.registerTypeAdapter(ObservableList.class, new ObservableListCreator())
|
||||
.registerTypeAdapter(ObservableSet.class, new ObservableSetCreator())
|
||||
.registerTypeAdapter(ObservableMap.class, new ObservableMapCreator())
|
||||
.registerTypeAdapterFactory(new JavaFxPropertyTypeAdapterFactory(true, true))
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
public static final String SETTINGS_FILE_NAME = "hmcl.json";
|
||||
public static final File SETTINGS_FILE = new File(SETTINGS_FILE_NAME).getAbsoluteFile();
|
||||
|
||||
public static final Config SETTINGS = initSettings();
|
||||
|
||||
public static final Settings INSTANCE = new Settings();
|
||||
|
||||
@@ -83,11 +56,12 @@ public class Settings {
|
||||
private final boolean firstLaunch;
|
||||
|
||||
private Settings() {
|
||||
firstLaunch = SETTINGS.firstLaunch.get();
|
||||
firstLaunch = ConfigHolder.CONFIG.firstLaunch.get();
|
||||
ConfigHolder.CONFIG.firstLaunch.set(false);
|
||||
|
||||
loadProxy();
|
||||
|
||||
for (Iterator<Map<Object, Object>> iterator = SETTINGS.accounts.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<Map<Object, Object>> iterator = ConfigHolder.CONFIG.accounts.iterator(); iterator.hasNext();) {
|
||||
Map<Object, Object> settings = iterator.next();
|
||||
AccountFactory<?> factory = Accounts.ACCOUNT_FACTORY.get(tryCast(settings.get("type"), String.class).orElse(""));
|
||||
if (factory == null) {
|
||||
@@ -108,7 +82,9 @@ public class Settings {
|
||||
accounts.put(Accounts.getAccountId(account), account);
|
||||
}
|
||||
|
||||
SETTINGS.authlibInjectorServers.addListener(onInvalidating(this::removeDanglingAuthlibInjectorAccounts));
|
||||
ConfigHolder.CONFIG.authlibInjectorServers.addListener(onInvalidating(this::removeDanglingAuthlibInjectorAccounts));
|
||||
|
||||
this.selectedAccount.set(accounts.get(ConfigHolder.CONFIG.selectedAccount.get()));
|
||||
|
||||
checkProfileMap();
|
||||
|
||||
@@ -123,51 +99,26 @@ public class Settings {
|
||||
Lang.ignoringException(() -> Runtime.getRuntime().addShutdownHook(new Thread(this::save)));
|
||||
}
|
||||
|
||||
private static Config initSettings() {
|
||||
Config c = new Config();
|
||||
if (SETTINGS_FILE.exists())
|
||||
try {
|
||||
String str = FileUtils.readText(SETTINGS_FILE);
|
||||
if (StringUtils.isBlank(str))
|
||||
Logging.LOG.finer("Settings file is empty, use the default settings.");
|
||||
else {
|
||||
Config d = GSON.fromJson(str, Config.class);
|
||||
if (d != null)
|
||||
c = d;
|
||||
}
|
||||
Logging.LOG.finest("Initialized settings.");
|
||||
} catch (Exception e) {
|
||||
Logging.LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
SETTINGS.accounts.clear();
|
||||
SETTINGS.firstLaunch.set(false);
|
||||
for (Account account : accounts.values()) {
|
||||
Map<Object, Object> storage = account.toStorage();
|
||||
storage.put("type", Accounts.getAccountType(account));
|
||||
SETTINGS.accounts.add(storage);
|
||||
}
|
||||
|
||||
FileUtils.writeText(SETTINGS_FILE, GSON.toJson(SETTINGS));
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to save config", ex);
|
||||
ConfigHolder.CONFIG.accounts.clear();
|
||||
for (Account account : accounts.values()) {
|
||||
Map<Object, Object> storage = account.toStorage();
|
||||
storage.put("type", Accounts.getAccountType(account));
|
||||
ConfigHolder.CONFIG.accounts.add(storage);
|
||||
}
|
||||
ConfigHolder.saveConfig(ConfigHolder.CONFIG);
|
||||
}
|
||||
|
||||
public boolean isFirstLaunch() {
|
||||
return firstLaunch;
|
||||
}
|
||||
|
||||
private final StringProperty commonPath = new ImmediateStringProperty(this, "commonPath", SETTINGS.commonDirectory.get()) {
|
||||
private final StringProperty commonPath = new ImmediateStringProperty(this, "commonPath", ConfigHolder.CONFIG.commonDirectory.get()) {
|
||||
@Override
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.commonDirectory.set(get());
|
||||
ConfigHolder.CONFIG.commonDirectory.set(get());
|
||||
save();
|
||||
}
|
||||
};
|
||||
@@ -184,7 +135,7 @@ public class Settings {
|
||||
this.commonPath.set(commonPath);
|
||||
}
|
||||
|
||||
private Locales.SupportedLocale locale = Locales.getLocaleByName(SETTINGS.localization.get());
|
||||
private Locales.SupportedLocale locale = Locales.getLocaleByName(ConfigHolder.CONFIG.localization.get());
|
||||
|
||||
public Locales.SupportedLocale getLocale() {
|
||||
return locale;
|
||||
@@ -192,7 +143,7 @@ public class Settings {
|
||||
|
||||
public void setLocale(Locales.SupportedLocale locale) {
|
||||
this.locale = locale;
|
||||
SETTINGS.localization.set(Locales.getNameByLocale(locale));
|
||||
ConfigHolder.CONFIG.localization.set(Locales.getNameByLocale(locale));
|
||||
save();
|
||||
}
|
||||
|
||||
@@ -202,7 +153,7 @@ public class Settings {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
private Proxy.Type proxyType = Proxies.getProxyType(SETTINGS.proxyType.get());
|
||||
private Proxy.Type proxyType = Proxies.getProxyType(ConfigHolder.CONFIG.proxyType.get());
|
||||
|
||||
public Proxy.Type getProxyType() {
|
||||
return proxyType;
|
||||
@@ -210,62 +161,62 @@ public class Settings {
|
||||
|
||||
public void setProxyType(Proxy.Type proxyType) {
|
||||
this.proxyType = proxyType;
|
||||
SETTINGS.proxyType.set(Proxies.PROXIES.indexOf(proxyType));
|
||||
ConfigHolder.CONFIG.proxyType.set(Proxies.PROXIES.indexOf(proxyType));
|
||||
save();
|
||||
loadProxy();
|
||||
}
|
||||
|
||||
public String getProxyHost() {
|
||||
return SETTINGS.proxyHost.get();
|
||||
return ConfigHolder.CONFIG.proxyHost.get();
|
||||
}
|
||||
|
||||
public void setProxyHost(String proxyHost) {
|
||||
SETTINGS.proxyHost.set(proxyHost);
|
||||
ConfigHolder.CONFIG.proxyHost.set(proxyHost);
|
||||
save();
|
||||
}
|
||||
|
||||
public String getProxyPort() {
|
||||
return SETTINGS.proxyPort.get();
|
||||
return ConfigHolder.CONFIG.proxyPort.get();
|
||||
}
|
||||
|
||||
public void setProxyPort(String proxyPort) {
|
||||
SETTINGS.proxyPort.set(proxyPort);
|
||||
ConfigHolder.CONFIG.proxyPort.set(proxyPort);
|
||||
save();
|
||||
}
|
||||
|
||||
public String getProxyUser() {
|
||||
return SETTINGS.proxyUser.get();
|
||||
return ConfigHolder.CONFIG.proxyUser.get();
|
||||
}
|
||||
|
||||
public void setProxyUser(String proxyUser) {
|
||||
SETTINGS.proxyUser.set(proxyUser);
|
||||
ConfigHolder.CONFIG.proxyUser.set(proxyUser);
|
||||
save();
|
||||
}
|
||||
|
||||
public String getProxyPass() {
|
||||
return SETTINGS.proxyPass.get();
|
||||
return ConfigHolder.CONFIG.proxyPass.get();
|
||||
}
|
||||
|
||||
public void setProxyPass(String proxyPass) {
|
||||
SETTINGS.proxyPass.set(proxyPass);
|
||||
ConfigHolder.CONFIG.proxyPass.set(proxyPass);
|
||||
save();
|
||||
}
|
||||
|
||||
public boolean hasProxy() {
|
||||
return SETTINGS.hasProxy.get();
|
||||
return ConfigHolder.CONFIG.hasProxy.get();
|
||||
}
|
||||
|
||||
public void setHasProxy(boolean hasProxy) {
|
||||
SETTINGS.hasProxy.set(hasProxy);
|
||||
ConfigHolder.CONFIG.hasProxy.set(hasProxy);
|
||||
save();
|
||||
}
|
||||
|
||||
public boolean hasProxyAuth() {
|
||||
return SETTINGS.hasProxyAuth.get();
|
||||
return ConfigHolder.CONFIG.hasProxyAuth.get();
|
||||
}
|
||||
|
||||
public void setHasProxyAuth(boolean hasProxyAuth) {
|
||||
SETTINGS.hasProxyAuth.set(hasProxyAuth);
|
||||
ConfigHolder.CONFIG.hasProxyAuth.set(hasProxyAuth);
|
||||
save();
|
||||
}
|
||||
|
||||
@@ -296,21 +247,21 @@ public class Settings {
|
||||
}
|
||||
|
||||
public Font getFont() {
|
||||
return Font.font(SETTINGS.fontFamily.get(), SETTINGS.fontSize.get());
|
||||
return Font.font(ConfigHolder.CONFIG.fontFamily.get(), ConfigHolder.CONFIG.fontSize.get());
|
||||
}
|
||||
|
||||
public void setFont(Font font) {
|
||||
SETTINGS.fontFamily.set(font.getFamily());
|
||||
SETTINGS.fontSize.set(font.getSize());
|
||||
ConfigHolder.CONFIG.fontFamily.set(font.getFamily());
|
||||
ConfigHolder.CONFIG.fontSize.set(font.getSize());
|
||||
save();
|
||||
}
|
||||
|
||||
public int getLogLines() {
|
||||
return Math.max(SETTINGS.logLines.get(), 100);
|
||||
return Math.max(ConfigHolder.CONFIG.logLines.get(), 100);
|
||||
}
|
||||
|
||||
public void setLogLines(int logLines) {
|
||||
SETTINGS.logLines.set(logLines);
|
||||
ConfigHolder.CONFIG.logLines.set(logLines);
|
||||
save();
|
||||
}
|
||||
|
||||
@@ -326,7 +277,7 @@ public class Settings {
|
||||
private void removeDanglingAuthlibInjectorAccounts() {
|
||||
accounts.values().stream()
|
||||
.filter(AuthlibInjectorAccount.class::isInstance)
|
||||
.filter(it -> !SETTINGS.authlibInjectorServers.contains(((AuthlibInjectorAccount) it).getServer()))
|
||||
.filter(it -> !ConfigHolder.CONFIG.authlibInjectorServers.contains(((AuthlibInjectorAccount) it).getServer()))
|
||||
.collect(toList())
|
||||
.forEach(this::deleteAccount);
|
||||
}
|
||||
@@ -336,14 +287,14 @@ public class Settings {
|
||||
****************************************/
|
||||
|
||||
public DownloadProvider getDownloadProvider() {
|
||||
return DownloadProviders.getDownloadProvider(SETTINGS.downloadType.get());
|
||||
return DownloadProviders.getDownloadProvider(ConfigHolder.CONFIG.downloadType.get());
|
||||
}
|
||||
|
||||
public void setDownloadProvider(DownloadProvider downloadProvider) {
|
||||
int index = DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(downloadProvider);
|
||||
if (index == -1)
|
||||
throw new IllegalArgumentException("Unknown download provider: " + downloadProvider);
|
||||
SETTINGS.downloadType.set(index);
|
||||
ConfigHolder.CONFIG.downloadType.set(index);
|
||||
save();
|
||||
}
|
||||
|
||||
@@ -351,7 +302,7 @@ public class Settings {
|
||||
* ACCOUNTS *
|
||||
****************************************/
|
||||
|
||||
private final ImmediateObjectProperty<Account> selectedAccount = new ImmediateObjectProperty<Account>(this, "selectedAccount", accounts.get(SETTINGS.selectedAccount.get())) {
|
||||
private final ImmediateObjectProperty<Account> selectedAccount = new ImmediateObjectProperty<Account>(this, "selectedAccount", null) {
|
||||
@Override
|
||||
public Account get() {
|
||||
Account a = super.get();
|
||||
@@ -373,7 +324,7 @@ public class Settings {
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.selectedAccount.set(getValue() == null ? "" : Accounts.getAccountId(getValue()));
|
||||
ConfigHolder.CONFIG.selectedAccount.set(getValue() == null ? "" : Accounts.getAccountId(getValue()));
|
||||
save();
|
||||
}
|
||||
};
|
||||
@@ -423,12 +374,12 @@ public class Settings {
|
||||
* BACKGROUND *
|
||||
****************************************/
|
||||
|
||||
private final ImmediateStringProperty backgroundImage = new ImmediateStringProperty(this, "backgroundImage", SETTINGS.backgroundImage.get()) {
|
||||
private final ImmediateStringProperty backgroundImage = new ImmediateStringProperty(this, "backgroundImage", ConfigHolder.CONFIG.backgroundImage.get()) {
|
||||
@Override
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.backgroundImage.set(get());
|
||||
ConfigHolder.CONFIG.backgroundImage.set(get());
|
||||
save();
|
||||
}
|
||||
};
|
||||
@@ -445,12 +396,12 @@ public class Settings {
|
||||
this.backgroundImage.set(backgroundImage);
|
||||
}
|
||||
|
||||
private final ImmediateObjectProperty<EnumBackgroundImage> backgroundImageType = new ImmediateObjectProperty<EnumBackgroundImage>(this, "backgroundImageType", EnumBackgroundImage.indexOf(SETTINGS.backgroundImageType.get())) {
|
||||
private final ImmediateObjectProperty<EnumBackgroundImage> backgroundImageType = new ImmediateObjectProperty<EnumBackgroundImage>(this, "backgroundImageType", EnumBackgroundImage.indexOf(ConfigHolder.CONFIG.backgroundImageType.get())) {
|
||||
@Override
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.backgroundImageType.set(get().ordinal());
|
||||
ConfigHolder.CONFIG.backgroundImageType.set(get().ordinal());
|
||||
save();
|
||||
}
|
||||
};
|
||||
@@ -471,12 +422,12 @@ public class Settings {
|
||||
* THEME *
|
||||
****************************************/
|
||||
|
||||
private final ImmediateObjectProperty<Theme> theme = new ImmediateObjectProperty<Theme>(this, "theme", Theme.getTheme(SETTINGS.theme.get()).orElse(Theme.BLUE)) {
|
||||
private final ImmediateObjectProperty<Theme> theme = new ImmediateObjectProperty<Theme>(this, "theme", Theme.getTheme(ConfigHolder.CONFIG.theme.get()).orElse(Theme.BLUE)) {
|
||||
@Override
|
||||
public void invalidated() {
|
||||
super.invalidated();
|
||||
|
||||
SETTINGS.theme.set(get().getName().toLowerCase());
|
||||
ConfigHolder.CONFIG.theme.set(get().getName().toLowerCase());
|
||||
save();
|
||||
}
|
||||
};
|
||||
@@ -500,19 +451,19 @@ public class Settings {
|
||||
public Profile getSelectedProfile() {
|
||||
checkProfileMap();
|
||||
|
||||
if (!hasProfile(SETTINGS.selectedProfile.get())) {
|
||||
if (!hasProfile(ConfigHolder.CONFIG.selectedProfile.get())) {
|
||||
getProfileMap().keySet().stream().findFirst().ifPresent(selectedProfile -> {
|
||||
SETTINGS.selectedProfile.set(selectedProfile);
|
||||
ConfigHolder.CONFIG.selectedProfile.set(selectedProfile);
|
||||
save();
|
||||
});
|
||||
Schedulers.computation().schedule(this::onProfileChanged);
|
||||
}
|
||||
return getProfile(SETTINGS.selectedProfile.get());
|
||||
return getProfile(ConfigHolder.CONFIG.selectedProfile.get());
|
||||
}
|
||||
|
||||
public void setSelectedProfile(Profile selectedProfile) {
|
||||
if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), SETTINGS.selectedProfile.get())) {
|
||||
SETTINGS.selectedProfile.set(selectedProfile.getName());
|
||||
if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), ConfigHolder.CONFIG.selectedProfile.get())) {
|
||||
ConfigHolder.CONFIG.selectedProfile.set(selectedProfile.getName());
|
||||
save();
|
||||
Schedulers.computation().schedule(this::onProfileChanged);
|
||||
}
|
||||
@@ -530,7 +481,7 @@ public class Settings {
|
||||
}
|
||||
|
||||
public Map<String, Profile> getProfileMap() {
|
||||
return SETTINGS.configurations;
|
||||
return ConfigHolder.CONFIG.configurations;
|
||||
}
|
||||
|
||||
public Collection<Profile> getProfiles() {
|
||||
@@ -589,6 +540,6 @@ public class Settings {
|
||||
}
|
||||
|
||||
public Config getRawConfig() {
|
||||
return SETTINGS.clone();
|
||||
return ConfigHolder.CONFIG.clone();
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,13 @@ import com.jfoenix.controls.JFXPasswordField;
|
||||
import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||
import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -35,7 +35,6 @@ public class AccountLoginPane extends StackPane {
|
||||
private final Account oldAccount;
|
||||
private final Consumer<AuthInfo> success;
|
||||
private final Runnable failed;
|
||||
private final Consumer<Region> closeConsumer;
|
||||
|
||||
@FXML
|
||||
private Label lblUsername;
|
||||
@@ -43,11 +42,10 @@ public class AccountLoginPane extends StackPane {
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private JFXProgressBar progressBar;
|
||||
|
||||
public AccountLoginPane(Account oldAccount, Consumer<Region> closeConsumer, Consumer<AuthInfo> success, Runnable failed) {
|
||||
public AccountLoginPane(Account oldAccount, Consumer<AuthInfo> success, Runnable failed) {
|
||||
this.oldAccount = oldAccount;
|
||||
this.success = success;
|
||||
this.failed = failed;
|
||||
this.closeConsumer = closeConsumer;
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account-login.fxml");
|
||||
|
||||
@@ -70,9 +68,9 @@ public class AccountLoginPane extends StackPane {
|
||||
Object account = variable.get("login");
|
||||
if (account instanceof AuthInfo) {
|
||||
success.accept(((AuthInfo) account));
|
||||
closeConsumer.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
} else if (account instanceof NoSelectedCharacterException) {
|
||||
closeConsumer.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
} else if (account instanceof Exception) {
|
||||
lblCreationWarning.setText(AddAccountPane.accountException((Exception) account));
|
||||
}
|
||||
@@ -84,6 +82,6 @@ public class AccountLoginPane extends StackPane {
|
||||
@FXML
|
||||
private void onCancel() {
|
||||
failed.run();
|
||||
closeConsumer.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import javafx.scene.layout.StackPane;
|
||||
|
||||
import org.jackhuang.hmcl.auth.Account;
|
||||
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.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.game.AccountHelper;
|
||||
@@ -39,6 +40,8 @@ import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.installTooltip;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -78,18 +81,16 @@ public class AccountPage extends StackPane implements DecoratorPage {
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account.fxml");
|
||||
|
||||
FXUtils.setLimitWidth(this, 300);
|
||||
if (account instanceof AuthlibInjectorAccount) {
|
||||
lblServer.setText(((AuthlibInjectorAccount) account).getServer().getName());
|
||||
FXUtils.setLimitHeight(this, 182);
|
||||
AuthlibInjectorServer server = ((AuthlibInjectorAccount) account).getServer();
|
||||
lblServer.setText(server.getName());
|
||||
installTooltip(lblServer, server.getUrl());
|
||||
} else {
|
||||
componentList.removeChildren(paneServer);
|
||||
|
||||
if (account instanceof OfflineAccount) {
|
||||
componentList.removeChildren(paneEmail);
|
||||
FXUtils.setLimitHeight(this, 110);
|
||||
} else
|
||||
FXUtils.setLimitHeight(this, 145);
|
||||
}
|
||||
}
|
||||
|
||||
btnDelete.setGraphic(SVG.delete(Theme.blackFillBinding(), 15, 15));
|
||||
|
||||
@@ -19,16 +19,17 @@ package org.jackhuang.hmcl.ui;
|
||||
|
||||
import com.jfoenix.concurrency.JFXUtilities;
|
||||
import com.jfoenix.controls.*;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import org.jackhuang.hmcl.auth.*;
|
||||
@@ -40,23 +41,25 @@ import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||
import org.jackhuang.hmcl.game.AccountHelper;
|
||||
import org.jackhuang.hmcl.setting.Accounts;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem;
|
||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class AddAccountPane extends StackPane {
|
||||
@@ -72,47 +75,49 @@ public class AddAccountPane extends StackPane {
|
||||
@FXML private JFXDialogLayout layout;
|
||||
@FXML private JFXButton btnAccept;
|
||||
@FXML private SpinnerPane acceptPane;
|
||||
private final Consumer<Region> finalization;
|
||||
|
||||
public AddAccountPane(Consumer<Region> finalization) {
|
||||
this.finalization = finalization;
|
||||
|
||||
public AddAccountPane() {
|
||||
FXUtils.loadFXML(this, "/assets/fxml/account-add.fxml");
|
||||
|
||||
cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
|
||||
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
|
||||
cboServers.setItems(Settings.SETTINGS.authlibInjectorServers);
|
||||
cboServers.setPromptText(i18n("general.prompt.empty"));
|
||||
|
||||
// workaround: otherwise the combox will be black
|
||||
if (!cboServers.getItems().isEmpty())
|
||||
cboServers.getSelectionModel().select(0);
|
||||
Bindings.bindContent(cboServers.getItems(), ConfigHolder.CONFIG.authlibInjectorServers);
|
||||
cboServers.getItems().addListener(onInvalidating(this::selectDefaultServer));
|
||||
selectDefaultServer();
|
||||
|
||||
cboType.getItems().setAll(i18n("account.methods.offline"), i18n("account.methods.yggdrasil"), i18n("account.methods.authlib_injector"));
|
||||
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
|
||||
txtPassword.setVisible(newValue.intValue() != 0);
|
||||
lblPassword.setVisible(newValue.intValue() != 0);
|
||||
cboServers.setVisible(newValue.intValue() == 2);
|
||||
linkManageInjectorServers.setVisible(newValue.intValue() == 2);
|
||||
lblInjectorServer.setVisible(newValue.intValue() == 2);
|
||||
validateAcceptButton();
|
||||
});
|
||||
cboType.getSelectionModel().select(0);
|
||||
|
||||
txtPassword.setOnAction(e -> onCreationAccept());
|
||||
txtUsername.setOnAction(e -> onCreationAccept());
|
||||
ReadOnlyIntegerProperty loginTypeIdProperty = cboType.getSelectionModel().selectedIndexProperty();
|
||||
|
||||
txtPassword.visibleProperty().bind(loginTypeIdProperty.isNotEqualTo(0));
|
||||
lblPassword.visibleProperty().bind(txtPassword.visibleProperty());
|
||||
|
||||
cboServers.visibleProperty().bind(loginTypeIdProperty.isEqualTo(2));
|
||||
lblInjectorServer.visibleProperty().bind(cboServers.visibleProperty());
|
||||
linkManageInjectorServers.visibleProperty().bind(cboServers.visibleProperty());
|
||||
|
||||
txtUsername.getValidators().add(new Validator(i18n("input.email"), str -> !txtPassword.isVisible() || str.contains("@")));
|
||||
|
||||
txtUsername.textProperty().addListener(it -> validateAcceptButton());
|
||||
txtPassword.textProperty().addListener(it -> validateAcceptButton());
|
||||
btnAccept.disableProperty().bind(Bindings.createBooleanBinding(
|
||||
() -> !txtUsername.validate() || (loginTypeIdProperty.get() != 0 && !txtPassword.validate()),
|
||||
txtUsername.textProperty(), txtPassword.textProperty(), loginTypeIdProperty));
|
||||
}
|
||||
|
||||
private void validateAcceptButton() {
|
||||
btnAccept.setDisable(!txtUsername.validate() || (cboType.getSelectionModel().getSelectedIndex() != 0 && !txtPassword.validate()));
|
||||
/**
|
||||
* Selects the first server if no server is selected.
|
||||
*/
|
||||
private void selectDefaultServer() {
|
||||
if (!cboServers.getItems().isEmpty() && cboServers.getSelectionModel().isEmpty()) {
|
||||
cboServers.getSelectionModel().select(0);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onCreationAccept() {
|
||||
if (btnAccept.isDisabled())
|
||||
return;
|
||||
|
||||
String username = txtUsername.getText();
|
||||
String password = txtPassword.getText();
|
||||
Object addtionalData;
|
||||
@@ -144,41 +149,35 @@ public class AddAccountPane extends StackPane {
|
||||
|
||||
acceptPane.showSpinner();
|
||||
lblCreationWarning.setText("");
|
||||
setDisable(true);
|
||||
|
||||
Task.ofResult("create_account", () -> factory.create(new Selector(), username, password, addtionalData, Settings.INSTANCE.getProxy()))
|
||||
.finalized(Schedulers.javafx(), variables -> {
|
||||
Settings.INSTANCE.addAccount(variables.get("create_account"));
|
||||
acceptPane.hideSpinner();
|
||||
finalization.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}, exception -> {
|
||||
if (exception instanceof NoSelectedCharacterException) {
|
||||
finalization.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
} else {
|
||||
lblCreationWarning.setText(accountException(exception));
|
||||
}
|
||||
setDisable(false);
|
||||
acceptPane.hideSpinner();
|
||||
}).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onCreationCancel() {
|
||||
finalization.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onManageInjecterServers() {
|
||||
finalization.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
Controllers.navigate(Controllers.getServersPage());
|
||||
}
|
||||
|
||||
private void showSelector(Node node) {
|
||||
getChildren().setAll(node);
|
||||
}
|
||||
|
||||
private void closeSelector() {
|
||||
getChildren().setAll(layout);
|
||||
}
|
||||
|
||||
private class Selector extends BorderPane implements CharacterSelector {
|
||||
private final AdvancedListBox listBox = new AdvancedListBox();
|
||||
private final JFXButton cancel = new JFXButton();
|
||||
@@ -235,7 +234,7 @@ public class AddAccountPane extends StackPane {
|
||||
listBox.add(accountItem);
|
||||
}
|
||||
|
||||
JFXUtilities.runInFX(() -> showSelector(this));
|
||||
JFXUtilities.runInFX(() -> Controllers.dialog(this));
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
@@ -243,11 +242,11 @@ public class AddAccountPane extends StackPane {
|
||||
if (selectedProfile == null)
|
||||
throw new NoSelectedCharacterException(account);
|
||||
|
||||
JFXUtilities.runInFX(AddAccountPane.this::closeSelector);
|
||||
|
||||
return selectedProfile;
|
||||
} catch (InterruptedException ignore) {
|
||||
throw new NoSelectedCharacterException(account);
|
||||
} finally {
|
||||
JFXUtilities.runInFX(() -> Selector.this.fireEvent(new DialogCloseEvent()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +269,7 @@ public class AddAccountPane extends StackPane {
|
||||
}
|
||||
return exception.getMessage();
|
||||
} else {
|
||||
return exception.getClass() + ": " + exception.getLocalizedMessage();
|
||||
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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 static org.jackhuang.hmcl.ui.FXUtils.loadFXML;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
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.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialogLayout;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
public class AddAuthlibInjectorServerPane extends StackPane {
|
||||
|
||||
@FXML private StackPane addServerContainer;
|
||||
@FXML private Label lblServerUrl;
|
||||
@FXML private Label lblServerName;
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private Label lblServerWarning;
|
||||
@FXML private JFXTextField txtServerUrl;
|
||||
@FXML private JFXDialogLayout addServerPane;
|
||||
@FXML private JFXDialogLayout confirmServerPane;
|
||||
@FXML private SpinnerPane nextPane;
|
||||
@FXML private JFXButton btnAddNext;
|
||||
|
||||
private TransitionHandler transitionHandler;
|
||||
|
||||
private AuthlibInjectorServer serverBeingAdded;
|
||||
|
||||
public AddAuthlibInjectorServerPane(String url) {
|
||||
this();
|
||||
txtServerUrl.setText(url);
|
||||
onAddNext();
|
||||
}
|
||||
|
||||
public AddAuthlibInjectorServerPane() {
|
||||
loadFXML(this, "/assets/fxml/authlib-injector-server-add.fxml");
|
||||
transitionHandler = new TransitionHandler(addServerContainer);
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||
|
||||
btnAddNext.disableProperty().bind(
|
||||
Bindings.createBooleanBinding(txtServerUrl::validate, txtServerUrl.textProperty()).not());
|
||||
nextPane.hideSpinner();
|
||||
}
|
||||
|
||||
private String fixInputUrl(String url) {
|
||||
if (!url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private String resolveFetchExceptionMessage(Throwable exception) {
|
||||
if (exception instanceof IOException) {
|
||||
return i18n("account.failed.connect_injector_server");
|
||||
} else {
|
||||
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddCancel() {
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddNext() {
|
||||
if (btnAddNext.isDisabled())
|
||||
return;
|
||||
|
||||
lblCreationWarning.setText("");
|
||||
|
||||
String url = fixInputUrl(txtServerUrl.getText());
|
||||
|
||||
nextPane.showSpinner();
|
||||
addServerPane.setDisable(true);
|
||||
|
||||
Task.of(() -> {
|
||||
serverBeingAdded = AuthlibInjectorServer.fetchServerInfo(url);
|
||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||
addServerPane.setDisable(false);
|
||||
nextPane.hideSpinner();
|
||||
|
||||
if (isDependentsSucceeded) {
|
||||
lblServerName.setText(serverBeingAdded.getName());
|
||||
lblServerUrl.setText(serverBeingAdded.getUrl());
|
||||
|
||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverBeingAdded.getUrl()).getProtocol()));
|
||||
|
||||
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
} else {
|
||||
lblCreationWarning.setText(resolveFetchExceptionMessage(variables.<Exception>get("lastException")));
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddPrev() {
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.SWIPE_RIGHT.getAnimationProducer());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddFinish() {
|
||||
if (!ConfigHolder.CONFIG.authlibInjectorServers.contains(serverBeingAdded)) {
|
||||
ConfigHolder.CONFIG.authlibInjectorServers.add(serverBeingAdded);
|
||||
}
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,134 +1,69 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.*;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.loadFXML;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.smoothScrolling;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.WeakInvalidationListener;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
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.construct.SpinnerPane;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", i18n("account.injector.server"));
|
||||
private final StringProperty title = new SimpleStringProperty(this, "title", i18n("account.injector.manage.title"));
|
||||
|
||||
@FXML private ScrollPane scrollPane;
|
||||
@FXML private StackPane addServerContainer;
|
||||
@FXML private Label lblServerUrl;
|
||||
@FXML private Label lblServerName;
|
||||
@FXML private Label lblCreationWarning;
|
||||
@FXML private Label lblServerWarning;
|
||||
@FXML private VBox listPane;
|
||||
@FXML private JFXTextField txtServerUrl;
|
||||
@FXML private JFXDialogLayout addServerPane;
|
||||
@FXML private JFXDialogLayout confirmServerPane;
|
||||
@FXML private JFXDialog dialog;
|
||||
@FXML private StackPane contentPane;
|
||||
@FXML private SpinnerPane nextPane;
|
||||
@FXML private JFXButton btnAddNext;
|
||||
|
||||
private final TransitionHandler transitionHandler;
|
||||
private InvalidationListener serversListener;
|
||||
|
||||
private AuthlibInjectorServer serverBeingAdded;
|
||||
public AuthlibInjectorServersPage() {
|
||||
loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml");
|
||||
smoothScrolling(scrollPane);
|
||||
|
||||
{
|
||||
FXUtils.loadFXML(this, "/assets/fxml/authlib-injector-servers.fxml");
|
||||
FXUtils.smoothScrolling(scrollPane);
|
||||
transitionHandler = new TransitionHandler(addServerContainer);
|
||||
|
||||
getChildren().remove(dialog);
|
||||
dialog.setDialogContainer(this);
|
||||
|
||||
txtServerUrl.textProperty().addListener((a, b, newValue) ->
|
||||
btnAddNext.setDisable(!txtServerUrl.validate()));
|
||||
|
||||
reload();
|
||||
serversListener = observable -> updateServersList();
|
||||
ConfigHolder.CONFIG.authlibInjectorServers.addListener(new WeakInvalidationListener(serversListener));
|
||||
updateServersList();
|
||||
}
|
||||
|
||||
private void removeServer(AuthlibInjectorServerItem item) {
|
||||
Settings.SETTINGS.authlibInjectorServers.remove(item.getServer());
|
||||
reload();
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
private void updateServersList() {
|
||||
listPane.getChildren().setAll(
|
||||
Settings.SETTINGS.authlibInjectorServers.stream()
|
||||
.map(server -> new AuthlibInjectorServerItem(server, this::removeServer))
|
||||
ConfigHolder.CONFIG.authlibInjectorServers.stream()
|
||||
.map(server -> new AuthlibInjectorServerItem(server,
|
||||
item -> ConfigHolder.CONFIG.authlibInjectorServers.remove(item.getServer())))
|
||||
.collect(toList()));
|
||||
if (Settings.SETTINGS.authlibInjectorServers.isEmpty()) {
|
||||
onAdd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@FXML
|
||||
private void onAdd() {
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||
txtServerUrl.setText("");
|
||||
txtServerUrl.resetValidation();
|
||||
lblCreationWarning.setText("");
|
||||
addServerPane.setDisable(false);
|
||||
nextPane.hideSpinner();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddCancel() {
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddNext() {
|
||||
String url = fixInputUrl(txtServerUrl.getText());
|
||||
|
||||
nextPane.showSpinner();
|
||||
addServerPane.setDisable(true);
|
||||
|
||||
Task.of(() -> {
|
||||
serverBeingAdded = AuthlibInjectorServer.fetchServerInfo(url);
|
||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||
nextPane.hideSpinner();
|
||||
addServerPane.setDisable(false);
|
||||
|
||||
if (isDependentsSucceeded) {
|
||||
lblServerName.setText(serverBeingAdded.getName());
|
||||
lblServerUrl.setText(serverBeingAdded.getUrl());
|
||||
|
||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverBeingAdded.getUrl()).getProtocol()));
|
||||
|
||||
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
|
||||
} else {
|
||||
lblCreationWarning.setText(resolveFetchExceptionMessage(variables.<Exception>get("lastException")));
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddPrev() {
|
||||
transitionHandler.setContent(addServerPane, ContainerAnimations.SWIPE_RIGHT.getAnimationProducer());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void onAddFinish() {
|
||||
if (!Settings.INSTANCE.SETTINGS.authlibInjectorServers.contains(serverBeingAdded)) {
|
||||
Settings.INSTANCE.SETTINGS.authlibInjectorServers.add(serverBeingAdded);
|
||||
}
|
||||
reload();
|
||||
dialog.close();
|
||||
Controllers.dialog(new AddAuthlibInjectorServerPane());
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
@@ -143,19 +78,4 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
|
||||
public void setTitle(String title) {
|
||||
this.title.set(title);
|
||||
}
|
||||
|
||||
private String fixInputUrl(String url) {
|
||||
if (!url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private String resolveFetchExceptionMessage(Throwable exception) {
|
||||
if (exception instanceof IOException) {
|
||||
return i18n("account.failed.connect_injector_server");
|
||||
} else {
|
||||
return exception.getClass() + ": " + exception.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.InputDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
@@ -132,15 +133,15 @@ public final class Controllers {
|
||||
}
|
||||
|
||||
public static void dialog(String text, String title, int type, Runnable onAccept) {
|
||||
dialog(new MessageDialogPane(text, title, Controllers::closeDialog, type, onAccept));
|
||||
dialog(new MessageDialogPane(text, title, type, onAccept));
|
||||
}
|
||||
|
||||
public static void confirmDialog(String text, String title, Runnable onAccept, Runnable onCancel) {
|
||||
dialog(new MessageDialogPane(text, title, Controllers::closeDialog, onAccept, onCancel));
|
||||
dialog(new MessageDialogPane(text, title, onAccept, onCancel));
|
||||
}
|
||||
|
||||
public static void inputDialog(String text, Consumer<String> onResult) {
|
||||
dialog(new InputDialogPane(text, Controllers::closeDialog, onResult));
|
||||
dialog(new InputDialogPane(text, onResult));
|
||||
}
|
||||
|
||||
public static Region taskDialog(TaskExecutor executor, String title, String subtitle) {
|
||||
@@ -156,6 +157,9 @@ public final class Controllers {
|
||||
return pane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link DialogCloseEvent}
|
||||
*/
|
||||
public static void closeDialog(Region content) {
|
||||
if (stage == null) // shut down
|
||||
return;
|
||||
|
||||
@@ -32,6 +32,7 @@ import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.BoundingBox;
|
||||
import javafx.geometry.Bounds;
|
||||
@@ -43,6 +44,7 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
@@ -52,6 +54,7 @@ import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDnD;
|
||||
import org.jackhuang.hmcl.setting.EnumBackgroundImage;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@@ -59,6 +62,7 @@ import org.jackhuang.hmcl.ui.animation.AnimationProducer;
|
||||
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.StackContainerPane;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogWizardDisplayer;
|
||||
import org.jackhuang.hmcl.ui.wizard.*;
|
||||
@@ -67,6 +71,7 @@ import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@@ -81,6 +86,8 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
private static final SVGGlyph close = Lang.apply(new SVGGlyph(0, "CLOSE", "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z", Color.WHITE),
|
||||
glyph -> { glyph.setPrefSize(12, 12); glyph.setSize(12, 12); });
|
||||
|
||||
private static final String PROPERTY_DIALOG_CLOSE_HANDLER = Decorator.class.getName() + ".dialog.closeListener";
|
||||
|
||||
private final ObjectProperty<Runnable> onCloseButtonAction;
|
||||
private final BooleanProperty customMaximize = new SimpleBooleanProperty(false);
|
||||
|
||||
@@ -89,13 +96,14 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
private final boolean max, min;
|
||||
private final WizardController wizardController = new WizardController(this);
|
||||
private final Queue<Object> cancelQueue = new ConcurrentLinkedQueue<>();
|
||||
private final JFXDialog dialog;
|
||||
|
||||
private double xOffset, yOffset, newX, newY, initX, initY;
|
||||
private boolean allowMove, isDragging, maximized;
|
||||
private BoundingBox originalBox, maximizedBox;
|
||||
private final TransitionHandler animationHandler;
|
||||
private final StackContainerPane dialogPane = new StackContainerPane();
|
||||
|
||||
private JFXDialog dialog;
|
||||
private StackContainerPane dialogPane;
|
||||
|
||||
@FXML
|
||||
private StackPane contentPlaceHolder;
|
||||
@@ -174,12 +182,6 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
btnMax.fire();
|
||||
});
|
||||
|
||||
dialog = new JFXDialog();
|
||||
dialog.setOverlayClose(false);
|
||||
drawerWrapper.getChildren().add(0, dialog);
|
||||
dialog.setDialogContainer(drawerWrapper);
|
||||
dialog.setContent(dialogPane);
|
||||
|
||||
welcomeView.setCursor(Cursor.HAND);
|
||||
welcomeView.setOnMouseClicked(e -> {
|
||||
Timeline nowAnimation = new Timeline();
|
||||
@@ -209,6 +211,8 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
animationHandler = new TransitionHandler(contentPlaceHolder);
|
||||
|
||||
loadBackground();
|
||||
|
||||
setupAuthlibInjectorDnD();
|
||||
}
|
||||
|
||||
private void loadBackground() {
|
||||
@@ -563,20 +567,37 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
public void showDialog(Node node) {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
if (dialogPane.isEmpty())
|
||||
if (dialog == null) {
|
||||
dialog = new JFXDialog();
|
||||
dialogPane = new StackContainerPane();
|
||||
|
||||
dialog.setContent(dialogPane);
|
||||
dialog.setDialogContainer(drawerWrapper);
|
||||
dialog.setOverlayClose(false);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
dialogPane.push(node);
|
||||
|
||||
EventHandler<DialogCloseEvent> handler = event -> closeDialog(node);
|
||||
node.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler);
|
||||
node.addEventHandler(DialogCloseEvent.CLOSE, handler);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void closeDialog(Node node) {
|
||||
FXUtils.checkFxUserThread();
|
||||
|
||||
Optional.ofNullable(node.getProperties().get(PROPERTY_DIALOG_CLOSE_HANDLER))
|
||||
.ifPresent(handler -> node.removeEventHandler(DialogCloseEvent.CLOSE, (EventHandler<DialogCloseEvent>) handler));
|
||||
|
||||
dialogPane.pop(node);
|
||||
if (dialogPane.getChildren().isEmpty())
|
||||
Platform.runLater(() -> {
|
||||
if (dialogPane.getChildren().isEmpty())
|
||||
dialog.close();
|
||||
});
|
||||
|
||||
if (dialogPane.getChildren().isEmpty()) {
|
||||
dialog.close();
|
||||
dialog = null;
|
||||
dialogPane = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void startWizard(WizardProvider wizardProvider) {
|
||||
@@ -670,4 +691,10 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
|
||||
public AdvancedListBox getLeftPane() {
|
||||
return leftPane;
|
||||
}
|
||||
|
||||
private void setupAuthlibInjectorDnD() {
|
||||
addEventFilter(DragEvent.DRAG_OVER, AuthlibInjectorDnD.dragOverHandler());
|
||||
addEventFilter(DragEvent.DRAG_DROPPED, AuthlibInjectorDnD.dragDroppedHandler(
|
||||
url -> Controllers.dialog(new AddAuthlibInjectorServerPane(url))));
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public final class DialogController {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<AuthInfo> res = new AtomicReference<>(null);
|
||||
JFXUtilities.runInFX(() -> {
|
||||
AccountLoginPane pane = new AccountLoginPane(account, Controllers::closeDialog, it -> {
|
||||
AccountLoginPane pane = new AccountLoginPane(account, it -> {
|
||||
res.set(it);
|
||||
latch.countDown();
|
||||
}, latch::countDown);
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.construct.AdvancedListBox;
|
||||
import org.jackhuang.hmcl.ui.construct.ClassTitle;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.IconedItem;
|
||||
import org.jackhuang.hmcl.ui.construct.RipplerContainer;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
@@ -100,7 +101,7 @@ public final class LeftPaneController {
|
||||
}
|
||||
|
||||
private void addNewAccount() {
|
||||
Controllers.dialog(new AddAccountPane(Controllers::closeDialog));
|
||||
Controllers.dialog(new AddAccountPane());
|
||||
}
|
||||
|
||||
private void onSelectedAccountChanged(Account newAccount) {
|
||||
@@ -228,7 +229,7 @@ public final class LeftPaneController {
|
||||
Modpack modpack = ModpackHelper.readModpackManifest(modpackFile);
|
||||
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
|
||||
.with(Task.of(Schedulers.javafx(), () -> {
|
||||
Controllers.closeDialog(region.get());
|
||||
region.get().fireEvent(new DialogCloseEvent());
|
||||
checkAccount();
|
||||
})).executor();
|
||||
region.set(Controllers.taskDialog(executor, i18n("modpack.installing"), ""));
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.ui.download.DownloadWizardProvider;
|
||||
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
|
||||
@@ -165,17 +166,17 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
AtomicReference<Region> region = new AtomicReference<>();
|
||||
try {
|
||||
TaskExecutor executor = ModpackHelper.getUpdateTask(profile, selectedFile, id, ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(id)))
|
||||
.then(Task.of(Schedulers.javafx(), () -> Controllers.closeDialog(region.get()))).executor();
|
||||
.then(Task.of(Schedulers.javafx(), () -> region.get().fireEvent(new DialogCloseEvent()))).executor();
|
||||
region.set(Controllers.taskDialog(executor, i18n("modpack.update"), ""));
|
||||
executor.start();
|
||||
} catch (UnsupportedModpackException e) {
|
||||
Controllers.closeDialog(region.get());
|
||||
region.get().fireEvent(new DialogCloseEvent());
|
||||
Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
|
||||
} catch (MismatchedModpackTypeException e) {
|
||||
Controllers.closeDialog(region.get());
|
||||
region.get().fireEvent(new DialogCloseEvent());
|
||||
Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
|
||||
} catch (IOException e) {
|
||||
Controllers.closeDialog(region.get());
|
||||
region.get().fireEvent(new DialogCloseEvent());
|
||||
Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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 javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
@@ -116,6 +116,7 @@ public final class VersionSettingsController {
|
||||
gameDirItem.setDisable(profile.getRepository().isModpack(versionId));
|
||||
globalItem.setDisable(profile.getRepository().isModpack(versionId));
|
||||
|
||||
// unbind data fields
|
||||
if (lastVersionSetting != null) {
|
||||
FXUtils.unbindInt(txtWidth, lastVersionSetting.widthProperty());
|
||||
FXUtils.unbindInt(txtHeight, lastVersionSetting.heightProperty());
|
||||
@@ -135,6 +136,12 @@ public final class VersionSettingsController {
|
||||
FXUtils.unbindEnum(cboLauncherVisibility);
|
||||
}
|
||||
|
||||
// unbind data fields
|
||||
globalItem.setToggleSelectedListener(null);
|
||||
javaItem.setToggleSelectedListener(null);
|
||||
gameDirItem.setToggleSelectedListener(null);
|
||||
|
||||
// bind new data fields
|
||||
FXUtils.bindInt(txtWidth, versionSetting.widthProperty());
|
||||
FXUtils.bindInt(txtHeight, versionSetting.heightProperty());
|
||||
FXUtils.bindInt(txtMaxMemory, versionSetting.maxMemoryProperty());
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
package org.jackhuang.hmcl.ui.animation;
|
||||
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface AnimationProducer {
|
||||
void init(AnimationHandler handler);
|
||||
|
||||
List<KeyFrame> animate(AnimationHandler handler);
|
||||
}
|
||||
@@ -20,17 +20,43 @@ package org.jackhuang.hmcl.ui.animation;
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.scene.Node;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public enum ContainerAnimations {
|
||||
NONE(c -> Collections.emptyList()),
|
||||
NONE(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}, c -> Collections.emptyList()),
|
||||
/**
|
||||
* A fade between the old and new view
|
||||
*/
|
||||
FADE(c ->
|
||||
FADE(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getPreviousNode().opacityProperty(), 1.0D, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getCurrentNode().opacityProperty(), 0.0D, Interpolator.EASE_BOTH)),
|
||||
@@ -40,7 +66,12 @@ public enum ContainerAnimations {
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_IN(c ->
|
||||
ZOOM_IN(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getPreviousNode().scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
@@ -52,7 +83,12 @@ public enum ContainerAnimations {
|
||||
/**
|
||||
* A zoom effect
|
||||
*/
|
||||
ZOOM_OUT(c ->
|
||||
ZOOM_OUT(c -> {
|
||||
c.getPreviousNode().setTranslateX(0);
|
||||
c.getPreviousNode().setTranslateY(0);
|
||||
c.getCurrentNode().setTranslateX(0);
|
||||
c.getCurrentNode().setTranslateY(0);
|
||||
}, c ->
|
||||
(Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getPreviousNode().scaleXProperty(), 1, Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().scaleYProperty(), 1, Interpolator.EASE_BOTH),
|
||||
@@ -64,7 +100,14 @@ public enum ContainerAnimations {
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_LEFT(c ->
|
||||
SWIPE_LEFT(c -> {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH)),
|
||||
@@ -75,7 +118,14 @@ public enum ContainerAnimations {
|
||||
/**
|
||||
* A swipe effect
|
||||
*/
|
||||
SWIPE_RIGHT(c ->
|
||||
SWIPE_RIGHT(c -> {
|
||||
c.getPreviousNode().setScaleX(1);
|
||||
c.getPreviousNode().setScaleY(1);
|
||||
c.getPreviousNode().setOpacity(0);
|
||||
c.getCurrentNode().setScaleX(1);
|
||||
c.getCurrentNode().setScaleY(1);
|
||||
c.getCurrentNode().setOpacity(1);
|
||||
}, c ->
|
||||
Arrays.asList(new KeyFrame(Duration.ZERO,
|
||||
new KeyValue(c.getCurrentNode().translateXProperty(), -c.getCurrentRoot().getWidth(), Interpolator.EASE_BOTH),
|
||||
new KeyValue(c.getPreviousNode().translateXProperty(), 0, Interpolator.EASE_BOTH)),
|
||||
@@ -85,8 +135,18 @@ public enum ContainerAnimations {
|
||||
|
||||
private final AnimationProducer animationProducer;
|
||||
|
||||
ContainerAnimations(AnimationProducer animationProducer) {
|
||||
this.animationProducer = animationProducer;
|
||||
ContainerAnimations(Consumer<AnimationHandler> init, Function<AnimationHandler, List<KeyFrame>> animationProducer) {
|
||||
this.animationProducer = new AnimationProducer() {
|
||||
@Override
|
||||
public void init(AnimationHandler handler) {
|
||||
init.accept(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyFrame> animate(AnimationHandler handler) {
|
||||
return animationProducer.apply(handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public AnimationProducer getAnimationProducer() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui.animation;
|
||||
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.layout.StackPane;
|
||||
@@ -78,35 +79,38 @@ public final class TransitionHandler implements AnimationHandler {
|
||||
|
||||
updateContent(newView);
|
||||
|
||||
Timeline nowAnimation = new Timeline();
|
||||
nowAnimation.getKeyFrames().addAll(transition.animate(this));
|
||||
nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
|
||||
previousNode.setMouseTransparent((Boolean) previousNode.getProperties().get(MOUSE_TRANSPARENT));
|
||||
view.getChildren().remove(previousNode);
|
||||
}));
|
||||
nowAnimation.play();
|
||||
animation = nowAnimation;
|
||||
transition.init(this);
|
||||
|
||||
// runLater or "init" will not work
|
||||
Platform.runLater(() -> {
|
||||
Timeline nowAnimation = new Timeline();
|
||||
nowAnimation.getKeyFrames().addAll(transition.animate(this));
|
||||
nowAnimation.getKeyFrames().add(new KeyFrame(duration, e -> {
|
||||
view.setMouseTransparent(false);
|
||||
view.getChildren().remove(previousNode);
|
||||
}));
|
||||
nowAnimation.play();
|
||||
animation = nowAnimation;
|
||||
});
|
||||
}
|
||||
|
||||
private void updateContent(Node newView) {
|
||||
if (view.getWidth() > 0 && view.getHeight() > 0) {
|
||||
previousNode = currentNode;
|
||||
if (previousNode == null)
|
||||
previousNode = NULL;
|
||||
previousNode = EMPTY_PANE;
|
||||
} else
|
||||
previousNode = NULL;
|
||||
previousNode = EMPTY_PANE;
|
||||
|
||||
if (previousNode == newView)
|
||||
previousNode = NULL;
|
||||
previousNode = EMPTY_PANE;
|
||||
|
||||
previousNode.getProperties().put(MOUSE_TRANSPARENT, previousNode.isMouseTransparent());
|
||||
previousNode.setMouseTransparent(true);
|
||||
view.setMouseTransparent(true);
|
||||
|
||||
currentNode = newView;
|
||||
|
||||
view.getChildren().setAll(previousNode, currentNode);
|
||||
}
|
||||
|
||||
private static final String MOUSE_TRANSPARENT = "TransitionHandler.MOUSE_TRANSPARENT";
|
||||
private static final StackPane NULL = new StackPane();
|
||||
private final StackPane EMPTY_PANE = new StackPane();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.construct;
|
||||
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventTarget;
|
||||
import javafx.event.EventType;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
/**
|
||||
* Indicates a close operation on the dialog.
|
||||
*
|
||||
* @author yushijinhun
|
||||
* @see Controllers#dialog(Region)
|
||||
*/
|
||||
public class DialogCloseEvent extends Event {
|
||||
|
||||
public static final EventType<DialogCloseEvent> CLOSE = new EventType<>("CLOSE");
|
||||
|
||||
public DialogCloseEvent() {
|
||||
super(CLOSE);
|
||||
}
|
||||
|
||||
public DialogCloseEvent(Object source, EventTarget target) {
|
||||
super(source, target, CLOSE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
@@ -38,13 +37,13 @@ public class InputDialogPane extends StackPane {
|
||||
@FXML
|
||||
private Label content;
|
||||
|
||||
public InputDialogPane(String text, Consumer<Region> closeConsumer, Consumer<String> onResult) {
|
||||
public InputDialogPane(String text, Consumer<String> onResult) {
|
||||
FXUtils.loadFXML(this, "/assets/fxml/input-dialog.fxml");
|
||||
content.setText(text);
|
||||
cancelButton.setOnMouseClicked(e -> closeConsumer.accept(this));
|
||||
cancelButton.setOnMouseClicked(e -> fireEvent(new DialogCloseEvent()));
|
||||
acceptButton.setOnMouseClicked(e -> {
|
||||
onResult.accept(textField.getText());
|
||||
closeConsumer.accept(this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
@@ -30,10 +29,8 @@ import org.jackhuang.hmcl.ui.SVG;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class MessageDialogPane extends StackPane {
|
||||
private boolean closingDialog = true;
|
||||
|
||||
@FXML
|
||||
private JFXButton acceptButton;
|
||||
@@ -48,7 +45,7 @@ public final class MessageDialogPane extends StackPane {
|
||||
@FXML
|
||||
private HBox actions;
|
||||
|
||||
public MessageDialogPane(String text, String title, Consumer<Region> closeConsumer, int type, Runnable onAccept) {
|
||||
public MessageDialogPane(String text, String title, int type, Runnable onAccept) {
|
||||
FXUtils.loadFXML(this, "/assets/fxml/message-dialog.fxml");
|
||||
|
||||
if (title != null)
|
||||
@@ -56,7 +53,7 @@ public final class MessageDialogPane extends StackPane {
|
||||
|
||||
content.setText(text);
|
||||
acceptButton.setOnMouseClicked(e -> {
|
||||
closeConsumer.accept(MessageDialogPane.this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
Optional.ofNullable(onAccept).ifPresent(Runnable::run);
|
||||
});
|
||||
|
||||
@@ -83,12 +80,12 @@ public final class MessageDialogPane extends StackPane {
|
||||
}
|
||||
}
|
||||
|
||||
public MessageDialogPane(String text, String title, Consumer<Region> closeConsumer, Runnable onAccept, Runnable onCancel) {
|
||||
this(text, title, closeConsumer, MessageBox.QUESTION_MESSAGE, onAccept);
|
||||
public MessageDialogPane(String text, String title, Runnable onAccept, Runnable onCancel) {
|
||||
this(text, title, MessageBox.QUESTION_MESSAGE, onAccept);
|
||||
|
||||
cancelButton.setVisible(true);
|
||||
cancelButton.setOnMouseClicked(e -> {
|
||||
closeConsumer.accept(MessageDialogPane.this);
|
||||
fireEvent(new DialogCloseEvent());
|
||||
Optional.ofNullable(onCancel).ifPresent(Runnable::run);
|
||||
});
|
||||
|
||||
@@ -97,8 +94,4 @@ public final class MessageDialogPane extends StackPane {
|
||||
|
||||
actions.getChildren().add(cancelButton);
|
||||
}
|
||||
|
||||
public void disableClosingDialog() {
|
||||
closingDialog = false;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -35,7 +35,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
||||
@Override
|
||||
default void handleTask(Map<String, Object> settings, Task task) {
|
||||
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> {
|
||||
Controllers.closeDialog(it);
|
||||
it.fireEvent(new DialogCloseEvent());
|
||||
Controllers.navigate(null);
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
|
||||
@Override
|
||||
public void onStop(boolean success, TaskExecutor executor) {
|
||||
JFXUtilities.runInFX(() -> {
|
||||
Controllers.closeDialog(pane);
|
||||
pane.fireEvent(new DialogCloseEvent());
|
||||
if (success) {
|
||||
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
|
||||
Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> Controllers.navigate(null));
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackManager;
|
||||
import org.jackhuang.hmcl.mod.Modpack;
|
||||
import org.jackhuang.hmcl.setting.Config;
|
||||
import org.jackhuang.hmcl.setting.ConfigHolder;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
@@ -93,7 +94,7 @@ public final class ExportWizardProvider implements WizardProvider {
|
||||
config.logLines.set(100);
|
||||
config.configurations.clear();
|
||||
|
||||
zip.putTextFile(Settings.GSON.toJson(config), Settings.SETTINGS_FILE_NAME);
|
||||
zip.putTextFile(config.toJson(), ConfigHolder.CONFIG_FILENAME);
|
||||
zip.putFile(tempModpack, "modpack.zip");
|
||||
|
||||
File bg = new File("bg").getAbsoluteFile();
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
@@ -120,7 +121,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
.directory(new File("").getAbsoluteFile()).start();
|
||||
System.exit(0);
|
||||
}
|
||||
JFXUtilities.runInFX(() -> Controllers.closeDialog(region.get()));
|
||||
JFXUtilities.runInFX(() -> region.get().fireEvent(new DialogCloseEvent()));
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
|
||||
}
|
||||
@@ -138,7 +139,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
.directory(new File("").getAbsoluteFile()).start();
|
||||
System.exit(0);
|
||||
}
|
||||
JFXUtilities.runInFX(() -> Controllers.closeDialog(region.get()));
|
||||
JFXUtilities.runInFX(() -> region.get().fireEvent(new DialogCloseEvent()));
|
||||
} catch (IOException ex) {
|
||||
Logging.LOG.log(Level.SEVERE, "Failed to create upgrader", ex);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
import org.jackhuang.hmcl.util.VersionNumber;
|
||||
import java.io.File;
|
||||
@@ -72,7 +73,7 @@ public class NewFileUpgrader extends IUpgrader {
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
JFXUtilities.runInFX(() -> Controllers.closeDialog(region.get()));
|
||||
JFXUtilities.runInFX(() -> region.get().fireEvent(new DialogCloseEvent()));
|
||||
}
|
||||
|
||||
private static String getRealPath() {
|
||||
|
||||
@@ -51,8 +51,6 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
|
||||
put("netscape.javascript.JSException", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.IncompatibleClassChangeError", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.ClassFormatError", i18n("crash.NoClassDefFound"));
|
||||
put("java.lang.OutOfMemoryError", "FUCKING MEMORY LIMIT!");
|
||||
put("Trampoline", i18n("launcher.update_java"));
|
||||
put("com.sun.javafx.css.StyleManager.findMatchingStyles", i18n("launcher.update_java"));
|
||||
put("NoSuchAlgorithmException", "Has your operating system been installed completely or is a ghost system?");
|
||||
}
|
||||
|
||||
@@ -569,7 +569,6 @@
|
||||
.list-cell:selected, .jfx-list-cell:selected {
|
||||
-fx-background-insets: 0.0;
|
||||
-fx-text-fill: BLACK;
|
||||
-fx-background-color: black;
|
||||
}
|
||||
|
||||
.jfx-list-cell:filled:hover,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<Label fx:id="lblInjectorServer" 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"/>
|
||||
<JFXComboBox fx:id="cboServers" promptText="%general.prompt.empty" maxHeight="25" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
|
||||
<Hyperlink fx:id="linkManageInjectorServers" text="%account.injector.manage"
|
||||
onMouseClicked="#onManageInjecterServers" GridPane.columnIndex="2" GridPane.rowIndex="1"/>
|
||||
@@ -37,7 +37,7 @@
|
||||
<Label text="%account.username" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
|
||||
|
||||
<JFXTextField fx:id="txtUsername" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.columnSpan="2"
|
||||
FXUtils.validateWhileTextChanged="true">
|
||||
FXUtils.validateWhileTextChanged="true" onAction="#onCreationAccept">
|
||||
<validators>
|
||||
<RequiredFieldValidator message="%input.not_empty">
|
||||
</RequiredFieldValidator>
|
||||
@@ -47,7 +47,7 @@
|
||||
<Label fx:id="lblPassword" text="%account.password" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
|
||||
|
||||
<JFXPasswordField fx:id="txtPassword" GridPane.columnIndex="1" GridPane.rowIndex="3"
|
||||
GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
|
||||
GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true" onAction="#onCreationAccept">
|
||||
<validators>
|
||||
<RequiredFieldValidator message="%input.not_empty">
|
||||
</RequiredFieldValidator>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
|
||||
<?import com.jfoenix.controls.JFXProgressBar?>
|
||||
@@ -20,6 +21,9 @@
|
||||
</left>
|
||||
<right>
|
||||
<VBox>
|
||||
<BorderPane.margin>
|
||||
<Insets left="15.0" />
|
||||
</BorderPane.margin>
|
||||
<Label fx:id="lblType" BorderPane.alignment="CENTER_LEFT"/>
|
||||
</VBox>
|
||||
</right>
|
||||
@@ -33,6 +37,9 @@
|
||||
</left>
|
||||
<right>
|
||||
<VBox>
|
||||
<BorderPane.margin>
|
||||
<Insets left="15.0" />
|
||||
</BorderPane.margin>
|
||||
<Label fx:id="lblCharacter" BorderPane.alignment="CENTER_LEFT"/>
|
||||
</VBox>
|
||||
</right>
|
||||
@@ -46,6 +53,9 @@
|
||||
</left>
|
||||
<right>
|
||||
<VBox>
|
||||
<BorderPane.margin>
|
||||
<Insets left="15.0" />
|
||||
</BorderPane.margin>
|
||||
<Label fx:id="lblEmail" BorderPane.alignment="CENTER_LEFT"/>
|
||||
</VBox>
|
||||
</right>
|
||||
@@ -59,24 +69,26 @@
|
||||
</left>
|
||||
<right>
|
||||
<VBox>
|
||||
<BorderPane.margin>
|
||||
<Insets left="15.0" />
|
||||
</BorderPane.margin>
|
||||
<Label fx:id="lblServer" BorderPane.alignment="CENTER_LEFT"/>
|
||||
</VBox>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</ComponentList>
|
||||
|
||||
<BorderPane pickOnBounds="false" style="-fx-padding: 4;">
|
||||
<left>
|
||||
<JFXButton BorderPane.alignment="BOTTOM_LEFT" fx:id="btnDelete" onMouseClicked="#onDelete"
|
||||
styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30"
|
||||
prefWidth="30" prefHeight="30"/>
|
||||
</left>
|
||||
<right>
|
||||
<JFXButton BorderPane.alignment="BOTTOM_RIGHT" fx:id="btnRefresh" onMouseClicked="#onRefresh"
|
||||
styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30"
|
||||
prefWidth="30" prefHeight="30"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</VBox>
|
||||
<BorderPane pickOnBounds="false" style="-fx-padding: 4;">
|
||||
<left>
|
||||
<JFXButton BorderPane.alignment="BOTTOM_LEFT" fx:id="btnDelete" onMouseClicked="#onDelete"
|
||||
styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30"
|
||||
prefWidth="30" prefHeight="30"/>
|
||||
</left>
|
||||
<right>
|
||||
<JFXButton BorderPane.alignment="BOTTOM_RIGHT" fx:id="btnRefresh" onMouseClicked="#onRefresh"
|
||||
styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30"
|
||||
prefWidth="30" prefHeight="30"/>
|
||||
</right>
|
||||
</BorderPane>
|
||||
<JFXProgressBar fx:id="progressBar" StackPane.alignment="TOP_CENTER" visible="false" />
|
||||
</fx:root>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.URLValidator?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<StackPane fx:id="addServerContainer">
|
||||
<JFXDialogLayout fx:id="addServerPane">
|
||||
<heading>
|
||||
<Label text="%account.injector.add" />
|
||||
</heading>
|
||||
<body>
|
||||
<JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url" onAction="#onAddNext">
|
||||
<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.cancel" styleClass="dialog-cancel" />
|
||||
<SpinnerPane fx:id="nextPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
|
||||
</SpinnerPane>
|
||||
</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_url" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
||||
<Label text="%account.injector.server_name" GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
||||
<Label fx:id="lblServerUrl" GridPane.columnIndex="1" GridPane.rowIndex="0" />
|
||||
<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" />
|
||||
</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>
|
||||
</fx:root>
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.URLValidator?>
|
||||
<?import java.lang.String?>
|
||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||
<?import com.jfoenix.controls.*?>
|
||||
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
@@ -23,61 +21,4 @@
|
||||
</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="txtServerUrl" promptText="%account.injector.server_url">
|
||||
<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.cancel" styleClass="dialog-cancel" />
|
||||
<SpinnerPane fx:id="nextPane" styleClass="small-spinner-pane">
|
||||
<JFXButton fx:id="btnAddNext" onMouseClicked="#onAddNext" text="%wizard.next" styleClass="dialog-accept" />
|
||||
</SpinnerPane>
|
||||
</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_url" GridPane.columnIndex="0" GridPane.rowIndex="0" />
|
||||
<Label text="%account.injector.server_name" GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
||||
|
||||
<Label fx:id="lblServerUrl" GridPane.columnIndex="1" GridPane.rowIndex="0" />
|
||||
<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" />
|
||||
</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>
|
||||
</StackPane>
|
||||
</JFXDialog>
|
||||
</fx:root>
|
||||
@@ -41,8 +41,9 @@ account.failed.invalid_token=Please log out and re-input your password to login.
|
||||
account.failed.no_character=No character in this account.
|
||||
account.failed.no_selected_server=No authentication server is selected.
|
||||
account.failed.connect_injector_server=Cannot connect to the authentication server. Check your network and ensure the URL is correct.
|
||||
account.injector.add=Add authentication server
|
||||
account.injector.add=Add an authentication server
|
||||
account.injector.manage=Manage authentication servers
|
||||
account.injector.manage.title=Authentication servers
|
||||
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_url=Server URL
|
||||
@@ -52,7 +53,7 @@ account.methods.authlib_injector=authlib-injector
|
||||
account.methods.no_method=No login method
|
||||
account.methods.offline=Offline
|
||||
account.methods.yggdrasil=Mojang
|
||||
account.missing=Missing account
|
||||
account.missing=None
|
||||
account.password=Password
|
||||
account.username=Name
|
||||
|
||||
@@ -127,7 +128,7 @@ install.select=Select an operation
|
||||
install.success=Installed successfully
|
||||
|
||||
lang=English
|
||||
lang.default=Belong to OS language.
|
||||
lang.default=Use system language
|
||||
|
||||
launch.advice.different_platform=Your OS is 64-Bit but your Java is 32-Bit. The 64-Bit Java is recommended.
|
||||
launch.advice.java8_1_13=Minecraft 1.13 and later versions can only run on Java 8 or later.
|
||||
@@ -306,8 +307,8 @@ settings.type.special=Specialized version settings(will not affect other version
|
||||
|
||||
update=Update
|
||||
update.failed=Failed to check for updates.
|
||||
update.found=Found Update!
|
||||
update.newest_version=Newest version: %s
|
||||
update.found=Update Available!
|
||||
update.newest_version=Latest version: %s
|
||||
update.latest=This is latest Version.
|
||||
update.no_browser=Cannot open any browser. The link has been copied to the clipboard. Paste it to a browser address bar to update.
|
||||
update.tooltip=Update
|
||||
|
||||
@@ -43,6 +43,7 @@ account.failed.no_selected_server=未選擇認證服務器
|
||||
account.failed.connect_injector_server=無法連接認證服務器,可能是網絡故障或 URL 輸入錯誤
|
||||
account.injector.add=添加認證服務器
|
||||
account.injector.manage=管理認證服務器
|
||||
account.injector.manage.title=認證服務器
|
||||
account.injector.http=警告:此服務器使用不安全的 HTTP 協議,您的密碼在登錄時會被明文傳輸。
|
||||
account.injector.server=認證服務器
|
||||
account.injector.server_url=服務器地址
|
||||
|
||||
@@ -43,6 +43,7 @@ account.failed.no_selected_server=未选择认证服务器
|
||||
account.failed.connect_injector_server=无法连接认证服务器,可能是网络故障或 URL 输入错误
|
||||
account.injector.add=添加认证服务器
|
||||
account.injector.manage=管理认证服务器
|
||||
account.injector.manage.title=认证服务器
|
||||
account.injector.http=警告:此服务器使用不安全的 HTTP 协议,您的密码在登录时会被明文传输。
|
||||
account.injector.server=认证服务器
|
||||
account.injector.server_url=服务器地址
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.auth.authlibinjector;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.Dragboard;
|
||||
import javafx.scene.input.TransferMode;
|
||||
|
||||
/**
|
||||
* @author yushijinhun
|
||||
* @see https://github.com/yushijinhun/authlib-injector/wiki/%E5%90%AF%E5%8A%A8%E5%99%A8%E6%8A%80%E6%9C%AF%E8%A7%84%E8%8C%83#dnd-%E6%96%B9%E5%BC%8F%E6%B7%BB%E5%8A%A0-yggdrasil-%E6%9C%8D%E5%8A%A1%E7%AB%AF
|
||||
*/
|
||||
public final class AuthlibInjectorDnD {
|
||||
|
||||
private static final String SCHEME = "authlib-injector";
|
||||
private static final String PATH_YGGDRASIL_SERVER = "yggdrasil-server";
|
||||
|
||||
private AuthlibInjectorDnD() {}
|
||||
|
||||
public static Optional<String> parseUrlFromDragboard(Dragboard dragboard) {
|
||||
String uri = dragboard.getString();
|
||||
if (uri == null) return Optional.empty();
|
||||
|
||||
String[] uriElements = uri.split(":");
|
||||
if (uriElements.length == 3 && SCHEME.equals(uriElements[0]) && PATH_YGGDRASIL_SERVER.equals(uriElements[1])) {
|
||||
try {
|
||||
return Optional.of(URLDecoder.decode(uriElements[2], "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static EventHandler<DragEvent> dragOverHandler() {
|
||||
return event -> parseUrlFromDragboard(event.getDragboard()).ifPresent(url -> {
|
||||
event.acceptTransferModes(TransferMode.COPY);
|
||||
event.consume();
|
||||
});
|
||||
}
|
||||
|
||||
public static EventHandler<DragEvent> dragDroppedHandler(Consumer<String> onUrlTransfered) {
|
||||
return event -> parseUrlFromDragboard(event.getDragboard()).ifPresent(url -> {
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
onUrlTransfered.accept(url);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -26,8 +26,8 @@ import org.jackhuang.hmcl.util.JsonUtils;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
public class AuthlibInjectorServer {
|
||||
|
||||
@@ -36,7 +36,7 @@ public class AuthlibInjectorServer {
|
||||
JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(NetworkUtils.toURL(url)), JsonObject.class);
|
||||
String name = extractServerName(response).orElse(url);
|
||||
return new AuthlibInjectorServer(url, name);
|
||||
} catch (JsonSyntaxException e) {
|
||||
} catch (JsonParseException e) {
|
||||
throw new IOException("Malformed response", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ public class BMCLAPIDownloadProvider implements DownloadProvider {
|
||||
.replace("https://libraries.minecraft.net", "https://bmclapi2.bangbang93.com/libraries")
|
||||
.replaceFirst("https?://files\\.minecraftforge\\.net/maven", "https://bmclapi2.bangbang93.com/maven")
|
||||
.replace("http://dl.liteloader.com/versions/versions.json", "https://bmclapi2.bangbang93.com/maven/com/mumfrey/liteloader/versions.json")
|
||||
.replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven");
|
||||
.replace("http://dl.liteloader.com/versions", "https://bmclapi2.bangbang93.com/maven")
|
||||
.replace("https://authlib-injector.yushi.moe", "https://bmclapi2.bangbang93.com/mirrors/authlib-injector");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,7 +49,8 @@ public final class LibraryDownloadTask extends Task {
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
|
||||
task = new FileDownloadTask(NetworkUtils.toURL(url),
|
||||
file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", library.getDownload().getSha1()));
|
||||
file, dependencyManager.getProxy(),
|
||||
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -56,8 +56,8 @@ public final class OptiFineBMCLVersionList extends VersionList<Void> {
|
||||
List<OptiFineVersion> root = Constants.GSON.fromJson(task.getResult(), new TypeToken<List<OptiFineVersion>>() {
|
||||
}.getType());
|
||||
for (OptiFineVersion element : root) {
|
||||
String version = element.getType();
|
||||
if (version == null)
|
||||
String version = element.getType() + "_" + element.getPatch();
|
||||
if (element.getType() == null || "pre".equals(element.getPatch()))
|
||||
continue;
|
||||
String mirror = "http://bmclapi2.bangbang93.com/optifine/" + element.getGameVersion() + "/" + element.getType() + "/" + element.getPatch();
|
||||
if (!duplicates.add(mirror))
|
||||
|
||||
@@ -58,12 +58,17 @@ public final class MultiMCInstanceConfiguration {
|
||||
private final boolean overrideCommands; // OverrideCommands
|
||||
private final boolean overrideWindow; // OverrideWindow
|
||||
|
||||
public MultiMCInstanceConfiguration(String defaultName, InputStream contentStream) throws IOException {
|
||||
private final MultiMCManifest mmcPack;
|
||||
|
||||
private MultiMCInstanceConfiguration(String defaultName, InputStream contentStream, MultiMCManifest mmcPack) throws IOException {
|
||||
Properties p = new Properties();
|
||||
p.load(contentStream);
|
||||
|
||||
this.mmcPack = mmcPack;
|
||||
|
||||
autoCloseConsole = Boolean.parseBoolean(p.getProperty("AutoCloseConsole"));
|
||||
gameVersion = p.getProperty("IntendedVersion");
|
||||
gameVersion = mmcPack != null ? mmcPack.getComponents().stream().filter(e -> "net.minecraft".equals(e.getUid())).findAny()
|
||||
.orElseThrow(() -> new IOException("Malformed mmc-pack.json")).getVersion() : p.getProperty("IntendedVersion");
|
||||
javaPath = p.getProperty("JavaPath");
|
||||
jvmArgs = p.getProperty("JvmArgs");
|
||||
fullscreen = Boolean.parseBoolean(p.getProperty("LaunchMaximized"));
|
||||
@@ -253,6 +258,10 @@ public final class MultiMCInstanceConfiguration {
|
||||
return overrideWindow;
|
||||
}
|
||||
|
||||
public MultiMCManifest getMmcPack() {
|
||||
return mmcPack;
|
||||
}
|
||||
|
||||
public static Modpack readMultiMCModpackManifest(File f) throws IOException {
|
||||
try (ZipFile zipFile = new ZipFile(f)) {
|
||||
ZipArchiveEntry firstEntry = zipFile.getEntries().nextElement();
|
||||
@@ -260,7 +269,8 @@ public final class MultiMCInstanceConfiguration {
|
||||
ZipArchiveEntry entry = zipFile.getEntry(name + "/instance.cfg");
|
||||
if (entry == null)
|
||||
throw new IOException("`instance.cfg` not found, " + f + " is not a valid MultiMC modpack.");
|
||||
MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, zipFile.getInputStream(entry));
|
||||
MultiMCManifest manifest = MultiMCManifest.readMultiMCModpackManifest(f);
|
||||
MultiMCInstanceConfiguration cfg = new MultiMCInstanceConfiguration(name, zipFile.getInputStream(entry), manifest);
|
||||
return new Modpack(cfg.getName(), "", "", cfg.getGameVersion(), cfg.getNotes(), cfg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <[email protected]>
|
||||
*
|
||||
* 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.mod;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Immutable
|
||||
public final class MultiMCManifest {
|
||||
|
||||
@SerializedName("formatVersion")
|
||||
private final int formatVersion;
|
||||
|
||||
@SerializedName("components")
|
||||
private final List<MultiMCManifestComponent> components;
|
||||
|
||||
public MultiMCManifest(int formatVersion, List<MultiMCManifestComponent> components) {
|
||||
this.formatVersion = formatVersion;
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
public int getFormatVersion() {
|
||||
return formatVersion;
|
||||
}
|
||||
|
||||
public List<MultiMCManifestComponent> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public static MultiMCManifest readMultiMCModpackManifest(File f) throws IOException {
|
||||
try (ZipFile zipFile = new ZipFile(f)) {
|
||||
ZipArchiveEntry firstEntry = zipFile.getEntries().nextElement();
|
||||
String name = StringUtils.substringBefore(firstEntry.getName(), '/');
|
||||
ZipArchiveEntry entry = zipFile.getEntry(name + "/mmc-pack.json");
|
||||
if (entry == null)
|
||||
return null;
|
||||
String json = IOUtils.readFullyAsString(zipFile.getInputStream(entry));
|
||||
MultiMCManifest manifest = JsonUtils.fromNonNullJson(json, MultiMCManifest.class);
|
||||
|
||||
if (manifest != null && manifest.getComponents() == null)
|
||||
throw new IOException("mmc-pack.json malformed.");
|
||||
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MultiMCManifestCachedRequires {
|
||||
@SerializedName("equals")
|
||||
private final String equalsVersion;
|
||||
|
||||
@SerializedName("uid")
|
||||
private final String uid;
|
||||
|
||||
@SerializedName("suggests")
|
||||
private final String suggests;
|
||||
|
||||
|
||||
|
||||
public MultiMCManifestCachedRequires(String equalsVersion, String uid, String suggests) {
|
||||
this.equalsVersion = equalsVersion;
|
||||
this.uid = uid;
|
||||
this.suggests = suggests;
|
||||
}
|
||||
|
||||
public String getEqualsVersion() {
|
||||
return equalsVersion;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public String getSuggests() {
|
||||
return suggests;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MultiMCManifestComponent {
|
||||
@SerializedName("cachedName")
|
||||
private final String cachedName;
|
||||
|
||||
@SerializedName("cachedRequires")
|
||||
private final List<MultiMCManifestCachedRequires> cachedRequires;
|
||||
|
||||
@SerializedName("cachedVersion")
|
||||
private final String cachedVersion;
|
||||
|
||||
@SerializedName("important")
|
||||
private final boolean important;
|
||||
|
||||
@SerializedName("uid")
|
||||
private final String uid;
|
||||
|
||||
@SerializedName("version")
|
||||
private final String version;
|
||||
|
||||
public MultiMCManifestComponent(String cachedName, List<MultiMCManifestCachedRequires> cachedRequires, String cachedVersion, boolean important, String uid, String version) {
|
||||
this.cachedName = cachedName;
|
||||
this.cachedRequires = cachedRequires;
|
||||
this.cachedVersion = cachedVersion;
|
||||
this.important = important;
|
||||
this.uid = uid;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getCachedName() {
|
||||
return cachedName;
|
||||
}
|
||||
|
||||
public List<MultiMCManifestCachedRequires> getCachedRequires() {
|
||||
return cachedRequires;
|
||||
}
|
||||
|
||||
public String getCachedVersion() {
|
||||
return cachedVersion;
|
||||
}
|
||||
|
||||
public boolean isImportant() {
|
||||
return important;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.google.gson.reflect.TypeToken;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.download.GameBuilder;
|
||||
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
|
||||
import org.jackhuang.hmcl.game.Arguments;
|
||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||
@@ -37,6 +38,7 @@ import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -61,7 +63,24 @@ public final class MultiMCModpackInstallTask extends Task {
|
||||
File json = repository.getModpackConfiguration(name);
|
||||
if (repository.hasVersion(name) && !json.exists())
|
||||
throw new IllegalArgumentException("Version " + name + " already exists.");
|
||||
dependents.add(dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion()).buildAsync());
|
||||
|
||||
GameBuilder builder = dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion());
|
||||
|
||||
if (manifest.getMmcPack() != null) {
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> forge = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("net.minecraftforge")).findAny();
|
||||
forge.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("forge", c.getVersion());
|
||||
});
|
||||
|
||||
Optional<MultiMCManifest.MultiMCManifestComponent> liteLoader = manifest.getMmcPack().getComponents().stream().filter(e -> e.getUid().equals("com.mumfrey.liteloader")).findAny();
|
||||
liteLoader.ifPresent(c -> {
|
||||
if (c.getVersion() != null)
|
||||
builder.version("liteloader", c.getVersion());
|
||||
});
|
||||
}
|
||||
|
||||
dependents.add(builder.buildAsync());
|
||||
onDone().register(event -> {
|
||||
if (event.isFailed())
|
||||
repository.removeVersionFromDisk(name);
|
||||
|
||||
Reference in new issue
Block a user