Merge pull request #394 from yushijinhun/refactor-settings

重构 Settings
This commit is contained in:
huanghongxun
2018-07-18 20:17:12 +08:00
committed by GitHub
47 changed files with 813 additions and 549 deletions

View File

@@ -33,7 +33,6 @@ import org.jackhuang.hmcl.ui.DialogController;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.io.File;
import java.net.Proxy;
import java.util.*;
public final class AccountHelper {
@@ -43,31 +42,19 @@ public final class AccountHelper {
public static final File SKIN_DIR = new File(Launcher.HMCL_DIRECTORY, "skins");
public static void loadSkins() {
loadSkins(Proxy.NO_PROXY);
}
public static void loadSkins(Proxy proxy) {
for (Account account : Settings.INSTANCE.getAccounts()) {
if (account instanceof YggdrasilAccount) {
new SkinLoadTask((YggdrasilAccount) account, proxy, false).start();
new SkinLoadTask((YggdrasilAccount) account, false).start();
}
}
}
public static Task loadSkinAsync(YggdrasilAccount account) {
return loadSkinAsync(account, Settings.INSTANCE.getProxy());
}
public static Task loadSkinAsync(YggdrasilAccount account, Proxy proxy) {
return new SkinLoadTask(account, proxy, false);
return new SkinLoadTask(account, false);
}
public static Task refreshSkinAsync(YggdrasilAccount account) {
return refreshSkinAsync(account, Settings.INSTANCE.getProxy());
}
public static Task refreshSkinAsync(YggdrasilAccount account, Proxy proxy) {
return new SkinLoadTask(account, proxy, true);
return new SkinLoadTask(account, true);
}
private static File getSkinFile(UUID uuid) {
@@ -94,9 +81,9 @@ public final class AccountHelper {
return getDefaultSkin(uuid, scaleRatio);
}
public static Image getSkinImmediately(YggdrasilAccount account, GameProfile profile, double scaleRatio, Proxy proxy) throws Exception {
public static Image getSkinImmediately(YggdrasilAccount account, GameProfile profile, double scaleRatio) throws Exception {
File file = getSkinFile(profile.getId());
downloadSkin(account, profile, true, proxy);
downloadSkin(account, profile, true);
if (!file.exists())
return getDefaultSkin(profile.getId(), scaleRatio);
@@ -111,17 +98,15 @@ public final class AccountHelper {
private static class SkinLoadTask extends Task {
private final YggdrasilAccount account;
private final Proxy proxy;
private final boolean refresh;
private final List<Task> dependencies = new LinkedList<>();
public SkinLoadTask(YggdrasilAccount account, Proxy proxy) {
this(account, proxy, false);
public SkinLoadTask(YggdrasilAccount account) {
this(account, false);
}
public SkinLoadTask(YggdrasilAccount account, Proxy proxy, boolean refresh) {
public SkinLoadTask(YggdrasilAccount account, boolean refresh) {
this.account = account;
this.proxy = proxy;
this.refresh = refresh;
}
@@ -140,11 +125,11 @@ public final class AccountHelper {
if (!account.isLoggedIn() && (account.getCharacter() == null || refresh))
DialogController.logIn(account);
downloadSkin(account, refresh, proxy);
downloadSkin(account, refresh);
}
}
private static void downloadSkin(YggdrasilAccount account, GameProfile profile, boolean refresh, Proxy proxy) throws Exception {
private static void downloadSkin(YggdrasilAccount account, GameProfile profile, boolean refresh) throws Exception {
account.clearCache();
Optional<Texture> texture = account.getSkin(profile);
@@ -153,10 +138,10 @@ public final class AccountHelper {
File file = getSkinFile(profile.getId());
if (!refresh && file.exists())
return;
new FileDownloadTask(NetworkUtils.toURL(url), file, proxy).run();
new FileDownloadTask(NetworkUtils.toURL(url), file).run();
}
private static void downloadSkin(YggdrasilAccount account, boolean refresh, Proxy proxy) throws Exception {
private static void downloadSkin(YggdrasilAccount account, boolean refresh) throws Exception {
account.clearCache();
if (account.getCharacter() == null) return;
@@ -166,7 +151,7 @@ public final class AccountHelper {
File file = getSkinFile(account.getUUID());
if (!refresh && file.exists())
return;
new FileDownloadTask(NetworkUtils.toURL(url), file, proxy).run();
new FileDownloadTask(NetworkUtils.toURL(url), file).run();
}
public static Image scale(String url, double scaleRatio) {

View File

@@ -22,8 +22,6 @@ import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.setting.Profile;
import java.net.Proxy;
/**
* @author huangyuhui
*/
@@ -31,11 +29,7 @@ public class HMCLDependencyManager extends DefaultDependencyManager {
private final Profile profile;
public HMCLDependencyManager(Profile profile, DownloadProvider downloadProvider) {
this(profile, downloadProvider, Proxy.NO_PROXY);
}
public HMCLDependencyManager(Profile profile, DownloadProvider downloadProvider, Proxy proxy) {
super(profile.getRepository(), downloadProvider, proxy);
super(profile.getRepository(), downloadProvider);
this.profile = profile;
}

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
import org.jackhuang.hmcl.task.Task;
@@ -32,6 +31,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
/**
* @author huangyuhui
*/
@@ -58,7 +59,7 @@ public class HMCLGameDownloadTask extends Task {
public void execute() {
File jar = profile.getRepository().getVersionJar(version);
File cache = new File(Settings.INSTANCE.getCommonPath(), "jars/" + gameVersion + ".jar");
File cache = new File(CONFIG.getCommonDirectory(), "jars/" + gameVersion + ".jar");
if (cache.exists())
try {
FileUtils.copyFile(cache, jar);
@@ -70,7 +71,6 @@ public class HMCLGameDownloadTask extends Task {
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
cache,
profile.getDependency().getProxy(),
new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1())
).then(Task.of(v -> FileUtils.copyFile(cache, jar))));
}

View File

@@ -24,7 +24,6 @@ import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.Logging;
@@ -34,6 +33,8 @@ import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
public class HMCLGameRepository extends DefaultGameRepository {
private final Profile profile;
private final Map<String, VersionSetting> versionSettings = new HashMap<>();
@@ -60,7 +61,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
if (useSelf(version, assetId))
return super.getAssetDirectory(version, assetId);
else
return new File(Settings.INSTANCE.getCommonPath(), "assets");
return new File(CONFIG.getCommonDirectory(), "assets");
}
@Override
@@ -85,7 +86,7 @@ public class HMCLGameRepository extends DefaultGameRepository {
if (self.exists() || vs.isNoCommon())
return self;
else
return new File(Settings.INSTANCE.getCommonPath(), "libraries/" + lib.getPath());
return new File(CONFIG.getCommonDirectory(), "libraries/" + lib.getPath());
}

View File

@@ -33,6 +33,7 @@ import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
@@ -71,7 +72,7 @@ public final class Accounts {
}
private static AuthlibInjectorServer getOrCreateAuthlibInjectorServer(String url) {
return ConfigHolder.CONFIG.authlibInjectorServers.stream()
return CONFIG.getAuthlibInjectorServers().stream()
.filter(server -> url.equals(server.getUrl()))
.findFirst()
.orElseGet(() -> {
@@ -85,7 +86,7 @@ public final class Accounts {
LOG.log(Level.WARNING, "Failed to migrate authlib injector server " + url, e);
}
ConfigHolder.CONFIG.authlibInjectorServers.add(server);
CONFIG.getAuthlibInjectorServers().add(server);
return server;
});
}

View File

@@ -18,8 +18,11 @@
package org.jackhuang.hmcl.setting;
import java.io.File;
import java.lang.reflect.Modifier;
import java.net.Proxy;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Stream;
import org.hildan.fxgson.creators.ObservableListCreator;
import org.hildan.fxgson.creators.ObservableMapCreator;
@@ -27,19 +30,25 @@ 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.EnumOrdinalDeserializer;
import org.jackhuang.hmcl.util.FileTypeAdapter;
import org.jackhuang.hmcl.util.ObservableHelper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
@@ -47,7 +56,7 @@ import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.collections.ObservableSet;
public final class Config implements Cloneable {
public final class Config implements Cloneable, Observable {
private static final Gson CONFIG_GSON = new GsonBuilder()
.registerTypeAdapter(VersionSetting.class, VersionSetting.Serializer.INSTANCE)
@@ -57,77 +66,117 @@ public final class Config implements Cloneable {
.registerTypeAdapter(ObservableSet.class, new ObservableSetCreator())
.registerTypeAdapter(ObservableMap.class, new ObservableMapCreator())
.registerTypeAdapterFactory(new JavaFxPropertyTypeAdapterFactory(true, true))
.registerTypeAdapter(EnumBackgroundImage.class, new EnumOrdinalDeserializer<>(EnumBackgroundImage.class)) // backward compatibility for backgroundType
.registerTypeAdapter(Proxy.Type.class, new EnumOrdinalDeserializer<>(Proxy.Type.class)) // backward compatibility for hasProxy
.setPrettyPrinting()
.create();
public static Config fromJson(String json) throws JsonParseException {
return CONFIG_GSON.fromJson(json, Config.class);
Config instance = CONFIG_GSON.fromJson(json, Config.class);
// Gson will replace the property fields (even they are final!)
// So we have to add the listeners again after deserialization
instance.addListenerToProperties();
return instance;
}
@SerializedName("last")
public final StringProperty selectedProfile = new SimpleStringProperty("");
private StringProperty selectedProfile = new SimpleStringProperty("");
@SerializedName("backgroundType")
public final IntegerProperty backgroundImageType = new SimpleIntegerProperty(0);
private ObjectProperty<EnumBackgroundImage> backgroundImageType = new SimpleObjectProperty<>(EnumBackgroundImage.DEFAULT);
@SerializedName("bgpath")
public final StringProperty backgroundImage = new SimpleStringProperty();
private StringProperty backgroundImage = new SimpleStringProperty();
@SerializedName("commonpath")
public final StringProperty commonDirectory = new SimpleStringProperty(Launcher.MINECRAFT_DIRECTORY.getAbsolutePath());
private StringProperty commonDirectory = new SimpleStringProperty(Launcher.MINECRAFT_DIRECTORY.getAbsolutePath());
@SerializedName("hasProxy")
public final BooleanProperty hasProxy = new SimpleBooleanProperty();
private BooleanProperty hasProxy = new SimpleBooleanProperty();
@SerializedName("hasProxyAuth")
public final BooleanProperty hasProxyAuth = new SimpleBooleanProperty();
private BooleanProperty hasProxyAuth = new SimpleBooleanProperty();
@SerializedName("proxyType")
public final IntegerProperty proxyType = new SimpleIntegerProperty();
private ObjectProperty<Proxy.Type> proxyType = new SimpleObjectProperty<>(Proxy.Type.DIRECT);
@SerializedName("proxyHost")
public final StringProperty proxyHost = new SimpleStringProperty();
private StringProperty proxyHost = new SimpleStringProperty();
@SerializedName("proxyPort")
public final StringProperty proxyPort = new SimpleStringProperty();
private StringProperty proxyPort = new SimpleStringProperty();
@SerializedName("proxyUserName")
public final StringProperty proxyUser = new SimpleStringProperty();
private StringProperty proxyUser = new SimpleStringProperty();
@SerializedName("proxyPassword")
public final StringProperty proxyPass = new SimpleStringProperty();
private StringProperty proxyPass = new SimpleStringProperty();
@SerializedName("theme")
public final StringProperty theme = new SimpleStringProperty();
private StringProperty theme = new SimpleStringProperty();
@SerializedName("localization")
public final StringProperty localization = new SimpleStringProperty();
private StringProperty localization = new SimpleStringProperty();
@SerializedName("downloadtype")
public final IntegerProperty downloadType = new SimpleIntegerProperty(1);
private IntegerProperty downloadType = new SimpleIntegerProperty(1);
@SerializedName("configurations")
public final ObservableMap<String, Profile> configurations = FXCollections.observableMap(new TreeMap<>());
private ObservableMap<String, Profile> configurations = FXCollections.observableMap(new TreeMap<>());
@SerializedName("accounts")
public final ObservableList<Map<Object, Object>> accounts = FXCollections.observableArrayList();
private ObservableList<Map<Object, Object>> accounts = FXCollections.observableArrayList();
@SerializedName("selectedAccount")
public final StringProperty selectedAccount = new SimpleStringProperty("");
private StringProperty selectedAccount = new SimpleStringProperty("");
@SerializedName("fontFamily")
public final StringProperty fontFamily = new SimpleStringProperty("Consolas");
private StringProperty fontFamily = new SimpleStringProperty("Consolas");
@SerializedName("fontSize")
public final DoubleProperty fontSize = new SimpleDoubleProperty(12);
private DoubleProperty fontSize = new SimpleDoubleProperty(12);
@SerializedName("logLines")
public final IntegerProperty logLines = new SimpleIntegerProperty(100);
private IntegerProperty logLines = new SimpleIntegerProperty(100);
@SerializedName("firstLaunch")
public final BooleanProperty firstLaunch = new SimpleBooleanProperty(true);
private BooleanProperty firstLaunch = new SimpleBooleanProperty(true);
public final ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
@SerializedName("authlibInjectorServers")
private ObservableList<AuthlibInjectorServer> authlibInjectorServers = FXCollections.observableArrayList();
private transient ObservableHelper helper = new ObservableHelper(this);
public Config() {
addListenerToProperties();
}
private void addListenerToProperties() {
Stream.of(getClass().getDeclaredFields())
.filter(it -> {
int modifiers = it.getModifiers();
return !Modifier.isTransient(modifiers) && !Modifier.isStatic(modifiers);
})
.filter(it -> Observable.class.isAssignableFrom(it.getType()))
.map(it -> {
try {
return (Observable) it.get(this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to get my own properties");
}
})
.forEach(helper::receiveUpdatesFrom);
}
@Override
public void addListener(InvalidationListener listener) {
helper.addListener(listener);
}
@Override
public void removeListener(InvalidationListener listener) {
helper.removeListener(listener);
}
public String toJson() {
return CONFIG_GSON.toJson(this);
@@ -137,4 +186,246 @@ public final class Config implements Cloneable {
public Config clone() {
return fromJson(this.toJson());
}
// Getters & Setters & Properties
public String getSelectedProfile() {
return selectedProfile.get();
}
public void setSelectedProfile(String selectedProfile) {
this.selectedProfile.set(selectedProfile);
}
public StringProperty selectedProfileProperty() {
return selectedProfile;
}
public EnumBackgroundImage getBackgroundImageType() {
return backgroundImageType.get();
}
public void setBackgroundImageType(EnumBackgroundImage backgroundImageType) {
this.backgroundImageType.set(backgroundImageType);
}
public ObjectProperty<EnumBackgroundImage> backgroundImageTypeProperty() {
return backgroundImageType;
}
public String getBackgroundImage() {
return backgroundImage.get();
}
public void setBackgroundImage(String backgroundImage) {
this.backgroundImage.set(backgroundImage);
}
public StringProperty backgroundImageProperty() {
return backgroundImage;
}
public String getCommonDirectory() {
return commonDirectory.get();
}
public void setCommonDirectory(String commonDirectory) {
this.commonDirectory.set(commonDirectory);
}
public StringProperty commonDirectoryProperty() {
return commonDirectory;
}
public boolean hasProxy() {
return hasProxy.get();
}
public void setHasProxy(boolean hasProxy) {
this.hasProxy.set(hasProxy);
}
public BooleanProperty hasProxyProperty() {
return hasProxy;
}
public boolean hasProxyAuth() {
return hasProxyAuth.get();
}
public void setHasProxyAuth(boolean hasProxyAuth) {
this.hasProxyAuth.set(hasProxyAuth);
}
public BooleanProperty hasProxyAuthProperty() {
return hasProxyAuth;
}
public Proxy.Type getProxyType() {
return proxyType.get();
}
public void setProxyType(Proxy.Type proxyType) {
this.proxyType.set(proxyType);
}
public ObjectProperty<Proxy.Type> proxyTypeProperty() {
return proxyType;
}
public String getProxyHost() {
return proxyHost.get();
}
public void setProxyHost(String proxyHost) {
this.proxyHost.set(proxyHost);
}
public StringProperty proxyHostProperty() {
return proxyHost;
}
public String getProxyPort() {
return proxyPort.get();
}
public void setProxyPort(String proxyPort) {
this.proxyPort.set(proxyPort);
}
public StringProperty proxyPortProperty() {
return proxyPort;
}
public String getProxyUser() {
return proxyUser.get();
}
public void setProxyUser(String proxyUser) {
this.proxyUser.set(proxyUser);
}
public StringProperty proxyUserProperty() {
return proxyUser;
}
public String getProxyPass() {
return proxyPass.get();
}
public void setProxyPass(String proxyPass) {
this.proxyPass.set(proxyPass);
}
public StringProperty proxyPassProperty() {
return proxyPass;
}
public String getTheme() {
return theme.get();
}
public void setTheme(String theme) {
this.theme.set(theme);
}
public StringProperty themeProperty() {
return theme;
}
public String getLocalization() {
return localization.get();
}
public void setLocalization(String localization) {
this.localization.set(localization);
}
public StringProperty localizationProperty() {
return localization;
}
public int getDownloadType() {
return downloadType.get();
}
public void setDownloadType(int downloadType) {
this.downloadType.set(downloadType);
}
public IntegerProperty downloadTypeProperty() {
return downloadType;
}
public ObservableMap<String, Profile> getConfigurations() {
return configurations;
}
public ObservableList<Map<Object, Object>> getAccounts() {
return accounts;
}
public String getSelectedAccount() {
return selectedAccount.get();
}
public void setSelectedAccount(String selectedAccount) {
this.selectedAccount.set(selectedAccount);
}
public StringProperty selectedAccountProperty() {
return selectedAccount;
}
public String getFontFamily() {
return fontFamily.get();
}
public void setFontFamily(String fontFamily) {
this.fontFamily.set(fontFamily);
}
public StringProperty fontFamilyProperty() {
return fontFamily;
}
public double getFontSize() {
return fontSize.get();
}
public void setFontSize(double fontSize) {
this.fontSize.set(fontSize);
}
public DoubleProperty fontSizeProperty() {
return fontSize;
}
public int getLogLines() {
return logLines.get();
}
public void setLogLines(int logLines) {
this.logLines.set(logLines);
}
public IntegerProperty logLinesProperty() {
return logLines;
}
public boolean isFirstLaunch() {
return firstLaunch.get();
}
public void setFirstLaunch(boolean firstLaunch) {
this.firstLaunch.set(firstLaunch);
}
public BooleanProperty firstLaunchProperty() {
return firstLaunch;
}
public ObservableList<AuthlibInjectorServer> getAuthlibInjectorServers() {
return authlibInjectorServers;
}
}

View File

@@ -122,7 +122,7 @@ public final class Profile {
}
public HMCLDependencyManager getDependency() {
return new HMCLDependencyManager(this, Settings.INSTANCE.getDownloadProvider(), Settings.INSTANCE.getProxy());
return new HMCLDependencyManager(this, Settings.INSTANCE.getDownloadProvider());
}
public VersionSetting getVersionSetting(String id) {

View File

@@ -1,36 +0,0 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.setting;
import org.jackhuang.hmcl.util.Lang;
import java.net.Proxy;
import java.util.List;
/**
* @author huangyuhui
*/
public final class Proxies {
private Proxies() {}
public static final List<Proxy.Type> PROXIES = Lang.immutableListOf(Proxy.Type.DIRECT, Proxy.Type.HTTP, Proxy.Type.SOCKS);
public static Proxy.Type getProxyType(int index) {
return Lang.get(PROXIES, index).orElse(null);
}
}

View File

@@ -0,0 +1,117 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.setting;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.Proxy.Type;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.value.ObservableObjectValue;
public final class ProxyManager {
private ProxyManager() {
}
private static final ObjectBinding<Proxy> proxyProperty = Bindings.createObjectBinding(
() -> {
String host = CONFIG.getProxyHost();
Integer port = Lang.toIntOrNull(CONFIG.getProxyPort());
if (!CONFIG.hasProxy() || StringUtils.isBlank(host) || port == null || CONFIG.getProxyType() == Proxy.Type.DIRECT) {
return Proxy.NO_PROXY;
} else {
return new Proxy(CONFIG.getProxyType(), new InetSocketAddress(host, port));
}
},
CONFIG.proxyTypeProperty(),
CONFIG.proxyHostProperty(),
CONFIG.proxyPortProperty(),
CONFIG.hasProxyProperty());
public static Proxy getProxy() {
return proxyProperty.get();
}
public static ObservableObjectValue<Proxy> proxyProperty() {
return proxyProperty;
}
static {
initProxy();
}
private static void initProxy() {
proxyProperty.addListener(observable -> updateSystemProxy());
updateSystemProxy();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (CONFIG.hasProxyAuth()) {
String username = CONFIG.getProxyUser();
String password = CONFIG.getProxyPass();
if (username != null && password != null) {
return new PasswordAuthentication(username, password.toCharArray());
}
}
return null;
}
});
}
private static void updateSystemProxy() {
Proxy proxy = proxyProperty.get();
if (proxy.type() == Proxy.Type.DIRECT) {
System.clearProperty("http.proxyHost");
System.clearProperty("http.proxyPort");
System.clearProperty("https.proxyHost");
System.clearProperty("https.proxyPort");
System.clearProperty("socksProxyHost");
System.clearProperty("socksProxyPort");
} else {
InetSocketAddress address = (InetSocketAddress) proxy.address();
String host = address.getHostString();
String port = String.valueOf(address.getPort());
if (proxy.type() == Type.HTTP) {
System.clearProperty("socksProxyHost");
System.clearProperty("socksProxyPort");
System.setProperty("http.proxyHost", host);
System.setProperty("http.proxyPort", port);
System.setProperty("https.proxyHost", host);
System.setProperty("https.proxyPort", port);
} else if (proxy.type() == Type.SOCKS) {
System.clearProperty("http.proxyHost");
System.clearProperty("http.proxyPort");
System.clearProperty("https.proxyHost");
System.clearProperty("https.proxyPort");
System.setProperty("socksProxyHost", host);
System.setProperty("socksProxyPort", port);
}
}
}
}

View File

@@ -17,8 +17,8 @@
*/
package org.jackhuang.hmcl.setting;
import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.text.Font;
@@ -33,16 +33,13 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.i18n.Locales;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.util.Lang.tryCast;
import static org.jackhuang.hmcl.util.Logging.LOG;
@@ -55,13 +52,23 @@ public class Settings {
private final boolean firstLaunch;
private InvalidationListener accountChangeListener =
source -> CONFIG.getAccounts().setAll(
accounts.values().stream()
.map(account -> {
Map<Object, Object> storage = account.toStorage();
storage.put("type", Accounts.getAccountType(account));
return storage;
})
.collect(toList()));
private Settings() {
firstLaunch = ConfigHolder.CONFIG.firstLaunch.get();
ConfigHolder.CONFIG.firstLaunch.set(false);
firstLaunch = CONFIG.isFirstLaunch();
CONFIG.setFirstLaunch(false);
loadProxy();
ProxyManager.getProxy(); // init ProxyManager
for (Iterator<Map<Object, Object>> iterator = ConfigHolder.CONFIG.accounts.iterator(); iterator.hasNext();) {
for (Iterator<Map<Object, Object>> iterator = CONFIG.getAccounts().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) {
@@ -72,7 +79,7 @@ public class Settings {
Account account;
try {
account = factory.fromStorage(settings, getProxy());
account = factory.fromStorage(settings);
} catch (Exception e) {
LOG.log(Level.WARNING, "Malformed account storage, removing: " + settings, e);
iterator.remove();
@@ -80,62 +87,37 @@ public class Settings {
}
accounts.put(Accounts.getAccountId(account), account);
account.addListener(accountChangeListener);
}
ConfigHolder.CONFIG.authlibInjectorServers.addListener(onInvalidating(this::removeDanglingAuthlibInjectorAccounts));
CONFIG.getAuthlibInjectorServers().addListener(onInvalidating(this::removeDanglingAuthlibInjectorAccounts));
this.selectedAccount.set(accounts.get(ConfigHolder.CONFIG.selectedAccount.get()));
this.selectedAccount.set(accounts.get(CONFIG.getSelectedAccount()));
checkProfileMap();
save();
for (Map.Entry<String, Profile> entry2 : getProfileMap().entrySet()) {
entry2.getValue().setName(entry2.getKey());
entry2.getValue().nameProperty().setChangedListener(this::profileNameChanged);
entry2.getValue().addPropertyChangedListener(e -> save());
for (Map.Entry<String, Profile> profileEntry : getProfileMap().entrySet()) {
profileEntry.getValue().setName(profileEntry.getKey());
profileEntry.getValue().nameProperty().setChangedListener(this::profileNameChanged);
profileEntry.getValue().addPropertyChangedListener(e -> save());
}
Lang.ignoringException(() -> Runtime.getRuntime().addShutdownHook(new Thread(this::save)));
CONFIG.addListener(source -> save());
}
public void save() {
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);
private void save() {
ConfigHolder.saveConfig(CONFIG);
}
public boolean isFirstLaunch() {
return firstLaunch;
}
private final StringProperty commonPath = new ImmediateStringProperty(this, "commonPath", ConfigHolder.CONFIG.commonDirectory.get()) {
@Override
public void invalidated() {
super.invalidated();
ConfigHolder.CONFIG.commonDirectory.set(get());
save();
}
};
public String getCommonPath() {
return commonPath.get();
}
public StringProperty commonPathProperty() {
return commonPath;
}
public void setCommonPath(String commonPath) {
this.commonPath.set(commonPath);
}
private Locales.SupportedLocale locale = Locales.getLocaleByName(ConfigHolder.CONFIG.localization.get());
private Locales.SupportedLocale locale = Locales.getLocaleByName(CONFIG.getLocalization());
public Locales.SupportedLocale getLocale() {
return locale;
@@ -143,126 +125,24 @@ public class Settings {
public void setLocale(Locales.SupportedLocale locale) {
this.locale = locale;
ConfigHolder.CONFIG.localization.set(Locales.getNameByLocale(locale));
save();
}
private Proxy proxy = Proxy.NO_PROXY;
public Proxy getProxy() {
return proxy;
}
private Proxy.Type proxyType = Proxies.getProxyType(ConfigHolder.CONFIG.proxyType.get());
public Proxy.Type getProxyType() {
return proxyType;
}
public void setProxyType(Proxy.Type proxyType) {
this.proxyType = proxyType;
ConfigHolder.CONFIG.proxyType.set(Proxies.PROXIES.indexOf(proxyType));
save();
loadProxy();
}
public String getProxyHost() {
return ConfigHolder.CONFIG.proxyHost.get();
}
public void setProxyHost(String proxyHost) {
ConfigHolder.CONFIG.proxyHost.set(proxyHost);
save();
}
public String getProxyPort() {
return ConfigHolder.CONFIG.proxyPort.get();
}
public void setProxyPort(String proxyPort) {
ConfigHolder.CONFIG.proxyPort.set(proxyPort);
save();
}
public String getProxyUser() {
return ConfigHolder.CONFIG.proxyUser.get();
}
public void setProxyUser(String proxyUser) {
ConfigHolder.CONFIG.proxyUser.set(proxyUser);
save();
}
public String getProxyPass() {
return ConfigHolder.CONFIG.proxyPass.get();
}
public void setProxyPass(String proxyPass) {
ConfigHolder.CONFIG.proxyPass.set(proxyPass);
save();
}
public boolean hasProxy() {
return ConfigHolder.CONFIG.hasProxy.get();
}
public void setHasProxy(boolean hasProxy) {
ConfigHolder.CONFIG.hasProxy.set(hasProxy);
save();
}
public boolean hasProxyAuth() {
return ConfigHolder.CONFIG.hasProxyAuth.get();
}
public void setHasProxyAuth(boolean hasProxyAuth) {
ConfigHolder.CONFIG.hasProxyAuth.set(hasProxyAuth);
save();
}
private void loadProxy() {
String host = getProxyHost();
Integer port = Lang.toIntOrNull(getProxyPort());
if (!hasProxy() || StringUtils.isBlank(host) || port == null || getProxyType() == Proxy.Type.DIRECT)
proxy = Proxy.NO_PROXY;
else {
System.setProperty("http.proxyHost", getProxyHost());
System.setProperty("http.proxyPort", getProxyPort());
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
String user = getProxyUser();
String pass = getProxyPass();
if (hasProxyAuth() && StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
System.setProperty("http.proxyUser", user);
System.setProperty("http.proxyPassword", pass);
Authenticator.setDefault(new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, pass.toCharArray());
}
});
}
}
CONFIG.setLocalization(Locales.getNameByLocale(locale));
}
public Font getFont() {
return Font.font(ConfigHolder.CONFIG.fontFamily.get(), ConfigHolder.CONFIG.fontSize.get());
return Font.font(CONFIG.getFontFamily(), CONFIG.getFontSize());
}
public void setFont(Font font) {
ConfigHolder.CONFIG.fontFamily.set(font.getFamily());
ConfigHolder.CONFIG.fontSize.set(font.getSize());
save();
CONFIG.setFontFamily(font.getFamily());
CONFIG.setFontSize(font.getSize());
}
public int getLogLines() {
return Math.max(ConfigHolder.CONFIG.logLines.get(), 100);
return Math.max(CONFIG.getLogLines(), 100);
}
public void setLogLines(int logLines) {
ConfigHolder.CONFIG.logLines.set(logLines);
save();
CONFIG.setLogLines(logLines);
}
/****************************************
@@ -277,7 +157,7 @@ public class Settings {
private void removeDanglingAuthlibInjectorAccounts() {
accounts.values().stream()
.filter(AuthlibInjectorAccount.class::isInstance)
.filter(it -> !ConfigHolder.CONFIG.authlibInjectorServers.contains(((AuthlibInjectorAccount) it).getServer()))
.filter(it -> !CONFIG.getAuthlibInjectorServers().contains(((AuthlibInjectorAccount) it).getServer()))
.collect(toList())
.forEach(this::deleteAccount);
}
@@ -287,15 +167,14 @@ public class Settings {
****************************************/
public DownloadProvider getDownloadProvider() {
return DownloadProviders.getDownloadProvider(ConfigHolder.CONFIG.downloadType.get());
return DownloadProviders.getDownloadProvider(CONFIG.getDownloadType());
}
public void setDownloadProvider(DownloadProvider downloadProvider) {
int index = DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(downloadProvider);
if (index == -1)
throw new IllegalArgumentException("Unknown download provider: " + downloadProvider);
ConfigHolder.CONFIG.downloadType.set(index);
save();
CONFIG.setDownloadType(index);
}
/****************************************
@@ -324,8 +203,7 @@ public class Settings {
public void invalidated() {
super.invalidated();
ConfigHolder.CONFIG.selectedAccount.set(getValue() == null ? "" : Accounts.getAccountId(getValue()));
save();
CONFIG.setSelectedAccount(getValue() == null ? "" : Accounts.getAccountId(getValue()));
}
};
@@ -343,6 +221,9 @@ public class Settings {
public void addAccount(Account account) {
accounts.put(Accounts.getAccountId(account), account);
account.addListener(accountChangeListener);
accountChangeListener.invalidated(account);
onAccountLoading();
EventBus.EVENT_BUS.fireEvent(new AccountAddedEvent(this, account));
@@ -357,78 +238,35 @@ public class Settings {
}
public void deleteAccount(String name, String character) {
accounts.remove(Accounts.getAccountId(name, character));
Account removed = accounts.remove(Accounts.getAccountId(name, character));
if (removed != null) {
removed.removeListener(accountChangeListener);
accountChangeListener.invalidated(removed);
onAccountLoading();
selectedAccount.get();
onAccountLoading();
selectedAccount.get();
}
}
public void deleteAccount(Account account) {
accounts.remove(Accounts.getAccountId(account));
account.removeListener(accountChangeListener);
accountChangeListener.invalidated(account);
onAccountLoading();
selectedAccount.get();
}
/****************************************
* BACKGROUND *
****************************************/
private final ImmediateStringProperty backgroundImage = new ImmediateStringProperty(this, "backgroundImage", ConfigHolder.CONFIG.backgroundImage.get()) {
@Override
public void invalidated() {
super.invalidated();
ConfigHolder.CONFIG.backgroundImage.set(get());
save();
}
};
public String getBackgroundImage() {
return backgroundImage.get();
}
public ImmediateStringProperty backgroundImageProperty() {
return backgroundImage;
}
public void setBackgroundImage(String backgroundImage) {
this.backgroundImage.set(backgroundImage);
}
private final ImmediateObjectProperty<EnumBackgroundImage> backgroundImageType = new ImmediateObjectProperty<EnumBackgroundImage>(this, "backgroundImageType", EnumBackgroundImage.indexOf(ConfigHolder.CONFIG.backgroundImageType.get())) {
@Override
public void invalidated() {
super.invalidated();
ConfigHolder.CONFIG.backgroundImageType.set(get().ordinal());
save();
}
};
public EnumBackgroundImage getBackgroundImageType() {
return backgroundImageType.get();
}
public ImmediateObjectProperty<EnumBackgroundImage> backgroundImageTypeProperty() {
return backgroundImageType;
}
public void setBackgroundImageType(EnumBackgroundImage backgroundImageType) {
this.backgroundImageType.set(backgroundImageType);
}
/****************************************
* THEME *
****************************************/
private final ImmediateObjectProperty<Theme> theme = new ImmediateObjectProperty<Theme>(this, "theme", Theme.getTheme(ConfigHolder.CONFIG.theme.get()).orElse(Theme.BLUE)) {
private final ImmediateObjectProperty<Theme> theme = new ImmediateObjectProperty<Theme>(this, "theme", Theme.getTheme(CONFIG.getTheme()).orElse(Theme.BLUE)) {
@Override
public void invalidated() {
super.invalidated();
ConfigHolder.CONFIG.theme.set(get().getName().toLowerCase());
save();
CONFIG.setTheme(get().getName().toLowerCase());
}
};
@@ -451,20 +289,18 @@ public class Settings {
public Profile getSelectedProfile() {
checkProfileMap();
if (!hasProfile(ConfigHolder.CONFIG.selectedProfile.get())) {
if (!hasProfile(CONFIG.getSelectedProfile())) {
getProfileMap().keySet().stream().findFirst().ifPresent(selectedProfile -> {
ConfigHolder.CONFIG.selectedProfile.set(selectedProfile);
save();
CONFIG.setSelectedProfile(selectedProfile);
});
Schedulers.computation().schedule(this::onProfileChanged);
}
return getProfile(ConfigHolder.CONFIG.selectedProfile.get());
return getProfile(CONFIG.getSelectedProfile());
}
public void setSelectedProfile(Profile selectedProfile) {
if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), ConfigHolder.CONFIG.selectedProfile.get())) {
ConfigHolder.CONFIG.selectedProfile.set(selectedProfile.getName());
save();
if (hasProfile(selectedProfile.getName()) && !Objects.equals(selectedProfile.getName(), CONFIG.getSelectedProfile())) {
CONFIG.setSelectedProfile(selectedProfile.getName());
Schedulers.computation().schedule(this::onProfileChanged);
}
}
@@ -481,7 +317,7 @@ public class Settings {
}
public Map<String, Profile> getProfileMap() {
return ConfigHolder.CONFIG.configurations;
return CONFIG.getConfigurations();
}
public Collection<Profile> getProfiles() {
@@ -496,8 +332,6 @@ public class Settings {
Schedulers.computation().schedule(this::onProfileLoading);
ver.nameProperty().setChangedListener(this::profileNameChanged);
save();
}
public void deleteProfile(Profile profile) {
@@ -542,8 +376,4 @@ public class Settings {
public void onAccountLoading() {
EventBus.EVENT_BUS.fireEvent(new AccountLoadingEvent(this, getAccounts()));
}
public Config getRawConfig() {
return ConfigHolder.CONFIG.clone();
}
}

View File

@@ -30,6 +30,8 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
/**
*
* @author huangyuhui
@@ -517,10 +519,10 @@ public final class VersionSetting {
.setFullscreen(isFullscreen())
.setServerIp(getServerIp())
.setWrapper(getWrapper())
.setProxyHost(Settings.INSTANCE.getProxyHost())
.setProxyPort(Settings.INSTANCE.getProxyPort())
.setProxyUser(Settings.INSTANCE.getProxyUser())
.setProxyPass(Settings.INSTANCE.getProxyPass())
.setProxyHost(CONFIG.getProxyHost())
.setProxyPort(CONFIG.getProxyPort())
.setProxyUser(CONFIG.getProxyUser())
.setProxyPass(CONFIG.getProxyPass())
.setPrecalledCommand(getPreLaunchCommand())
.setNoGeneratedJVMArgs(isNoJVMArgs())
.create();

View File

@@ -41,7 +41,6 @@ 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;
@@ -52,6 +51,7 @@ 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.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.ui.FXUtils.jfxListCellFactory;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
@@ -81,7 +81,7 @@ public class AddAccountPane extends StackPane {
cboServers.setCellFactory(jfxListCellFactory(server -> new TwoLineListItem(server.getName(), server.getUrl())));
cboServers.setConverter(stringConverter(AuthlibInjectorServer::getName));
Bindings.bindContent(cboServers.getItems(), ConfigHolder.CONFIG.authlibInjectorServers);
Bindings.bindContent(cboServers.getItems(), CONFIG.getAuthlibInjectorServers());
cboServers.getItems().addListener(onInvalidating(this::selectDefaultServer));
selectDefaultServer();
@@ -151,7 +151,7 @@ public class AddAccountPane extends StackPane {
lblCreationWarning.setText("");
setDisable(true);
Task.ofResult("create_account", () -> factory.create(new Selector(), username, password, addtionalData, Settings.INSTANCE.getProxy()))
Task.ofResult("create_account", () -> factory.create(new Selector(), username, password, addtionalData))
.finalized(Schedulers.javafx(), variables -> {
Settings.INSTANCE.addAccount(variables.get("create_account"));
acceptPane.hideSpinner();
@@ -211,7 +211,7 @@ public class AddAccountPane extends StackPane {
for (GameProfile profile : names) {
Image image;
try {
image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, 4, Settings.INSTANCE.getProxy());
image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, 4);
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Failed to get skin for " + profile.getName(), e);
image = null;

View File

@@ -22,7 +22,6 @@ 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;
@@ -40,6 +39,8 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
public class AddAuthlibInjectorServerPane extends StackPane {
@FXML private StackPane addServerContainer;
@@ -132,8 +133,8 @@ public class AddAuthlibInjectorServerPane extends StackPane {
@FXML
private void onAddFinish() {
if (!ConfigHolder.CONFIG.authlibInjectorServers.contains(serverBeingAdded)) {
ConfigHolder.CONFIG.authlibInjectorServers.add(serverBeingAdded);
if (!CONFIG.getAuthlibInjectorServers().contains(serverBeingAdded)) {
CONFIG.getAuthlibInjectorServers().add(serverBeingAdded);
}
fireEvent(new DialogCloseEvent());
}

View File

@@ -22,7 +22,6 @@ 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;
@@ -34,6 +33,8 @@ import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
public class AuthlibInjectorServersPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", i18n("account.injector.manage.title"));
@@ -48,15 +49,15 @@ public class AuthlibInjectorServersPage extends StackPane implements DecoratorPa
smoothScrolling(scrollPane);
serversListener = observable -> updateServersList();
ConfigHolder.CONFIG.authlibInjectorServers.addListener(new WeakInvalidationListener(serversListener));
CONFIG.getAuthlibInjectorServers().addListener(new WeakInvalidationListener(serversListener));
updateServersList();
}
private void updateServersList() {
listPane.getChildren().setAll(
ConfigHolder.CONFIG.authlibInjectorServers.stream()
CONFIG.getAuthlibInjectorServers().stream()
.map(server -> new AuthlibInjectorServerItem(server,
item -> ConfigHolder.CONFIG.authlibInjectorServers.remove(item.getServer())))
item -> CONFIG.getAuthlibInjectorServers().remove(item.getServer())))
.collect(toList()));
}

View File

@@ -76,6 +76,8 @@ import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
public final class Decorator extends StackPane implements TaskExecutorDialogWizardDisplayer {
private static final SVGGlyph minus = Lang.apply(new SVGGlyph(0, "MINUS", "M804.571 420.571v109.714q0 22.857-16 38.857t-38.857 16h-694.857q-22.857 0-38.857-16t-16-38.857v-109.714q0-22.857 16-38.857t38.857-16h694.857q22.857 0 38.857 16t16 38.857z", Color.WHITE),
glyph -> { glyph.setSize(12, 2); glyph.setTranslateY(4); });
@@ -219,10 +221,10 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
try {
Image background;
if (Settings.INSTANCE.getBackgroundImageType() == EnumBackgroundImage.DEFAULT)
if (CONFIG.getBackgroundImageType() == EnumBackgroundImage.DEFAULT)
background = searchBackgroundImage(new Image("/assets/img/background.jpg"), "");
else
background = searchBackgroundImage(new Image("/assets/img/background.jpg"), Settings.INSTANCE.getBackgroundImage());
background = searchBackgroundImage(new Image("/assets/img/background.jpg"), CONFIG.getBackgroundImage());
drawerWrapper.setBackground(new Background(new BackgroundImage(background, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize(800, 480, false, false, true, true))));
} catch (IllegalArgumentException ignore) {

View File

@@ -20,7 +20,8 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import com.jfoenix.effects.JFXDepthManager;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
@@ -28,7 +29,6 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
@@ -45,12 +45,13 @@ import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.i18n.Locales;
import static org.jackhuang.hmcl.setting.ConfigHolder.CONFIG;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.net.Proxy;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.Objects;
public final class SettingsPage extends StackPane implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty(this, "title", i18n("settings.launcher"));
@@ -88,9 +89,7 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
@FXML
private StackPane themeColorPickerContainer;
@FXML
private JFXRadioButton chkNoProxy;
@FXML
private JFXRadioButton chkManualProxy;
private JFXCheckBox chkEnableProxy;
@FXML
private JFXRadioButton chkProxyHttp;
@FXML
@@ -107,18 +106,6 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
FXUtils.smoothScrolling(scroll);
txtProxyHost.setText(Settings.INSTANCE.getProxyHost());
txtProxyHost.textProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyHost(newValue));
txtProxyPort.setText(Settings.INSTANCE.getProxyPort());
txtProxyPort.textProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyPort(newValue));
txtProxyUsername.setText(Settings.INSTANCE.getProxyUser());
txtProxyUsername.textProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyUser(newValue));
txtProxyPassword.setText(Settings.INSTANCE.getProxyPass());
txtProxyPassword.textProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyPass(newValue));
cboDownloadSource.getSelectionModel().select(DownloadProviders.DOWNLOAD_PROVIDERS.indexOf(Settings.INSTANCE.getDownloadProvider()));
cboDownloadSource.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setDownloadProvider(DownloadProviders.getDownloadProvider(newValue.intValue())));
@@ -149,37 +136,50 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
cboLanguage.getSelectionModel().select(Locales.LOCALES.indexOf(Settings.INSTANCE.getLocale()));
cboLanguage.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setLocale(Locales.getLocale(newValue.intValue())));
// ==== Proxy ====
txtProxyHost.textProperty().bindBidirectional(CONFIG.proxyHostProperty());
txtProxyPort.textProperty().bindBidirectional(CONFIG.proxyPortProperty());
txtProxyUsername.textProperty().bindBidirectional(CONFIG.proxyUserProperty());
txtProxyPassword.textProperty().bindBidirectional(CONFIG.proxyPassProperty());
proxyPane.disableProperty().bind(chkEnableProxy.selectedProperty().not());
authPane.disableProperty().bind(chkProxyAuthentication.selectedProperty().not());
chkEnableProxy.selectedProperty().bindBidirectional(CONFIG.hasProxyProperty());
chkProxyAuthentication.selectedProperty().bindBidirectional(CONFIG.hasProxyAuthProperty());
ObjectProperty<Proxy.Type> selectedProxyType = new SimpleObjectProperty<Proxy.Type>(Proxy.Type.HTTP) {
{
invalidated();
}
@Override
protected void invalidated() {
Proxy.Type type = Objects.requireNonNull(get());
if (type == Proxy.Type.DIRECT) {
set(Proxy.Type.HTTP); // HTTP by default
} else {
chkProxyHttp.setSelected(type == Proxy.Type.HTTP);
chkProxySocks.setSelected(type == Proxy.Type.SOCKS);
}
}
};
selectedProxyType.bindBidirectional(CONFIG.proxyTypeProperty());
ToggleGroup proxyConfigurationGroup = new ToggleGroup();
chkProxyHttp.setUserData(Proxy.Type.HTTP);
chkProxyHttp.setToggleGroup(proxyConfigurationGroup);
chkProxySocks.setUserData(Proxy.Type.SOCKS);
chkProxySocks.setToggleGroup(proxyConfigurationGroup);
proxyConfigurationGroup.getToggles().forEach(
toggle -> toggle.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
selectedProxyType.set((Proxy.Type) toggle.getUserData());
}
}));
// ====
for (Toggle toggle : proxyConfigurationGroup.getToggles())
if (toggle.getUserData() == Settings.INSTANCE.getProxyType())
toggle.setSelected(true);
ToggleGroup hasProxyGroup = new ToggleGroup();
chkNoProxy.setToggleGroup(hasProxyGroup);
chkManualProxy.setToggleGroup(hasProxyGroup);
if (!Settings.INSTANCE.hasProxy())
chkNoProxy.setSelected(true);
else
chkManualProxy.setSelected(true);
proxyPane.disableProperty().bind(chkNoProxy.selectedProperty());
hasProxyGroup.selectedToggleProperty().addListener((a, b, newValue) ->
Settings.INSTANCE.setHasProxy(newValue != chkNoProxy));
proxyConfigurationGroup.selectedToggleProperty().addListener((a, b, newValue) ->
Settings.INSTANCE.setProxyType((Proxy.Type) newValue.getUserData()));
chkProxyAuthentication.setSelected(Settings.INSTANCE.hasProxyAuth());
chkProxyAuthentication.selectedProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setHasProxyAuth(newValue));
authPane.disableProperty().bind(chkProxyAuthentication.selectedProperty().not());
fileCommonLocation.pathProperty().bindBidirectional(Settings.INSTANCE.commonPathProperty());
fileCommonLocation.pathProperty().bindBidirectional(CONFIG.commonDirectoryProperty());
FXUtils.installTooltip(btnUpdate, i18n("update.tooltip"));
checkUpdate();
@@ -189,17 +189,17 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
backgroundItem.createChildren(i18n("launcher.background.default"), EnumBackgroundImage.DEFAULT)
));
FXUtils.bindString(backgroundItem.getTxtCustom(), Settings.INSTANCE.backgroundImageProperty());
FXUtils.bindString(backgroundItem.getTxtCustom(), CONFIG.backgroundImageProperty());
backgroundItem.setCustomUserData(EnumBackgroundImage.CUSTOM);
backgroundItem.getGroup().getToggles().stream().filter(it -> it.getUserData() == Settings.INSTANCE.getBackgroundImageType()).findFirst().ifPresent(it -> it.setSelected(true));
backgroundItem.getGroup().getToggles().stream().filter(it -> it.getUserData() == CONFIG.getBackgroundImageType()).findFirst().ifPresent(it -> it.setSelected(true));
Settings.INSTANCE.backgroundImageProperty().setChangedListener(it -> initBackgroundItemSubtitle());
Settings.INSTANCE.backgroundImageTypeProperty().setChangedListener(it -> initBackgroundItemSubtitle());
CONFIG.backgroundImageProperty().addListener(onInvalidating(this::initBackgroundItemSubtitle));
CONFIG.backgroundImageTypeProperty().addListener(onInvalidating(this::initBackgroundItemSubtitle));
initBackgroundItemSubtitle();
backgroundItem.setToggleSelectedListener(newValue ->
Settings.INSTANCE.setBackgroundImageType((EnumBackgroundImage) newValue.getUserData()));
CONFIG.setBackgroundImageType((EnumBackgroundImage) newValue.getUserData()));
// theme
JFXColorPicker picker = new JFXColorPicker(Color.web(Settings.INSTANCE.getTheme().getColor()), null);
@@ -216,12 +216,12 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
}
private void initBackgroundItemSubtitle() {
switch (Settings.INSTANCE.getBackgroundImageType()) {
switch (CONFIG.getBackgroundImageType()) {
case DEFAULT:
backgroundItem.setSubtitle(i18n("launcher.background.default"));
break;
case CUSTOM:
backgroundItem.setSubtitle(Settings.INSTANCE.getBackgroundImage());
backgroundItem.setSubtitle(CONFIG.getBackgroundImage());
break;
}
}

View File

@@ -25,7 +25,6 @@ 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;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
@@ -81,18 +80,18 @@ public final class ExportWizardProvider implements WizardProvider {
boolean flag = true;
try (ZipEngine zip = new ZipEngine(modpackFile)) {
Config config = Settings.INSTANCE.getRawConfig();
Config config = ConfigHolder.CONFIG.clone();
config.hasProxy.set(false);
config.selectedProfile.set("");
config.commonDirectory.set(null);
config.fontFamily.set("Consolas");
config.fontSize.set(12);
config.localization.set(null);
config.accounts.clear();
config.selectedAccount.set("");
config.logLines.set(100);
config.configurations.clear();
config.setHasProxy(false);
config.setSelectedProfile("");
config.setCommonDirectory(null);
config.setFontFamily("Consolas");
config.setFontSize(12);
config.setLocalization(null);
config.getAccounts().clear();
config.setSelectedAccount("");
config.setLogLines(100);
config.getConfigurations().clear();
zip.putTextFile(config.toJson(), ConfigHolder.CONFIG_FILENAME);
zip.putFile(tempModpack, "modpack.zip");

View File

@@ -185,7 +185,7 @@ public class AppDataUpgrader extends IUpgrader {
@Override
public Collection<Task> getDependents() {
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, new IntegrityCheck("SHA-1", hash)));
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, new IntegrityCheck("SHA-1", hash)));
}
@Override
@@ -235,7 +235,7 @@ public class AppDataUpgrader extends IUpgrader {
@Override
public Collection<Task> getDependents() {
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, new IntegrityCheck("SHA-1", hash)));
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, new IntegrityCheck("SHA-1", hash)));
}
@Override

View File

@@ -55,8 +55,7 @@
<ComponentList title="%settings.launcher.proxy"> <!-- proxy -->
<VBox spacing="10">
<JFXRadioButton fx:id="chkNoProxy" text="%settings.launcher.proxy.no_proxy" />
<JFXRadioButton fx:id="chkManualProxy" text="%settings.launcher.proxy.has_proxy" />
<JFXCheckBox fx:id="chkEnableProxy" text="%settings.launcher.proxy.enable" />
<VBox fx:id="proxyPane" style="-fx-padding: 0 0 0 30;">
<HBox>
<JFXRadioButton fx:id="chkProxyHttp" text="%settings.launcher.proxy.http" />

View File

@@ -288,8 +288,7 @@ settings.launcher.language=Language
settings.launcher.log_font=Log Font
settings.launcher.proxy=Proxy
settings.launcher.proxy.authentication=Proxy Authentication
settings.launcher.proxy.no_proxy=No proxy
settings.launcher.proxy.has_proxy=Proxy configuration
settings.launcher.proxy.enable=Enable Proxy
settings.launcher.proxy.host=Host
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=Password

View File

@@ -287,9 +287,8 @@ settings.launcher.download_source=下載源
settings.launcher.language=語言
settings.launcher.log_font=日誌字體
settings.launcher.proxy=代理
settings.launcher.proxy.authentication=代理賬戶
settings.launcher.proxy.no_proxy=直連
settings.launcher.proxy.has_proxy=啓用驗證
settings.launcher.proxy.authentication=身份驗證
settings.launcher.proxy.enable=启用代理
settings.launcher.proxy.host=主機
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=密碼

View File

@@ -287,9 +287,8 @@ settings.launcher.download_source=下载源
settings.launcher.language=语言
settings.launcher.log_font=日志字体
settings.launcher.proxy=代理
settings.launcher.proxy.authentication=代理账户
settings.launcher.proxy.no_proxy=直连
settings.launcher.proxy.has_proxy=启用验证
settings.launcher.proxy.authentication=身份验证
settings.launcher.proxy.enable=启用代理
settings.launcher.proxy.host=主机
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=密码

View File

@@ -17,8 +17,13 @@
*/
package org.jackhuang.hmcl.auth;
import org.jackhuang.hmcl.util.ObservableHelper;
import org.jackhuang.hmcl.util.ToStringBuilder;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@@ -27,7 +32,7 @@ import java.util.UUID;
*
* @author huangyuhui
*/
public abstract class Account {
public abstract class Account implements Observable {
/**
* @return the name of the account who owns the character
@@ -66,6 +71,26 @@ public abstract class Account {
public abstract void clearCache();
private ObservableHelper helper = new ObservableHelper(this);
@Override
public void addListener(InvalidationListener listener) {
helper.addListener(listener);
}
@Override
public void removeListener(InvalidationListener listener) {
helper.removeListener(listener);
}
/**
* Called when the account has changed.
* This method can be called from any thread.
*/
protected void invalidate() {
Platform.runLater(helper::invalidate);
}
@Override
public String toString() {
return new ToStringBuilder(this)

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.auth;
import java.net.Proxy;
import java.util.Objects;
public final class AccountBuilder<T extends Account> {
@@ -25,7 +24,6 @@ public final class AccountBuilder<T extends Account> {
private String username;
private String password = null;
private Object additionalData = null;
private Proxy proxy = Proxy.NO_PROXY;
public AccountBuilder() {
}
@@ -50,12 +48,7 @@ public final class AccountBuilder<T extends Account> {
return this;
}
public AccountBuilder setProxy(Proxy proxy) {
this.proxy = Objects.requireNonNull(proxy);
return this;
}
public T create(AccountFactory<T> factory) throws AuthenticationException {
return factory.create(selector, Objects.requireNonNull(username), password, additionalData, proxy);
return factory.create(selector, Objects.requireNonNull(username), password, additionalData);
}
}

View File

@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.auth;
import java.net.Proxy;
import java.util.Map;
/**
@@ -26,7 +25,7 @@ import java.util.Map;
*/
public abstract class AccountFactory<T extends Account> {
public abstract T create(CharacterSelector selector, String username, String password, Object additionalData, Proxy proxy) throws AuthenticationException;
public abstract T create(CharacterSelector selector, String username, String password, Object additionalData) throws AuthenticationException;
public abstract T fromStorage(Map<Object, Object> storage, Proxy proxy);
public abstract T fromStorage(Map<Object, Object> storage);
}

View File

@@ -25,7 +25,6 @@ import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
import org.jackhuang.hmcl.util.ExceptionalSupplier;
import java.io.IOException;
import java.net.Proxy;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
@@ -45,24 +44,22 @@ public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjecto
}
@Override
public AuthlibInjectorAccount create(CharacterSelector selector, String username, String password, Object additionalData, Proxy proxy) throws AuthenticationException {
public AuthlibInjectorAccount create(CharacterSelector selector, String username, String password, Object additionalData) throws AuthenticationException {
Objects.requireNonNull(selector);
Objects.requireNonNull(username);
Objects.requireNonNull(password);
Objects.requireNonNull(proxy);
AuthlibInjectorServer server = (AuthlibInjectorServer) additionalData;
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl()), proxy),
AuthlibInjectorAccount account = new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl())),
server, authlibInjectorDownloader, username, null, null);
account.logInWithPassword(password, selector);
return account;
}
@Override
public AuthlibInjectorAccount fromStorage(Map<Object, Object> storage, Proxy proxy) {
public AuthlibInjectorAccount fromStorage(Map<Object, Object> storage) {
Objects.requireNonNull(storage);
Objects.requireNonNull(proxy);
YggdrasilSession session = YggdrasilSession.fromStorage(storage);
@@ -73,7 +70,7 @@ public class AuthlibInjectorAccountFactory extends AccountFactory<AuthlibInjecto
AuthlibInjectorServer server = serverLookup.apply(apiRoot);
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl()), proxy),
return new AuthlibInjectorAccount(new YggdrasilService(new AuthlibInjectorProvider(server.getUrl())),
server, authlibInjectorDownloader, username, session.getSelectedProfile().getId(), session);
}
}

View File

@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.auth.authlibinjector;
import static org.jackhuang.hmcl.util.Logging.LOG;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -81,7 +80,7 @@ public class AuthlibInjectorDownloader {
}
try {
new FileDownloadTask(new URL(downloadProvider.get().injectURL(latest.downloadUrl)), artifactLocation.toFile(), Proxy.NO_PROXY,
new FileDownloadTask(new URL(downloadProvider.get().injectURL(latest.downloadUrl)), artifactLocation.toFile(),
Optional.ofNullable(latest.checksums.get("sha256"))
.map(checksum -> new IntegrityCheck("SHA-256", checksum))
.orElse(null))

View File

@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.CharacterSelector;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
import java.net.Proxy;
import java.util.Map;
import java.util.UUID;
@@ -39,12 +38,12 @@ public class OfflineAccountFactory extends AccountFactory<OfflineAccount> {
}
@Override
public OfflineAccount create(CharacterSelector selector, String username, String password, Object additionalData, Proxy proxy) {
public OfflineAccount create(CharacterSelector selector, String username, String password, Object additionalData) {
return new OfflineAccount(username, getUUIDFromUserName(username));
}
@Override
public OfflineAccount fromStorage(Map<Object, Object> storage, Proxy proxy) {
public OfflineAccount fromStorage(Map<Object, Object> storage) {
String username = tryCast(storage.get("username"), String.class)
.orElseThrow(() -> new IllegalStateException("Offline account configuration malformed."));
UUID uuid = tryCast(storage.get("uuid"), String.class)

View File

@@ -114,6 +114,7 @@ public class YggdrasilAccount extends Account {
}
this.characterUUID = this.session.getSelectedProfile().getId();
invalidate();
}
@Override

View File

@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.CharacterSelector;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
import java.net.Proxy;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@@ -42,28 +41,26 @@ public class YggdrasilAccountFactory extends AccountFactory<YggdrasilAccount> {
}
@Override
public YggdrasilAccount create(CharacterSelector selector, String username, String password, Object additionalData, Proxy proxy) throws AuthenticationException {
public YggdrasilAccount create(CharacterSelector selector, String username, String password, Object additionalData) throws AuthenticationException {
Objects.requireNonNull(selector);
Objects.requireNonNull(username);
Objects.requireNonNull(password);
Objects.requireNonNull(proxy);
YggdrasilAccount account = new YggdrasilAccount(new YggdrasilService(provider, proxy), username, null, null);
YggdrasilAccount account = new YggdrasilAccount(new YggdrasilService(provider), username, null, null);
account.logInWithPassword(password, selector);
return account;
}
@Override
public YggdrasilAccount fromStorage(Map<Object, Object> storage, Proxy proxy) {
public YggdrasilAccount fromStorage(Map<Object, Object> storage) {
Objects.requireNonNull(storage);
Objects.requireNonNull(proxy);
YggdrasilSession session = YggdrasilSession.fromStorage(storage);
String username = tryCast(storage.get("username"), String.class)
.orElseThrow(() -> new IllegalArgumentException("storage does not have username"));
return new YggdrasilAccount(new YggdrasilService(provider, proxy), username, session.getSelectedProfile().getId(), session);
return new YggdrasilAccount(new YggdrasilService(provider), username, session.getSelectedProfile().getId(), session);
}
public static String randomToken() {

View File

@@ -9,7 +9,6 @@ import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.UUIDTypeAdapter;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.util.*;
@@ -21,15 +20,9 @@ import static org.jackhuang.hmcl.util.Pair.pair;
public class YggdrasilService {
private final YggdrasilProvider provider;
private final Proxy proxy;
public YggdrasilService(YggdrasilProvider provider) {
this(provider, Proxy.NO_PROXY);
}
public YggdrasilService(YggdrasilProvider provider, Proxy proxy) {
this.provider = provider;
this.proxy = proxy;
}
public YggdrasilSession authenticate(String username, String password, String clientToken) throws AuthenticationException {
@@ -155,7 +148,7 @@ public class YggdrasilService {
private String request(URL url, Object payload) throws AuthenticationException {
try {
if (payload == null)
return NetworkUtils.doGet(url, proxy);
return NetworkUtils.doGet(url);
else
return NetworkUtils.doPost(url, payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json");
} catch (IOException e) {

View File

@@ -29,8 +29,6 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import java.net.Proxy;
/**
* Note: This class has no state.
*
@@ -40,16 +38,10 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
private final DefaultGameRepository repository;
private final DownloadProvider downloadProvider;
private final Proxy proxy;
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider) {
this(repository, downloadProvider, Proxy.NO_PROXY);
}
public DefaultDependencyManager(DefaultGameRepository repository, DownloadProvider downloadProvider, Proxy proxy) {
this.repository = repository;
this.downloadProvider = downloadProvider;
this.proxy = proxy;
}
@Override
@@ -62,11 +54,6 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
return downloadProvider;
}
@Override
public Proxy getProxy() {
return proxy;
}
@Override
public GameBuilder gameBuilder() {
return new DefaultGameBuilder(this);

View File

@@ -21,8 +21,6 @@ import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import java.net.Proxy;
/**
* Do everything that will connect to Internet.
* Downloading Minecraft files.
@@ -36,11 +34,6 @@ public interface DependencyManager {
*/
GameRepository getGameRepository();
/**
* The proxy that all network operations should go through.
*/
Proxy getProxy();
/**
* Check if the game is complete.
* Check libraries, assets, logging files and so on.

View File

@@ -53,7 +53,7 @@ public final class GameAssetDownloadTask extends Task {
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
@@ -107,7 +107,7 @@ public final class GameAssetDownloadTask extends Task {
flag = !file.exists();
}
if (flag) {
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", assetObject.getHash()));
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, new IntegrityCheck("SHA-1", assetObject.getHash()));
task.setName(assetObject.getHash());
dependencies.add(task);
}

View File

@@ -44,7 +44,7 @@ public final class GameAssetIndexDownloadTask extends Task {
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
@@ -67,7 +67,7 @@ public final class GameAssetIndexDownloadTask extends Task {
File assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(assetIndexInfo.getUrl())),
assetIndexFile, dependencyManager.getProxy()
assetIndexFile
));
}

View File

@@ -51,7 +51,7 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) {

View File

@@ -56,7 +56,6 @@ public final class GameDownloadTask extends Task {
dependencies.add(new FileDownloadTask(
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
jar,
dependencyManager.getProxy(),
new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1())
));
}

View File

@@ -41,7 +41,7 @@ public final class GameLibrariesTask extends Task {
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {

View File

@@ -44,7 +44,7 @@ public final class GameLoggingDownloadTask extends Task {
/**
* Constructor.
*
* @param dependencyManager the dependency manager that can provides proxy settings and {@link org.jackhuang.hmcl.game.GameRepository}
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the <b>resolved</b> version
*/
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
@@ -66,7 +66,7 @@ public final class GameLoggingDownloadTask extends Task {
LoggingInfo logging = version.getLogging().get(DownloadType.CLIENT);
File file = dependencyManager.getGameRepository().getLoggingObject(version.getId(), version.getAssetIndex().getId(), logging);
if (!file.exists())
dependencies.add(new FileDownloadTask(NetworkUtils.toURL(logging.getFile().getUrl()), file, dependencyManager.getProxy()));
dependencies.add(new FileDownloadTask(NetworkUtils.toURL(logging.getFile().getUrl()), file));
}
}

View File

@@ -43,13 +43,13 @@ public final class LibraryDownloadTask extends Task {
xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz");
xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"),
xzFile, dependencyManager.getProxy(), null, 1);
xzFile, null, 1);
xzTask.setSignificance(TaskSignificance.MINOR);
setSignificance(TaskSignificance.MODERATE);
task = new FileDownloadTask(NetworkUtils.toURL(url),
file, dependencyManager.getProxy(),
file,
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
}

View File

@@ -24,7 +24,6 @@ import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.NetworkUtils;
import java.net.Proxy;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -66,7 +65,7 @@ public final class VersionJsonDownloadTask extends Task {
RemoteVersion<?> remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), Proxy.NO_PROXY, ID));
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), ID));
}
public static final String ID = "raw_version_json";

View File

@@ -109,7 +109,7 @@ public final class CurseCompletionTask extends Task {
updateProgress(finished.incrementAndGet(), manifest.getFiles().size());
if (StringUtils.isBlank(file.getFileName())) {
try {
return file.withFileName(NetworkUtils.detectFileName(file.getUrl(), dependencyManager.getProxy()));
return file.withFileName(NetworkUtils.detectFileName(file.getUrl()));
} catch (IOException ioe) {
Logging.LOG.log(Level.WARNING, "Unable to fetch the file name of URL: " + file.getUrl(), ioe);
flag.set(false);
@@ -125,7 +125,7 @@ public final class CurseCompletionTask extends Task {
if (StringUtils.isNotBlank(file.getFileName())) {
File dest = new File(run, "mods/" + file.getFileName());
if (!dest.exists())
dependencies.add(new FileDownloadTask(file.getUrl(), dest, dependencyManager.getProxy()));
dependencies.add(new FileDownloadTask(file.getUrl(), dest));
}
// Let this task fail if the curse manifest has not been completed.

View File

@@ -31,7 +31,6 @@ import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.security.MessageDigest;
import java.util.logging.Level;
@@ -79,7 +78,6 @@ public class FileDownloadTask extends Task {
private final File file;
private final IntegrityCheck integrityCheck;
private final int retry;
private final Proxy proxy;
private final EventManager<FailedEvent<URL>> onFailed = new EventManager<>();
private RandomAccessFile rFile;
private InputStream stream;
@@ -89,26 +87,16 @@ public class FileDownloadTask extends Task {
* @param file the location that download to.
*/
public FileDownloadTask(URL url, File file) {
this(url, file, Proxy.NO_PROXY);
this(url, file, null);
}
/**
* @param url the URL of remote file.
* @param file the location that download to.
* @param proxy the proxy.
*/
public FileDownloadTask(URL url, File file, Proxy proxy) {
this(url, file, proxy, null);
}
/**
* @param url the URL of remote file.
* @param file the location that download to.
* @param proxy the proxy.
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
*/
public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck) {
this(url, file, proxy, integrityCheck, 5);
public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck) {
this(url, file, integrityCheck, 5);
}
/**
@@ -116,14 +104,12 @@ public class FileDownloadTask extends Task {
* @param file the location that download to.
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
* @param retry the times for retrying if downloading fails.
* @param proxy the proxy.
*/
public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck, int retry) {
public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck, int retry) {
this.url = url;
this.file = file;
this.integrityCheck = integrityCheck;
this.retry = retry;
this.proxy = proxy;
setName(file.getName());
}
@@ -174,7 +160,7 @@ public class FileDownloadTask extends Task {
try {
updateProgress(0);
HttpURLConnection con = NetworkUtils.createConnection(url, proxy);
HttpURLConnection con = NetworkUtils.createConnection(url);
con.connect();
if (con.getResponseCode() / 100 != 2)

View File

@@ -25,7 +25,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.logging.Level;
@@ -41,30 +40,24 @@ public final class GetTask extends TaskResult<String> {
private final URL url;
private final Charset charset;
private final int retry;
private final Proxy proxy;
private final String id;
public GetTask(URL url) {
this(url, Proxy.NO_PROXY);
this(url, ID);
}
public GetTask(URL url, Proxy proxy) {
this(url, proxy, ID);
public GetTask(URL url, String id) {
this(url, id, UTF_8);
}
public GetTask(URL url, Proxy proxy, String id) {
this(url, proxy, id, UTF_8);
public GetTask(URL url, String id, Charset charset) {
this(url, id, charset, 5);
}
public GetTask(URL url, Proxy proxy, String id, Charset charset) {
this(url, proxy, id, charset, 5);
}
public GetTask(URL url, Proxy proxy, String id, Charset charset, int retry) {
public GetTask(URL url, String id, Charset charset, int retry) {
this.url = url;
this.charset = charset;
this.retry = retry;
this.proxy = proxy;
this.id = id;
setName(url.toString());
@@ -88,7 +81,7 @@ public final class GetTask extends TaskResult<String> {
Logging.LOG.log(Level.WARNING, "Failed to download, repeat times: " + time);
try {
updateProgress(0);
HttpURLConnection conn = NetworkUtils.createConnection(url, proxy);
HttpURLConnection conn = NetworkUtils.createConnection(url);
InputStream input = conn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[IOUtils.DEFAULT_BUFFER_SIZE];

View File

@@ -0,0 +1,63 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
/**
* A deserializer that supports deserializing strings and **numbers** into enums.
*
* @author yushijinhun
*/
public class EnumOrdinalDeserializer<T extends Enum<T>> implements JsonDeserializer<T> {
private Map<String, T> mapping = new HashMap<>();
public EnumOrdinalDeserializer(Class<T> enumClass) {
for (T constant : enumClass.getEnumConstants()) {
mapping.put(String.valueOf(constant.ordinal()), constant);
String name = constant.name();
try {
SerializedName annotation = enumClass.getField(name).getAnnotation(SerializedName.class);
if (annotation != null) {
name = annotation.value();
for (String alternate : annotation.alternate()) {
mapping.put(alternate, constant);
}
}
} catch (NoSuchFieldException e) {
throw new AssertionError(e);
}
mapping.put(name, constant);
}
}
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return mapping.get(json.getAsString());
}
}

View File

@@ -48,8 +48,8 @@ public final class NetworkUtils {
NetworkUtils.userAgentSupplier = Objects.requireNonNull(userAgentSupplier);
}
public static HttpURLConnection createConnection(URL url, Proxy proxy) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
public static HttpURLConnection createConnection(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setConnectTimeout(15000);
@@ -59,11 +59,7 @@ public final class NetworkUtils {
}
public static String doGet(URL url) throws IOException {
return IOUtils.readFullyAsString(createConnection(url, Proxy.NO_PROXY).getInputStream());
}
public static String doGet(URL url, Proxy proxy) throws IOException {
return IOUtils.readFullyAsString(createConnection(url, proxy).getInputStream());
return IOUtils.readFullyAsString(createConnection(url).getInputStream());
}
public static String doPost(URL u, Map<String, String> params) throws IOException {
@@ -80,14 +76,10 @@ public final class NetworkUtils {
return doPost(u, post, "application/x-www-form-urlencoded");
}
public static String doPost(URL u, String post, String contentType) throws IOException {
return doPost(u, post, contentType, Proxy.NO_PROXY);
}
public static String doPost(URL url, String post, String contentType, Proxy proxy) throws IOException {
public static String doPost(URL url, String post, String contentType) throws IOException {
byte[] bytes = post.getBytes(UTF_8);
HttpURLConnection con = createConnection(url, proxy);
HttpURLConnection con = createConnection(url);
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setRequestProperty("Content-Type", contentType + "; charset=utf-8");
@@ -120,11 +112,7 @@ public final class NetworkUtils {
}
public static String detectFileName(URL url) throws IOException {
return detectFileName(url, Proxy.NO_PROXY);
}
public static String detectFileName(URL url, Proxy proxy) throws IOException {
HttpURLConnection conn = createConnection(url, proxy);
HttpURLConnection conn = createConnection(url);
conn.connect();
if (conn.getResponseCode() / 100 != 2)
throw new IOException("Response code " + conn.getResponseCode());

View File

@@ -0,0 +1,69 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.util;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
/**
* Helper class for implementing {@link Observable}.
*
* @author yushijinhun
*/
public class ObservableHelper implements Observable, InvalidationListener {
private List<InvalidationListener> listeners = new CopyOnWriteArrayList<>();
private Observable source;
public ObservableHelper(Observable source) {
this.source = source;
}
/**
* This method can be called from any thread.
*/
@Override
public void addListener(InvalidationListener listener) {
listeners.add(listener);
}
/**
* This method can be called from any thread.
*/
@Override
public void removeListener(InvalidationListener listener) {
listeners.remove(listener);
}
public void invalidate() {
listeners.forEach(it -> it.invalidated(source));
}
@Override
public void invalidated(Observable observable) {
this.invalidate();
}
public void receiveUpdatesFrom(Observable observable) {
observable.removeListener(this); // remove the previously added listener(if any)
observable.addListener(this);
}
}