make launch script
This commit is contained in:
@@ -39,11 +39,11 @@ import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.DialogController;
|
||||
import org.jackhuang.hmcl.ui.LaunchingStepsPane;
|
||||
import org.jackhuang.hmcl.ui.LogWindow;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||
import org.jackhuang.hmcl.util.ManagedProcess;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageBox;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.Buffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -55,7 +55,7 @@ public final class LauncherHelper {
|
||||
private final LaunchingStepsPane launchingStepsPane = new LaunchingStepsPane();
|
||||
public static final Queue<ManagedProcess> PROCESSES = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public void launch(String selectedVersion) {
|
||||
public void launch(String selectedVersion, String launcherName) {
|
||||
Profile profile = Settings.INSTANCE.getSelectedProfile();
|
||||
GameRepository repository = profile.getRepository();
|
||||
DefaultDependencyManager dependencyManager = profile.getDependency();
|
||||
@@ -86,16 +86,26 @@ public final class LauncherHelper {
|
||||
repository, selectedVersion, variables.get("account"), setting.toLaunchOptions(profile.getGameDir()), new HMCLProcessListener(variables.get("account"), setting)
|
||||
));
|
||||
}))
|
||||
.then(variables -> variables.<DefaultLauncher>get("launcher").launchAsync())
|
||||
.then(variables -> {
|
||||
if (launcherName == null) {
|
||||
return variables.<DefaultLauncher>get("launcher").launchAsync();
|
||||
} else {
|
||||
variables.set("script", variables.<DefaultLauncher>get("launcher").makeLaunchScript(launcherName));
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.then(Task.of(variables -> {
|
||||
PROCESSES.add(variables.get(DefaultLauncher.LAUNCH_ASYNC_ID));
|
||||
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
|
||||
Main.stopApplication();
|
||||
if (launcherName == null) {
|
||||
PROCESSES.add(variables.get(DefaultLauncher.LAUNCH_ASYNC_ID));
|
||||
if (setting.getLauncherVisibility() == LauncherVisibility.CLOSE)
|
||||
Main.stopApplication();
|
||||
}
|
||||
}))
|
||||
.executor();
|
||||
|
||||
executor.setTaskListener(new TaskListener() {
|
||||
executor.addTaskListener(new TaskListener() {
|
||||
AtomicInteger finished = new AtomicInteger(0);
|
||||
|
||||
@Override
|
||||
public void onFinished(Task task) {
|
||||
finished.incrementAndGet();
|
||||
@@ -105,14 +115,43 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
Platform.runLater(Controllers::closeDialog);
|
||||
public void onTerminate(AutoTypingMap<String> variables) {
|
||||
Platform.runLater(() -> {
|
||||
Controllers.closeDialog();
|
||||
|
||||
if (variables.containsKey("script")) {
|
||||
Controllers.dialog(Main.i18n("version.launch_script.success", variables.<File>get("script").getAbsolutePath()));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
executor.start();
|
||||
}
|
||||
|
||||
private void checkGameState(VersionSetting setting) throws InterruptedException {
|
||||
JavaVersion java = setting.getJavaVersion();
|
||||
if (java == null) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
if (java.getParsedVersion() < JavaVersion.JAVA_8) {
|
||||
MessageBox.show(Main.i18n("launch.advice.newer_java"));
|
||||
}
|
||||
if (java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
|
||||
org.jackhuang.hmcl.util.Platform.IS_64_BIT) {
|
||||
MessageBox.show(Main.i18n("launch.advice.different_platform"));
|
||||
}
|
||||
if (java.getPlatform() == org.jackhuang.hmcl.util.Platform.BIT_32 &&
|
||||
setting.getMaxMemory() > 1.5 * 1024) {
|
||||
MessageBox.show(Main.i18n("launch.advice.too_large_memory_for_32bit"));
|
||||
}
|
||||
if (OperatingSystem.TOTAL_MEMORY > 0 && OperatingSystem.TOTAL_MEMORY < setting.getMaxMemory()) {
|
||||
MessageBox.show(Main.i18n("launch.advice.not_enough_space"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopManagedProcesses() {
|
||||
synchronized (PROCESSES) {
|
||||
while (!PROCESSES.isEmpty())
|
||||
|
||||
@@ -46,6 +46,12 @@ public final class VersionSetting {
|
||||
this.global = global;
|
||||
}
|
||||
|
||||
private final ImmediateBooleanProperty usesGlobalProperty = new ImmediateBooleanProperty(this, "usesGlobal", false);
|
||||
|
||||
public ImmediateBooleanProperty usesGlobalProperty() {
|
||||
return usesGlobalProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* HMCL Version Settings have been divided into 2 parts.
|
||||
* 1. Global settings.
|
||||
@@ -54,12 +60,6 @@ public final class VersionSetting {
|
||||
*
|
||||
* Defaults false because if one version uses global first, custom version file will not be generated.
|
||||
*/
|
||||
private final ImmediateBooleanProperty usesGlobalProperty = new ImmediateBooleanProperty(this, "usesGlobal", false);
|
||||
|
||||
public ImmediateBooleanProperty usesGlobalProperty() {
|
||||
return usesGlobalProperty;
|
||||
}
|
||||
|
||||
public boolean isUsesGlobal() {
|
||||
return usesGlobalProperty.get();
|
||||
}
|
||||
@@ -70,15 +70,15 @@ public final class VersionSetting {
|
||||
|
||||
// java
|
||||
|
||||
/**
|
||||
* Java version or null if user customizes java directory.
|
||||
*/
|
||||
private final ImmediateStringProperty javaProperty = new ImmediateStringProperty(this, "java", "");
|
||||
|
||||
public ImmediateStringProperty javaProperty() {
|
||||
return javaProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Java version or null if user customizes java directory.
|
||||
*/
|
||||
public String getJava() {
|
||||
return javaProperty.get();
|
||||
}
|
||||
@@ -87,15 +87,15 @@ public final class VersionSetting {
|
||||
javaProperty.set(java);
|
||||
}
|
||||
|
||||
/**
|
||||
* User customized java directory or null if user uses system Java.
|
||||
*/
|
||||
private final ImmediateStringProperty javaDirProperty = new ImmediateStringProperty(this, "javaDir", "");
|
||||
|
||||
public ImmediateStringProperty javaDirProperty() {
|
||||
return javaDirProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* User customized java directory or null if user uses system Java.
|
||||
*/
|
||||
public String getJavaDir() {
|
||||
return javaDirProperty.get();
|
||||
}
|
||||
@@ -104,15 +104,15 @@ public final class VersionSetting {
|
||||
javaDirProperty.set(javaDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* The command to launch java, i.e. optirun.
|
||||
*/
|
||||
private final ImmediateStringProperty wrapperProperty = new ImmediateStringProperty(this, "wrapper", "");
|
||||
|
||||
public ImmediateStringProperty wrapperProperty() {
|
||||
return wrapperProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command to launch java, i.e. optirun.
|
||||
*/
|
||||
public String getWrapper() {
|
||||
return wrapperProperty.get();
|
||||
}
|
||||
@@ -121,15 +121,15 @@ public final class VersionSetting {
|
||||
wrapperProperty.set(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* The permanent generation size of JVM garbage collection.
|
||||
*/
|
||||
private final ImmediateStringProperty permSizeProperty = new ImmediateStringProperty(this, "permSize", "");
|
||||
|
||||
public ImmediateStringProperty permSizeProperty() {
|
||||
return permSizeProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The permanent generation size of JVM garbage collection.
|
||||
*/
|
||||
public String getPermSize() {
|
||||
return permSizeProperty.get();
|
||||
}
|
||||
@@ -138,15 +138,15 @@ public final class VersionSetting {
|
||||
permSizeProperty.set(permSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum memory that JVM can allocate for heap.
|
||||
*/
|
||||
private final ImmediateIntegerProperty maxMemoryProperty = new ImmediateIntegerProperty(this, "maxMemory", (int) OperatingSystem.SUGGESTED_MEMORY);
|
||||
|
||||
public ImmediateIntegerProperty maxMemoryProperty() {
|
||||
return maxMemoryProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum memory/MB that JVM can allocate for heap.
|
||||
*/
|
||||
public int getMaxMemory() {
|
||||
return maxMemoryProperty.get();
|
||||
}
|
||||
@@ -172,16 +172,16 @@ public final class VersionSetting {
|
||||
minMemoryProperty.set(minMemory);
|
||||
}
|
||||
|
||||
/**
|
||||
* The command that will be executed before launching the Minecraft.
|
||||
* Operating system relevant.
|
||||
*/
|
||||
private final ImmediateStringProperty preLaunchCommandProperty = new ImmediateStringProperty(this, "precalledCommand", "");
|
||||
|
||||
public ImmediateStringProperty preLaunchCommandProperty() {
|
||||
return preLaunchCommandProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The command that will be executed before launching the Minecraft.
|
||||
* Operating system relevant.
|
||||
*/
|
||||
public String getPreLaunchCommand() {
|
||||
return preLaunchCommandProperty.get();
|
||||
}
|
||||
@@ -192,15 +192,15 @@ public final class VersionSetting {
|
||||
|
||||
// options
|
||||
|
||||
/**
|
||||
* The user customized arguments passed to JVM.
|
||||
*/
|
||||
private final ImmediateStringProperty javaArgsProperty = new ImmediateStringProperty(this, "javaArgs", "");
|
||||
|
||||
public ImmediateStringProperty javaArgsProperty() {
|
||||
return javaArgsProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user customized arguments passed to JVM.
|
||||
*/
|
||||
public String getJavaArgs() {
|
||||
return javaArgsProperty.get();
|
||||
}
|
||||
@@ -209,16 +209,15 @@ public final class VersionSetting {
|
||||
javaArgsProperty.set(javaArgs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The user customized arguments passed to Minecraft.
|
||||
*/
|
||||
private final ImmediateStringProperty minecraftArgsProperty = new ImmediateStringProperty(this, "minecraftArgs", "");
|
||||
|
||||
public ImmediateStringProperty minecraftArgsProperty() {
|
||||
return minecraftArgsProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user customized arguments passed to Minecraft.
|
||||
*/
|
||||
public String getMinecraftArgs() {
|
||||
return minecraftArgsProperty.get();
|
||||
}
|
||||
@@ -227,15 +226,15 @@ public final class VersionSetting {
|
||||
minecraftArgsProperty.set(minecraftArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if disallow HMCL use default JVM arguments.
|
||||
*/
|
||||
private final ImmediateBooleanProperty noJVMArgsProperty = new ImmediateBooleanProperty(this, "noJVMArgs", false);
|
||||
|
||||
public ImmediateBooleanProperty noJVMArgsProperty() {
|
||||
return noJVMArgsProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if disallow HMCL use default JVM arguments.
|
||||
*/
|
||||
public boolean isNoJVMArgs() {
|
||||
return noJVMArgsProperty.get();
|
||||
}
|
||||
@@ -244,15 +243,15 @@ public final class VersionSetting {
|
||||
noJVMArgsProperty.set(noJVMArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if HMCL does not check game's completeness.
|
||||
*/
|
||||
private final ImmediateBooleanProperty notCheckGameProperty = new ImmediateBooleanProperty(this, "notCheckGame", false);
|
||||
|
||||
public ImmediateBooleanProperty notCheckGameProperty() {
|
||||
return notCheckGameProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if HMCL does not check game's completeness.
|
||||
*/
|
||||
public boolean isNotCheckGame() {
|
||||
return notCheckGameProperty.get();
|
||||
}
|
||||
@@ -261,16 +260,15 @@ public final class VersionSetting {
|
||||
notCheckGameProperty.set(notCheckGame);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* True if HMCL does not find/download libraries in/to common path.
|
||||
*/
|
||||
private final ImmediateBooleanProperty noCommonProperty = new ImmediateBooleanProperty(this, "noCommon", false);
|
||||
|
||||
public ImmediateBooleanProperty noCommonProperty() {
|
||||
return noCommonProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if HMCL does not find/download libraries in/to common path.
|
||||
*/
|
||||
public boolean isNoCommon() {
|
||||
return noCommonProperty.get();
|
||||
}
|
||||
@@ -279,15 +277,15 @@ public final class VersionSetting {
|
||||
noCommonProperty.set(noCommon);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if show the logs after game launched.
|
||||
*/
|
||||
private final ImmediateBooleanProperty showLogsProperty = new ImmediateBooleanProperty(this, "showLogs", false);
|
||||
|
||||
public ImmediateBooleanProperty showLogsProperty() {
|
||||
return showLogsProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if show the logs after game launched.
|
||||
*/
|
||||
public boolean isShowLogs() {
|
||||
return showLogsProperty.get();
|
||||
}
|
||||
@@ -298,17 +296,17 @@ public final class VersionSetting {
|
||||
|
||||
// Minecraft settings.
|
||||
|
||||
/**
|
||||
* The server ip that will be entered after Minecraft successfully loaded immediately.
|
||||
*
|
||||
* Format: ip:port or without port.
|
||||
*/
|
||||
private final ImmediateStringProperty serverIpProperty = new ImmediateStringProperty(this, "serverIp", "");
|
||||
|
||||
public ImmediateStringProperty serverIpProperty() {
|
||||
return serverIpProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The server ip that will be entered after Minecraft successfully loaded immediately.
|
||||
*
|
||||
* Format: ip:port or without port.
|
||||
*/
|
||||
public String getServerIp() {
|
||||
return serverIpProperty.get();
|
||||
}
|
||||
@@ -318,15 +316,15 @@ public final class VersionSetting {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* True if Minecraft started in fullscreen mode.
|
||||
*/
|
||||
private final ImmediateBooleanProperty fullscreenProperty = new ImmediateBooleanProperty(this, "fullscreen", false);
|
||||
|
||||
public ImmediateBooleanProperty fullscreenProperty() {
|
||||
return fullscreenProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if Minecraft started in fullscreen mode.
|
||||
*/
|
||||
public boolean isFullscreen() {
|
||||
return fullscreenProperty.get();
|
||||
}
|
||||
@@ -335,19 +333,19 @@ public final class VersionSetting {
|
||||
fullscreenProperty.set(fullscreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of Minecraft window, defaults 800.
|
||||
*
|
||||
* The field saves int value.
|
||||
* String type prevents unexpected value from causing JsonSyntaxException.
|
||||
* We can only reset this field instead of recreating the whole setting file.
|
||||
*/
|
||||
private final ImmediateIntegerProperty widthProperty = new ImmediateIntegerProperty(this, "width", 854);
|
||||
|
||||
public ImmediateIntegerProperty widthProperty() {
|
||||
return widthProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of Minecraft window, defaults 800.
|
||||
*
|
||||
* The field saves int value.
|
||||
* String type prevents unexpected value from JsonParseException.
|
||||
* We can only reset this field instead of recreating the whole setting file.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return widthProperty.get();
|
||||
}
|
||||
@@ -357,19 +355,19 @@ public final class VersionSetting {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The height of Minecraft window, defaults 480.
|
||||
*
|
||||
* The field saves int value.
|
||||
* String type prevents unexpected value from causing JsonSyntaxException.
|
||||
* We can only reset this field instead of recreating the whole setting file.
|
||||
*/
|
||||
private final ImmediateIntegerProperty heightProperty = new ImmediateIntegerProperty(this, "height", 480);
|
||||
|
||||
public ImmediateIntegerProperty heightProperty() {
|
||||
return heightProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of Minecraft window, defaults 480.
|
||||
*
|
||||
* The field saves int value.
|
||||
* String type prevents unexpected value from JsonParseException.
|
||||
* We can only reset this field instead of recreating the whole setting file.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return heightProperty.get();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
@@ -51,7 +52,29 @@ public class AdvancedListBox extends ScrollPane {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdvancedListBox remove(Node child) {
|
||||
if (child instanceof Pane)
|
||||
container.getChildren().remove(child);
|
||||
else {
|
||||
StackPane pane = null;
|
||||
for (Node node : container.getChildren())
|
||||
if (node instanceof StackPane) {
|
||||
ObservableList<Node> list = ((StackPane) node).getChildren();
|
||||
if (list.size() == 1 && list.get(0) == child)
|
||||
pane = (StackPane) node;
|
||||
}
|
||||
if (pane == null)
|
||||
throw new Error();
|
||||
container.getChildren().remove(pane);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AdvancedListBox startCategory(String category) {
|
||||
return add(new ClassTitle(category));
|
||||
}
|
||||
|
||||
public void setSpacing(double spacing) {
|
||||
container.setSpacing(spacing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,12 @@ import javafx.stage.Stage;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.ui.construct.InputDialogPane;
|
||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||
import org.jackhuang.hmcl.util.JavaVersion;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class Controllers {
|
||||
|
||||
private static Scene scene;
|
||||
@@ -103,6 +107,10 @@ public final class Controllers {
|
||||
dialog(new MessageDialogPane(text, decorator.getDialog()));
|
||||
}
|
||||
|
||||
public static void inputDialog(String text, Consumer<String> onResult) {
|
||||
dialog(new InputDialogPane(text, decorator.getDialog(), onResult));
|
||||
}
|
||||
|
||||
public static void closeDialog() {
|
||||
decorator.getDialog().close();
|
||||
}
|
||||
|
||||
@@ -76,11 +76,16 @@ public final class MainPage extends StackPane implements DecoratorPage {
|
||||
if (Settings.INSTANCE.getSelectedAccount() == null)
|
||||
Controllers.dialog(Main.i18n("login.no_Player007"));
|
||||
else
|
||||
LauncherHelper.INSTANCE.launch(version);
|
||||
LauncherHelper.INSTANCE.launch(version, null);
|
||||
});
|
||||
item.setOnDeleteButtonClicked(e -> {
|
||||
profile.getRepository().removeVersionFromDisk(version);
|
||||
Platform.runLater(this::loadVersions);
|
||||
item.setOnScriptButtonClicked(e -> {
|
||||
if (Settings.INSTANCE.getSelectedAccount() == null)
|
||||
Controllers.dialog(Main.i18n("login.no_Player007"));
|
||||
else {
|
||||
Controllers.inputDialog(Main.i18n("mainwindow.enter_script_name"), file -> {
|
||||
LauncherHelper.INSTANCE.launch(version, file);
|
||||
});
|
||||
}
|
||||
});
|
||||
item.setOnSettingsButtonClicked(e -> {
|
||||
Controllers.getDecorator().showPage(Controllers.getVersionPage());
|
||||
|
||||
@@ -81,6 +81,10 @@ public final class SVG {
|
||||
return createSVGPath("M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.14311.429 0 20.571 6.286z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node script(String fill, double width, double height) {
|
||||
return createSVGPath("M14,20A2,2 0 0,0 16,18V5H9A1,1 0 0,0 8,6V16H5V5A3,3 0 0,1 8,2H19A3,3 0 0,1 22,5V6H18V18L18,19A3,3 0 0,1 15,22H5A3,3 0 0,1 2,19V18H12A2,2 0 0,0 14,20Z", fill, width, height);
|
||||
}
|
||||
|
||||
public static Node pencil(String fill, double width, double height) {
|
||||
return createSVGPath("M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z", fill, width, height);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,15 @@ import javafx.scene.effect.BlurType;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class VersionItem extends StackPane {
|
||||
@FXML
|
||||
private Pane icon;
|
||||
@@ -42,29 +45,37 @@ public final class VersionItem extends StackPane {
|
||||
@FXML
|
||||
private StackPane body;
|
||||
@FXML
|
||||
private JFXButton btnDelete;
|
||||
@FXML
|
||||
private JFXButton btnSettings;
|
||||
@FXML
|
||||
private JFXButton btnLaunch;
|
||||
@FXML
|
||||
private JFXButton btnScript;
|
||||
@FXML
|
||||
private Label lblVersionName;
|
||||
@FXML
|
||||
private Label lblGameVersion;
|
||||
@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));
|
||||
btnDelete.setGraphic(SVG.delete("black", 15, 15));
|
||||
btnLaunch.setGraphic(SVG.launch("black", 15, 15));
|
||||
btnScript.setGraphic(SVG.script("black", 15, 15));
|
||||
|
||||
icon.translateYProperty().bind(Bindings.createDoubleBinding(() -> header.getBoundsInParent().getHeight() - icon.getHeight() / 2 - 16, header.boundsInParentProperty(), icon.heightProperty()));
|
||||
FXUtils.limitSize(iconView, 32, 32);
|
||||
|
||||
setOnMouseClicked(e -> {
|
||||
if (e.getButton() == MouseButton.PRIMARY)
|
||||
if (e.getClickCount() == 2)
|
||||
Optional.ofNullable(launchClickedHandler).ifPresent(h -> h.handle(e));
|
||||
});
|
||||
}
|
||||
|
||||
public void setVersionName(String versionName) {
|
||||
@@ -83,11 +94,12 @@ public final class VersionItem extends StackPane {
|
||||
btnSettings.setOnMouseClicked(handler);
|
||||
}
|
||||
|
||||
public void setOnDeleteButtonClicked(EventHandler<? super MouseEvent> handler) {
|
||||
btnDelete.setOnMouseClicked(handler);
|
||||
public void setOnScriptButtonClicked(EventHandler<? super MouseEvent> handler) {
|
||||
btnScript.setOnMouseClicked(handler);
|
||||
}
|
||||
|
||||
public void setOnLaunchButtonClicked(EventHandler<? super MouseEvent> handler) {
|
||||
launchClickedHandler = handler;
|
||||
btnLaunch.setOnMouseClicked(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
|
||||
FXUtils.installTooltip(btnBrowseMenu, 0, 5000, 0, new Tooltip(Main.i18n("game_settings.exploration")));
|
||||
FXUtils.installTooltip(btnManagementMenu, 0, 5000, 0, new Tooltip(Main.i18n("game_settings.management")));
|
||||
FXUtils.installTooltip(btnExport, 0, 5000, 0, new Tooltip(Main.i18n("modpack.task.save")));
|
||||
FXUtils.installTooltip(btnExport, 0, 5000, 0, new Tooltip(Main.i18n("modpack.export")));
|
||||
}
|
||||
|
||||
public void load(String id, Profile profile) {
|
||||
@@ -108,6 +108,12 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
managementPopup.show(btnManagementMenu, JFXPopup.PopupVPosition.TOP, JFXPopup.PopupHPosition.RIGHT, -12, 15);
|
||||
}
|
||||
|
||||
public void onDelete() {
|
||||
profile.getRepository().removeVersionFromDisk(version);
|
||||
profile.getRepository().refreshVersions();
|
||||
Controllers.navigate(null);
|
||||
}
|
||||
|
||||
public void onExport() {
|
||||
Controllers.getDecorator().startWizard(new ExportWizardProvider(profile, version), Main.i18n("modpack.wizard"));
|
||||
}
|
||||
@@ -145,7 +151,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
public void onManagement() {
|
||||
switch (managementList.getSelectionModel().getSelectedIndex()) {
|
||||
case 0: // rename a version
|
||||
Optional<String> res = FXUtils.inputDialog("Input", Main.i18n("versions.manage.rename.message"), null, version);
|
||||
Optional<String> res = FXUtils.inputDialog("Input", Main.i18n("version.manage.rename.message"), null, version);
|
||||
if (res.isPresent()) {
|
||||
if (profile.getRepository().renameVersion(version, res.get())) {
|
||||
profile.getRepository().refreshVersions();
|
||||
@@ -154,7 +160,7 @@ public final class VersionPage extends StackPane implements DecoratorPage {
|
||||
}
|
||||
break;
|
||||
case 1: // remove a version
|
||||
if (FXUtils.alert(Alert.AlertType.CONFIRMATION, "Confirm", Main.i18n("versions.manage.remove.confirm") + version)) {
|
||||
if (FXUtils.alert(Alert.AlertType.CONFIRMATION, "Confirm", Main.i18n("version.manage.remove.confirm") + version)) {
|
||||
if (profile.getRepository().removeVersionFromDisk(version)) {
|
||||
profile.getRepository().refreshVersions();
|
||||
Controllers.navigate(null);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class InputDialogPane extends StackPane {
|
||||
private final String text;
|
||||
private final JFXDialog dialog;
|
||||
|
||||
@FXML
|
||||
private JFXButton acceptButton;
|
||||
@FXML
|
||||
private JFXButton cancelButton;
|
||||
@FXML
|
||||
private JFXTextField textField;
|
||||
@FXML
|
||||
private Label content;
|
||||
|
||||
public InputDialogPane(String text, JFXDialog dialog, Consumer<String> onResult) {
|
||||
this.text = text;
|
||||
this.dialog = dialog;
|
||||
|
||||
FXUtils.loadFXML(this, "/assets/fxml/input-dialog.fxml");
|
||||
content.setText(text);
|
||||
cancelButton.setOnMouseClicked(e -> dialog.close());
|
||||
acceptButton.setOnMouseClicked(e -> {
|
||||
onResult.accept(textField.getText());
|
||||
dialog.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,14 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui;
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
public final class MessageDialogPane extends StackPane {
|
||||
private final String text;
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXProgressBar;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import org.jackhuang.hmcl.Main;
|
||||
import org.jackhuang.hmcl.download.forge.ForgeInstallTask;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
|
||||
import org.jackhuang.hmcl.download.game.GameAssetRefreshTask;
|
||||
import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
|
||||
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackExportTask;
|
||||
import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
|
||||
import org.jackhuang.hmcl.mod.CurseCompletionTask;
|
||||
import org.jackhuang.hmcl.mod.CurseInstallTask;
|
||||
import org.jackhuang.hmcl.mod.MultiMCModpackInstallTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.task.TaskListener;
|
||||
import org.jackhuang.hmcl.ui.AdvancedListBox;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class TaskListPane extends StackPane {
|
||||
private final TaskExecutor executor;
|
||||
private final AdvancedListBox listBox = new AdvancedListBox();
|
||||
private final Map<Task, ProgressListNode> nodes = new HashMap<>();
|
||||
|
||||
public TaskListPane(TaskExecutor executor, Runnable onTerminate) {
|
||||
this.executor = executor;
|
||||
listBox.setSpacing(0);
|
||||
|
||||
executor.addTaskListener(new TaskListener() {
|
||||
@Override
|
||||
public void onReady(Task task) {
|
||||
if (!task.getSignificance().shouldShow())
|
||||
return;
|
||||
ProgressListNode node = new ProgressListNode(task);
|
||||
nodes.put(task, node);
|
||||
Platform.runLater(() -> listBox.add(node));
|
||||
|
||||
if (task instanceof GameAssetRefreshTask) {
|
||||
task.setName(Main.i18n("assets.download"));
|
||||
} else if (task instanceof GameAssetDownloadTask) {
|
||||
task.setName(Main.i18n("assets.download_all"));
|
||||
} else if (task instanceof ForgeInstallTask) {
|
||||
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.forge")));
|
||||
} else if (task instanceof LiteLoaderInstallTask) {
|
||||
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.liteloader")));
|
||||
} else if (task instanceof OptiFineInstallTask) {
|
||||
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine")));
|
||||
} else if (task instanceof CurseCompletionTask) {
|
||||
task.setName(Main.i18n("modpack.type.curse.completion"));
|
||||
} else if (task instanceof CurseInstallTask) {
|
||||
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
|
||||
} else if (task instanceof MultiMCModpackInstallTask) {
|
||||
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
|
||||
} else if (task instanceof HMCLModpackInstallTask) {
|
||||
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
|
||||
} else if (task instanceof HMCLModpackExportTask) {
|
||||
task.setName(Main.i18n("modpack.export"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(Task task) {
|
||||
ProgressListNode node = nodes.remove(task);
|
||||
if (node == null)
|
||||
return;
|
||||
node.unbind();
|
||||
Platform.runLater(() -> listBox.remove(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Task task, Throwable throwable) {
|
||||
ProgressListNode node = nodes.remove(task);
|
||||
if (node == null)
|
||||
return;
|
||||
Platform.runLater(() -> node.setThrowable(throwable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
Optional.ofNullable(onTerminate).ifPresent(Runnable::run);
|
||||
}
|
||||
});
|
||||
|
||||
getChildren().setAll(listBox);
|
||||
}
|
||||
|
||||
private static class ProgressListNode extends StackPane {
|
||||
private final JFXProgressBar bar = new JFXProgressBar();
|
||||
private final Label title = new Label();
|
||||
private final Label state = new Label();
|
||||
|
||||
public ProgressListNode(Task task) {
|
||||
bar.progressProperty().bind(task.progressProperty());
|
||||
title.setText(task.getName());
|
||||
state.textProperty().bind(task.messageProperty());
|
||||
|
||||
BorderPane borderPane = new BorderPane();
|
||||
borderPane.setLeft(title);
|
||||
borderPane.setRight(state);
|
||||
getChildren().addAll(bar, borderPane);
|
||||
|
||||
bar.setMinHeight(20);
|
||||
bar.minWidthProperty().bind(widthProperty());
|
||||
bar.prefWidthProperty().bind(widthProperty());
|
||||
bar.maxWidthProperty().bind(widthProperty());
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
bar.progressProperty().unbind();
|
||||
state.textProperty().unbind();
|
||||
}
|
||||
|
||||
public void setThrowable(Throwable throwable) {
|
||||
unbind();
|
||||
state.setText(throwable.getLocalizedMessage());
|
||||
bar.setProgress(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.task.Schedulers;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.task.TaskListener;
|
||||
import org.jackhuang.hmcl.ui.construct.TaskListPane;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -91,45 +92,9 @@ public interface AbstractWizardDisplayer extends WizardDisplayer {
|
||||
|
||||
@Override
|
||||
default void handleTask(Map<String, Object> settings, Task task) {
|
||||
VBox vbox = new VBox();
|
||||
JFXProgressBar progressBar = new JFXProgressBar();
|
||||
Label label = new Label();
|
||||
progressBar.setMaxHeight(10);
|
||||
vbox.getChildren().addAll(progressBar, label);
|
||||
|
||||
StackPane root = new StackPane();
|
||||
root.getChildren().add(vbox);
|
||||
navigateTo(root, Navigation.NavigationDirection.FINISH);
|
||||
|
||||
AtomicInteger finishedTasks = new AtomicInteger(0);
|
||||
|
||||
TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor(e -> new TaskListener() {
|
||||
@Override
|
||||
public void onReady(Task task) {
|
||||
Platform.runLater(() -> progressBar.setProgress(finishedTasks.get() * 1.0 / e.getRunningTasks()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(Task task) {
|
||||
Platform.runLater(() -> {
|
||||
label.setText(task.getName());
|
||||
progressBar.setProgress(finishedTasks.incrementAndGet() * 1.0 / e.getRunningTasks());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(Task task, Throwable throwable) {
|
||||
Platform.runLater(() -> {
|
||||
label.setText(task.getName());
|
||||
progressBar.setProgress(finishedTasks.incrementAndGet() * 1.0 / e.getRunningTasks());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
Platform.runLater(AbstractWizardDisplayer.this::navigateToSuccess);
|
||||
}
|
||||
});
|
||||
TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor();
|
||||
TaskListPane pane = new TaskListPane(executor, () -> Platform.runLater(AbstractWizardDisplayer.this::navigateToSuccess));
|
||||
navigateTo(pane, Navigation.NavigationDirection.FINISH);
|
||||
getCancelQueue().add(executor);
|
||||
executor.start();
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class WizardController implements Navigation {
|
||||
if (result instanceof DeferredWizardResult) displayer.handleDeferredWizardResult(settings, ((DeferredWizardResult) result));
|
||||
else if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT);
|
||||
else if (result instanceof Task) displayer.handleTask(settings, ((Task) result));
|
||||
else throw new IllegalStateException("Unrecognized wizard result: " + result);
|
||||
else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
23
HMCL/src/main/resources/assets/fxml/input-dialog.fxml
Normal file
23
HMCL/src/main/resources/assets/fxml/input-dialog.fxml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import com.jfoenix.controls.JFXButton?>
|
||||
<?import com.jfoenix.controls.JFXDialogLayout?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import com.jfoenix.controls.JFXTextField?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane">
|
||||
<JFXDialogLayout>
|
||||
<heading>
|
||||
<Label id="content" />
|
||||
</heading>
|
||||
<body>
|
||||
<JFXTextField fx:id="textField" />
|
||||
</body>
|
||||
<actions>
|
||||
<JFXButton fx:id="cancelButton" styleClass="dialog-cancel" text="%button.cancel" />
|
||||
<JFXButton fx:id="acceptButton" styleClass="dialog-accept" text="%button.ok" />
|
||||
</actions>
|
||||
</JFXDialogLayout>
|
||||
</fx:root>
|
||||
@@ -6,6 +6,7 @@
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
type="StackPane" pickOnBounds="false">
|
||||
@@ -28,12 +29,26 @@
|
||||
<BorderPane>
|
||||
<left>
|
||||
<HBox spacing="8">
|
||||
<JFXButton fx:id="btnSettings" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
|
||||
<JFXButton fx:id="btnDelete" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30" />
|
||||
<JFXButton fx:id="btnSettings" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30">
|
||||
<tooltip>
|
||||
<Tooltip text="%version.manage.settings" />
|
||||
</tooltip>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
</left>
|
||||
<right>
|
||||
<JFXButton fx:id="btnLaunch" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minHeight="30" minWidth="30" prefWidth="30" prefHeight="30" />
|
||||
<HBox spacing="8">
|
||||
<JFXButton fx:id="btnScript" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minWidth="30" minHeight="30" prefWidth="30" prefHeight="30">
|
||||
<tooltip>
|
||||
<Tooltip text="%version.launch_script" />
|
||||
</tooltip>
|
||||
</JFXButton>
|
||||
<JFXButton fx:id="btnLaunch" styleClass="toggle-icon4" maxWidth="30" maxHeight="30" minHeight="30" minWidth="30" prefWidth="30" prefHeight="30">
|
||||
<tooltip>
|
||||
<Tooltip text="%version.launch" />
|
||||
</tooltip>
|
||||
</JFXButton>
|
||||
</HBox>
|
||||
</right>
|
||||
</BorderPane>
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
</JFXTabPane>
|
||||
|
||||
<HBox alignment="TOP_RIGHT" style="-fx-padding: 3px;" pickOnBounds="false">
|
||||
<JFXButton fx:id="btnDelete" maxHeight="28.0" minHeight="28.0" onMouseClicked="#onDelete"
|
||||
styleClass="toggle-icon3" ripplerFill="white">
|
||||
<graphic>
|
||||
<fx:include source="/assets/svg/delete-black.fxml"/>
|
||||
</graphic>
|
||||
</JFXButton>
|
||||
<JFXButton fx:id="btnExport" maxHeight="28.0" minHeight="28.0" onMouseClicked="#onExport"
|
||||
styleClass="toggle-icon3" ripplerFill="white">
|
||||
<graphic>
|
||||
@@ -57,9 +63,9 @@
|
||||
|
||||
<JFXListView fx:id="managementList" styleClass="option-list-view" onMouseClicked="#onManagement"
|
||||
maxWidth="300.0" minWidth="300.0">
|
||||
<Label text="%versions.manage.rename"/>
|
||||
<Label text="%versions.manage.remove"/>
|
||||
<Label text="%versions.manage.redownload_assets_index"/>
|
||||
<Label text="%versions.manage.remove_libraries"/>
|
||||
<Label text="%version.manage.rename"/>
|
||||
<Label text="%version.manage.remove"/>
|
||||
<Label text="%version.manage.redownload_assets_index"/>
|
||||
<Label text="%version.manage.remove_libraries"/>
|
||||
</JFXListView>
|
||||
</fx:root>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<?import javafx.scene.layout.*?>
|
||||
<fx:root xmlns="http://javafx.com/javafx"
|
||||
type="StackPane"
|
||||
style="-fx-background-color: gray;"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<VBox>
|
||||
<JFXToolbar fx:id="toolbar" styleClass="jfx-tool-bar">
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
#author: huangyuhui, dxNeil
|
||||
launch.failed=Failed to launch.
|
||||
launch.failed_creating_process=Failed to create process, maybe your java path is wrong, please modify your java path.
|
||||
launch.failed_sh_permission=Failed to add permission to the launch script.
|
||||
launch.failed_packing_jar=Failed to pack the jar.
|
||||
launch.failed.sh_permission=Failed to add permission to the launch script.
|
||||
launch.failed.packing_jar=Failed to pack the jar.
|
||||
launch.unsupported_launcher_version=Sorry, the launcher cannot launch minecraft, but will retry launching it.
|
||||
launch.too_big_memory_alloc_64bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game probably crash. The maximum memory is 1024MB. The launcher will try to launch it.
|
||||
launch.too_big_memory_alloc_free_space_too_low=You have allocated too much memory, because the physical memory size is %dMB, your game probably crash. The launcher will try to launch it.
|
||||
launch.cannot_create_jvm=We find that it cannot create java virutal machine. The Java argements may have problems. You can enable the no args mode in the settings.
|
||||
launch.advice.too_large_memory_for_32bit=You have allocated too much memory, because of your 32-Bit Java Runtime Environment, your game probably crash. The maximum memory is 1024MB. The launcher will try to launch it.
|
||||
launch.advice.not_enough_space=You have allocated too much memory, because the physical memory size is %dMB, your game probably crash. The launcher will try to launch it.
|
||||
launch.failed.cannot_create_jvm=We find that it cannot create java virutal machine. The Java argements may have problems. You can enable the no args mode in the settings.
|
||||
launch.circular_dependency_versions=Found circular dependency versions, please check if your client has been modified.
|
||||
launch.not_finished_downloading_libraries=Did not finish downloading libraries, continue launching game?
|
||||
launch.not_finished_decompressing_natives=Did not finish decompressing native libraries, continue launching game?
|
||||
launch.failed.downloading_libraries=Did not finish downloading libraries, continue launching game?
|
||||
launch.failed.decompressing_natives=Did not finish decompressing native libraries, continue launching game?
|
||||
launch.wrong_javadir=Incorrect Java directory, will reset to default Java directory.
|
||||
launch.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help.
|
||||
launch.failed.exited_abnormally=Game exited abnormally, please visit the log, or ask someone for help.
|
||||
|
||||
launch.state.logging_in=Logging In
|
||||
launch.state.generating_launching_codes=Generating Launching Codes
|
||||
@@ -111,9 +111,9 @@ ui.label.newProfileWindow.copy_from=Copy From:
|
||||
ui.newProfileWindow.title=New Config
|
||||
|
||||
ui.button.ok=OK
|
||||
ui.button.refresh=Refresh
|
||||
ui.button.run=Play
|
||||
ui.button.settings=Settings
|
||||
ui.button.refresh=Refresh
|
||||
version.launch=Play
|
||||
version.manage.settings=Settings
|
||||
ui.button.about=About
|
||||
ui.button.others=Others
|
||||
ui.button.logout=Log Out
|
||||
@@ -208,8 +208,8 @@ modpack.export_finished=Exporting the modpack finished. See
|
||||
modpack.included_launcher=The modpack is included the launcher, you can publish it directly.
|
||||
modpack.not_included_launcher=You can use the modpack by clicking the "Import Modpack" button.
|
||||
modpack.enter_name=Enter your desired name for this game.
|
||||
|
||||
modpack.task.save=Export Modpack
|
||||
modpack.author=Author
|
||||
modpack.export=Export Modpack
|
||||
modpack.task.install=Import Modpack
|
||||
modpack.task.install.error=Failed to install the modpack. Maybe the files is incorrect, or a management issue occurred.
|
||||
modpack.task.install.will=Install the modpack:
|
||||
@@ -274,10 +274,10 @@ advancedsettings.server_ip=Server Host
|
||||
advancedsettings.dont_check_game_completeness=Don't check game completeness
|
||||
|
||||
mainwindow.show_log=Show Logs
|
||||
mainwindow.make_launch_script=Make Launching Script.
|
||||
mainwindow.make_launch_script_failed=Failed to make script.
|
||||
version.launch_script=Make Launching Script.
|
||||
version.launch_script.failed=Failed to make script.
|
||||
mainwindow.enter_script_name=Enter the script name.
|
||||
mainwindow.make_launch_succeed=Finished script creation.
|
||||
version.launch_script.success=Finished script creation, %s.
|
||||
mainwindow.no_version=No version found. Switch to Game Downloads Tab?
|
||||
|
||||
launcher.about=About Author\nMinecraft Forum ID: klkl6523\nCopyright (c) 2013 huangyuhui\nOpened source under GPL v3 license:http://github.com/huanghongxun/HMCL/\nThis software used project Gson which is under Apache License 2.0, thanks contributors.
|
||||
@@ -308,21 +308,21 @@ game_settings=Games
|
||||
main_page=Home
|
||||
launcher_settings=Launcher
|
||||
|
||||
versions.release=Release
|
||||
versions.snapshot=Snapshot
|
||||
versions.old_beta=Beta
|
||||
versions.old_alpha=Old Alpha
|
||||
version.game.release=Release
|
||||
version.game.snapshot=Snapshot
|
||||
version.game.old_beta=Beta
|
||||
version.game.old_alpha=Old Alpha
|
||||
|
||||
versions.manage.rename=Rename this version
|
||||
versions.manage.rename.message=Please enter the new name
|
||||
versions.manage.remove=Delete this version
|
||||
versions.manage.remove.confirm=Sure to remove the version
|
||||
versions.manage.redownload_json=Redownload Minecraft Configuration(minecraft.json)
|
||||
versions.manage.redownload_assets_index=Redownload Assets Index
|
||||
versions.manage.remove_libraries=Delete library files
|
||||
version.manage.rename=Rename this version
|
||||
version.manage.rename.message=Please enter the new name
|
||||
version.manage.remove=Delete this version
|
||||
version.manage.remove.confirm=Sure to remove the version
|
||||
version.manage.redownload_json=Redownload Minecraft Configuration(minecraft.json)
|
||||
version.manage.redownload_assets_index=Redownload Assets Index
|
||||
version.manage.remove_libraries=Delete library files
|
||||
|
||||
advice.os64butjdk32=Your OS is 64-Bit but your Java is 32-Bit. The 64-Bit Java is recommended.
|
||||
advice.java8=Java 8 is suggested, which can make game run more fluently. And many mods and Minecraft 1.12 and newer versions requires Java 8.
|
||||
launch.advice.different_platform=Your OS is 64-Bit but your Java is 32-Bit. The 64-Bit Java is recommended.
|
||||
launch.advice.newer_java=Java 8 is suggested, which can make game run more fluently. And many mods and Minecraft 1.12 and newer versions requires Java 8.
|
||||
|
||||
assets.download_all=Download Assets Files
|
||||
assets.failed=Failed to get the list, try again.
|
||||
@@ -407,3 +407,9 @@ install.select=Select an operation
|
||||
install=Install New Game
|
||||
settings.icon=Game Icon
|
||||
launcher=Launcher
|
||||
install.installer.install=
|
||||
modpack.install=Install %s modpack
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.type.hmcl=HMCL
|
||||
modpack.type.curse.completion=Install relative files to Curse modpack
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
#author: huangyuhui
|
||||
launch.failed=启动失败
|
||||
launch.failed_creating_process=启动失败,在创建新进程时发生错误,可能是Java路径错误。
|
||||
launch.failed_sh_permission=为启动文件添加权限时发生错误
|
||||
launch.failed_packing_jar=在打包jar时发生错误
|
||||
launch.failed.sh_permission=为启动文件添加权限时发生错误
|
||||
launch.failed.packing_jar=在打包jar时发生错误
|
||||
launch.unsupported_launcher_version=对不起,本启动器现在可能不能启动这个版本的Minecraft,但启动器还是会尝试启动,请尽快将此问题报告给作者。
|
||||
launch.too_big_memory_alloc_64bit=您设置的内存大小过大,由于可能超过了32位Java的内存分配限制,所以可能无法启动游戏,请将内存调至1024MB或更小,启动器仍会尝试启动。
|
||||
launch.too_big_memory_alloc_free_space_too_low=您设置的内存大小过大,由于超过了系统内存大小%dMB,所以可能影响游戏体验或无法启动游戏,启动器仍会尝试启动。
|
||||
launch.cannot_create_jvm=截获到无法创建Java虚拟机,可能是Java参数有问题,可以在设置中开启无参数模式启动
|
||||
launch.advice.too_large_memory_for_32bit=您设置的内存大小过大,由于可能超过了32位Java的内存分配限制,所以可能无法启动游戏,请将内存调至1024MB或更小,启动器仍会尝试启动。
|
||||
launch.advice.not_enough_space=您设置的内存大小过大,由于超过了系统内存大小%dMB,所以可能影响游戏体验或无法启动游戏,启动器仍会尝试启动。
|
||||
launch.failed.cannot_create_jvm=截获到无法创建Java虚拟机,可能是Java参数有问题,可以在设置中开启无参数模式启动
|
||||
launch.circular_dependency_versions=发现游戏版本循环引用,请确认您的客户端未被修改或修改导致出现此问题。
|
||||
launch.not_finished_downloading_libraries=未完成游戏依赖库的下载,还要继续启动游戏吗?
|
||||
launch.not_finished_decompressing_natives=未能解压游戏本地库,还要继续启动游戏吗?
|
||||
launch.failed.downloading_libraries=未完成游戏依赖库的下载,还要继续启动游戏吗?
|
||||
launch.failed.decompressing_natives=未能解压游戏本地库,还要继续启动游戏吗?
|
||||
launch.wrong_javadir=错误的Java路径,将自动重置为默认Java路径。
|
||||
launch.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
||||
launch.failed.exited_abnormally=游戏非正常退出,请查看日志文件,或联系他人寻求帮助。
|
||||
|
||||
launch.state.logging_in=登录中
|
||||
launch.state.generating_launching_codes=正在生成启动代码
|
||||
@@ -112,8 +112,8 @@ ui.newProfileWindow.title=新建配置
|
||||
|
||||
ui.button.ok=确认
|
||||
ui.button.refresh=刷新
|
||||
ui.button.run=启动Minecraft
|
||||
ui.button.settings=
|
||||
version.launch=启动游戏
|
||||
version.manage.settings=游戏设置
|
||||
ui.button.about=关于
|
||||
ui.button.others=其他
|
||||
ui.button.logout=登出
|
||||
@@ -209,7 +209,7 @@ modpack.not_included_launcher=整合包未包含启动器,可使用本软件
|
||||
modpack.enter_name=给游戏起个你喜欢的名字
|
||||
modpack.author=作者
|
||||
|
||||
modpack.task.save=导出整合包
|
||||
modpack.export=导出整合包
|
||||
modpack.task.install=导入整合包
|
||||
modpack.task.install.error=安装失败,可能是整合包格式不正确或操作文件失败
|
||||
modpack.task.install.will=将会安装整合包:
|
||||
@@ -274,10 +274,10 @@ advancedsettings.server_ip=直入服务器ip地址(不必填写,启动游戏
|
||||
advancedsettings.dont_check_game_completeness=不检查游戏完整性
|
||||
|
||||
mainwindow.show_log=查看日志
|
||||
mainwindow.make_launch_script=生成启动脚本
|
||||
mainwindow.make_launch_script_failed=生成启动脚本失败
|
||||
version.launch_script=生成启动脚本
|
||||
version.launch_script.failed=生成启动脚本失败
|
||||
mainwindow.enter_script_name=输入要生成脚本的文件名
|
||||
mainwindow.make_launch_succeed=启动脚本已生成完毕:
|
||||
version.launch_script.success=启动脚本已生成完毕: %s.
|
||||
mainwindow.no_version=未找到任何版本,是否进入游戏下载?
|
||||
|
||||
launcher.about=默认背景图感谢gamerteam提供。\n关于作者:\n百度ID:huanghongxun20\nmcbbs:huanghongxun\nMinecraft Forum ID: klkl6523\n欢迎提交Bug哦\nCopyright (c) 2013-2017 huangyuhui.\n免责声明:Minecraft软件版权归Mojang AB所有,使用本软件产生的版权问题本软件制作方概不负责。\n本启动器在GPLv3协议下开源:https://github.com/huanghongxun/HMCL/ ,感谢issues和pull requests贡献者\n本软件使用了基于Apache License 2.0的Gson项目,感谢贡献者。
|
||||
@@ -308,21 +308,21 @@ game_settings=游戏设置
|
||||
main_page=主页
|
||||
launcher_settings=启动器设置
|
||||
|
||||
versions.release=稳定版
|
||||
versions.snapshot=快照版
|
||||
versions.old_beta=测试版
|
||||
versions.old_alpha=远古版
|
||||
version.game.release=稳定版
|
||||
version.game.snapshot=快照版
|
||||
version.game.old_beta=测试版
|
||||
version.game.old_alpha=远古版
|
||||
|
||||
versions.manage.rename=重命名该版本
|
||||
versions.manage.rename.message=请输入要改成的名字
|
||||
versions.manage.remove=删除该版本
|
||||
versions.manage.remove.confirm=真的要删除版本
|
||||
versions.manage.redownload_json=重新下载版本配置(minecraft.json)
|
||||
versions.manage.redownload_assets_index=重新下载资源配置(assets_index.json)
|
||||
versions.manage.remove_libraries=删除所有库文件
|
||||
version.manage.rename=重命名该版本
|
||||
version.manage.rename.message=请输入要改成的名字
|
||||
version.manage.remove=删除该版本
|
||||
version.manage.remove.confirm=真的要删除版本
|
||||
version.manage.redownload_json=重新下载版本配置(minecraft.json)
|
||||
version.manage.redownload_assets_index=重新下载资源配置(assets_index.json)
|
||||
version.manage.remove_libraries=删除所有库文件
|
||||
|
||||
advice.os64butjdk32=您的系统是64位的但是Java是32位的,推荐您安装64位Java.
|
||||
advice.java8=检测到您未使用Java 8及更新版本,Java 8能使游戏更流畅而且Minecraft 1.12及更新版本和很多Mod强制需要需要Java 8。
|
||||
launch.advice.different_platform=您的系统是64位的但是Java是32位的,推荐您安装64位Java.
|
||||
launch.advice.newer_java=检测到您未使用Java 8及更新版本,Java 8能使游戏更流畅而且Minecraft 1.12及更新版本和很多Mod强制需要需要Java 8。
|
||||
|
||||
assets.download_all=下载资源文件
|
||||
assets.failed=获取列表失败,请刷新重试。
|
||||
@@ -407,3 +407,9 @@ install.select=请选择安装方式
|
||||
install=添加游戏
|
||||
settings.icon=游戏图标
|
||||
launcher=启动器
|
||||
install.installer.install=安装%s
|
||||
modpack.install=安装%s整合包
|
||||
modpack.type.curse=Curse
|
||||
modpack.type.multimc=MultiMC
|
||||
modpack.type.hmcl=HMCL
|
||||
modpack.type.curse.completion=下载Curse整合包相关文件
|
||||
|
||||
3
HMCL/src/main/resources/assets/svg/script.fxml
Normal file
3
HMCL/src/main/resources/assets/svg/script.fxml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?import javafx.scene.shape.SVGPath?>
|
||||
<javafx.scene.shape.SVGPath fill="white" content="M14,20A2,2 0 0,0 16,18V5H9A1,1 0 0,0 8,6V16H5V5A3,3 0 0,1 8,2H19A3,3 0 0,1 22,5V6H18V18L18,19A3,3 0 0,1 15,22H5A3,3 0 0,1 2,19V18H12A2,2 0 0,0 14,20Z" />
|
||||
@@ -23,6 +23,7 @@ import org.jackhuang.hmcl.task.ParallelTask;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||
import org.jackhuang.hmcl.util.Constants;
|
||||
import org.jackhuang.hmcl.util.ExceptionalFunction;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -63,7 +64,7 @@ public class DefaultGameBuilder extends GameBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
private Function<AutoTypingMap<String>, Task> libraryTaskHelper(String gameVersion, String libraryId) {
|
||||
private ExceptionalFunction<AutoTypingMap<String>, Task, ?> libraryTaskHelper(String gameVersion, String libraryId) {
|
||||
return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ public final class GameAssetIndexDownloadTask extends Task {
|
||||
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,6 +39,8 @@ public final class GameDownloadTask extends Task {
|
||||
public GameDownloadTask(DefaultDependencyManager dependencyManager, Version version) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -49,6 +49,7 @@ public final class GameLibrariesTask extends Task {
|
||||
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -50,6 +50,7 @@ public final class GameLoggingDownloadTask extends Task {
|
||||
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
|
||||
this.dependencyManager = dependencyManager;
|
||||
this.version = version;
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,6 +47,8 @@ public final class VersionJsonDownloadTask extends Task {
|
||||
|
||||
if (!gameVersionList.isLoaded())
|
||||
dependents.add(gameVersionList.refreshAsync(dependencyManager.getDownloadProvider()));
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,6 +45,8 @@ public final class VersionJsonSaveTask extends Task {
|
||||
public VersionJsonSaveTask(DefaultGameRepository repository, Version version) {
|
||||
this.repository = repository;
|
||||
this.version = version;
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
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;
|
||||
@@ -35,7 +36,7 @@ final class CoupleTask<P extends Task> extends Task {
|
||||
private final boolean relyingOnDependents;
|
||||
private final Collection<Task> dependents;
|
||||
private final List<Task> dependencies = new LinkedList<>();
|
||||
private final Function<AutoTypingMap<String>, Task> succ;
|
||||
private final ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ;
|
||||
|
||||
/**
|
||||
* A task that combines two tasks and make sure pred runs before succ.
|
||||
@@ -44,24 +45,21 @@ final class CoupleTask<P extends Task> extends Task {
|
||||
* @param succ 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.
|
||||
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
|
||||
*/
|
||||
public CoupleTask(P pred, Function<AutoTypingMap<String>, Task> succ, boolean relyingOnDependents) {
|
||||
public CoupleTask(P pred, ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ, boolean relyingOnDependents) {
|
||||
this.dependents = Collections.singleton(pred);
|
||||
this.succ = succ;
|
||||
this.relyingOnDependents = relyingOnDependents;
|
||||
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
public void execute() throws Exception {
|
||||
Task task = succ.apply(getVariables());
|
||||
if (task != null)
|
||||
dependencies.add(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return dependents;
|
||||
|
||||
@@ -88,6 +88,8 @@ public class FileDownloadTask extends Task {
|
||||
this.hash = hash;
|
||||
this.retry = retry;
|
||||
this.proxy = proxy;
|
||||
|
||||
setName(file.getName());
|
||||
}
|
||||
|
||||
private void closeFiles() {
|
||||
@@ -101,6 +103,10 @@ public class FileDownloadTask extends Task {
|
||||
return onFailed;
|
||||
}
|
||||
|
||||
public URL getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
URL currentURL = url;
|
||||
|
||||
@@ -65,6 +65,8 @@ public final class GetTask extends TaskResult<String> {
|
||||
this.retry = retry;
|
||||
this.proxy = proxy;
|
||||
this.id = id;
|
||||
|
||||
setName(url.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,17 +37,13 @@ public final class ParallelTask extends Task {
|
||||
*/
|
||||
public ParallelTask(Task... tasks) {
|
||||
this.tasks = tasks;
|
||||
setSignificance(TaskSignificance.MINOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return Arrays.asList(tasks);
|
||||
|
||||
@@ -22,10 +22,7 @@ import javafx.beans.property.ReadOnlyDoubleWrapper;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||
import org.jackhuang.hmcl.event.EventManager;
|
||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||
import org.jackhuang.hmcl.util.ExceptionalConsumer;
|
||||
import org.jackhuang.hmcl.util.ExceptionalRunnable;
|
||||
import org.jackhuang.hmcl.util.Properties;
|
||||
import org.jackhuang.hmcl.util.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -42,11 +39,17 @@ public abstract class Task {
|
||||
|
||||
private final EventManager<TaskEvent> onDone = new EventManager<>();
|
||||
|
||||
private TaskSignificance significance = TaskSignificance.MAJOR;
|
||||
|
||||
/**
|
||||
* True if not logging when executing this task.
|
||||
*/
|
||||
public boolean isHidden() {
|
||||
return false;
|
||||
public final TaskSignificance getSignificance() {
|
||||
return significance;
|
||||
}
|
||||
|
||||
public void setSignificance(TaskSignificance significance) {
|
||||
this.significance = significance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,10 +142,10 @@ public abstract class Task {
|
||||
}
|
||||
|
||||
protected void updateProgress(double progress) {
|
||||
if (progress > 1.0)
|
||||
if (progress < 0 || progress > 1.0)
|
||||
throw new IllegalArgumentException("Progress is must between 0 and 1.");
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastTime >= getProgressInterval()) {
|
||||
if (lastTime == Long.MIN_VALUE || now - lastTime >= getProgressInterval()) {
|
||||
updateProgressImmediately(progress);
|
||||
lastTime = now;
|
||||
}
|
||||
@@ -186,7 +189,7 @@ public abstract class Task {
|
||||
|
||||
public final TaskExecutor executor(Function<TaskExecutor, TaskListener> taskListener) {
|
||||
TaskExecutor executor = new TaskExecutor(this);
|
||||
executor.setTaskListener(taskListener.apply(executor));
|
||||
executor.addTaskListener(taskListener.apply(executor));
|
||||
return executor;
|
||||
}
|
||||
|
||||
@@ -224,7 +227,7 @@ public abstract class Task {
|
||||
return then(s -> b);
|
||||
}
|
||||
|
||||
public final Task then(Function<AutoTypingMap<String>, Task> b) {
|
||||
public final Task then(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
|
||||
return new CoupleTask<>(this, b, true);
|
||||
}
|
||||
|
||||
@@ -232,7 +235,7 @@ public abstract class Task {
|
||||
return with(s -> b);
|
||||
}
|
||||
|
||||
public final Task with(Function<AutoTypingMap<String>, Task> b) {
|
||||
public final Task with(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
|
||||
return new CoupleTask<>(this, b, false);
|
||||
}
|
||||
|
||||
@@ -264,4 +267,18 @@ public abstract class Task {
|
||||
public static <V> TaskResult<V> ofResult(String id, Function<AutoTypingMap<String>, V> closure) {
|
||||
return new TaskCallable2<>(id, closure);
|
||||
}
|
||||
|
||||
public enum TaskSignificance {
|
||||
MAJOR,
|
||||
MODERATE,
|
||||
MINOR;
|
||||
|
||||
public boolean shouldLog() {
|
||||
return this != MINOR;
|
||||
}
|
||||
|
||||
public boolean shouldShow() {
|
||||
return this == MAJOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,11 @@ import org.jackhuang.hmcl.util.ExceptionalRunnable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
@@ -40,7 +38,7 @@ import java.util.logging.Level;
|
||||
public final class TaskExecutor {
|
||||
|
||||
private final Task firstTask;
|
||||
private TaskListener taskListener = TaskListener.DefaultTaskListener.INSTANCE;
|
||||
private List<TaskListener> taskListeners = new LinkedList<>();
|
||||
private boolean canceled = false;
|
||||
private Exception lastException;
|
||||
private final AtomicInteger totTask = new AtomicInteger(0);
|
||||
@@ -52,15 +50,8 @@ public final class TaskExecutor {
|
||||
this.firstTask = task;
|
||||
}
|
||||
|
||||
public TaskListener getTaskListener() {
|
||||
return taskListener;
|
||||
}
|
||||
|
||||
public void setTaskListener(TaskListener taskListener) {
|
||||
if (taskListener == null)
|
||||
this.taskListener = TaskListener.DefaultTaskListener.INSTANCE;
|
||||
else
|
||||
this.taskListener = taskListener;
|
||||
public void addTaskListener(TaskListener taskListener) {
|
||||
taskListeners.add(taskListener);
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
@@ -78,7 +69,10 @@ public final class TaskExecutor {
|
||||
public void start() {
|
||||
workerQueue.add(scheduler.schedule(() -> {
|
||||
if (!executeTasks(Collections.singleton(firstTask)))
|
||||
taskListener.onTerminate();
|
||||
taskListeners.forEach(it -> {
|
||||
it.onTerminate();
|
||||
it.onTerminate(variables);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -86,7 +80,10 @@ public final class TaskExecutor {
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
Future<?> future = scheduler.schedule(() -> {
|
||||
if (!executeTasks(Collections.singleton(firstTask))) {
|
||||
taskListener.onTerminate();
|
||||
taskListeners.forEach(it -> {
|
||||
it.onTerminate();
|
||||
it.onTerminate(variables);
|
||||
});
|
||||
flag.set(false);
|
||||
}
|
||||
});
|
||||
@@ -108,7 +105,7 @@ public final class TaskExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeTasks(Collection<Task> tasks) {
|
||||
private boolean executeTasks(Collection<Task> tasks) throws InterruptedException {
|
||||
if (tasks.isEmpty())
|
||||
return true;
|
||||
|
||||
@@ -119,38 +116,35 @@ public final class TaskExecutor {
|
||||
if (canceled)
|
||||
return false;
|
||||
Invoker invoker = new Invoker(task, latch, success);
|
||||
Future<?> future = task.getScheduler().schedule(invoker);
|
||||
if (future != null)
|
||||
workerQueue.add(future);
|
||||
try {
|
||||
Future<?> future = task.getScheduler().schedule(invoker);
|
||||
if (future != null)
|
||||
workerQueue.add(future);
|
||||
} catch (RejectedExecutionException e) {
|
||||
throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
if (canceled)
|
||||
return false;
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
return success.get() && !canceled;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
// Once interrupted, we are aborting the subscription.
|
||||
// and operations fail.
|
||||
return false;
|
||||
}
|
||||
latch.await();
|
||||
return success.get() && !canceled;
|
||||
}
|
||||
|
||||
private boolean executeTask(Task task) {
|
||||
if (canceled)
|
||||
return false;
|
||||
|
||||
if (!task.isHidden())
|
||||
if (task.getSignificance().shouldLog())
|
||||
Logging.LOG.log(Level.FINE, "Executing task: {0}", task.getName());
|
||||
|
||||
taskListener.onReady(task);
|
||||
taskListeners.forEach(it -> it.onReady(task));
|
||||
|
||||
boolean doDependentsSucceeded = executeTasks(task.getDependents());
|
||||
boolean flag = false;
|
||||
|
||||
try {
|
||||
boolean doDependentsSucceeded = executeTasks(task.getDependents());
|
||||
if (!doDependentsSucceeded && task.isRelyingOnDependents() || canceled)
|
||||
throw new SilentException();
|
||||
|
||||
@@ -166,27 +160,29 @@ public final class TaskExecutor {
|
||||
throw new IllegalStateException("Subtasks failed for " + task.getName());
|
||||
|
||||
flag = true;
|
||||
if (!task.isHidden()) {
|
||||
if (task.getSignificance().shouldLog()) {
|
||||
Logging.LOG.log(Level.FINER, "Task finished: {0}", task.getName());
|
||||
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, false));
|
||||
taskListener.onFinished(task);
|
||||
taskListeners.forEach(it -> it.onFinished(task));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
if (!task.isHidden()) {
|
||||
if (task.getSignificance().shouldLog()) {
|
||||
lastException = e;
|
||||
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName(), e);
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||
taskListener.onFailed(task, e);
|
||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||
}
|
||||
} catch (SilentException e) {
|
||||
// do nothing
|
||||
} catch (RejectedExecutionException e) {
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
if (!task.isHidden()) {
|
||||
if (task.getSignificance().shouldLog()) {
|
||||
lastException = e;
|
||||
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
|
||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||
taskListener.onFailed(task, e);
|
||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||
}
|
||||
} finally {
|
||||
task.setVariables(null);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.task;
|
||||
|
||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
@@ -37,6 +39,9 @@ public abstract class TaskListener implements EventListener {
|
||||
public void onTerminate() {
|
||||
}
|
||||
|
||||
public void onTerminate(AutoTypingMap<String> variables) {
|
||||
}
|
||||
|
||||
public static class DefaultTaskListener extends TaskListener {
|
||||
|
||||
public static final DefaultTaskListener INSTANCE = new DefaultTaskListener();
|
||||
|
||||
Reference in New Issue
Block a user