Compare commits

..
8 Commits
49 changed files with 677 additions and 278 deletions

No files matched your search

@@ -20,10 +20,7 @@ package org.jackhuang.hmcl.game;
import com.jfoenix.concurrency.JFXUtilities;
import javafx.application.Platform;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.SpecificCharacterSelector;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.launch.*;
import org.jackhuang.hmcl.mod.CurseCompletionTask;
@@ -76,6 +73,7 @@ public final class LauncherHelper {
Version version = repository.getVersion(selectedVersion);
Account account = Settings.INSTANCE.getSelectedAccount();
VersionSetting setting = profile.getVersionSetting(selectedVersion);
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
executor = Task.of(Schedulers.javafx(), () -> Controllers.dialog(launchingStepsPane))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES)))
@@ -90,7 +88,14 @@ public final class LauncherHelper {
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.then(Task.of(Main.i18n("account.methods"), variables -> {
try {
variables.set("account", account.logIn(new SpecificCharacterSelector(Accounts.getCurrentCharacter(account)), Settings.INSTANCE.getProxy()));
try {
variables.set("account", account.logIn(new SpecificCharacterSelector(Accounts.getCurrentCharacter(account)), Settings.INSTANCE.getProxy()));
} catch (ServerDisconnectException e) {
if (account.canPlayOffline())
variables.set("account", account.playOffline());
else
throw e;
}
} catch (AuthenticationException e) {
variables.set("account", DialogController.logIn(account));
JFXUtilities.runInFX(() -> Controllers.dialog(launchingStepsPane));
@@ -99,7 +104,7 @@ public final class LauncherHelper {
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LAUNCHING)))
.then(Task.of(variables -> {
variables.set("launcher", new HMCLGameLauncher(
repository, selectedVersion, variables.get("account"), setting.toLaunchOptions(profile.getGameDir()), new HMCLProcessListener(variables.get("account"), setting)
repository, selectedVersion, variables.get("account"), setting.toLaunchOptions(profile.getGameDir()), new HMCLProcessListener(variables.get("account"), setting, gameVersion.isPresent())
));
}))
.then(variables -> {
@@ -282,11 +287,13 @@ public final class LauncherHelper {
private ManagedProcess process;
private boolean lwjgl;
private LogWindow logWindow;
private final boolean detectWindow;
private final LinkedList<Pair<String, Log4jLevel>> logs;
private final CountDownLatch latch = new CountDownLatch(1);
public HMCLProcessListener(AuthInfo authInfo, VersionSetting setting) {
public HMCLProcessListener(AuthInfo authInfo, VersionSetting setting, boolean detectWindow) {
this.setting = setting;
this.detectWindow = detectWindow;
if (authInfo == null)
forbiddenTokens = Collections.emptyMap();
@@ -337,8 +344,7 @@ public final class LauncherHelper {
Platform.runLater(() -> logWindow.logLine(log, level));
}
if (!lwjgl && log.contains("LWJGL Version: ")) {
if (!lwjgl && log.contains("LWJGL Version: ") || !detectWindow) {
lwjgl = true;
switch (visibility) {
case HIDE_AND_REOPEN:
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.task.FinalizedCallback;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
@@ -84,13 +85,13 @@ public final class ModpackHelper {
public static Task getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
profile.getRepository().markVersionAsModpack(name);
Task finalizeTask = Task.of(() -> {
FinalizedCallback finalizeTask = (variables, isDependentsSucceeded) -> {
profile.getRepository().refreshVersions();
VersionSetting vs = profile.specializeVersionSetting(name);
profile.getRepository().undoMark(name);
if (vs != null)
vs.setGameDirType(EnumGameDirectory.VERSION_FOLDER);
});
};
if (modpack.getManifest() instanceof CurseManifest)
return new CurseInstallTask(profile.getDependency(), zipFile, ((CurseManifest) modpack.getManifest()), name)
@@ -40,6 +40,12 @@ public final class Config {
@SerializedName("commonpath")
private String commonDirectory = Main.MINECRAFT_DIRECTORY.getAbsolutePath();
@SerializedName("hasProxy")
private boolean hasProxy = false;
@SerializedName("hasProxyAuth")
private boolean hasProxyAuth = false;
@SerializedName("proxyType")
private int proxyType = 0;
@@ -121,6 +127,24 @@ public final class Config {
Settings.INSTANCE.save();
}
public boolean isHasProxy() {
return hasProxy;
}
public void setHasProxy(boolean hasProxy) {
this.hasProxy = hasProxy;
Settings.INSTANCE.save();
}
public boolean isHasProxyAuth() {
return hasProxyAuth;
}
public void setHasProxyAuth(boolean hasProxyAuth) {
this.hasProxyAuth = hasProxyAuth;
Settings.INSTANCE.save();
}
public int getProxyType() {
return proxyType;
}
@@ -0,0 +1,36 @@
/*
* 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.setting;
import org.jackhuang.hmcl.Main;
public final class Profiles {
private Profiles() {
}
public static String getProfileDisplayName(Profile profile) {
switch (profile.getName()) {
case Settings.DEFAULT_PROFILE:
return Main.i18n("profile.default");
case Settings.HOME_PROFILE:
return Main.i18n("profile.home");
default:
return profile.getName();
}
}
}
@@ -124,11 +124,6 @@ public class Settings {
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Something happened wrongly when load settings.", e);
}
else {
Logging.LOG.config("No settings file here, may be first loading.");
if (!c.getConfigurations().containsKey(HOME_PROFILE))
c.getConfigurations().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
}
return c;
}
@@ -229,22 +224,27 @@ public class Settings {
SETTINGS.setProxyPass(proxyPass);
}
public boolean hasProxy() { return SETTINGS.isHasProxy(); }
public void setHasProxy(boolean hasProxy) { SETTINGS.setHasProxy(hasProxy); }
public boolean hasProxyAuth() { return SETTINGS.isHasProxyAuth(); }
public void setHasProxyAuth(boolean hasProxyAuth) { SETTINGS.setHasProxyAuth(hasProxyAuth); }
private void loadProxy() {
String host = getProxyHost();
Integer port = Lang.toIntOrNull(getProxyPort());
if (StringUtils.isBlank(host) || port == null)
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());
if (getProxyType() == Proxy.Type.DIRECT)
proxy = Proxy.NO_PROXY;
else
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
proxy = new Proxy(proxyType, new InetSocketAddress(host, port));
String user = getProxyUser();
String pass = getProxyPass();
if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
if (hasProxyAuth() && StringUtils.isNotBlank(user) && StringUtils.isNotBlank(pass)) {
System.setProperty("http.proxyUser", user);
System.setProperty("http.proxyPassword", pass);
@@ -463,7 +463,7 @@ public class Settings {
}
public boolean hasProfile(String name) {
return getProfileMap().containsKey(Lang.nonNull(name, DEFAULT_PROFILE));
return getProfileMap().containsKey(name);
}
public Map<String, Profile> getProfileMap() {
@@ -494,8 +494,10 @@ public class Settings {
}
private void checkProfileMap() {
if (getProfileMap().isEmpty())
if (getProfileMap().isEmpty()) {
getProfileMap().put(DEFAULT_PROFILE, new Profile(DEFAULT_PROFILE));
getProfileMap().put(HOME_PROFILE, new Profile(HOME_PROFILE, Main.MINECRAFT_DIRECTORY));
}
}
private void onProfileChanged() {
@@ -67,9 +67,6 @@ public final class AccountItem extends StackPane {
FXUtils.loadFXML(this, "/assets/fxml/account-item.fxml");
FXUtils.limitWidth(this, 160);
FXUtils.limitHeight(this, 156);
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -0.5, 1.0));
chkSelected.setToggleGroup(toggleGroup);
@@ -73,8 +73,6 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
dialog.setDialogContainer(this);
FXUtils.smoothScrolling(scrollPane);
FXUtils.setValidateWhileTextChanged(txtUsername);
FXUtils.setValidateWhileTextChanged(txtPassword);
cboType.getItems().setAll(Main.i18n("account.methods.offline"), Main.i18n("account.methods.yggdrasil"));
cboType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> {
@@ -160,20 +158,10 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
Settings.INSTANCE.addAccount((Account) account);
dialog.close();
loadAccounts();
} else if (account instanceof InvalidCredentialsException) {
lblCreationWarning.setText(Main.i18n("account.failed.wrong_password"));
} else if (account instanceof NoCharacterException) {
lblCreationWarning.setText(Main.i18n("account.failed.no_charactor"));
} else if (account instanceof ServerDisconnectException) {
lblCreationWarning.setText(Main.i18n("account.failed.connect_authentication_server"));
} else if (account instanceof InvalidTokenException) {
lblCreationWarning.setText(Main.i18n("account.failed.invalid_token"));
} else if (account instanceof InvalidPasswordException) {
lblCreationWarning.setText(Main.i18n("account.failed.invalid_password"));
} else if (account instanceof NoSelectedCharacterException) {
dialog.close();
} else if (account instanceof Exception) {
lblCreationWarning.setText(account.getClass() + ": " + ((Exception) account).getLocalizedMessage());
lblCreationWarning.setText(accountException((Exception) account));
}
progressBar.setVisible(false);
});
@@ -197,6 +185,22 @@ public final class AccountsPage extends StackPane implements DecoratorPage {
this.title.set(title);
}
public static String accountException(Exception account) {
if (account instanceof InvalidCredentialsException) {
return Main.i18n("account.failed.invalid_credentials");
} else if (account instanceof NoCharacterException) {
return Main.i18n("account.failed.no_charactor");
} else if (account instanceof ServerDisconnectException) {
return Main.i18n("account.failed.connect_authentication_server");
} else if (account instanceof InvalidTokenException) {
return Main.i18n("account.failed.invalid_token");
} else if (account instanceof InvalidPasswordException) {
return Main.i18n("account.failed.invalid_password");
} else {
return account.getClass() + ": " + ((Exception) account).getLocalizedMessage();
}
}
public static String accountType(Account account) {
if (account instanceof OfflineAccount) return Main.i18n("account.methods.offline");
else if (account instanceof YggdrasilAccount) return Main.i18n("account.methods.yggdrasil");
@@ -180,9 +180,6 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
animationHandler = new TransitionHandler(contentPlaceHolder);
FXUtils.setOverflowHidden((Pane) lookup("#contentPlaceHolderRoot"));
FXUtils.setOverflowHidden(drawerWrapper);
loadBackground();
}
@@ -487,7 +484,7 @@ public final class Decorator extends StackPane implements TaskExecutorDialogWiza
if (content instanceof Region) {
((Region) content).setMinSize(0, 0);
FXUtils.setOverflowHidden((Region) content);
FXUtils.setOverflowHidden((Region) content, true);
}
if (content instanceof Refreshable)
@@ -38,6 +38,7 @@ import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
@@ -45,6 +46,7 @@ import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.OperatingSystem;
import org.jackhuang.hmcl.util.Pair;
import java.io.File;
import java.io.IOException;
@@ -92,35 +94,79 @@ public final class FXUtils {
});
}
public static void setValidateWhileTextChanged(JFXTextField field) {
field.textProperty().addListener(o -> field.validate());
field.validate();
public static <T> void addListener(Node node, String key, ObservableValue<T> value, Consumer<T> callback) {
ChangeListener<T> listener = (a, b, newValue) -> callback.accept(newValue);
node.getProperties().put(key, new Pair<>(callback, listener));
value.addListener(listener);
}
public static void setValidateWhileTextChanged(JFXPasswordField field) {
field.textProperty().addListener(o -> field.validate());
field.validate();
public static <T> void removeListener(Node node, String key) {
if (node.getProperties().get(key) instanceof Pair) {
Pair pair = (Pair) node.getProperties().get(key);
if (pair.getValue() instanceof ObservableValue && pair.getKey() instanceof ChangeListener) {
((ObservableValue) pair.getValue()).removeListener((ChangeListener) pair.getKey());
}
}
}
public static void setOverflowHidden(Region region) {
Rectangle rectangle = new Rectangle();
rectangle.widthProperty().bind(region.widthProperty());
rectangle.heightProperty().bind(region.heightProperty());
region.setClip(rectangle);
public static void setValidateWhileTextChanged(Node field, boolean validate) {
if (field instanceof JFXTextField) {
if (validate) {
addListener(field, "FXUtils.validation", ((JFXTextField) field).textProperty(), o -> ((JFXTextField) field).validate());
} else {
removeListener(field, "FXUtils.validation");
}
((JFXTextField) field).validate();
} else if (field instanceof JFXPasswordField) {
if (validate) {
addListener(field, "FXUtils.validation", ((JFXPasswordField) field).textProperty(), o -> ((JFXPasswordField) field).validate());
} else {
removeListener(field, "FXUtils.validation");
}
((JFXPasswordField) field).validate();
} else
throw new IllegalArgumentException("Only JFXTextField and JFXPasswordField allowed");
}
public static void limitWidth(Region region, double width) {
public static boolean getValidateWhileTextChanged(Node field) {
return field.getProperties().containsKey("FXUtils.validation");
}
public static void setOverflowHidden(Region region, boolean hidden) {
if (hidden) {
Rectangle rectangle = new Rectangle();
rectangle.widthProperty().bind(region.widthProperty());
rectangle.heightProperty().bind(region.heightProperty());
region.setClip(rectangle);
} else {
region.setClip(null);
}
}
public static boolean getOverflowHidden(Region region) {
return region.getClip() != null;
}
public static void setLimitWidth(Region region, double width) {
region.setMaxWidth(width);
region.setMinWidth(width);
region.setPrefWidth(width);
}
public static void limitHeight(Region region, double height) {
public static double getLimitWidth(Region region) {
return region.getMaxWidth();
}
public static void setLimitHeight(Region region, double height) {
region.setMaxHeight(height);
region.setMinHeight(height);
region.setPrefHeight(height);
}
public static double getLimitHeight(Region region) {
return region.getMaxHeight();
}
public static void smoothScrolling(ScrollPane scrollPane) {
JFXScrollPane.smoothScrolling(scrollPane);
}
@@ -39,6 +39,7 @@ import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
@@ -122,7 +123,7 @@ public final class LeftPaneController {
private void onProfilesLoading() {
LinkedList<RipplerContainer> list = new LinkedList<>();
for (Profile profile : Settings.INSTANCE.getProfiles()) {
VersionListItem item = new VersionListItem(profile.getName());
VersionListItem item = new VersionListItem(Profiles.getProfileDisplayName(profile));
RipplerContainer ripplerContainer = new RipplerContainer(item);
item.setOnSettingsButtonClicked(() -> Controllers.getDecorator().showPage(new ProfilePage(profile)));
ripplerContainer.setOnMouseClicked(e -> Settings.INSTANCE.setSelectedProfile(profile));
@@ -32,14 +32,13 @@ import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.download.game.VersionJsonSaveTask;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.ProfileChangedEvent;
import org.jackhuang.hmcl.event.ProfileLoadingEvent;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.MismatchedModpackTypeException;
import org.jackhuang.hmcl.mod.UnsupportedModpackException;
import org.jackhuang.hmcl.setting.Profile;
@@ -53,11 +52,13 @@ import org.jackhuang.hmcl.ui.download.DownloadWizardProvider;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.OperatingSystem;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
public final class MainPage extends StackPane implements DecoratorPage {
@@ -101,6 +102,21 @@ public final class MainPage extends StackPane implements DecoratorPage {
item.setUpdate(profile.getRepository().isModpack(version));
item.setGameVersion(game);
item.setVersionName(version);
StringBuilder libraries = new StringBuilder();
for (Library library : profile.getRepository().getVersion(version).getLibraries()) {
if (library.getGroupId().equalsIgnoreCase("net.minecraftforge") && library.getArtifactId().equalsIgnoreCase("forge")) {
libraries.append(Main.i18n("install.installer.forge")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)forge", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
if (library.getGroupId().equalsIgnoreCase("com.mumfrey") && library.getArtifactId().equalsIgnoreCase("liteloader")) {
libraries.append(Main.i18n("install.installer.liteloader")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)liteloader", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
if (library.getGroupId().equalsIgnoreCase("net.optifine") && library.getArtifactId().equalsIgnoreCase("optifine")) {
libraries.append(Main.i18n("install.installer.optifine")).append(": ").append(StringUtils.removeSuffix(StringUtils.removePrefix(library.getVersion().replaceAll("(?i)optifine", "").replace(game, "").trim(), "-"), "-")).append("\n");
}
}
item.setLibraries(libraries.toString());
item.setOnLaunchButtonClicked(e -> {
if (Settings.INSTANCE.getSelectedAccount() == null)
Controllers.dialog(Main.i18n("login.empty_username"));
@@ -25,6 +25,7 @@ import javafx.fxml.FXML;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.setting.Settings;
import org.jackhuang.hmcl.ui.construct.FileItem;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
@@ -58,7 +59,7 @@ public final class ProfilePage extends StackPane implements DecoratorPage {
FXUtils.loadFXML(this, "/assets/fxml/profile.fxml");
txtProfileName.setText(Optional.ofNullable(profile).map(Profile::getName).orElse(""));
txtProfileName.setText(Optional.ofNullable(profile).map(Profiles::getProfileDisplayName).orElse(""));
FXUtils.onChangeAndOperate(txtProfileName.textProperty(), it -> {
btnSave.setDisable(!txtProfileName.validate() || StringUtils.isBlank(getLocation()));
});
@@ -17,18 +17,16 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tooltip;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Main;
@@ -40,6 +38,7 @@ import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
import java.net.Proxy;
import java.util.Arrays;
import java.util.Collections;
@@ -53,12 +52,10 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
@FXML
private JFXTextField txtProxyUsername;
@FXML
private JFXTextField txtProxyPassword;
private JFXPasswordField txtProxyPassword;
@FXML
private JFXTextField txtFontSize;
@FXML
private JFXComboBox<?> cboProxyType;
@FXML
private JFXComboBox<Label> cboLanguage;
@FXML
private JFXComboBox<?> cboDownloadSource;
@@ -80,13 +77,24 @@ public final class SettingsPage extends StackPane implements DecoratorPage {
private MultiFileItem backgroundItem;
@FXML
private MultiFileItem themeItem;
@FXML
private JFXRadioButton chkNoProxy;
@FXML
private JFXRadioButton chkManualProxy;
@FXML
private JFXRadioButton chkProxyHttp;
@FXML
private JFXRadioButton chkProxySocks;
@FXML
private JFXCheckBox chkProxyAuthentication;
@FXML
private GridPane authPane;
@FXML
private Pane proxyPane;
{
FXUtils.loadFXML(this, "/assets/fxml/setting.fxml");
FXUtils.limitWidth(cboLanguage, 400);
FXUtils.limitWidth(cboDownloadSource, 400);
FXUtils.smoothScrolling(scroll);
txtProxyHost.setText(Settings.INSTANCE.getProxyHost());
@@ -131,8 +139,37 @@ 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())));
cboProxyType.getSelectionModel().select(Proxies.PROXIES.indexOf(Settings.INSTANCE.getProxyType()));
cboProxyType.getSelectionModel().selectedIndexProperty().addListener((a, b, newValue) -> Settings.INSTANCE.setProxyType(Proxies.getProxyType(newValue.intValue())));
ToggleGroup proxyConfigurationGroup = new ToggleGroup();
chkProxyHttp.setUserData(Proxy.Type.HTTP);
chkProxyHttp.setToggleGroup(proxyConfigurationGroup);
chkProxySocks.setUserData(Proxy.Type.SOCKS);
chkProxySocks.setToggleGroup(proxyConfigurationGroup);
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.setProperty(Settings.INSTANCE.commonPathProperty());
@@ -59,14 +59,14 @@ public final class VersionItem extends StackPane {
@FXML
private Label lblGameVersion;
@FXML
private Label lblLibraries;
@FXML
private ImageView iconView;
private EventHandler<? super MouseEvent> launchClickedHandler = null;
public VersionItem() {
FXUtils.loadFXML(this, "/assets/fxml/version-item.fxml");
FXUtils.limitWidth(this, 160);
FXUtils.limitHeight(this, 156);
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.26), 5.0, 0.12, -1.0, 1.0));
btnSettings.setGraphic(SVG.gear("black", 15, 15));
btnUpdate.setGraphic(SVG.update("black", 15, 15));
@@ -100,6 +100,10 @@ public final class VersionItem extends StackPane {
lblGameVersion.setText(gameVersion);
}
public void setLibraries(String libraries) {
lblLibraries.setText(libraries);
}
public void setImage(Image image) {
iconView.setImage(image);
}
@@ -46,8 +46,6 @@ public final class VersionListItem extends StackPane {
lblGameVersion.setText(gameVersion);
FXUtils.limitSize(imageView, 32, 32);
FXUtils.limitWidth(imageViewContainer, 32);
FXUtils.limitHeight(imageViewContainer, 32);
}
@FXML
@@ -85,28 +85,6 @@ public final class VersionSettingsController {
FXUtils.smoothScrolling(scroll);
double limitWidth = 300;
FXUtils.limitWidth(txtMaxMemory, limitWidth);
FXUtils.limitWidth(cboLauncherVisibility, limitWidth);
double limitHeight = 10;
FXUtils.limitHeight(chkNoJVMArgs, limitHeight);
FXUtils.limitHeight(chkNoCommon, limitHeight);
FXUtils.limitHeight(chkNoGameCheck, limitHeight);
FXUtils.limitHeight(chkShowLogs, limitHeight);
NumberValidator nonnull = new NumberValidator("Must be a number.", false);
NumberValidator nullable = new NumberValidator("Must be a number.", true);
txtWidth.setValidators(nonnull);
FXUtils.setValidateWhileTextChanged(txtWidth);
txtHeight.setValidators(nonnull);
FXUtils.setValidateWhileTextChanged(txtHeight);
txtMaxMemory.setValidators(nonnull);
FXUtils.setValidateWhileTextChanged(txtMaxMemory);
txtMetaspace.setValidators(nullable);
FXUtils.setValidateWhileTextChanged(txtMetaspace);
Task.of(variables -> {
variables.set("list", JavaVersion.getJREs().values().stream().map(javaVersion ->
javaItem.createChildren(javaVersion.getVersion(), javaVersion.getBinary().getAbsolutePath(), javaVersion)
@@ -24,10 +24,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.SpecificCharacterSelector;
import org.jackhuang.hmcl.auth.InvalidCredentialsException;
import org.jackhuang.hmcl.auth.*;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
import org.jackhuang.hmcl.setting.Accounts;
@@ -78,10 +75,10 @@ public class YggdrasilAccountLoginPane extends StackPane {
if (account instanceof AuthInfo) {
success.accept(((AuthInfo) account));
dialog.close();
} else if (account instanceof InvalidCredentialsException) {
lblCreationWarning.setText(Main.i18n("account.failed.wrong_password"));
} else if (account instanceof NoSelectedCharacterException) {
dialog.close();
} else if (account instanceof Exception) {
lblCreationWarning.setText(account.getClass().toString() + ": " + ((Exception) account).getLocalizedMessage());
lblCreationWarning.setText(AccountsPage.accountException((Exception) account));
}
progressBar.setVisible(false);
@@ -103,7 +103,7 @@ public class ComponentListCell extends StackPane {
VBox container = new VBox();
container.setStyle("-fx-padding: 8 0 0 0;");
FXUtils.limitHeight(container, 0);
FXUtils.setLimitHeight(container, 0);
Rectangle clipRect = new Rectangle();
clipRect.widthProperty().bind(container.widthProperty());
clipRect.heightProperty().bind(container.heightProperty());
@@ -96,7 +96,7 @@ public class MultiFileItem extends ComponentList {
right.setSpacing(3);
right.getChildren().addAll(txtCustom, btnSelect);
custom.setRight(right);
FXUtils.limitHeight(custom, 20);
FXUtils.setLimitHeight(custom, 20);
pane.setStyle("-fx-padding: 0 0 10 0;");
pane.setSpacing(8);
@@ -122,7 +122,7 @@ public class MultiFileItem extends ComponentList {
public Node createChildren(String title, String subtitle, Object userData) {
BorderPane pane = new BorderPane();
pane.setStyle("-fx-padding: 3;");
FXUtils.limitHeight(pane, 20);
FXUtils.setLimitHeight(pane, 20);
JFXRadioButton left = new JFXRadioButton(title);
left.setToggleGroup(group);
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.validation.base.ValidatorBase;
import javafx.beans.NamedArg;
import javafx.scene.control.TextInputControl;
import org.jackhuang.hmcl.util.StringUtils;
@@ -28,11 +29,11 @@ public class NumberValidator extends ValidatorBase {
this(false);
}
public NumberValidator(boolean nullable) {
public NumberValidator(@NamedArg("nullable") boolean nullable) {
this.nullable = nullable;
}
public NumberValidator(String message, boolean nullable) {
public NumberValidator(@NamedArg("message") String message, @NamedArg("nullable") boolean nullable) {
super(message);
this.nullable = nullable;
}
@@ -47,9 +47,6 @@ public class TaskExecutorDialogPane extends StackPane {
public TaskExecutorDialogPane(Runnable cancel) {
FXUtils.loadFXML(this, "/assets/fxml/task-dialog.fxml");
FXUtils.limitHeight(this, 200);
FXUtils.limitWidth(this, 400);
setCancel(cancel);
btnCancel.setOnMouseClicked(e -> {
@@ -64,7 +64,7 @@ public final class DownloadWizardProvider implements WizardProvider {
if (settings.containsKey("optifine"))
builder.version("optifine", (String) settings.get("optifine"));
return builder.buildAsync();
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions());
}
private Task finishModpackInstallingAsync(Map<String, Object> settings) {
@@ -99,9 +99,6 @@ public final class ModpackPage extends StackPane implements WizardPage {
txtModpackName.setText(Main.i18n("modpack.task.install.error"));
}
}
//FXUtils.limitHeight(borderPane, 100.0);
FXUtils.limitWidth(borderPane, 500.0);
}
@Override
@@ -17,14 +17,20 @@
*/
package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXSpinner;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.fxml.FXML;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
import org.jackhuang.hmcl.download.game.GameVersionList;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
@@ -33,7 +39,10 @@ import org.jackhuang.hmcl.ui.wizard.Refreshable;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardPage;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class VersionsPage extends StackPane implements WizardPage, Refreshable {
private final WizardController controller;
@@ -45,7 +54,20 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
@FXML
private JFXListView<VersionsPageItem> list;
@FXML private JFXSpinner spinner;
@FXML
private JFXSpinner spinner;
@FXML
private StackPane failedPane;
@FXML
private JFXCheckBox chkRelease;
@FXML
private JFXCheckBox chkSnapshot;
@FXML
private JFXCheckBox chkOld;
@FXML
private HBox checkPane;
@FXML
private VBox centrePane;
private final TransitionHandler transitionHandler = new TransitionHandler(this);
private final VersionList<?> versionList;
@@ -58,11 +80,20 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
this.downloadProvider = downloadProvider;
this.libraryId = libraryId;
this.callback = callback;
this.versionList = downloadProvider.getVersionListById(libraryId);
FXUtils.loadFXML(this, "/assets/fxml/download/versions.fxml");
getChildren().setAll(spinner);
if (versionList instanceof GameVersionList) {
centrePane.getChildren().setAll(checkPane, list);
} else
centrePane.getChildren().setAll(list);
InvalidationListener listener = o -> list.getItems().setAll(loadVersions());
chkRelease.selectedProperty().addListener(listener);
chkSnapshot.selectedProperty().addListener(listener);
chkOld.selectedProperty().addListener(listener);
list.getSelectionModel().selectedItemProperty().addListener((a, b, newValue) -> {
controller.getSettings().put(libraryId, newValue.getRemoteVersion().getSelfVersion());
callback.run();
@@ -70,17 +101,42 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
refresh();
}
private List<VersionsPageItem> loadVersions() {
boolean isGameVersionList = versionList instanceof GameVersionList;
return versionList.getVersions(gameVersion).stream()
.filter(it -> {
if (isGameVersionList)
switch (((GameRemoteVersionTag) it.getTag()).getType()) {
case RELEASE:
return chkRelease.isSelected();
case SNAPSHOT:
return chkSnapshot.isSelected();
default:
return chkOld.isSelected();
}
else return true;
})
.sorted()
.map(VersionsPageItem::new).collect(Collectors.toList());
}
@Override
public void refresh() {
executor = versionList.refreshAsync(downloadProvider).subscribe(Schedulers.javafx(), () -> {
versionList.getVersions(gameVersion).stream()
.sorted(RemoteVersion.RemoteVersionComparator.INSTANCE)
.forEach(version -> {
list.getItems().add(new VersionsPageItem(version));
});
getChildren().setAll(spinner);
executor = versionList.refreshAsync(downloadProvider).finalized((variables, isDependentsSucceeded) -> {
if (isDependentsSucceeded) {
List<VersionsPageItem> items = loadVersions();
transitionHandler.setContent(list, ContainerAnimations.FADE.getAnimationProducer());
});
Platform.runLater(() -> {
list.getItems().setAll(items);
transitionHandler.setContent(centrePane, ContainerAnimations.FADE.getAnimationProducer());
});
} else {
Platform.runLater(() -> {
transitionHandler.setContent(failedPane, ContainerAnimations.FADE.getAnimationProducer());
});
}
}).executor().start();
}
@Override
@@ -94,4 +150,9 @@ public final class VersionsPage extends StackPane implements WizardPage, Refresh
if (executor != null)
executor.cancel();
}
@FXML
private void onRefresh() {
refresh();
}
}
@@ -20,7 +20,9 @@ package org.jackhuang.hmcl.ui.download;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.GameRemoteVersionTag;
import org.jackhuang.hmcl.ui.FXUtils;
/**
@@ -38,7 +40,22 @@ public final class VersionsPageItem extends StackPane {
FXUtils.loadFXML(this, "/assets/fxml/download/versions-list-item.fxml");
lblSelfVersion.setText(remoteVersion.getSelfVersion());
lblGameVersion.setText(remoteVersion.getGameVersion());
if (remoteVersion.getTag() instanceof GameRemoteVersionTag) {
switch (((GameRemoteVersionTag) remoteVersion.getTag()).getType()) {
case RELEASE:
lblGameVersion.setText(Main.i18n("version.game.release"));
break;
case SNAPSHOT:
lblGameVersion.setText(Main.i18n("version.game.snapshot"));
break;
default:
lblGameVersion.setText(Main.i18n("version.game.old"));
break;
}
} else {
lblGameVersion.setText(remoteVersion.getGameVersion());
}
}
public RemoteVersion<?> getRemoteVersion() {
+2 -45
View File
@@ -4,7 +4,7 @@
*/
.root {
-fx-font-family: "微软雅黑", "Roboto";
-fx-font-family: "微软雅黑", "Microsoft YaHei", "Roboto";
}
.disabled Label {
@@ -899,6 +899,7 @@
.jfx-spinner > .arc {
-fx-stroke-width: 3.0;
-fx-fill: transparent;
-fx-stroke: -fx-base-color;
}
.first-spinner {
@@ -957,50 +958,6 @@
-fx-stroke-width: 5.0;
}
.blue-spinner .arc {
-fx-stroke: #4285f4;
}
.red-spinner .arc {
-fx-stroke: #db4437;
}
.green-spinner .arc {
-fx-stroke: #f4b400;
}
.yellow-spinner .arc {
-fx-stroke: -fx-base-check-color;
}
.materialDesign-purple .arc {
-fx-stroke: #ab47bc;
}
.materialDesign-blue .arc {
-fx-stroke: #2962ff;
}
.materialDesign-cyan .arc {
-fx-stroke: #00b8d4;
}
.materialDesign-green .arc {
-fx-stroke: #00c853;
}
.materialDesign-yellow .arc {
-fx-stroke: #ffd600;
}
.materialDesign-orange .arc {
-fx-stroke: #ff6d00;
}
.materialDesign-red .arc {
-fx-stroke: #d50000;
}
/*******************************************************************************
* *
* JFX Combo Box *
@@ -8,9 +8,10 @@
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
type="StackPane" FXUtils.limitWidth="160" FXUtils.limitHeight="156">
<VBox fx:id="content">
<StackPane fx:id="header" VBox.vgrow="ALWAYS">
<BorderPane>
@@ -6,7 +6,7 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.*?>
<?import java.lang.String?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
@@ -38,13 +38,13 @@
<JFXComboBox fx:id="cboType" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<JFXTextField fx:id="txtUsername" promptText="%account.username" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2">
<JFXTextField fx:id="txtUsername" promptText="%account.username" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
</validators>
</JFXTextField>
<JFXPasswordField fx:id="txtPassword" promptText="%account.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2">
<JFXPasswordField fx:id="txtPassword" promptText="%account.password" labelFloat="true" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnSpan="2" FXUtils.validateWhileTextChanged="true">
<validators>
<RequiredFieldValidator message="%input.not_empty">
</RequiredFieldValidator>
@@ -7,6 +7,7 @@
<?import javafx.scene.shape.Rectangle?>
<?import org.jackhuang.hmcl.ui.construct.AdvancedListBox?>
<?import java.lang.String?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
type="StackPane"
xmlns:fx="http://javafx.com/fxml">
@@ -18,7 +19,7 @@
maxHeight="519"
maxWidth="800">
<center>
<StackPane fx:id="drawerWrapper" styleClass="jfx-decorator-drawer">
<StackPane fx:id="drawerWrapper" styleClass="jfx-decorator-drawer" FXUtils.overflowHidden="true">
<JFXDialog fx:id="dialog" overlayClose="false" />
<BorderPane>
<left>
@@ -47,7 +48,7 @@
</StackPane>
</left>
<center>
<StackPane fx:id="contentPlaceHolderRoot" styleClass="jfx-decorator-content-container"
<StackPane fx:id="contentPlaceHolderRoot" styleClass="jfx-decorator-content-container" FXUtils.overflowHidden="true"
VBox.vgrow="ALWAYS">
<StackPane fx:id="contentPlaceHolder" styleClass="jfx-decorator-content-container">
<!-- Node -->
@@ -29,7 +29,7 @@
</JFXListView>
</StackPane>
<HBox alignment="BOTTOM_CENTER" style="-fx-padding: 20;">
<HBox alignment="BOTTOM_CENTER" style="-fx-padding: 20;" pickOnBounds="false">
<Label text="%modpack.introduction" />
</HBox>
</fx:root>
@@ -6,13 +6,14 @@
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
<fx:define>
<Insets fx:id="insets" bottom="12" />
</fx:define>
<VBox fx:id="borderPane" alignment="CENTER">
<VBox fx:id="borderPane" alignment="CENTER" FXUtils.limitWidth="500">
<HBox style="-fx-padding: 0 0 16 5;"><Label text="%modpack.task.install" /></HBox>
<ComponentList>
<BorderPane><left><Label text="%modpack.task.install.will" /></left><right><Label fx:id="lblModpackLocation" /></right></BorderPane>
@@ -3,11 +3,25 @@
<?import com.jfoenix.controls.JFXListView?>
<?import com.jfoenix.controls.JFXSpinner?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?>
<?import com.jfoenix.controls.JFXCheckBox?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane"
prefHeight="400.0" prefWidth="600.0">
<JFXSpinner fx:id="spinner" style="-fx-radius:16" styleClass="materialDesign-purple, first-spinner" />
<JFXListView fx:id="list" styleClass="jfx-list-view">
</JFXListView>
<JFXSpinner fx:id="spinner" styleClass="first-spinner" />
<VBox fx:id="centrePane">
<HBox fx:id="checkPane" spacing="10" style="-fx-padding: 10;">
<JFXCheckBox fx:id="chkRelease" text="%version.game.release" selected="true" />
<JFXCheckBox fx:id="chkSnapshot" text="%version.game.snapshot" />
<JFXCheckBox fx:id="chkOld" text="%version.game.old" />
</HBox>
<JFXListView fx:id="list" styleClass="jfx-list-view" VBox.vgrow="ALWAYS">
</JFXListView>
</VBox>
<StackPane fx:id="failedPane">
<Label onMouseClicked="#onRefresh" style="-fx-text-fill: #0079FF; -fx-font-size: 20;" text="%download.failed.refresh" />
</StackPane>
</fx:root>
@@ -8,6 +8,8 @@
<?import org.jackhuang.hmcl.ui.construct.FileItem?>
<?import org.jackhuang.hmcl.ui.construct.FontComboBox?>
<?import org.jackhuang.hmcl.ui.construct.MultiFileItem?>
<?import org.jackhuang.hmcl.ui.construct.NumberValidator?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
@@ -38,7 +40,7 @@
<MultiFileItem fx:id="backgroundItem" title="%launcher.background" chooserTitle="%launcher.background.choose"
hasSubtitle="true" customText="%settings.custom" />
<BorderPane><left><Label text="%settings.launcher.download_source" BorderPane.alignment="CENTER_LEFT" /></left><right><JFXComboBox fx:id="cboDownloadSource">
<BorderPane><left><Label text="%settings.launcher.download_source" BorderPane.alignment="CENTER_LEFT" /></left><right><JFXComboBox fx:id="cboDownloadSource" FXUtils.limitWidth="400">
<items>
<FXCollections fx:factory="observableArrayList">
<Label text="%download.mojang" />
@@ -46,26 +48,69 @@
</FXCollections>
</items>
</JFXComboBox></right></BorderPane>
<BorderPane><left><Label text="%settings.launcher.language" BorderPane.alignment="CENTER_LEFT" /></left><right><JFXComboBox fx:id="cboLanguage" /></right></BorderPane>
<BorderPane><left><Label text="%settings.launcher.proxy" BorderPane.alignment="CENTER_LEFT" /></left><right><HBox alignment="CENTER_LEFT" spacing="5">
<JFXComboBox fx:id="cboProxyType">
<items>
<FXCollections fx:factory="observableArrayList">
<Label text="%settings.launcher.proxy.direct" />
<Label text="HTTP" />
<Label text="Socks" />
</FXCollections>
</items>
</JFXComboBox>
<Label text="%settings.launcher.proxy.host" />
<JFXTextField fx:id="txtProxyHost" maxWidth="50" />
<Label text="%settings.launcher.proxy.port" />
<JFXTextField fx:id="txtProxyPort" maxWidth="50" />
<Label text="%settings.launcher.proxy.username" />
<JFXTextField fx:id="txtProxyUsername" maxWidth="50" />
<Label text="%settings.launcher.proxy.password" />
<JFXTextField fx:id="txtProxyPassword" maxWidth="50" />
</HBox></right></BorderPane>
<BorderPane>
<left>
<Label text="%settings.launcher.language" BorderPane.alignment="CENTER_LEFT" />
</left>
<right>
<JFXComboBox fx:id="cboLanguage" FXUtils.limitWidth="400" />
</right>
</BorderPane>
<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" />
<VBox fx:id="proxyPane" style="-fx-padding: 0 0 0 30;">
<HBox>
<JFXRadioButton fx:id="chkProxyHttp" text="%settings.launcher.proxy.http" />
<JFXRadioButton fx:id="chkProxySocks" text="%settings.launcher.proxy.socks" />
</HBox>
<GridPane hgap="20" vgap="10" style="-fx-padding: 0 0 0 15;">
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<Label text="%settings.launcher.proxy.host" GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.halignment="RIGHT" />
<JFXTextField fx:id="txtProxyHost" styleClass="fit-width" GridPane.rowIndex="1" GridPane.columnIndex="1" />
<Label text="%settings.launcher.proxy.port" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.halignment="RIGHT" />
<JFXTextField fx:id="txtProxyPort" styleClass="fit-width" GridPane.rowIndex="2" GridPane.columnIndex="1" FXUtils.validateWhileTextChanged="true">
<validators>
<NumberValidator message="%input.number" nullable="false" />
</validators>
</JFXTextField>
</GridPane>
<VBox style="-fx-padding: 20 0 20 5;">
<JFXCheckBox fx:id="chkProxyAuthentication" text="%settings.launcher.proxy.authentication" />
</VBox>
<GridPane fx:id="authPane" hgap="20" vgap="10" style="-fx-padding: 0 0 0 15;">
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<Label fx:id="" text="%settings.launcher.proxy.username" GridPane.rowIndex="0" GridPane.columnIndex="0" />
<JFXTextField fx:id="txtProxyUsername" styleClass="fit-width" GridPane.rowIndex="0" GridPane.columnIndex="1" />
<Label text="%settings.launcher.proxy.password" GridPane.rowIndex="1" GridPane.columnIndex="0" />
<JFXPasswordField fx:id="txtProxyPassword" styleClass="fit-width" GridPane.rowIndex="1" GridPane.columnIndex="1" />
</GridPane>
</VBox>
</VBox>
</ComponentList>
<MultiFileItem fx:id="themeItem" title="%settings.launcher.theme" hasSubtitle="true" hasCustom="false" />
@@ -88,17 +133,17 @@
<RowConstraints minHeight="-Infinity" valignment="TOP" vgrow="SOMETIMES" />
<RowConstraints minHeight="-Infinity" valignment="TOP" vgrow="SOMETIMES" />
</rowConstraints>
<Label text="%about.copyright" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<Label text="%about.copyright" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<Label wrapText="true" text="%about.copyright.statement" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<Label text="%about.author" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label text="%about.author" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label wrapText="true" text="%about.author.statement" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="%about.thanks_to" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<Label text="%about.thanks_to" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<Label wrapText="true" text="%about.thanks_to.statement" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="%about.dependency" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="3" />
<Label text="%about.dependency" GridPane.columnIndex="0" GridPane.rowIndex="3" />
<Label wrapText="true" text="%about.dependency.statement" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Label text="%about.claim" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="4" />
<Label text="%about.claim" GridPane.columnIndex="0" GridPane.rowIndex="4" />
<Label textAlignment="JUSTIFY" wrapText="true" text="%about.claim.statement" GridPane.columnIndex="1" GridPane.rowIndex="4" />
<Label text="%about.open_source" style="-fx-font-weight: bold;" GridPane.columnIndex="0" GridPane.rowIndex="5" />
<Label text="%about.open_source" GridPane.columnIndex="0" GridPane.rowIndex="5" />
<Label wrapText="true" text="%about.open_source.statement" GridPane.columnIndex="1" GridPane.rowIndex="5" />
</GridPane>
</StackPane>
@@ -10,9 +10,10 @@
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ScrollPane?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane">
type="StackPane" FXUtils.limitWidth="400" FXUtils.limitHeight="200">
<BorderPane>
<top>
<JFXProgressBar fx:id="progressBar" StackPane.alignment="TOP_CENTER" />
@@ -6,10 +6,10 @@
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Tooltip?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
type="StackPane" pickOnBounds="false">
type="StackPane" pickOnBounds="false" FXUtils.limitWidth="160" FXUtils.limitHeight="156">
<VBox fx:id="content" pickOnBounds="false">
<StackPane fx:id="header" VBox.vgrow="ALWAYS" pickOnBounds="false" style="-fx-background-radius: 2 2 0 0; -fx-background-color: rgb(255,255,255,0.87); -fx-padding: 8;">
<BorderPane>
@@ -21,6 +21,7 @@
<VBox style="-fx-padding: 8 8 0 8;">
<Label fx:id="lblVersionName" style="-fx-font-size: 15;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblGameVersion" style="-fx-font-size: 10;" textAlignment="JUSTIFY" wrapText="true" />
<Label fx:id="lblLibraries" style="-fx-font-size: 10; -fx-text-fill: gray;" textAlignment="JUSTIFY" wrapText="true" />
</VBox>
</center>
</BorderPane>
@@ -5,6 +5,7 @@
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<fx:root xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
styleClass="transparent"
@@ -13,10 +14,10 @@
<BorderPane pickOnBounds="false">
<left>
<HBox alignment="CENTER" mouseTransparent="true">
<StackPane fx:id="imageViewContainer">
<ImageView preserveRatio="true" fx:id="imageView" smooth="false">
<Image url="/assets/img/icon.png" />
</ImageView>
<StackPane fx:id="imageViewContainer" FXUtils.limitWidth="32" FXUtils.limitHeight="32">
<ImageView preserveRatio="true" fx:id="imageView" smooth="false">
<Image url="/assets/img/icon.png" />
</ImageView>
</StackPane>
<BorderPane style="-fx-padding: 0 0 0 10;">
<top>
@@ -9,6 +9,8 @@
<?import javafx.scene.layout.*?>
<?import org.jackhuang.hmcl.ui.construct.ComponentList?>
<?import org.jackhuang.hmcl.ui.construct.MultiFileItem?>
<?import org.jackhuang.hmcl.ui.construct.NumberValidator?>
<?import org.jackhuang.hmcl.ui.FXUtils?>
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jackhuang.hmcl.ui.VersionSettingsController">
@@ -56,7 +58,11 @@
</VBox>
</left>
<right>
<JFXTextField fx:id="txtMaxMemory" BorderPane.alignment="CENTER_RIGHT"/>
<JFXTextField fx:id="txtMaxMemory" BorderPane.alignment="CENTER_RIGHT" FXUtils.validateWhileTextChanged="true" FXUtils.limitWidth="300">
<validators>
<NumberValidator message="%input.number" nullable="false" />
</validators>
</JFXTextField>
</right>
</BorderPane>
@@ -65,7 +71,7 @@
<Label text="%settings.advanced.launcher_visible" BorderPane.alignment="CENTER_LEFT"/>
</left>
<right>
<JFXComboBox fx:id="cboLauncherVisibility" BorderPane.alignment="CENTER_RIGHT">
<JFXComboBox fx:id="cboLauncherVisibility" BorderPane.alignment="CENTER_RIGHT" FXUtils.limitWidth="300">
<items>
<FXCollections fx:factory="observableArrayList">
<Label text="%settings.advanced.launcher_visibility.close"/>
@@ -86,9 +92,17 @@
<BorderPane>
<left>
<HBox prefWidth="210" spacing="3" alignment="CENTER" BorderPane.alignment="CENTER">
<JFXTextField fx:id="txtWidth" promptText="800" prefWidth="100"/>
<JFXTextField fx:id="txtWidth" promptText="800" prefWidth="100" FXUtils.validateWhileTextChanged="true">
<validators>
<NumberValidator message="%input.number" nullable="false" />
</validators>
</JFXTextField>
<Label>x</Label>
<JFXTextField fx:id="txtHeight" promptText="480" prefWidth="100"/>
<JFXTextField fx:id="txtHeight" promptText="480" prefWidth="100" FXUtils.validateWhileTextChanged="true">
<validators>
<NumberValidator message="%input.number" nullable="false" />
</validators>
</JFXTextField>
</HBox>
</left>
<right>
@@ -108,7 +122,7 @@
<Label text="%settings.show_log"/>
</left>
<right>
<JFXToggleButton fx:id="chkShowLogs" size="7"/>
<JFXToggleButton fx:id="chkShowLogs" size="7" FXUtils.limitHeight="10" />
</right>
</BorderPane>
</ComponentList>
@@ -128,7 +142,11 @@
<JFXTextField labelFloat="true" promptText="%settings.advanced.minecraft_arguments"
styleClass="fit-width" fx:id="txtGameArgs" StackPane.margin="$insets"/>
<JFXTextField labelFloat="true" promptText="%settings.advanced.java_permanent_generation_space"
styleClass="fit-width" fx:id="txtMetaspace" StackPane.margin="$insets"/>
styleClass="fit-width" fx:id="txtMetaspace" StackPane.margin="$insets" FXUtils.validateWhileTextChanged="true">
<validators>
<NumberValidator message="%input.number" nullable="true" />
</validators>
</JFXTextField>
<JFXTextField labelFloat="true" promptText="%settings.advanced.wrapper_launcher" styleClass="fit-width"
fx:id="txtWrapper" StackPane.margin="$insets"/>
<JFXTextField labelFloat="true" promptText="%settings.advanced.precall_command" styleClass="fit-width"
@@ -140,7 +158,7 @@
<Label text="%settings.advanced.no_jvm_args"/>
</left>
<right>
<JFXToggleButton fx:id="chkNoJVMArgs" size="7"/>
<JFXToggleButton fx:id="chkNoJVMArgs" size="7" FXUtils.limitHeight="10" />
</right>
</BorderPane>
<BorderPane>
@@ -148,7 +166,7 @@
<Label text="%settings.advanced.no_common"/>
</left>
<right>
<JFXToggleButton fx:id="chkNoCommon" size="7"/>
<JFXToggleButton fx:id="chkNoCommon" size="7" FXUtils.limitHeight="10" />
</right>
</BorderPane>
<BorderPane>
@@ -156,7 +174,7 @@
<Label text="%settings.advanced.dont_check_game_completeness"/>
</left>
<right>
<JFXToggleButton fx:id="chkNoGameCheck" size="7"/>
<JFXToggleButton fx:id="chkNoGameCheck" size="7" FXUtils.limitHeight="10" />
</right>
</BorderPane>
</ComponentList>
@@ -34,10 +34,10 @@ account.current=Current account
account.create=Create a new account
account.email=Email
account.failed.connect_authentication_server=Cannot connect the authentication server. Check your network.
account.failed.invalid_credentials=Incorrect password. Or forbidden to log in temporarily.
account.failed.invalid_password=Invalid password
account.failed.invalid_token=Please log out and re-input your password to log in.
account.failed.no_charactor=No character in this account.
account.failed.wrong_password=Incorrect password or username
account.methods=Login Type
account.methods.no_method=No login method
account.methods.offline=Offline
@@ -105,6 +105,7 @@ crash.user_fault=Your OS or Java environment may not be properly installed resul
download=Download
download.BMCL=BMCLAPI (bangbang93, http://bmclapi.bangbang93.com/)
download.failed=Failed to download
download.failed.refresh=Unable to load version list. Click here to retry.
download.mojang=Mojang
download.not_200=Failed to download, the response code is %s.
download.source=Download Source
@@ -124,6 +125,7 @@ folder.saves=Saves
folder.screenshots=Screenshots
input.email=The username must be an e-mail.
input.number=Must be a number.
input.not_empty=Input Requrired!
install=Install New Game
@@ -295,13 +297,14 @@ operation.confirm_stop=Terminate the operations?
operation.stopped=The operation was aborted.
profile=Profile
profile.copy_from=Copy From:
profile.default=Current directory
profile.home=User home
profile.instance_directory=Game Directory
profile.new=New Config
profile.new_name=New Profile Name:
profile.remove=Sure to remove profile %s?
profile.title=Game Directories
profile.selected=Current directory
profile.selected=Selected
selector.choose=Choose
selector.choose_file=Select a file
@@ -350,10 +353,14 @@ settings.launcher.download_source=Download Source
settings.launcher.language=Language
settings.launcher.log_font=Log Font
settings.launcher.proxy=Proxy
settings.launcher.proxy.direct=Direct
settings.launcher.proxy.authentication=Proxy Authentication
settings.launcher.proxy.no_proxy=No proxy
settings.launcher.proxy.has_proxy=Proxy configuration
settings.launcher.proxy.host=Host
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=Password
settings.launcher.proxy.port=Port
settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=Account
settings.launcher.theme=Theme
@@ -381,8 +388,7 @@ update.tooltip=Update
version.cannot_read=Unable to gather the game version. Cannot continue auto-installing.
version.forbidden_name=Forbidden name, do not use this.
version.game.old_alpha=Old Alpha
version.game.old_beta=Beta
version.game.old=Old
version.game.release=Release
version.game.snapshot=Snapshot
version.launch=Play
@@ -34,10 +34,10 @@ account.current=当前账户
account.create=新建账户
account.email=邮箱
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题
account.failed.invalid_credentials=您的用户名或密码错误,或者登录次数过多被暂时禁止登录,请稍后再试
account.failed.invalid_password=无效的密码
account.failed.invalid_token=请尝试登出并重新输入密码登录
account.failed.no_charactor=该帐号没有角色
account.failed.wrong_password=可能是您的用户名或密码错误
account.methods=登录方式
account.methods.no_method=没有登入方式...
account.methods.offline=离线模式
@@ -105,6 +105,7 @@ crash.user_fault=您的系统或Java环境可能安装不当导致本软件崩
download=下载
download.BMCL=BMCLAPI (bangbang93, http://bmclapi.bangbang93.com/)
download.failed=下载失败
download.failed.refresh=加载版本列表失败,点击此处重试。
download.mojang=官方
download.not_200=下载失败,HTTP状态码:%s.
download.source=下载源
@@ -124,6 +125,7 @@ folder.saves=存档文件夹
folder.screenshots=截图文件夹
input.email=用户名必须是邮箱
input.number=必须是数字
input.not_empty=必填项
install=添加游戏
@@ -295,13 +297,14 @@ operation.confirm_stop=真的要终止操作吗?
operation.stopped=操作被强行终止
profile=配置
profile.copy_from=复制配置:
profile.default=当前目录
profile.home=主文件夹
profile.instance_directory=游戏路径
profile.new=新建配置
profile.new_name=新配置名:
profile.remove=真的要删除配置%s吗?
profile.title=游戏目录
profile.selected=当前目录
profile.selected=已选中
selector.choose=选择
selector.choose_file=选择文件
@@ -350,10 +353,14 @@ settings.launcher.download_source=下载源
settings.launcher.language=语言
settings.launcher.log_font=日志字体
settings.launcher.proxy=代理
settings.launcher.proxy.direct=直连
settings.launcher.proxy.authentication=代理账户
settings.launcher.proxy.no_proxy=直连
settings.launcher.proxy.has_proxy=启用验证
settings.launcher.proxy.host=主机
settings.launcher.proxy.http=HTTP
settings.launcher.proxy.password=密码
settings.launcher.proxy.port=端口
settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=账户
settings.launcher.theme=主题
@@ -381,10 +388,9 @@ update.tooltip=更新
version.cannot_read=读取游戏版本失败,无法进行自动安装
version.forbidden_name=此版本名称不受支持,请换一个名字
version.game.old_alpha=远古版
version.game.old_beta=测试版
version.game.old=远古版
version.game.release=稳定版
version.game.snapshot=快照版
version.game.snapshot=测试版
version.launch=启动游戏
version.launch_script=生成启动脚本
version.launch_script.failed=生成启动脚本失败
@@ -57,6 +57,14 @@ public abstract class Account {
*/
public abstract AuthInfo logIn(MultiCharacterSelector selector, Proxy proxy) throws AuthenticationException;
public abstract boolean canPlayOffline();
/**
* Play offline.
* @return the specific offline player's info.
*/
public abstract AuthInfo playOffline();
public abstract void logOut();
protected abstract Map<Object, Object> toStorageImpl();
@@ -69,6 +69,16 @@ public class OfflineAccount extends Account {
// Offline account need not log out.
}
@Override
public boolean canPlayOffline() {
return false;
}
@Override
public AuthInfo playOffline() {
throw new IllegalStateException();
}
@Override
public Map<Object, Object> toStorageImpl() {
return Lang.mapOf(
@@ -171,6 +171,19 @@ public final class YggdrasilAccount extends Account {
userProperties.putAll(user.getProperties());
}
@Override
public boolean canPlayOffline() {
return isLoggedIn() && getSelectedProfile() != null && !canPlayOnline();
}
@Override
public AuthInfo playOffline() {
if (!canPlayOffline())
throw new IllegalStateException("Current account " + this + " cannot play offline.");
return new AuthInfo(selectedProfile, accessToken, userType, GSON.toJson(userProperties));
}
@Override
public void logOut() {
password = null;
@@ -62,7 +62,7 @@ public class DefaultGameBuilder extends GameBuilder {
downloadGameAsync(gameVersion, version),
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version)); // using [with] because download failure here are tolerant.
if (toolVersions.containsKey("forge"))
result = result.then(libraryTaskHelper(gameVersion, "forge"));
if (toolVersions.containsKey("liteloader"))
@@ -70,12 +70,9 @@ public class DefaultGameBuilder extends GameBuilder {
if (toolVersions.containsKey("optifine"))
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
return result;
}).finalized(new Task() {
@Override
public void execute() {
if (!isDependentsSucceeded())
dependencyManager.getGameRepository().getVersionRoot(name).delete();
}
}).finalized((variables, isDependentsSucceeded) -> {
if (!isDependentsSucceeded)
dependencyManager.getGameRepository().getVersionRoot(name).delete();
});
}
@@ -78,20 +78,6 @@ public class RemoteVersion<T> implements Comparable<RemoteVersion<T>> {
@Override
public int compareTo(RemoteVersion<T> o) {
// newer versions are smaller than older versions
return -selfVersion.compareTo(o.selfVersion);
}
public static class RemoteVersionComparator implements Comparator<RemoteVersion<?>> {
public static final RemoteVersionComparator INSTANCE = new RemoteVersionComparator();
private RemoteVersionComparator() {
}
@Override
public int compare(RemoteVersion<?> o1, RemoteVersion<?> o2) {
return -VersionNumber.asVersion(o1.selfVersion).compareTo(VersionNumber.asVersion(o2.selfVersion));
}
return -VersionNumber.asVersion(selfVersion).compareTo(VersionNumber.asVersion(o.selfVersion));
}
}
@@ -56,10 +56,7 @@ public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
GameRemoteVersions root = Constants.GSON.fromJson(task.getResult(), GameRemoteVersions.class);
for (GameRemoteVersion remoteVersion : root.getVersions()) {
Optional<String> gameVersion = VersionNumber.parseVersion(remoteVersion.getGameVersion());
if (!gameVersion.isPresent())
continue;
versions.put(gameVersion.get(), new RemoteVersion<>(
versions.put(remoteVersion.getGameVersion(), new RemoteVersionGame(
remoteVersion.getGameVersion(),
remoteVersion.getGameVersion(),
remoteVersion.getUrl(),
@@ -70,4 +67,14 @@ public final class GameVersionList extends VersionList<GameRemoteVersionTag> {
};
}
private static class RemoteVersionGame extends RemoteVersion<GameRemoteVersionTag> {
public RemoteVersionGame(String gameVersion, String selfVersion, String url, GameRemoteVersionTag tag) {
super(gameVersion, selfVersion, url, tag);
}
@Override
public int compareTo(RemoteVersion<GameRemoteVersionTag> o) {
return -getTag().getTime().compareTo(o.getTag().getTime());
}
}
}
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.mod;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.ImmediateBooleanProperty;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
@@ -59,11 +60,16 @@ public final class ModInfo implements Comparable<ModInfo> {
activeProperty = new ImmediateBooleanProperty(this, "active", !DISABLED_EXTENSION.equals(FileUtils.getExtension(file))) {
@Override
protected void invalidated() {
File f = file.getAbsoluteFile(), newF;
File f = ModInfo.this.file.getAbsoluteFile(), newF;
if (DISABLED_EXTENSION.equals(FileUtils.getExtension(f)))
newF = new File(f.getParentFile(), FileUtils.getNameWithoutExtension(f));
else
newF = new File(f.getParentFile(), f.getName() + ".disabled");
if (f.renameTo(newF))
ModInfo.this.file = newF;
else
Logging.LOG.severe("Unable to rename file " + f + " to " + newF);
}
};
@@ -0,0 +1,24 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.AutoTypingMap;
public interface FinalizedCallback {
void execute(AutoTypingMap<String> variables, boolean isDependentsSucceeded) throws Exception;
}
@@ -0,0 +1,78 @@
/*
* 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.task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.ExceptionalFunction;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
final class FinalizedTask extends Task {
private final Collection<Task> dependents;
private final FinalizedCallback callback;
private final Scheduler scheduler;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param pred the task that runs before succ.
* @param callback a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
*/
public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback) {
this.dependents = Collections.singleton(pred);
this.scheduler = scheduler;
this.callback = callback;
setSignificance(TaskSignificance.MODERATE);
setName(callback.toString());
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
setName(callback.toString());
callback.execute(getVariables(), isDependentsSucceeded());
if (!isDependentsSucceeded())
throw new SilentException();
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
}
@@ -260,12 +260,12 @@ public abstract class Task {
return new CoupleTask<>(this, b, false, false);
}
public final Task finalized(Task b) {
return finalized(convert(b));
public final Task finalized(FinalizedCallback b) {
return finalized(Schedulers.defaultScheduler(), b);
}
public final Task finalized(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
return new CoupleTask<>(this, b, false, true);
public final Task finalized(Scheduler scheduler, FinalizedCallback b) {
return new FinalizedTask(this, scheduler, b);
}
public static Task empty() {