Compare commits

..
Author SHA1 Message Date
huanghongxun 32d0d6a7ee Add some task test 2019-02-26 15:28:40 +08:00
huanghongxun d9a336e50b Remove redundant codes 2019-02-26 13:29:09 +08:00
huanghongxun 3c958d06c8 Replace scheduler to executor 2019-02-26 11:39:38 +08:00
huanghongxun 15a75a69b3 Fix exception handling 2019-02-26 08:17:46 +08:00
huanghongxun cae32cff4c remove isDependentSucceeded in whenComplete 2019-02-25 23:59:21 +08:00
huanghongxun eeef76594f Rename task.lastException -> task.exception 2019-02-25 22:17:30 +08:00
huanghongxun 8f2a1030bc Refactor Task 2019-02-24 23:27:53 +08:00
122 changed files with 1555 additions and 2201 deletions

No files matched your search

+2 -2
View File
@@ -112,7 +112,7 @@ def createExecutable(String suffix, String header) {
processResources {
ext.convertToBSS = { String resource ->
// exclude resource
exclude resource
doFirst {
def cssFile = new File(this.projectDir, "src/main/resources/" + resource)
def bssFile = new File(this.projectDir, "build/compiled-resources/" + resource[0..-4] + "bss")
@@ -140,7 +140,7 @@ task makePack(dependsOn: jar) {
task makePackXz(dependsOn: makePack) doLast {
def packXz = new File(makePack.outputPath.parentFile, makePack.outputPath.name + ".xz")
new XZOutputStream(packXz.newOutputStream(), new LZMA2Options(5)).withCloseable { it << makePack.outputPath.bytes }
new XZOutputStream(packXz.newOutputStream(), new LZMA2Options(6)).withCloseable { it << makePack.outputPath.bytes }
createChecksum(packXz)
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl;
import com.jfoenix.utils.JFXUtilities;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
@@ -24,9 +25,7 @@ import org.jackhuang.hmcl.setting.ConfigHolder;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.CrashReporter;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
@@ -40,7 +39,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -99,7 +97,7 @@ public final class Launcher extends Application {
public static void stopApplication() {
LOG.info("Stopping application.\n" + StringUtils.getStackTrace(Thread.currentThread().getStackTrace()));
runInFX(() -> {
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
@@ -113,7 +111,7 @@ public final class Launcher extends Application {
public static void stopWithoutPlatform() {
LOG.info("Stopping application without JavaFX Toolkit.\n" + StringUtils.getStackTrace(Thread.currentThread().getStackTrace()));
runInFX(() -> {
JFXUtilities.runInFX(() -> {
if (Controllers.getStage() == null)
return;
Controllers.getStage().close();
@@ -30,7 +30,7 @@ import java.util.List;
/**
* Export the game to a mod pack file.
*/
public class HMCLModpackExportTask extends Task {
public class HMCLModpackExportTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String version;
private final List<String> whitelist;
@@ -36,13 +36,13 @@ import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public final class HMCLModpackInstallTask extends Task {
public final class HMCLModpackInstallTask extends Task<Void> {
private final File zipFile;
private final String name;
private final HMCLGameRepository repository;
private final Modpack modpack;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
public HMCLModpackInstallTask(Profile profile, File zipFile, Modpack modpack, String name) {
DependencyManager dependency = profile.getDependency();
@@ -77,12 +77,12 @@ public final class HMCLModpackInstallTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@Override
public List<Task> getDependents() {
public List<Task<?>> getDependents() {
return dependents;
}
@@ -102,7 +102,7 @@ public final class LauncherHelper {
try {
checkGameState(profile, setting, version, () -> {
Controllers.dialog(launchingStepsPane);
Schedulers.newThread().schedule(this::launch0);
Schedulers.newThread().execute(this::launch0);
});
} catch (InterruptedException ignore) {
}
@@ -121,15 +121,15 @@ public final class LauncherHelper {
Version version = MaintainTask.maintain(repository.getResolvedVersion(selectedVersion));
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
.then(() -> {
TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
.thenCompose(() -> {
if (setting.isNotCheckGame())
return null;
else
return dependencyManager.checkGameCompletionAsync(version);
})
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
.then(() -> {
.thenRun(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))
.thenCompose(() -> {
try {
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
if ("Curse".equals(configuration.getType()))
@@ -140,8 +140,8 @@ public final class LauncherHelper {
return null;
}
})
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.thenCompose(() -> Task.ofResult(i18n("account.methods"), () -> {
.thenRun(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN))
.thenSupply(i18n("account.methods"), () -> {
try {
return account.logIn();
} catch (CredentialExpiredException e) {
@@ -151,7 +151,7 @@ public final class LauncherHelper {
LOG.warning("Authentication failed, try playing offline: " + e);
return account.playOffline().orElseThrow(() -> e);
}
}))
})
.thenApply(Schedulers.javafx(), authInfo -> {
emitStatus(LoadingState.LAUNCHING);
return authInfo;
@@ -163,7 +163,7 @@ public final class LauncherHelper {
setting.toLaunchOptions(profile.getGameDir()),
launcherVisibility == LauncherVisibility.CLOSE
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
: new HMCLProcessListener(authInfo, setting, gameVersion.isPresent())
: new HMCLProcessListener(authInfo, gameVersion.isPresent())
))
.thenCompose(launcher -> { // launcher is prev task's result
if (scriptFile == null) {
@@ -199,7 +199,7 @@ public final class LauncherHelper {
final AtomicInteger finished = new AtomicInteger(0);
@Override
public void onFinished(Task task) {
public void onFinished(Task<?> task) {
finished.incrementAndGet();
int runningTasks = executor.getRunningTasks();
Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks));
@@ -213,7 +213,7 @@ public final class LauncherHelper {
// because onStop will be invoked if tasks fail when the executor service shut down.
if (!Controllers.isStopped()) {
launchingStepsPane.fireEvent(new DialogCloseEvent());
Exception ex = executor.getLastException();
Exception ex = executor.getException();
if (ex != null) {
String message;
if (ex instanceof CurseCompletionException) {
@@ -416,7 +416,7 @@ public final class LauncherHelper {
}
}
private static class LaunchTask<T> extends TaskResult<T> {
private static class LaunchTask<T> extends Task<T> {
private final ExceptionalSupplier<T, Exception> supplier;
public LaunchTask(ExceptionalSupplier<T, Exception> supplier) {
@@ -436,7 +436,6 @@ public final class LauncherHelper {
*/
class HMCLProcessListener implements ProcessListener {
private final VersionSetting setting;
private final Map<String, String> forbiddenTokens;
private ManagedProcess process;
private boolean lwjgl;
@@ -445,8 +444,7 @@ public final class LauncherHelper {
private final LinkedList<Pair<String, Log4jLevel>> logs;
private final CountDownLatch latch = new CountDownLatch(1);
public HMCLProcessListener(AuthInfo authInfo, VersionSetting setting, boolean detectWindow) {
this.setting = setting;
public HMCLProcessListener(AuthInfo authInfo, boolean detectWindow) {
this.detectWindow = detectWindow;
if (authInfo == null)
@@ -86,7 +86,7 @@ public final class ModpackHelper {
throw new UnsupportedModpackException();
}
public static Task getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
public static Task<Void> getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
profile.getRepository().markVersionAsModpack(name);
ExceptionalRunnable<?> success = () -> {
@@ -117,11 +117,11 @@ public final class ModpackHelper {
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
.whenComplete(Schedulers.defaultScheduler(), success, failure)
.then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
.thenCompose(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
else throw new IllegalStateException("Unrecognized modpack: " + modpack);
}
public static Task getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
public static Task<Void> getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
Modpack modpack = ModpackHelper.readModpackManifest(zipFile.toPath(), charset);
switch (configuration.getType()) {
@@ -20,13 +20,12 @@ package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.mod.MultiMCInstanceConfiguration;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.task.Scheduler;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import java.util.Objects;
public final class MultiMCInstallVersionSettingTask extends Task {
public final class MultiMCInstallVersionSettingTask extends Task<Void> {
private final Profile profile;
private final MultiMCInstanceConfiguration manifest;
private final String version;
@@ -35,11 +34,8 @@ public final class MultiMCInstallVersionSettingTask extends Task {
this.profile = profile;
this.manifest = manifest;
this.version = version;
}
@Override
public Scheduler getScheduler() {
return Schedulers.javafx();
setExecutor(Schedulers.javafx());
}
@Override
@@ -197,7 +197,7 @@ public final class Accounts {
Account selected = selectedAccount.get();
if (selected != null) {
Schedulers.io().schedule(() -> {
Schedulers.io().execute(() -> {
try {
selected.logIn();
} catch (AuthenticationException e) {
@@ -209,7 +209,7 @@ public final class Accounts {
for (AuthlibInjectorServer server : config().getAuthlibInjectorServers()) {
if (selected instanceof AuthlibInjectorAccount && ((AuthlibInjectorAccount) selected).getServer() == server)
continue;
Schedulers.io().schedule(() -> {
Schedulers.io().execute(() -> {
try {
server.fetchMetadataResponse();
} catch (IOException e) {
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.setting;
import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import com.jfoenix.utils.JFXUtilities;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
@@ -31,7 +32,7 @@ import org.jackhuang.hmcl.game.HMCLCacheRepository;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
import java.io.File;
@@ -39,7 +40,6 @@ import java.lang.reflect.Type;
import java.util.Optional;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
/**
*
@@ -144,7 +144,7 @@ public final class Profile implements Observable {
}
private void checkSelectedVersion() {
runInFX(() -> {
JFXUtilities.runInFX(() -> {
if (!repository.isLoaded()) return;
String newValue = selectedVersion.get();
if (!repository.hasVersion(newValue)) {
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.setting;
import com.jfoenix.utils.JFXUtilities;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.*;
@@ -35,7 +36,6 @@ import java.util.stream.Collectors;
import static javafx.collections.FXCollections.observableArrayList;
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class Profiles {
@@ -162,7 +162,7 @@ public final class Profiles {
});
EventBus.EVENT_BUS.channel(RefreshedVersionsEvent.class).registerWeak(event -> {
runInFX(() -> {
JFXUtilities.runInFX(() -> {
Profile profile = selectedProfile.get();
if (profile != null && profile.getRepository() == event.getSource()) {
selectedVersion.bind(profile.selectedVersionProperty());
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.utils.JFXUtilities;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
@@ -33,11 +34,8 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.account.AccountList;
import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage;
import org.jackhuang.hmcl.ui.construct.InputDialogPane;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.PopupMenu;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.ui.decorator.DecoratorController;
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
import org.jackhuang.hmcl.ui.profile.ProfileList;
@@ -60,7 +58,6 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class Controllers {
@@ -177,7 +174,7 @@ public final class Controllers {
return node;
})
.collect(Collectors.toList());
runInFX(() -> {
JFXUtilities.runInFX(() -> {
if (profile == Profiles.getSelectedProfile())
mainPage.getVersions().setAll(children);
});
@@ -208,7 +205,7 @@ public final class Controllers {
dialog(i18n("launcher.cache_directory.invalid"));
}
Task.of(JavaVersion::initialize).start();
Task.runAsync(JavaVersion::initialize).start();
scene = new Scene(decorator.getDecorator(), 800, 519);
scene.getStylesheets().setAll(config().getTheme().getStylesheets());
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.utils.JFXUtilities;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.AuthenticationException;
@@ -28,15 +29,13 @@ import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
public final class DialogController {
public static AuthInfo logIn(Account account) throws CancellationException, AuthenticationException, InterruptedException {
if (account instanceof YggdrasilAccount) {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<AuthInfo> res = new AtomicReference<>(null);
runInFX(() -> {
JFXUtilities.runInFX(() -> {
AccountLoginPane pane = new AccountLoginPane(account, it -> {
res.set(it);
latch.countDown();
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.utils.JFXUtilities;
import com.jfoenix.controls.*;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
@@ -30,7 +31,6 @@ import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
@@ -44,7 +44,7 @@ import javafx.scene.shape.Rectangle;
import javafx.util.Callback;
import javafx.util.Duration;
import javafx.util.StringConverter;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.javafx.ExtendedProperties;
@@ -75,14 +75,6 @@ public final class FXUtils {
private FXUtils() {
}
public static void runInFX(Runnable runnable) {
if (Platform.isFxApplicationThread()) {
runnable.run();
} else {
Platform.runLater(runnable);
}
}
public static void checkFxUserThread() {
if (!Platform.isFxApplicationThread()) {
throw new IllegalStateException("Not on FX application thread; currentThread = "
@@ -162,20 +154,6 @@ public final class FXUtils {
});
}
public static <K, T> void setupCellValueFactory(JFXTreeTableColumn<K, T> column, Function<K, ObservableValue<T>> mapper) {
column.setCellValueFactory(param -> {
if (column.validateValue(param))
return mapper.apply(param.getValue().getValue());
else
return column.getComputedValue(param);
});
}
public static Node wrapMargin(Node node, Insets insets) {
StackPane.setMargin(node, insets);
return new StackPane(node);
}
public static void setValidateWhileTextChanged(Node field, boolean validate) {
if (field instanceof JFXTextField) {
if (validate) {
@@ -274,7 +252,7 @@ public final class FXUtils {
}
public static void installTooltip(Node node, double openDelay, double visibleDelay, double closeDelay, Tooltip tooltip) {
runInFX(() -> {
JFXUtilities.runInFX(() -> {
try {
// Java 8
Class<?> behaviorClass = Class.forName("javafx.scene.control.Tooltip$TooltipBehavior");
@@ -395,7 +373,7 @@ public final class FXUtils {
*/
@SuppressWarnings("unchecked")
@Deprecated
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum> property) {
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum<?>> property) {
unbindEnum(comboBox);
ChangeListener<Number> listener = (a, b, newValue) ->
((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]);
@@ -17,13 +17,10 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.effects.JFXDepthManager;
import javafx.geometry.Pos;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import java.util.function.Consumer;
@@ -33,37 +30,26 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
* @author huangyuhui
*/
public class InstallerItem extends BorderPane {
private final Consumer<InstallerItem> deleteCallback;
@FXML
private Label lblInstallerArtifact;
@FXML
private Label lblInstallerVersion;
public InstallerItem(String artifact, String version, Consumer<InstallerItem> deleteCallback) {
this.deleteCallback = deleteCallback;
FXUtils.loadFXML(this, "/assets/fxml/version/installer-item.fxml");
public InstallerItem(String artifact, String version, Runnable upgrade, Consumer<InstallerItem> deleteCallback) {
getStyleClass().add("two-line-list-item");
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
{
TwoLineListItem item = new TwoLineListItem();
item.setTitle(artifact);
item.setSubtitle(i18n("archive.version") + ": " + version);
setCenter(item);
}
{
HBox hBox = new HBox();
JFXButton upgradeButton = new JFXButton();
upgradeButton.setGraphic(SVG.update(Theme.blackFillBinding(), -1, -1));
upgradeButton.getStyleClass().add("toggle-icon4");
FXUtils.installFastTooltip(upgradeButton, i18n("install.change_version"));
upgradeButton.setOnMouseClicked(e -> upgrade.run());
JFXButton deleteButton = new JFXButton();
deleteButton.setGraphic(SVG.close(Theme.blackFillBinding(), -1, -1));
deleteButton.getStyleClass().add("toggle-icon4");
deleteButton.setOnMouseClicked(e -> deleteCallback.accept(this));
hBox.setAlignment(Pos.CENTER_RIGHT);
hBox.getChildren().setAll(upgradeButton, deleteButton);
setRight(hBox);
}
lblInstallerArtifact.setText(artifact);
lblInstallerVersion.setText(i18n("archive.version") + ": " + version);
}
@FXML
private void onDelete() {
deleteCallback.accept(this);
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.utils.JFXUtilities;
import javafx.application.Platform;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
@@ -42,7 +43,6 @@ import org.jackhuang.hmcl.util.io.CompressingUtils;
import java.io.File;
import java.util.concurrent.atomic.AtomicReference;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class LeftPaneController extends AdvancedListBox {
@@ -111,19 +111,19 @@ public final class LeftPaneController extends AdvancedListBox {
private boolean checkedModpack = false;
private void onRefreshedVersions(HMCLGameRepository repository) {
runInFX(() -> {
JFXUtilities.runInFX(() -> {
if (!checkedModpack) {
checkedModpack = true;
if (repository.getVersionCount() == 0) {
File modpackFile = new File("modpack.zip").getAbsoluteFile();
if (modpackFile.exists()) {
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
.thenApply(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenAccept(modpack -> {
AtomicReference<Region> region = new AtomicReference<>();
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor();
.withRun(Schedulers.javafx(), this::checkAccount).executor();
region.set(Controllers.taskDialog(executor, i18n("modpack.installing")));
executor.start();
}).start();
@@ -18,11 +18,18 @@
package org.jackhuang.hmcl.ui;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
public abstract class ListPage<T extends Node> extends ListPageBase<T> {
public abstract class ListPage<T extends Node> extends Control {
private final ListProperty<T> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList());
private final BooleanProperty loading = new SimpleBooleanProperty(this, "loading", false);
private final BooleanProperty refreshable = new SimpleBooleanProperty(this, "refreshable", false);
public abstract void add();
@@ -35,6 +42,30 @@ public abstract class ListPage<T extends Node> extends ListPageBase<T> {
return new ListPageSkin(this);
}
public ObservableList<T> getItems() {
return items.get();
}
public void setItems(ObservableList<T> items) {
this.items.set(items);
}
public ListProperty<T> itemsProperty() {
return items;
}
public boolean isLoading() {
return loading.get();
}
public void setLoading(boolean loading) {
this.loading.set(loading);
}
public BooleanProperty loadingProperty() {
return loading;
}
public boolean isRefreshable() {
return refreshable.get();
}
@@ -1,55 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Control;
public class ListPageBase<T> extends Control {
private final ListProperty<T> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList());
private final BooleanProperty loading = new SimpleBooleanProperty(this, "loading", false);
public ObservableList<T> getItems() {
return items.get();
}
public void setItems(ObservableList<T> items) {
this.items.set(items);
}
public ListProperty<T> itemsProperty() {
return items;
}
public boolean isLoading() {
return loading.get();
}
public void setLoading(boolean loading) {
this.loading.set(loading);
}
public BooleanProperty loadingProperty() {
return loading;
}
}
@@ -33,7 +33,6 @@ import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import org.jackhuang.hmcl.setting.Profile;
@@ -29,10 +29,6 @@ public final class SVG {
private SVG() {
}
public interface SVGIcon {
Node createIcon(ObjectBinding<? extends Paint> fill, double width, double height);
}
private static Node createSVGPath(String d, ObjectBinding<? extends Paint> fill, double width, double height) {
SVGPath path = new SVGPath();
path.getStyleClass().add("svg");
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.*;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Cursor;
@@ -73,7 +72,7 @@ public abstract class SettingsView extends StackPane {
{
VBox rootPane = new VBox();
rootPane.setPadding(new Insets(36, 18, 36, 18));
rootPane.setStyle("-fx-padding: 18;");
{
ComponentList settingsPane = new ComponentList();
{
@@ -81,31 +80,12 @@ public abstract class SettingsView extends StackPane {
StackPane sponsorPane = new StackPane();
sponsorPane.setCursor(Cursor.HAND);
sponsorPane.setOnMouseClicked(e -> onSponsor());
GridPane gridPane = new GridPane();
ColumnConstraints col = new ColumnConstraints();
col.setHgrow(Priority.SOMETIMES);
col.setMaxWidth(Double.POSITIVE_INFINITY);
gridPane.getColumnConstraints().setAll(col);
RowConstraints row = new RowConstraints();
row.setMinHeight(Double.NEGATIVE_INFINITY);
row.setValignment(VPos.TOP);
row.setVgrow(Priority.SOMETIMES);
gridPane.getRowConstraints().setAll(row);
{
Label label = new Label(i18n("sponsor.hmcl"));
label.setWrapText(true);
label.setTextAlignment(TextAlignment.JUSTIFY);
GridPane.setRowIndex(label, 0);
GridPane.setColumnIndex(label, 0);
gridPane.getChildren().add(label);
label.prefWidthProperty().bind(sponsorPane.widthProperty());
sponsorPane.getChildren().add(label);
}
sponsorPane.getChildren().setAll(gridPane);
settingsPane.getContent().add(sponsorPane);
}
@@ -1,96 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXScrollPane;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import java.util.List;
public abstract class ToolbarListPageSkin<T extends ListPageBase<? extends Node>> extends SkinBase<T> {
public ToolbarListPageSkin(T skinnable) {
super(skinnable);
SpinnerPane spinnerPane = new SpinnerPane();
spinnerPane.getStyleClass().add("large-spinner-pane");
BorderPane root = new BorderPane();
{
HBox toolbar = new HBox();
toolbar.getStyleClass().add("jfx-tool-bar-second");
JFXDepthManager.setDepth(toolbar, 1);
toolbar.setPickOnBounds(false);
toolbar.getChildren().setAll(initializeToolbar(skinnable));
root.setTop(toolbar);
}
{
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToWidth(true);
VBox content = new VBox();
content.setSpacing(10);
content.setPadding(new Insets(10));
Bindings.bindContent(content.getChildren(), skinnable.itemsProperty());
scrollPane.setContent(content);
JFXScrollPane.smoothScrolling(scrollPane);
root.setCenter(scrollPane);
}
spinnerPane.loadingProperty().bind(skinnable.loadingProperty());
spinnerPane.setContent(root);
getChildren().setAll(spinnerPane);
}
public static Node wrap(Node node) {
StackPane stackPane = new StackPane();
stackPane.setPadding(new Insets(0, 5, 0, 2));
stackPane.getChildren().setAll(node);
return stackPane;
}
public static JFXButton createToolbarButton(String text, SVG.SVGIcon creator, Runnable onClick) {
JFXButton ret = new JFXButton();
ret.getStyleClass().add("jfx-tool-bar-button");
ret.textFillProperty().bind(Theme.foregroundFillBinding());
ret.setGraphic(wrap(creator.createIcon(Theme.foregroundFillBinding(), -1, -1)));
ret.setText(text);
ret.setOnMouseClicked(e -> onClick.run());
return ret;
}
protected abstract List<Node> initializeToolbar(T skinnable);
}
@@ -17,9 +17,11 @@
*/
package org.jackhuang.hmcl.ui.account;
import com.jfoenix.utils.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXRadioButton;
import com.jfoenix.effects.JFXDepthManager;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
@@ -28,16 +30,17 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount;
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
public class AccountListItemSkin extends SkinBase<AccountListItem> {
public AccountListItemSkin(AccountListItem skinnable) {
@@ -88,7 +91,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
btnRefresh.setOnMouseClicked(e -> skinnable.refresh());
btnRefresh.getStyleClass().add("toggle-icon4");
btnRefresh.setGraphic(SVG.refresh(Theme.blackFillBinding(), -1, -1));
runInFX(() -> FXUtils.installFastTooltip(btnRefresh, i18n("button.refresh")));
JFXUtilities.runInFX(() -> FXUtils.installFastTooltip(btnRefresh, i18n("button.refresh")));
right.getChildren().add(btnRefresh);
JFXButton btnRemove = new JFXButton();
@@ -96,7 +99,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
btnRemove.getStyleClass().add("toggle-icon4");
BorderPane.setAlignment(btnRemove, Pos.CENTER);
btnRemove.setGraphic(SVG.delete(Theme.blackFillBinding(), -1, -1));
runInFX(() -> FXUtils.installFastTooltip(btnRemove, i18n("button.delete")));
JFXUtilities.runInFX(() -> FXUtils.installFastTooltip(btnRemove, i18n("button.delete")));
right.getChildren().add(btnRemove);
root.setRight(right);
@@ -61,7 +61,7 @@ public class AccountLoginPane extends StackPane {
String password = txtPassword.getText();
progressBar.setVisible(true);
lblCreationWarning.setText("");
Task.ofResult(() -> oldAccount.logInWithPassword(password))
Task.supplyAsync(() -> oldAccount.logInWithPassword(password))
.whenComplete(Schedulers.javafx(), authInfo -> {
success.accept(authInfo);
fireEvent(new DialogCloseEvent());
@@ -17,10 +17,9 @@
*/
package org.jackhuang.hmcl.ui.account;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXPasswordField;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.utils.JFXUtilities;
import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ListProperty;
@@ -54,7 +53,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
@@ -197,7 +195,7 @@ public class AddAccountPane extends StackPane {
AccountFactory<?> factory = cboType.getSelectionModel().getSelectedItem();
Object additionalData = getAuthAdditionalData();
Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData))
Task.supplyAsync(() -> factory.create(new Selector(), username, password, additionalData))
.whenComplete(Schedulers.javafx(), account -> {
int oldIndex = Accounts.getAccounts().indexOf(account);
if (oldIndex == -1) {
@@ -295,7 +293,7 @@ public class AddAccountPane extends StackPane {
} catch (InterruptedException ignore) {
throw new NoSelectedCharacterException();
} finally {
runInFX(() -> Selector.this.fireEvent(new DialogCloseEvent()));
JFXUtilities.runInFX(() -> Selector.this.fireEvent(new DialogCloseEvent()));
}
}
}
@@ -104,13 +104,13 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
nextPane.showSpinner();
addServerPane.setDisable(true);
Task.of(() -> {
Task.runAsync(() -> {
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
}).whenComplete(Schedulers.javafx(), (isDependentSucceeded, exception) -> {
}).whenComplete(Schedulers.javafx(), exception -> {
addServerPane.setDisable(false);
nextPane.hideSpinner();
if (isDependentSucceeded) {
if (exception == null) {
lblServerName.setText(serverBeingAdded.getName());
lblServerUrl.setText(serverBeingAdded.getUrl());
@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.ui.construct;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
@@ -26,12 +26,11 @@ import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeTableCell;
import javafx.scene.layout.StackPane;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class JFXCheckBoxTreeTableCell<S,T> extends TreeTableCell<S,T> {
private final StackPane pane;
private final CheckBox checkBox;
private boolean showLabel;
private ObservableValue<Boolean> booleanProperty;
@@ -50,8 +49,6 @@ public class JFXCheckBoxTreeTableCell<S,T> extends TreeTableCell<S,T> {
final StringConverter<T> converter) {
this.getStyleClass().add("check-box-tree-table-cell");
this.checkBox = new JFXCheckBox();
this.pane = new StackPane(checkBox);
this.pane.setAlignment(Pos.CENTER);
setGraphic(null);
setSelectedStateCallback(getSelectedProperty);
setConverter(converter);
@@ -104,8 +101,7 @@ public class JFXCheckBoxTreeTableCell<S,T> extends TreeTableCell<S,T> {
if (showLabel) {
setText(c.toString(item));
}
setGraphic(pane);
setGraphic(checkBox);
if (booleanProperty instanceof BooleanProperty) {
checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty);
@@ -75,12 +75,12 @@ public class RipplerContainer extends StackPane {
focusedProperty().addListener((a, b, newValue) -> {
if (newValue) {
if (!isPressed())
buttonRippler.showOverlay();
buttonRippler.setOverlayVisible(true);
} else {
buttonRippler.hideOverlay();
buttonRippler.setOverlayVisible(false);
}
});
pressedProperty().addListener(o -> buttonRippler.hideOverlay());
pressedProperty().addListener(o -> buttonRippler.setOverlayVisible(false));
setPickOnBounds(false);
buttonContainer.setPickOnBounds(false);
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.utils.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXProgressBar;
import javafx.application.Platform;
@@ -33,8 +34,6 @@ import org.jackhuang.hmcl.ui.FXUtils;
import java.util.Optional;
import java.util.function.Consumer;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
public class TaskExecutorDialogPane extends StackPane {
private TaskExecutor executor;
private Consumer<Region> onCancel;
@@ -122,6 +121,6 @@ public class TaskExecutorDialogPane extends StackPane {
public void setCancel(Consumer<Region> onCancel) {
this.onCancel = onCancel;
runInFX(() -> btnCancel.setDisable(onCancel == null));
JFXUtilities.runInFX(() -> btnCancel.setDisable(onCancel == null));
}
}
@@ -42,7 +42,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class TaskListPane extends StackPane {
private final AdvancedListBox listBox = new AdvancedListBox();
private final Map<Task, ProgressListNode> nodes = new HashMap<>();
private final Map<Task<?>, ProgressListNode> nodes = new HashMap<>();
private final ReadOnlyIntegerWrapper finishedTasks = new ReadOnlyIntegerWrapper();
private final ReadOnlyIntegerWrapper totTasks = new ReadOnlyIntegerWrapper();
@@ -72,12 +72,12 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onReady(Task task) {
public void onReady(Task<?> task) {
Platform.runLater(() -> totTasks.set(totTasks.getValue() + 1));
}
@Override
public void onRunning(Task task) {
public void onRunning(Task<?> task) {
if (!task.getSignificance().shouldShow())
return;
@@ -113,7 +113,7 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onFinished(Task task) {
public void onFinished(Task<?> task) {
ProgressListNode node = nodes.remove(task);
if (node == null)
return;
@@ -125,7 +125,7 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onFailed(Task task, Throwable throwable) {
public void onFailed(Task<?> task, Throwable throwable) {
ProgressListNode node = nodes.remove(task);
if (node == null)
return;
@@ -142,7 +142,7 @@ public final class TaskListPane extends StackPane {
private final Label title = new Label();
private final Label state = new Label();
public ProgressListNode(Task task) {
public ProgressListNode(Task<?> task) {
bar.progressProperty().bind(task.progressProperty());
title.setText(task.getName());
state.textProperty().bind(task.messageProperty());
@@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.collections.ListChangeListener;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -56,7 +55,6 @@ public class DecoratorSkin extends SkinBase<Decorator> {
private double xOffset, yOffset, newX, newY, initX, initY;
private boolean allowMove, isDragging;
private BoundingBox originalBox, maximizedBox;
/**
* Constructor for all SkinBase instances.
@@ -22,13 +22,11 @@ import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.game.LibraryDownloadException;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.DownloadException;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.wizard.WizardController;
@@ -91,7 +89,7 @@ public final class InstallerWizardProvider implements WizardProvider {
settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next));
TaskResult<Version> ret = Task.ofResult(() -> version);
Task<Version> ret = Task.supplyAsync(() -> version);
if (settings.containsKey("forge"))
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
@@ -102,7 +100,7 @@ public final class InstallerWizardProvider implements WizardProvider {
if (settings.containsKey("optifine"))
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
return ret.then(profile.getRepository().refreshVersionsAsync());
return ret.thenCompose(profile.getRepository().refreshVersionsAsync());
}
@Override
@@ -126,8 +124,6 @@ public final class InstallerWizardProvider implements WizardProvider {
Controllers.dialog(i18n("launch.failed.download_library", ((LibraryDownloadException) exception).getLibrary().getName()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageType.ERROR, next);
} else if (exception instanceof DownloadException) {
Controllers.dialog(i18n("install.failed.downloading.detail", ((DownloadException) exception).getUrl()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageType.ERROR, next);
} else if (exception instanceof OptiFineInstallTask.UnsupportedOptiFineInstallationException) {
Controllers.dialog(i18n("install.failed.optifine_conflict"), i18n("install.failed"), MessageType.ERROR, next);
} else {
Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageType.ERROR, next);
}
@@ -71,7 +71,7 @@ public class ModpackInstallWizardProvider implements WizardProvider {
settings.put(PROFILE, profile);
}
private Task finishModpackInstallingAsync(Map<String, Object> settings) {
private Task<Void> finishModpackInstallingAsync(Map<String, Object> settings) {
if (!settings.containsKey(ModpackPage.MODPACK_FILE))
return null;
@@ -93,7 +93,7 @@ public class ModpackInstallWizardProvider implements WizardProvider {
return null;
} else {
return ModpackHelper.getInstallTask(profile, selected, name, modpack)
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
.thenRun(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
}
}
@@ -109,7 +109,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
}
spinnerPane.showSpinner();
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
.thenApply(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
.whenComplete(Schedulers.javafx(), manifest -> {
spinnerPane.hideSpinner();
@@ -1,88 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.download;
import javafx.scene.Node;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.MaintainTask;
import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import java.util.LinkedList;
import java.util.Map;
import static org.jackhuang.hmcl.ui.download.InstallerWizardProvider.alertFailureMessage;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class UpdateInstallerWizardProvider implements WizardProvider {
private final Profile profile;
private final String gameVersion;
private final Version version;
private final String libraryId;
private final Library oldLibrary;
public UpdateInstallerWizardProvider(Profile profile, String gameVersion, Version version, String libraryId, Library oldLibrary) {
this.profile = profile;
this.gameVersion = gameVersion;
this.version = version;
this.libraryId = libraryId;
this.oldLibrary = oldLibrary;
}
@Override
public void start(Map<String, Object> settings) {
}
@Override
public Object finish(Map<String, Object> settings) {
settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next));
// We remove library but not save it,
// so if installation failed will not break down current version.
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
newList.remove(oldLibrary);
return new MaintainTask(version.setLibraries(newList))
.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get(libraryId)))
.then(profile.getRepository().refreshVersionsAsync());
}
@Override
public Node createPage(WizardController controller, int step, Map<String, Object> settings) {
DownloadProvider provider = profile.getDependency().getDownloadProvider();
switch (step) {
case 0:
return new VersionsPage(controller, i18n("install.installer.choose", i18n("install.installer." + libraryId)), gameVersion, provider, libraryId, () -> {
Controllers.confirmDialog(i18n("install.change_version.confirm", i18n("install.installer." + libraryId), oldLibrary.getVersion(), ((RemoteVersion) settings.get(libraryId)).getSelfVersion()),
i18n("install.change_version"), controller::onFinish, controller::onCancel);
});
default:
throw new IllegalStateException();
}
}
@Override
public boolean cancel() {
return true;
}
}
@@ -43,7 +43,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
settings.put(PROFILE, profile);
}
private Task finishVersionDownloadingAsync(Map<String, Object> settings) {
private Task<Void> finishVersionDownloadingAsync(Map<String, Object> settings) {
GameBuilder builder = profile.getDependency().gameBuilder();
String name = (String) settings.get("name");
@@ -59,8 +59,8 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
if (settings.containsKey("optifine"))
builder.version((RemoteVersion) settings.get("optifine"));
return builder.buildAsync().whenComplete((a, b) -> profile.getRepository().refreshVersions())
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
return builder.buildAsync().whenComplete(any -> profile.getRepository().refreshVersions())
.thenRun(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
}
@Override
@@ -128,8 +128,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
@Override
public void refresh() {
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
executor = versionList.refreshAsync(gameVersion, downloadProvider).whenComplete((isDependentSucceeded, exception) -> {
if (isDependentSucceeded) {
executor = versionList.refreshAsync(gameVersion, downloadProvider).whenComplete(exception -> {
if (exception == null) {
List<VersionsPageItem> items = loadVersions();
Platform.runLater(() -> {
@@ -60,8 +60,8 @@ public final class ExportWizardProvider implements WizardProvider {
List<File> launcherJar = Launcher.getCurrentJarFiles();
boolean includeLauncher = (Boolean) settings.get(ModpackInfoPage.MODPACK_INCLUDE_LAUNCHER) && launcherJar != null;
return new Task() {
Task dependency = null;
return new Task<Void>() {
Task<?> dependency = null;
@Override
public void execute() throws Exception {
@@ -80,7 +80,7 @@ public final class ExportWizardProvider implements WizardProvider {
), tempModpack);
if (includeLauncher) {
dependency = dependency.then(Task.of(() -> {
dependency = dependency.thenRun(() -> {
try (Zipper zip = new Zipper(modpackFile.toPath())) {
Config exported = new Config();
@@ -109,12 +109,12 @@ public final class ExportWizardProvider implements WizardProvider {
for (File jar : launcherJar)
zip.putFile(jar, jar.getName());
}
}));
});
}
}
@Override
public Collection<? extends Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency);
}
};
@@ -26,11 +26,9 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.FileItem;
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
@@ -38,7 +36,6 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.nio.file.Paths;
import java.util.Optional;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -0,0 +1,60 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.effects.JFXDepthManager;
import javafx.geometry.Pos;
import javafx.scene.layout.BorderPane;
import org.jackhuang.hmcl.mod.Datapack;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import java.util.function.Consumer;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class DatapackListItem extends BorderPane {
public DatapackListItem(Datapack.Pack info, Consumer<DatapackListItem> deleteCallback) {
JFXCheckBox chkEnabled = new JFXCheckBox();
BorderPane.setAlignment(chkEnabled, Pos.CENTER);
setLeft(chkEnabled);
TwoLineListItem modItem = new TwoLineListItem();
BorderPane.setAlignment(modItem, Pos.CENTER);
setCenter(modItem);
JFXButton btnRemove = new JFXButton();
FXUtils.installFastTooltip(btnRemove, i18n("datapack.remove"));
btnRemove.setOnMouseClicked(e -> deleteCallback.accept(this));
btnRemove.getStyleClass().add("toggle-icon4");
BorderPane.setAlignment(btnRemove, Pos.CENTER);
btnRemove.setGraphic(SVG.close(Theme.blackFillBinding(), 15, 15));
setRight(btnRemove);
setStyle("-fx-background-radius: 2; -fx-background-color: white; -fx-padding: 8;");
JFXDepthManager.setDepth(this, 1);
modItem.setTitle(info.getId());
modItem.setSubtitle(info.getDescription());
chkEnabled.selectedProperty().bindBidirectional(info.activeProperty());
}
}
@@ -19,15 +19,11 @@ package org.jackhuang.hmcl.ui.versions;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;
import javafx.scene.control.Skin;
import javafx.scene.control.TreeItem;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.mod.Datapack;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPageBase;
import org.jackhuang.hmcl.ui.ListPage;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -36,19 +32,16 @@ import org.jackhuang.hmcl.util.javafx.MappedObservableList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.logging.Level;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class DatapackListPage extends ListPageBase<DatapackListPageSkin.DatapackInfoObject> implements DecoratorPage {
public class DatapackListPage extends ListPage<DatapackListItem> implements DecoratorPage {
private final StringProperty title = new SimpleStringProperty();
private final Path worldDir;
private final Datapack datapack;
private final ObservableList<DatapackListPageSkin.DatapackInfoObject> items;
public DatapackListPage(String worldName, Path worldDir) {
this.worldDir = worldDir;
@@ -57,7 +50,14 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
datapack = new Datapack(worldDir.resolve("datapacks"));
datapack.loadFromDir();
setItems(items = MappedObservableList.create(datapack.getInfo(), DatapackListPageSkin.DatapackInfoObject::new));
setItems(MappedObservableList.create(datapack.getInfo(),
pack -> new DatapackListItem(pack, item -> {
try {
datapack.deletePack(pack);
} catch (IOException e) {
Logging.LOG.warning("Failed to delete datapack");
}
})));
FXUtils.applyDragListener(this, it -> Objects.equals("zip", FileUtils.getExtension(it)), mods -> {
mods.forEach(it -> {
@@ -69,19 +69,7 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
Logging.LOG.log(Level.WARNING, "Unable to parse datapack file " + it, e);
}
});
}, this::refresh);
}
@Override
protected Skin<?> createDefaultSkin() {
return new DatapackListPageSkin(this);
}
public void refresh() {
setLoading(true);
Task.of(datapack::loadFromDir)
.with(Task.of(() -> setLoading(false)))
.start();
}, datapack::loadFromDir);
}
@Override
@@ -89,6 +77,7 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
return title;
}
@Override
public void add() {
FileChooser chooser = new FileChooser();
chooser.setTitle(i18n("datapack.choose_datapack"));
@@ -108,32 +97,4 @@ public class DatapackListPage extends ListPageBase<DatapackListPageSkin.Datapack
datapack.loadFromDir();
}
void removeSelected(ObservableList<TreeItem<DatapackListPageSkin.DatapackInfoObject>> selectedItems) {
selectedItems.stream()
.map(TreeItem::getValue)
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
.forEach(pack -> {
try {
datapack.deletePack(pack);
} catch (IOException e) {
// Fail to remove mods if the game is running or the datapack is absent.
Logging.LOG.warning("Failed to delete datapack " + pack);
}
});
}
void enableSelected(ObservableList<TreeItem<DatapackListPageSkin.DatapackInfoObject>> selectedItems) {
selectedItems.stream()
.map(TreeItem::getValue)
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
.forEach(info -> info.setActive(true));
}
void disableSelected(ObservableList<TreeItem<DatapackListPageSkin.DatapackInfoObject>> selectedItems) {
selectedItems.stream()
.map(TreeItem::getValue)
.map(DatapackListPageSkin.DatapackInfoObject::getPackInfo)
.forEach(info -> info.setActive(false));
}
}
@@ -1,126 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXTreeTableColumn;
import com.jfoenix.controls.JFXTreeTableView;
import com.jfoenix.controls.RecursiveTreeItem;
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import org.jackhuang.hmcl.mod.Datapack;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.JFXCheckBoxTreeTableCell;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.util.StringUtils;
import static org.jackhuang.hmcl.ui.FXUtils.setupCellValueFactory;
import static org.jackhuang.hmcl.ui.FXUtils.wrapMargin;
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
class DatapackListPageSkin extends SkinBase<DatapackListPage> {
DatapackListPageSkin(DatapackListPage skinnable) {
super(skinnable);
BorderPane root = new BorderPane();
JFXTreeTableView<DatapackInfoObject> tableView = new JFXTreeTableView<>();
{
HBox toolbar = new HBox();
toolbar.getStyleClass().add("jfx-tool-bar-second");
JFXDepthManager.setDepth(toolbar, 1);
toolbar.setPickOnBounds(false);
toolbar.getChildren().add(createToolbarButton(i18n("button.refresh"), SVG::refresh, skinnable::refresh));
toolbar.getChildren().add(createToolbarButton(i18n("datapack.add"), SVG::plus, skinnable::add));
toolbar.getChildren().add(createToolbarButton(i18n("mods.remove"), SVG::delete, () ->
skinnable.removeSelected(tableView.getSelectionModel().getSelectedItems())));
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () ->
skinnable.enableSelected(tableView.getSelectionModel().getSelectedItems())));
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () ->
skinnable.disableSelected(tableView.getSelectionModel().getSelectedItems())));
root.setTop(toolbar);
}
{
SpinnerPane center = new SpinnerPane();
center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty());
tableView.getStyleClass().addAll("no-header");
tableView.setShowRoot(false);
tableView.setEditable(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tableView.setRoot(new RecursiveTreeItem<>(skinnable.getItems(), RecursiveTreeObject::getChildren));
JFXTreeTableColumn<DatapackInfoObject, Boolean> activeColumn = new JFXTreeTableColumn<>();
setupCellValueFactory(activeColumn, DatapackInfoObject::activeProperty);
activeColumn.setCellFactory(c -> new JFXCheckBoxTreeTableCell<>());
activeColumn.setEditable(true);
activeColumn.setMaxWidth(40);
activeColumn.setMinWidth(40);
JFXTreeTableColumn<DatapackInfoObject, Node> detailColumn = new JFXTreeTableColumn<>();
setupCellValueFactory(detailColumn, DatapackInfoObject::nodeProperty);
tableView.getColumns().setAll(activeColumn, detailColumn);
tableView.setColumnResizePolicy(JFXTreeTableView.CONSTRAINED_RESIZE_POLICY);
center.setContent(tableView);
root.setCenter(center);
}
getChildren().setAll(root);
}
static class DatapackInfoObject extends RecursiveTreeObject<DatapackInfoObject> {
private final BooleanProperty active;
private final Datapack.Pack packInfo;
private final ObjectProperty<Node> node;
DatapackInfoObject(Datapack.Pack packInfo) {
this.packInfo = packInfo;
this.active = packInfo.activeProperty();
this.node = new SimpleObjectProperty<>(wrapMargin(new TwoLineListItem(packInfo.getId(), StringUtils.parseColorEscapes(packInfo.getDescription())),
new Insets(8, 0, 8, 0)));
}
BooleanProperty activeProperty() {
return active;
}
ObjectProperty<Node> nodeProperty() {
return node;
}
Datapack.Pack getPackInfo() {
return packInfo;
}
}
}
@@ -17,10 +17,12 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.utils.JFXUtilities;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.Node;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.control.ToggleGroup;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
@@ -28,31 +30,32 @@ import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
import org.jackhuang.hmcl.ui.download.VanillaInstallWizardProvider;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class GameList extends ListPageBase<GameListItem> implements DecoratorPage {
public class GameList extends Control implements DecoratorPage {
private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(I18n.i18n("version.manage"));
private final BooleanProperty loading = new SimpleBooleanProperty(true);
private final ListProperty<GameListItem> items = new SimpleListProperty<>(FXCollections.observableArrayList());
private ToggleGroup toggleGroup;
public GameList() {
EventBus.EVENT_BUS.channel(RefreshingVersionsEvent.class).register(event -> {
if (event.getSource() == Profiles.getSelectedProfile().getRepository())
runInFX(() -> setLoading(true));
JFXUtilities.runInFX(() -> loading.set(true));
});
Profiles.registerVersionsListener(this::loadVersions);
@@ -69,10 +72,10 @@ public class GameList extends ListPageBase<GameListItem> implements DecoratorPag
.thenComparing(a -> VersionNumber.asVersion(a.getId())))
.map(version -> new GameListItem(toggleGroup, profile, version.getId()))
.collect(Collectors.toList());
runInFX(() -> {
JFXUtilities.runInFX(() -> {
if (profile == Profiles.getSelectedProfile()) {
setLoading(false);
itemsProperty().setAll(children);
loading.set(false);
items.setAll(children);
children.forEach(GameListItem::checkSelection);
profile.selectedVersionProperty().addListener(listenerHolder.weak((a, b, newValue) -> {
@@ -94,8 +97,8 @@ public class GameList extends ListPageBase<GameListItem> implements DecoratorPag
}
@Override
protected GameListSkin createDefaultSkin() {
return new GameListSkin();
protected Skin<?> createDefaultSkin() {
return new GameListSkin(this);
}
void addNewGame() {
@@ -125,20 +128,11 @@ public class GameList extends ListPageBase<GameListItem> implements DecoratorPag
return title.getReadOnlyProperty();
}
private class GameListSkin extends ToolbarListPageSkin<GameList> {
public BooleanProperty loadingProperty() {
return loading;
}
public GameListSkin() {
super(GameList.this);
}
@Override
protected List<Node> initializeToolbar(GameList skinnable) {
return Arrays.asList(
createToolbarButton(i18n("install.new_game"), SVG::plus, skinnable::addNewGame),
createToolbarButton(i18n("install.modpack"), SVG::importIcon, skinnable::importModpack),
createToolbarButton(i18n("button.refresh"), SVG::refresh, skinnable::refresh),
createToolbarButton(i18n("settings.type.global.manage"), SVG::gear, skinnable::modifyGlobalGameSettings)
);
}
public ListProperty<GameListItem> itemsProperty() {
return items;
}
}
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.utils.JFXUtilities;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXPopup;
import com.jfoenix.controls.JFXRadioButton;
@@ -32,7 +33,6 @@ import org.jackhuang.hmcl.ui.construct.IconedMenuItem;
import org.jackhuang.hmcl.ui.construct.MenuSeparator;
import org.jackhuang.hmcl.ui.construct.PopupMenu;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class GameListItemSkin extends SkinBase<GameListItem> {
@@ -73,7 +73,7 @@ public class GameListItemSkin extends SkinBase<GameListItem> {
btnUpgrade.setOnMouseClicked(e -> skinnable.update());
btnUpgrade.getStyleClass().add("toggle-icon4");
btnUpgrade.setGraphic(SVG.update(Theme.blackFillBinding(), -1, -1));
runInFX(() -> FXUtils.installFastTooltip(btnUpgrade, i18n("version.update")));
JFXUtilities.runInFX(() -> FXUtils.installFastTooltip(btnUpgrade, i18n("version.update")));
right.getChildren().add(btnUpgrade);
}
@@ -0,0 +1,116 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXScrollPane;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.util.i18n.I18n;
public class GameListSkin extends SkinBase<GameList> {
private static Node wrap(Node node) {
StackPane stackPane = new StackPane();
stackPane.setPadding(new Insets(0, 5, 0, 2));
stackPane.getChildren().setAll(node);
return stackPane;
}
public GameListSkin(GameList skinnable) {
super(skinnable);
BorderPane root = new BorderPane();
{
HBox toolbar = new HBox();
toolbar.getStyleClass().add("jfx-tool-bar-second");
JFXDepthManager.setDepth(toolbar, 1);
toolbar.setPickOnBounds(false);
JFXButton btnAddNewGame = new JFXButton();
btnAddNewGame.getStyleClass().add("jfx-tool-bar-button");
btnAddNewGame.textFillProperty().bind(Theme.foregroundFillBinding());
btnAddNewGame.setGraphic(wrap(SVG.plus(Theme.foregroundFillBinding(), -1, -1)));
btnAddNewGame.setText(I18n.i18n("install.new_game"));
btnAddNewGame.setOnMouseClicked(e -> skinnable.addNewGame());
toolbar.getChildren().add(btnAddNewGame);
JFXButton btnImportModpack = new JFXButton();
btnImportModpack.getStyleClass().add("jfx-tool-bar-button");
btnImportModpack.textFillProperty().bind(Theme.foregroundFillBinding());
btnImportModpack.setGraphic(wrap(SVG.importIcon(Theme.foregroundFillBinding(), -1, -1)));
btnImportModpack.setText(I18n.i18n("install.modpack"));
btnImportModpack.setOnMouseClicked(e -> skinnable.importModpack());
toolbar.getChildren().add(btnImportModpack);
JFXButton btnRefresh = new JFXButton();
btnRefresh.getStyleClass().add("jfx-tool-bar-button");
btnRefresh.textFillProperty().bind(Theme.foregroundFillBinding());
btnRefresh.setGraphic(wrap(SVG.refresh(Theme.foregroundFillBinding(), -1, -1)));
btnRefresh.setText(I18n.i18n("button.refresh"));
btnRefresh.setOnMouseClicked(e -> skinnable.refresh());
toolbar.getChildren().add(btnRefresh);
JFXButton btnModify = new JFXButton();
btnModify.getStyleClass().add("jfx-tool-bar-button");
btnModify.textFillProperty().bind(Theme.foregroundFillBinding());
btnModify.setGraphic(wrap(SVG.gear(Theme.foregroundFillBinding(), -1, -1)));
btnModify.setText(I18n.i18n("settings.type.global.manage"));
btnModify.setOnMouseClicked(e -> skinnable.modifyGlobalGameSettings());
toolbar.getChildren().add(btnModify);
root.setTop(toolbar);
}
{
SpinnerPane center = new SpinnerPane();
center.loadingProperty().bind(skinnable.loadingProperty());
center.getStyleClass().add("large-spinner-pane");
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToWidth(true);
VBox gameList = new VBox();
gameList.maxWidthProperty().bind(scrollPane.widthProperty());
gameList.setSpacing(10);
gameList.setStyle("-fx-padding: 10 10 10 10;");
Bindings.bindContent(gameList.getChildren(), skinnable.itemsProperty());
scrollPane.setContent(gameList);
JFXScrollPane.smoothScrolling(scrollPane);
center.setContent(scrollPane);
root.setCenter(center);
}
getChildren().setAll(root);
}
}
@@ -25,14 +25,13 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.InstallerItem;
import org.jackhuang.hmcl.ui.ListPage;
import org.jackhuang.hmcl.ui.download.InstallerWizardProvider;
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
import java.util.LinkedList;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -42,50 +41,37 @@ public class InstallerListPage extends ListPage<InstallerItem> {
private Profile profile;
private String versionId;
private Version version;
private String gameVersion;
public void loadVersion(Profile profile, String versionId) {
this.profile = profile;
this.versionId = versionId;
this.version = profile.getRepository().getResolvedVersion(versionId);
this.gameVersion = null;
Task.ofResult(() -> {
gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse(null);
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version);
return LibraryAnalyzer.analyze(version);
}).thenAccept(Schedulers.javafx(), analyzer -> {
Function<Library, Consumer<InstallerItem>> removeAction = library -> x -> {
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
newList.remove(library);
new MaintainTask(version.setLibraries(newList))
.then(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion))
.with(profile.getRepository().refreshVersionsAsync())
.with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)))
.start();
};
Function<Library, Consumer<InstallerItem>> removeAction = library -> x -> {
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
newList.remove(library);
new MaintainTask(version.setLibraries(newList))
.thenCompose(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion))
.withCompose(profile.getRepository().refreshVersionsAsync())
.withRun(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
.start();
};
itemsProperty().clear();
analyzer.getForge().ifPresent(library -> itemsProperty().add(
new InstallerItem("Forge", library.getVersion(), () -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "forge", library));
}, removeAction.apply(library))));
analyzer.getLiteLoader().ifPresent(library -> itemsProperty().add(
new InstallerItem("LiteLoader", library.getVersion(), () -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "liteloader", library));
}, removeAction.apply(library))));
analyzer.getOptiFine().ifPresent(library -> itemsProperty().add(
new InstallerItem("OptiFine", library.getVersion(), () -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, "optifine", library));
}, removeAction.apply(library))));
}).start();
itemsProperty().clear();
analyzer.getForge().ifPresent(library -> itemsProperty().add(new InstallerItem("Forge", library.getVersion(), removeAction.apply(library))));
analyzer.getLiteLoader().ifPresent(library -> itemsProperty().add(new InstallerItem("LiteLoader", library.getVersion(), removeAction.apply(library))));
analyzer.getOptiFine().ifPresent(library -> itemsProperty().add(new InstallerItem("OptiFine", library.getVersion(), removeAction.apply(library))));
}
@Override
public void add() {
if (gameVersion == null)
Optional<String> gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version));
if (!gameVersion.isPresent())
Controllers.dialog(i18n("version.cannot_read"));
else
Controllers.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion, version));
Controllers.getDecorator().startWizard(new InstallerWizardProvider(profile, gameVersion.get(), version));
}
}
@@ -17,14 +17,18 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.utils.JFXUtilities;
import com.jfoenix.controls.JFXTabPane;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.control.TreeItem;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.mod.ModInfo;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Profile;
@@ -32,7 +36,6 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPageBase;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -44,15 +47,14 @@ import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObject> {
private final BooleanProperty modded = new SimpleBooleanProperty(this, "modded", false);
public final class ModListPage extends Control {
private final ListProperty<ModListPageSkin.ModInfoObject> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList());
private final BooleanProperty loading = new SimpleBooleanProperty(this, "loading", false);
private JFXTabPane parentTab;
private ModManager modManager;
private LibraryAnalyzer libraryAnalyzer;
public ModListPage() {
@@ -78,22 +80,20 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
}
public void loadVersion(Profile profile, String id) {
libraryAnalyzer = LibraryAnalyzer.analyze(profile.getRepository().getResolvedVersion(id));
modded.set(libraryAnalyzer.hasForge() || libraryAnalyzer.hasLiteLoader());
loadMods(profile.getRepository().getModManager(id));
}
private void loadMods(ModManager modManager) {
public void loadMods(ModManager modManager) {
this.modManager = modManager;
Task.ofResult(() -> {
Task.supplyAsync(() -> {
synchronized (ModListPage.this) {
runInFX(() -> loadingProperty().set(true));
JFXUtilities.runInFX(() -> loadingProperty().set(true));
modManager.refreshMods();
return new LinkedList<>(modManager.getMods());
}
}).whenComplete(Schedulers.javafx(), (list, isDependentSucceeded, exception) -> {
}).whenComplete(Schedulers.javafx(), (list, exception) -> {
loadingProperty().set(false);
if (isDependentSucceeded)
if (exception == null)
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
if (newValue != null && newValue.getUserData() == ModListPage.this)
itemsProperty().setAll(list.stream().map(ModListPageSkin.ModInfoObject::new).collect(Collectors.toList()));
@@ -111,7 +111,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
List<String> succeeded = new LinkedList<>();
List<String> failed = new LinkedList<>();
if (res == null) return;
Task.of(() -> {
Task.runAsync(() -> {
for (File file : res) {
try {
modManager.addMod(file);
@@ -123,7 +123,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
}
}
}).with(Task.of(Schedulers.javafx(), () -> {
}).withRun(Schedulers.javafx(), () -> {
List<String> prompt = new LinkedList<>();
if (!succeeded.isEmpty())
prompt.add(i18n("mods.add.success", String.join(", ", succeeded)));
@@ -131,14 +131,14 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
prompt.add(i18n("mods.add.failed", String.join(", ", failed)));
Controllers.dialog(String.join("\n", prompt), i18n("mods.add"));
loadMods(modManager);
})).start();
}).start();
}
public void setParentTab(JFXTabPane parentTab) {
this.parentTab = parentTab;
}
public void removeSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
public void removeSelectedMods(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
try {
modManager.removeMods(selectedItems.stream()
.map(TreeItem::getValue)
@@ -150,29 +150,41 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
}
}
public void enableSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
public void enableSelectedMods(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
selectedItems.stream()
.map(TreeItem::getValue)
.map(ModListPageSkin.ModInfoObject::getModInfo)
.forEach(info -> info.setActive(true));
}
public void disableSelected(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
public void disableSelectedMods(ObservableList<TreeItem<ModListPageSkin.ModInfoObject>> selectedItems) {
selectedItems.stream()
.map(TreeItem::getValue)
.map(ModListPageSkin.ModInfoObject::getModInfo)
.forEach(info -> info.setActive(false));
}
public boolean isModded() {
return modded.get();
public ObservableList<ModListPageSkin.ModInfoObject> getItems() {
return items.get();
}
public BooleanProperty moddedProperty() {
return modded;
public void setItems(ObservableList<ModListPageSkin.ModInfoObject> items) {
this.items.set(items);
}
public void setModded(boolean modded) {
this.modded.set(modded);
public ListProperty<ModListPageSkin.ModInfoObject> itemsProperty() {
return items;
}
public boolean isLoading() {
return loading.get();
}
public void setLoading(boolean loading) {
this.loading.set(loading);
}
public BooleanProperty loadingProperty() {
return loading;
}
}
@@ -17,43 +17,42 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXTreeTableColumn;
import com.jfoenix.controls.JFXTreeTableView;
import com.jfoenix.controls.RecursiveTreeItem;
import com.jfoenix.controls.*;
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.SkinBase;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.util.Callback;
import org.jackhuang.hmcl.mod.ModInfo;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.JFXCheckBoxTreeTableCell;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import static org.jackhuang.hmcl.ui.FXUtils.setupCellValueFactory;
import static org.jackhuang.hmcl.ui.FXUtils.wrapMargin;
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton;
import static org.jackhuang.hmcl.util.StringUtils.isNotBlank;
import java.util.function.Function;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
class ModListPageSkin extends SkinBase<ModListPage> {
public class ModListPageSkin extends SkinBase<ModListPage> {
ModListPageSkin(ModListPage skinnable) {
private static Node wrap(Node node) {
StackPane stackPane = new StackPane();
stackPane.setPadding(new Insets(0, 5, 0, 2));
stackPane.getChildren().setAll(node);
return stackPane;
}
public ModListPageSkin(ModListPage skinnable) {
super(skinnable);
StackPane pane = new StackPane();
pane.getStyleClass().addAll("notice-pane", "white-background");
BorderPane root = new BorderPane();
JFXTreeTableView<ModInfoObject> tableView = new JFXTreeTableView<>();
@@ -63,14 +62,46 @@ class ModListPageSkin extends SkinBase<ModListPage> {
JFXDepthManager.setDepth(toolbar, 1);
toolbar.setPickOnBounds(false);
toolbar.getChildren().add(createToolbarButton(i18n("button.refresh"), SVG::refresh, skinnable::refresh));
toolbar.getChildren().add(createToolbarButton(i18n("mods.add"), SVG::plus, skinnable::add));
toolbar.getChildren().add(createToolbarButton(i18n("mods.remove"), SVG::delete, () ->
skinnable.removeSelected(tableView.getSelectionModel().getSelectedItems())));
toolbar.getChildren().add(createToolbarButton(i18n("mods.enable"), SVG::check, () ->
skinnable.enableSelected(tableView.getSelectionModel().getSelectedItems())));
toolbar.getChildren().add(createToolbarButton(i18n("mods.disable"), SVG::close, () ->
skinnable.disableSelected(tableView.getSelectionModel().getSelectedItems())));
JFXButton btnRefresh = new JFXButton();
btnRefresh.getStyleClass().add("jfx-tool-bar-button");
btnRefresh.textFillProperty().bind(Theme.foregroundFillBinding());
btnRefresh.setGraphic(wrap(SVG.refresh(Theme.foregroundFillBinding(), -1, -1)));
btnRefresh.setText(i18n("button.refresh"));
btnRefresh.setOnMouseClicked(e -> skinnable.refresh());
toolbar.getChildren().add(btnRefresh);
JFXButton btnAddMod = new JFXButton();
btnAddMod.getStyleClass().add("jfx-tool-bar-button");
btnAddMod.textFillProperty().bind(Theme.foregroundFillBinding());
btnAddMod.setGraphic(wrap(SVG.plus(Theme.foregroundFillBinding(), -1, -1)));
btnAddMod.setText(i18n("mods.add"));
btnAddMod.setOnMouseClicked(e -> skinnable.add());
toolbar.getChildren().add(btnAddMod);
JFXButton btnRemove = new JFXButton();
btnRemove.getStyleClass().add("jfx-tool-bar-button");
btnRemove.textFillProperty().bind(Theme.foregroundFillBinding());
btnRemove.setGraphic(wrap(SVG.delete(Theme.foregroundFillBinding(), -1, -1)));
btnRemove.setText(i18n("mods.remove"));
btnRemove.setOnMouseClicked(e -> skinnable.removeSelectedMods(tableView.getSelectionModel().getSelectedItems()));
toolbar.getChildren().add(btnRemove);
JFXButton btnEnable = new JFXButton();
btnEnable.getStyleClass().add("jfx-tool-bar-button");
btnEnable.textFillProperty().bind(Theme.foregroundFillBinding());
btnEnable.setGraphic(wrap(SVG.check(Theme.foregroundFillBinding(), -1, -1)));
btnEnable.setText(i18n("mods.enable"));
btnEnable.setOnMouseClicked(e -> skinnable.enableSelectedMods(tableView.getSelectionModel().getSelectedItems()));
toolbar.getChildren().add(btnEnable);
JFXButton btnDisable = new JFXButton();
btnDisable.getStyleClass().add("jfx-tool-bar-button");
btnDisable.textFillProperty().bind(Theme.foregroundFillBinding());
btnDisable.setGraphic(wrap(SVG.close(Theme.foregroundFillBinding(), -1, -1)));
btnDisable.setText(i18n("mods.disable"));
btnDisable.setOnMouseClicked(e -> skinnable.disableSelectedMods(tableView.getSelectionModel().getSelectedItems()));
toolbar.getChildren().add(btnDisable);
root.setTop(toolbar);
}
@@ -79,7 +110,6 @@ class ModListPageSkin extends SkinBase<ModListPage> {
center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty());
tableView.getStyleClass().add("no-header");
tableView.setShowRoot(false);
tableView.setEditable(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -92,54 +122,65 @@ class ModListPageSkin extends SkinBase<ModListPage> {
activeColumn.setMaxWidth(40);
activeColumn.setMinWidth(40);
JFXTreeTableColumn<ModInfoObject, Node> detailColumn = new JFXTreeTableColumn<>();
setupCellValueFactory(detailColumn, ModInfoObject::nodeProperty);
JFXTreeTableColumn<ModInfoObject, String> fileNameColumn = new JFXTreeTableColumn<>();
fileNameColumn.setText(i18n("archive.name"));
setupCellValueFactory(fileNameColumn, ModInfoObject::fileNameProperty);
fileNameColumn.prefWidthProperty().bind(tableView.widthProperty().subtract(40).multiply(0.8));
tableView.getColumns().setAll(activeColumn, detailColumn);
JFXTreeTableColumn<ModInfoObject, String> versionColumn = new JFXTreeTableColumn<>();
versionColumn.setText(i18n("archive.version"));
versionColumn.setPrefWidth(100);
setupCellValueFactory(versionColumn, ModInfoObject::versionProperty);
versionColumn.prefWidthProperty().bind(tableView.widthProperty().subtract(40).multiply(0.2));
tableView.getColumns().setAll(activeColumn, fileNameColumn, versionColumn);
tableView.setColumnResizePolicy(JFXTreeTableView.CONSTRAINED_RESIZE_POLICY);
center.setContent(tableView);
root.setCenter(center);
}
Label label = new Label(i18n("mods.not_modded"));
label.prefWidthProperty().bind(pane.widthProperty().add(-100));
FXUtils.onChangeAndOperate(skinnable.moddedProperty(), modded -> {
if (modded) pane.getChildren().setAll(root);
else pane.getChildren().setAll(label);
});
getChildren().setAll(pane);
getChildren().setAll(root);
}
static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> {
private final BooleanProperty active;
private final ModInfo modInfo;
private final ObjectProperty<Node> node;
private <T> void setupCellValueFactory(JFXTreeTableColumn<ModInfoObject, T> column, Function<ModInfoObject, ObservableValue<T>> mapper) {
column.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<ModInfoObject, T>, ObservableValue<T>>() {
@Override
public ObservableValue<T> call(TreeTableColumn.CellDataFeatures<ModInfoObject, T> param) {
if (column.validateValue(param))
return mapper.apply(param.getValue().getValue());
else
return column.getComputedValue(param);
}
});
}
ModInfoObject(ModInfo modInfo) {
public static class ModInfoObject extends RecursiveTreeObject<ModInfoObject> {
private final BooleanProperty active;
private final StringProperty fileName;
private final StringProperty version;
private final ModInfo modInfo;
public ModInfoObject(ModInfo modInfo) {
this.modInfo = modInfo;
this.active = modInfo.activeProperty();
StringBuilder message = new StringBuilder(modInfo.getName());
if (isNotBlank(modInfo.getVersion()))
message.append(", ").append(i18n("archive.version")).append(": ").append(modInfo.getVersion());
if (isNotBlank(modInfo.getGameVersion()))
message.append(", ").append(i18n("archive.game_version")).append(": ").append(modInfo.getGameVersion());
if (isNotBlank(modInfo.getAuthors()))
message.append(", ").append(i18n("archive.author")).append(": ").append(modInfo.getAuthors());
this.node = new SimpleObjectProperty<>(wrapMargin(new TwoLineListItem(modInfo.getFileName(), message.toString()), new Insets(8, 0, 8, 0)));
this.fileName = new SimpleStringProperty(modInfo.getFileName());
this.version = new SimpleStringProperty(modInfo.getVersion());
}
BooleanProperty activeProperty() {
public BooleanProperty activeProperty() {
return active;
}
ObjectProperty<Node> nodeProperty() {
return node;
public StringProperty fileNameProperty() {
return fileName;
}
ModInfo getModInfo() {
public StringProperty versionProperty() {
return version;
}
public ModInfo getModInfo() {
return modInfo;
}
}
@@ -118,7 +118,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.smoothScrolling(scroll);
Task.ofResult(JavaVersion::getJavas).thenAccept(Schedulers.javafx(), list -> {
Task.supplyAsync(JavaVersion::getJavas).thenAccept(Schedulers.javafx(), list -> {
javaItem.loadChildren(list.stream()
.map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit",
javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion))
@@ -273,7 +273,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
VersionSetting versionSetting = lastVersionSetting;
if (versionSetting == null)
return;
Task.ofResult(versionSetting::getJavaVersion)
Task.supplyAsync(versionSetting::getJavaVersion)
.thenAccept(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion)
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))
.start();
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.versions;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.game.Version;
@@ -74,6 +74,6 @@ public class WorldExportPage extends WizardSinglePage {
@Override
protected Object finish() {
return Task.of(i18n("world.export.wizard", worldName.get()), () -> world.export(Paths.get(path.get()), worldName.get()));
return Task.runAsync(i18n("world.export.wizard", worldName.get()), () -> world.export(Paths.get(path.get()), worldName.get()));
}
}
@@ -35,7 +35,6 @@ import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.jackhuang.hmcl.util.StringUtils.parseColorEscapes;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class WorldListItem extends Control {
@@ -49,7 +48,7 @@ public class WorldListItem extends Control {
this.world = world;
this.simpleDateFormat = new SimpleDateFormat(i18n("world.time"));
title.set(parseColorEscapes(world.getWorldName()));
title.set(world.getWorldName());
subtitle.set(i18n("world.description", world.getFileName(), simpleDateFormat.format(new Date(world.getLastPlayed())), world.getGameVersion() == null ? i18n("message.unknown") : world.getGameVersion()));
}
@@ -17,83 +17,48 @@
*/
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXCheckBox;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPage;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class WorldListPage extends ListPageBase<WorldListItem> {
private final BooleanProperty showAll = new SimpleBooleanProperty(this, "showAll", false);
public class WorldListPage extends ListPage<WorldListItem> {
private Path savesDir;
private List<World> worlds;
private Profile profile;
private String id;
private String gameVersion;
public WorldListPage() {
FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> {
installWorld(modpacks.get(0));
});
showAll.addListener(e -> {
if (worlds != null)
itemsProperty().setAll(worlds.stream()
.filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion))
.map(WorldListItem::new).collect(Collectors.toList()));
});
}
@Override
protected ToolbarListPageSkin createDefaultSkin() {
return new WorldListPageSkin();
}
public void loadVersion(Profile profile, String id) {
this.profile = profile;
this.id = id;
this.savesDir = profile.getRepository().getRunDirectory(id).toPath().resolve("saves");
refresh();
}
public void refresh() {
if (profile == null || id == null)
return;
setLoading(true);
Task
.of(() -> gameVersion = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse(null))
.thenSupply(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
.whenComplete(Schedulers.javafx(), (result, isDependentSucceeded, exception) -> {
worlds = result;
Task.supplyAsync(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
.whenComplete(Schedulers.javafx(), (result, exception) -> {
setLoading(false);
if (isDependentSucceeded)
itemsProperty().setAll(result.stream()
.filter(world -> isShowAll() || world.getGameVersion() == null || world.getGameVersion().equals(gameVersion))
.map(WorldListItem::new).collect(Collectors.toList()));
if (exception == null)
itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList()));
}).start();
}
@Override
public void add() {
FileChooser chooser = new FileChooser();
chooser.setTitle(i18n("world.import.choose"));
@@ -107,10 +72,10 @@ public class WorldListPage extends ListPageBase<WorldListItem> {
private void installWorld(File zipFile) {
// Only accept one world file because user is required to confirm the new world name
// Or too many input dialogs are popped.
Task.ofResult(() -> new World(zipFile.toPath()))
Task.supplyAsync(() -> new World(zipFile.toPath()))
.whenComplete(Schedulers.javafx(), world -> {
Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> {
Task.of(() -> world.install(savesDir, name))
Task.runAsync(() -> world.install(savesDir, name))
.whenComplete(Schedulers.javafx(), () -> {
itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name))));
resolve.run();
@@ -126,36 +91,4 @@ public class WorldListPage extends ListPageBase<WorldListItem> {
Controllers.dialog(i18n("world.import.invalid"));
}).start();
}
public boolean isShowAll() {
return showAll.get();
}
public BooleanProperty showAllProperty() {
return showAll;
}
public void setShowAll(boolean showAll) {
this.showAll.set(showAll);
}
private class WorldListPageSkin extends ToolbarListPageSkin<WorldListPage> {
WorldListPageSkin() {
super(WorldListPage.this);
}
@Override
protected List<Node> initializeToolbar(WorldListPage skinnable) {
JFXCheckBox chkShowAll = new JFXCheckBox();
chkShowAll.getStyleClass().add("jfx-tool-bar-checkbox");
chkShowAll.textFillProperty().bind(Theme.foregroundFillBinding());
chkShowAll.setText(i18n("world.show_all"));
chkShowAll.selectedProperty().bindBidirectional(skinnable.showAllProperty());
return Arrays.asList(chkShowAll,
createToolbarButton(i18n("button.refresh"), SVG::refresh, skinnable::refresh),
createToolbarButton(i18n("world.add"), SVG::plus, skinnable::add));
}
}
}
@@ -32,8 +32,8 @@ public interface AbstractWizardDisplayer extends WizardDisplayer {
Queue<Object> getCancelQueue();
@Override
default void handleTask(Map<String, Object> settings, Task task) {
TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor();
default void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutor executor = task.withRun(Schedulers.javafx(), this::navigateToSuccess).executor();
TaskListPane pane = new TaskListPane();
pane.setExecutor(executor);
navigateTo(pane, Navigation.NavigationDirection.FINISH);
@@ -23,7 +23,6 @@ import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.util.StringUtils;
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.wizard;
import com.jfoenix.utils.JFXUtilities;
import javafx.beans.property.StringProperty;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
@@ -29,13 +30,12 @@ import org.jackhuang.hmcl.util.StringUtils;
import java.util.Map;
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer {
@Override
default void handleTask(Map<String, Object> settings, Task task) {
default void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> {
it.fireEvent(new DialogCloseEvent());
onEnd();
@@ -59,11 +59,11 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
pane.setSubtitle((String) subtitle);
}
runInFX(() -> {
JFXUtilities.runInFX(() -> {
TaskExecutor executor = task.executor(new TaskListener() {
@Override
public void onStop(boolean success, TaskExecutor executor) {
runInFX(() -> {
JFXUtilities.runInFX(() -> {
pane.fireEvent(new DialogCloseEvent());
if (success) {
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
@@ -71,11 +71,11 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
else if (!settings.containsKey("forbid_success_message"))
Controllers.dialog(i18n("message.success"), null, MessageType.FINE, () -> onEnd());
} else {
if (executor.getLastException() == null)
if (executor.getException() == null)
return;
String appendix = StringUtils.getStackTrace(executor.getLastException());
String appendix = StringUtils.getStackTrace(executor.getException());
if (settings.get("failure_callback") instanceof WizardProvider.FailureCallback)
((WizardProvider.FailureCallback)settings.get("failure_callback")).onFail(settings, executor.getLastException(), () -> onEnd());
((WizardProvider.FailureCallback)settings.get("failure_callback")).onFail(settings, executor.getException(), () -> onEnd());
else if (settings.get("failure_message") instanceof String)
Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageType.ERROR, () -> onEnd());
else if (!settings.containsKey("forbid_failure_message"))
@@ -106,7 +106,7 @@ public class WizardController implements Navigation {
public void onFinish() {
Object result = provider.finish(settings);
if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT);
else if (result instanceof Task) displayer.handleTask(settings, ((Task) result));
else if (result instanceof Task<?>) displayer.handleTask(settings, ((Task<?>) result));
else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result);
}
@@ -27,5 +27,5 @@ public interface WizardDisplayer {
void onEnd();
void onCancel();
void navigateTo(Node page, Navigation.NavigationDirection nav);
void handleTask(Map<String, Object> settings, Task task);
void handleTask(Map<String, Object> settings, Task<?> task);
}
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.upgrade;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import javafx.application.Platform;
import javafx.scene.layout.Region;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.task.Task;
@@ -99,7 +99,7 @@ public final class UpdateHandler {
return;
}
Task task = new HMCLDownloadTask(version, downloaded);
Task<?> task = new HMCLDownloadTask(version, downloaded);
TaskExecutor executor = task.executor();
Controllers.taskDialog(executor, i18n("message.downloading"));
@@ -121,7 +121,7 @@ public final class UpdateHandler {
}
} else {
Throwable e = executor.getLastException();
Exception e = executor.getException();
LOG.log(Level.WARNING, "Failed to update to " + version, e);
Platform.runLater(() -> Controllers.dialog(e.toString(), i18n("update.failed"), MessageType.ERROR));
}
+6 -18
View File
@@ -57,11 +57,8 @@
-fx-text-fill: -fx-base-text-fill;
}
.notice-pane > .label {
-fx-text-fill: #0079FF;
-fx-font-size: 20;
-fx-wrap-text: true;
-fx-text-alignment: CENTER;
.rippler-container= .label {
-fx-text-fill: white;
}
.class-title {
@@ -382,8 +379,6 @@
-fx-pref-height: 42;
-fx-padding: 2 2 2 2;
-fx-background-color: -fx-base-check-color;
-fx-alignment: CENTER-LEFT;
-fx-spacing: 8;
}
.jfx-tool-bar-second .jfx-rippler {
@@ -997,8 +992,7 @@
}
.jfx-decorator-title {
-fx-text-fill: -fx-base-text-fill;
-fx-font-size: 15;
-fx-text-fill: -fx-base-text-fill; -fx-font-size: 15;
}
.resize-border {
@@ -1016,7 +1010,7 @@
}
.white-background {
-fx-background-color: rgb(255, 255, 255);
-fx-background-color: rgba(255, 255, 255);
}
/*******************************************************************************
@@ -1025,12 +1019,6 @@
* *
*******************************************************************************/
.no-header .column-header-background {
-fx-max-height: 0;
-fx-pref-height: 0;
-fx-min-height: 0;
}
.tree-table-view {
-fx-tree-table-color: rgba(82, 100, 174, 0.4);
-fx-tree-table-rippler-color: rgba(82, 100, 174, 0.6);
@@ -1081,7 +1069,7 @@
.tree-table-view .tree-table-cell {
-fx-border-width: 0 0 0 0;
-fx-padding: 0 0 0 0;
-fx-padding: 8 0 8 0;
/*-fx-alignment: top-center;*/
}
@@ -1127,7 +1115,7 @@
}
.tree-table-row-group {
-fx-background-color: rgb(230, 230, 230);
-fx-background-color: rgba(230, 230, 230);
}
.tree-table-view .menu-item:focused {
@@ -24,11 +24,11 @@
<JFXListView fx:id="list" styleClass="jfx-list-view" VBox.vgrow="ALWAYS">
</JFXListView>
</VBox>
<StackPane fx:id="failedPane" styleClass="notice-pane">
<Label onMouseClicked="#onRefresh" text="%download.failed.refresh" />
<StackPane fx:id="failedPane">
<Label onMouseClicked="#onRefresh" style="-fx-text-fill: #0079FF; -fx-font-size: 20;" text="%download.failed.refresh" />
</StackPane>
<StackPane fx:id="emptyPane" styleClass="notice-pane">
<Label onMouseClicked="#onBack" text="%download.failed.empty" />
<StackPane fx:id="emptyPane">
<Label onMouseClicked="#onBack" style="-fx-text-fill: #0079FF; -fx-font-size: 20;" text="%download.failed.empty" />
</StackPane>
</StackPane>
</center>
@@ -95,7 +95,7 @@ download.failed=Failed to download
download.failed.empty=No candidates. Click here to return.
download.failed.refresh=Unable to load version list. Click here to retry.
download.provider.bmclapi=BMCLAPI (bangbang93, https://bmclapi2.bangbang93.com/)
download.provider.mojang=Mojang (Forge and OptiFine installation being downloaded from BMCLAPI)
download.provider.mojang=Mojang
extension.bat=Windows Bat file
extension.mod=Mod file
@@ -124,12 +124,9 @@ input.not_empty=Required field
input.url=Must be a valid URL.
install=Install New Game
install.change_version=Change version
install.change_version.confirm=Sure to update %s from verison %s to %s?
install.failed=Failed to install
install.failed.downloading=Failed to install due to some files not downloaded successfully
install.failed.downloading.detail=Failed to download file: %s
install.failed.optifine_conflict=OptiFine and Forge are both installed simultaneously on Minecraft 1.13
install.installer.choose=Choose a %s version
install.installer.forge=Forge
install.installer.game=Game
@@ -261,7 +258,6 @@ mods.enable=Enable
mods.disable=Disable
mods.name=Name
mods.remove=Remove
mods.not_modded=You should install a mod loader first (Forge, or LiteLoader)
datapack=Data packs
datapack.add=Add data pack
@@ -271,7 +267,6 @@ datapack.title=World %s - Datapacks
datapack.remove=Remove
world=Worlds/Datapacks
world.add=Add world(.zip)
world.datapack=Manage data packs
world.datapack.1_13=Only Minecraft 1.13 and later versions support data packs.
world.description=%s. Last played time: %s. Game version: %s.
@@ -288,7 +283,6 @@ world.import.invalid=Not a valid world zip
world.name=World Name
world.name.enter=Enter the world name
world.reveal=Reveal in Explorer
world.show_all=Show all
world.time=EEE, MMM d, yyyy HH:mm:ss
profile=Game Directories
@@ -94,7 +94,7 @@ download.failed=下載失敗
download.failed.empty=沒有可供安裝的版本,點擊此處返回。
download.failed.refresh=載入版本列表失敗,點擊此處重試。
download.provider.bmclapi=BMCLAPI(bangbang93,https://bmclapi2.bangbang93.com/)
download.provider.mojang=官方伺服器(Forge 和 OptiFine 自動安裝的下載來源是 BMCLAPI)
download.provider.mojang=官方伺服器(Mojang)
extension.bat=Windows 腳本
extension.mod=模組檔案
@@ -123,12 +123,9 @@ input.not_empty=必填
input.url=必須為有效連結
install=新增遊戲
install.change_version=更換版本
install.change_version.confirm=你確定要將 %s 從 %s 更新到 %s 嗎?
install.failed=安裝失敗
install.failed.downloading=安裝失敗,部分文件未能完成下載
install.failed.downloading.detail=未能下載檔案:%s
install.failed.optifine_conflict=暫不支持 OptiFine 與 Forge 同時安裝於 Minecraft 1.13
install.installer.choose=選擇 %s 版本
install.installer.forge=Forge
install.installer.game=遊戲
@@ -259,7 +256,6 @@ mods.choose_mod=選擇模組
mods.enable=啟用
mods.disable=禁用
mods.remove=刪除
mods.not_modded=你需要先在自動安裝頁面安裝 Forge 或 LiteLoader 才能進行模組管理。
datapack=資料包
datapack.add=添加資料包
@@ -269,7 +265,6 @@ datapack.title=世界 %s - 資料包
datapack.remove=刪除
world=世界/資料包
world.add=添加世界
world.datapack=管理資料包
world.datapack.1_13=僅 Minecraft 1.13 及之後的版本支持資料包
world.description=%s. 上一次遊戲時間: %s. 遊戲版本: %s
@@ -286,7 +281,6 @@ world.game_version=遊戲版本
world.name=世界名稱
world.name.enter=輸入世界名稱
world.reveal=打開資料夾
world.show_all=顯示全部
world.time=yyyy 年 MM 月 dd 日 HH:mm:ss
profile=遊戲目錄
@@ -94,7 +94,7 @@ download.failed=下载失败
download.failed.empty=没有可供安装的版本,点击此处返回。
download.failed.refresh=加载版本列表失败,点击此处重试。
download.provider.bmclapi=BMCLAPI(bangbang93,https://bmclapi2.bangbang93.com/)
download.provider.mojang=官方(Forge 和 OptiFine 自动安装使用 BMCLAPI 下载源)
download.provider.mojang=官方(Mojang)
extension.bat=Windows 脚本
extension.mod=模组文件
@@ -123,12 +123,9 @@ input.not_empty=必填项
input.url=必须是合法的链接
install=添加游戏
install.change_version=更换版本
install.change_version.confirm=你确定要将 %s 从 %s 更新到 %s 吗?
install.failed=安装失败
install.failed.downloading=安装失败,部分文件未能完成下载
install.failed.downloading.detail=未能下载文件:%s
install.failed.optifine_conflict=暂不支持 OptiFine 与 Forge 同时安装于 Minecraft 1.13
install.installer.choose=选择 %s 版本
install.installer.forge=Forge
install.installer.game=游戏
@@ -259,7 +256,6 @@ mods.choose_mod=选择模组
mods.enable=启用
mods.disable=禁用
mods.remove=删除
mods.not_modded=你需要先在自动安装页面安装 Forge 或 LiteLoader 才能进行模组管理。
datapack=数据包
datapack.add=添加数据包
@@ -269,7 +265,6 @@ datapack.title=世界 %s - 数据包
datapack.remove=删除
world=世界/数据包
world.add=添加世界
world.datapack=管理数据包
world.datapack.1_13=仅 Minecraft 1.13 及之后的版本支持数据包
world.description=%s. 上一次游戏时间: %s. 游戏版本: %s
@@ -286,7 +281,6 @@ world.import.invalid=无法识别的存档压缩包
world.name=世界名称
world.name.enter=输入世界名称
world.reveal=打开文件夹
world.show_all=显示全部
world.time=yyyy 年 MM 月 dd 日 HH:mm:ss
profile=游戏目录
@@ -17,8 +17,6 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.util.CacheRepository;
/**
*
* @author huangyuhui
@@ -26,9 +26,7 @@ import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
/**
@@ -69,9 +67,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
@Override
public Task checkGameCompletionAsync(Version version) {
return new ParallelTask(
Task.ofThen(() -> {
public Task<?> checkGameCompletionAsync(Version version) {
return Task.allOf(
Task.composeAsync(() -> {
if (!repository.getVersionJar(version).exists())
return new GameDownloadTask(this, null, version);
else
@@ -83,12 +81,12 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
@Override
public Task checkLibraryCompletionAsync(Version version) {
public Task<?> checkLibraryCompletionAsync(Version version) {
return new GameLibrariesTask(this, version);
}
@Override
public TaskResult<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
public Task<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(gameVersion, getDownloadProvider())
.thenCompose(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
@@ -96,8 +94,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
@Override
public TaskResult<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
TaskResult<Version> task;
public Task<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
Task<Version> task;
if (libraryVersion instanceof ForgeRemoteVersion)
task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion);
else if (libraryVersion instanceof LiteLoaderRemoteVersion)
@@ -113,7 +111,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
public ExceptionalFunction<Version, TaskResult<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
public ExceptionalFunction<Version, Task<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
return version -> installLibraryAsync(version, libraryVersion);
}
}
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -48,16 +46,16 @@ public class DefaultGameBuilder extends GameBuilder {
}
@Override
public Task buildAsync() {
public Task<?> buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenCompose(rawJson -> {
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
Version version = original.setId(name).setJar(null);
Task vanillaTask = downloadGameAsync(gameVersion, version).then(new ParallelTask(
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenCompose(Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
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.
).withCompose(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
TaskResult<Version> libraryTask = vanillaTask.thenSupply(() -> version);
Task<Version> libraryTask = vanillaTask.thenSupply(() -> version);
if (toolVersions.containsKey("forge"))
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "forge"));
@@ -70,17 +68,17 @@ public class DefaultGameBuilder extends GameBuilder {
libraryTask = libraryTask.thenCompose(dependencyManager.installLibraryAsync(remoteVersion));
return libraryTask;
}).whenComplete((isDependentSucceeded, exception) -> {
if (!isDependentSucceeded)
dependencyManager.getGameRepository().removeVersionFromDisk(name);
}).whenComplete(exception -> {
if (exception != null)
dependencyManager.getGameRepository().getVersionRoot(name).delete();
});
}
private ExceptionalFunction<Version, TaskResult<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId) {
private ExceptionalFunction<Version, Task<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId) {
return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, toolVersions.get(libraryId));
}
protected Task downloadGameAsync(String gameVersion, Version version) {
protected Task<Void> downloadGameAsync(String gameVersion, Version version) {
return new GameDownloadTask(dependencyManager, gameVersion, version);
}
@@ -46,7 +46,7 @@ public interface DependencyManager {
*
* @return the task to check game completion.
*/
Task checkGameCompletionAsync(Version version);
Task<?> checkGameCompletionAsync(Version version);
/**
* Check if the game is complete.
@@ -54,7 +54,7 @@ public interface DependencyManager {
*
* @return the task to check game completion.
*/
Task checkLibraryCompletionAsync(Version version);
Task<?> checkLibraryCompletionAsync(Version version);
/**
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
@@ -71,7 +71,7 @@ public interface DependencyManager {
* @param libraryVersion the version of being installed library.
* @return the task to install the specific library.
*/
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
Task<?> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
/**
* Install a library to a version.
@@ -81,7 +81,7 @@ public interface DependencyManager {
* @param libraryVersion the remote version of being installed library.
* @return the task to install the specific library.
*/
Task installLibraryAsync(Version version, RemoteVersion libraryVersion);
Task<?> installLibraryAsync(Version version, RemoteVersion libraryVersion);
/**
* Get registered version list.
@@ -72,5 +72,5 @@ public abstract class GameBuilder {
/**
* @return the task that can build thw whole Minecraft environment
*/
public abstract Task buildAsync();
public abstract Task<?> buildAsync();
}
@@ -18,9 +18,9 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.task.Task;
public class MaintainTask extends TaskResult<Version> {
public class MaintainTask extends Task<Version> {
private final Version version;
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.forge.ForgeBMCLVersionList;
import org.jackhuang.hmcl.download.forge.ForgeVersionList;
import org.jackhuang.hmcl.download.game.GameVersionList;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
@@ -62,19 +62,19 @@ public abstract class VersionList<T extends RemoteVersion> {
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
public abstract Task refreshAsync(DownloadProvider downloadProvider);
public abstract Task<?> refreshAsync(DownloadProvider downloadProvider);
/**
* @param gameVersion the remote version depends on
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
public Task refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
public Task<?> refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
return refreshAsync(downloadProvider);
}
public Task loadAsync(DownloadProvider downloadProvider) {
return Task.ofThen(() -> {
public Task<?> loadAsync(DownloadProvider downloadProvider) {
return Task.composeAsync(() -> {
lock.readLock().lock();
boolean loaded;
@@ -87,8 +87,8 @@ public abstract class VersionList<T extends RemoteVersion> {
});
}
public Task loadAsync(String gameVersion, DownloadProvider downloadProvider) {
return Task.ofThen(() -> {
public Task<?> loadAsync(String gameVersion, DownloadProvider downloadProvider) {
return Task.composeAsync(() -> {
lock.readLock().lock();
boolean loaded;
@@ -46,21 +46,21 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
}
@Override
public Task loadAsync(DownloadProvider downloadProvider) {
public Task<?> loadAsync(DownloadProvider downloadProvider) {
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
}
@Override
public Task refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
public Task<?> refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL("https://bmclapi2.bangbang93.com/forge/minecraft/" + gameVersion));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -78,7 +78,7 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
continue;
String jar = null;
for (ForgeVersion.File file : version.getFiles())
if ("installer".equals(file.getCategory()) && "jar".equals(file.getFormat())) {
if ("installer".equals(file.getCategory())) {
String classifier = gameVersion + "-" + version.getVersion()
+ (StringUtils.isNotBlank(version.getBranch()) ? "-" + version.getBranch() : "");
String fileName = "forge-" + classifier + "-" + file.getCategory() + "." + file.getFormat();
@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -34,14 +33,14 @@ import java.util.Collections;
*
* @author huangyuhui
*/
public final class ForgeInstallTask extends TaskResult<Version> {
public final class ForgeInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private Path installer;
private final ForgeRemoteVersion remote;
private Task dependent;
private TaskResult<Version> dependency;
private Task<Void> dependent;
private Task<Version> dependency;
public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -58,8 +57,7 @@ public final class ForgeInstallTask extends TaskResult<Version> {
public void preExecute() throws Exception {
installer = Files.createTempFile("forge-installer", ".jar");
dependent = new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer.toFile())
.setCaching(true);
dependent = new FileDownloadTask(NetworkUtils.toURL(remote.getUrl()), installer.toFile());
}
@Override
@@ -74,12 +72,12 @@ public final class ForgeInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(dependent);
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency);
}
@@ -34,16 +34,14 @@ public class ForgeNewInstallProfile {
private final int spec;
private final String minecraft;
private final String json;
private final Artifact path;
private final List<Library> libraries;
private final List<Processor> processors;
private final Map<String, Datum> data;
public ForgeNewInstallProfile(int spec, String minecraft, String json, Artifact path, List<Library> libraries, List<Processor> processors, Map<String, Datum> data) {
public ForgeNewInstallProfile(int spec, String minecraft, String json, List<Library> libraries, List<Processor> processors, Map<String, Datum> data) {
this.spec = spec;
this.minecraft = minecraft;
this.json = json;
this.path = path;
this.libraries = libraries;
this.processors = processors;
this.data = data;
@@ -71,14 +69,6 @@ public class ForgeNewInstallProfile {
return json;
}
/**
* Maven artifact path for the main jar to install.
* @return artifact path of the main jar.
*/
public Artifact getPath() {
return path;
}
/**
* Libraries that processors depend on.
* @return the required dependencies.
@@ -19,10 +19,8 @@ package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -50,25 +48,23 @@ import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
import static org.jackhuang.hmcl.util.Logging.LOG;
public class ForgeNewInstallTask extends TaskResult<Version> {
public class ForgeNewInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final DefaultGameRepository gameRepository;
private final Version version;
private final Path installer;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private ForgeNewInstallProfile profile;
private Version forgeVersion;
public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
this.dependencyManager = dependencyManager;
this.gameRepository = dependencyManager.getGameRepository();
this.version = version;
this.installer = installer;
setSignificance(TaskSignificance.MINOR);
}
private <E extends Exception> String parseLiteral(String literal, Map<String, String> var, ExceptionalFunction<String, String, E> plainConverter) throws E {
@@ -83,12 +79,12 @@ public class ForgeNewInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public List<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -110,14 +106,6 @@ public class ForgeNewInstallTask extends TaskResult<Version> {
FileUtils.copyFile(file, dest);
}
}
{
Path mainJar = profile.getPath().getPath(fs.getPath("maven"));
if (Files.exists(mainJar)) {
Path dest = gameRepository.getArtifactFile(version, profile.getPath());
FileUtils.copyFile(mainJar, dest);
}
}
}
dependents.add(new GameLibrariesTask(dependencyManager, version, profile.getLibraries()));
@@ -125,9 +113,6 @@ public class ForgeNewInstallTask extends TaskResult<Version> {
@Override
public void execute() throws Exception {
if ("net.minecraft.launchwrapper.Launch".equals(version.getMainClass()))
throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException();
Path temp = Files.createTempDirectory("forge_installer");
int finished = 0;
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
@@ -31,23 +30,21 @@ import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ForgeOldInstallTask extends TaskResult<Version> {
public class ForgeOldInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final Path installer;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
this.dependencyManager = dependencyManager;
this.version = version;
this.installer = installer;
setSignificance(TaskSignificance.MINOR);
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@@ -45,12 +45,12 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -38,14 +38,14 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameAssetDownloadTask extends Task {
public final class GameAssetDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final AssetIndexInfo assetIndexInfo;
private final File assetIndexFile;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -64,12 +64,12 @@ public final class GameAssetDownloadTask extends Task {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -89,7 +89,6 @@ public final class GameAssetDownloadTask extends Task {
String url = dependencyManager.getDownloadProvider().getAssetBaseURL() + assetObject.getLocation();
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, new FileDownloadTask.IntegrityCheck("SHA-1", assetObject.getHash()));
task.setName(assetObject.getHash());
task.setCaching(true);
dependencies.add(task
.setCacheRepository(dependencyManager.getCacheRepository())
.setCaching(true)
@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.game.AssetIndexInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File;
@@ -34,11 +33,11 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameAssetIndexDownloadTask extends Task {
public final class GameAssetIndexDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -53,7 +52,7 @@ public final class GameAssetIndexDownloadTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -33,11 +34,11 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameDownloadTask extends Task {
public final class GameDownloadTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
this.dependencyManager = dependencyManager;
@@ -48,7 +49,7 @@ public final class GameDownloadTask extends Task {
}
@Override
public List<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -32,12 +32,12 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameLibrariesTask extends Task {
public final class GameLibrariesTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final List<Library> libraries;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -64,7 +64,7 @@ public final class GameLibrariesTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@@ -55,11 +55,11 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.CompatibilityRule;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -31,7 +31,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class LibrariesUniqueTask extends TaskResult<Version> {
public class LibrariesUniqueTask extends Task<Version> {
private final Version version;
public LibrariesUniqueTask(Version version) {
@@ -45,7 +45,7 @@ import java.util.logging.Level;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
public class LibraryDownloadTask extends Task {
public class LibraryDownloadTask extends Task<Void> {
private FileDownloadTask task;
protected final File jar;
protected final DefaultCacheRepository cacheRepository;
@@ -74,7 +74,7 @@ public class LibraryDownloadTask extends Task {
}
@Override
public Collection<? extends Task> getDependents() {
public Collection<Task<?>> getDependents() {
if (cached) return Collections.emptyList();
else return Collections.singleton(task);
}
@@ -91,7 +91,7 @@ public class LibraryDownloadTask extends Task {
if (!isDependentsSucceeded()) {
// Since FileDownloadTask wraps the actual exception with DownloadException.
// We should extract it letting the error message clearer.
Throwable t = task.getLastException();
Exception t = task.getException();
if (t instanceof DownloadException)
throw new LibraryDownloadException(library, t.getCause());
else
@@ -128,12 +128,12 @@ public class LibraryDownloadTask extends Task {
try {
URL packXz = NetworkUtils.toURL(url + ".pack.xz");
if (NetworkUtils.urlExists(packXz)) {
task = new FileDownloadTask(packXz, xzFile, null).setCaching(true);
task = new FileDownloadTask(packXz, xzFile, null);
xz = true;
} else {
task = new FileDownloadTask(NetworkUtils.toURL(url),
jar,
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null).setCaching(true);
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
xz = false;
}
} catch (IOException e) {
@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.download.RemoteVersion;
import org.jackhuang.hmcl.download.VersionList;
import org.jackhuang.hmcl.task.GetTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.util.Collection;
@@ -33,11 +32,11 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class VersionJsonDownloadTask extends TaskResult<String> {
public final class VersionJsonDownloadTask extends Task<String> {
private final String gameVersion;
private final DefaultDependencyManager dependencyManager;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final VersionList<?> gameVersionList;
public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) {
@@ -52,12 +51,12 @@ public final class VersionJsonDownloadTask extends TaskResult<String> {
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -30,7 +30,7 @@ import java.io.File;
*
* @author huangyuhui
*/
public final class VersionJsonSaveTask extends TaskResult<Version> {
public final class VersionJsonSaveTask extends Task<Version> {
private final DefaultGameRepository repository;
private final Version version;
@@ -33,7 +33,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
*
@@ -52,11 +51,11 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
import java.util.Collection;
@@ -36,13 +35,13 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class LiteLoaderInstallTask extends TaskResult<Version> {
public final class LiteLoaderInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final LiteLoaderRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -51,12 +50,12 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -33,7 +33,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
*
@@ -52,11 +51,11 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -46,11 +46,11 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi2.bangbang93.com/optifine/versionlist"));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
import java.util.Collection;
@@ -35,13 +34,13 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class OptiFineInstallTask extends TaskResult<Version> {
public final class OptiFineInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final OptiFineRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -50,12 +49,12 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -66,9 +65,6 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
@Override
public void execute() {
if ("cpw.mods.modlauncher.Launcher".equals(version.getMainClass()))
throw new UnsupportedOptiFineInstallationException();
String remoteVersion = remote.getGameVersion() + "_" + remote.getSelfVersion();
Library library = new Library(
@@ -93,6 +89,4 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
dependencies.add(dependencyManager.checkLibraryCompletionAsync(version.setLibraries(libraries)));
}
public static class UnsupportedOptiFineInstallationException extends UnsupportedOperationException {
}
}
@@ -77,8 +77,8 @@ public interface GameRepository extends VersionProvider {
*/
void refreshVersions();
default Task refreshVersionsAsync() {
return Task.of(this::refreshVersions);
default Task<Void> refreshVersionsAsync() {
return Task.runAsync(this::refreshVersions);
}
/**
@@ -36,7 +36,6 @@ import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -43,14 +43,14 @@ import java.util.stream.Collectors;
*
* @author huangyuhui
*/
public final class CurseCompletionTask extends Task {
public final class CurseCompletionTask extends Task<Void> {
private final DefaultGameRepository repository;
private final ModManager modManager;
private final String version;
private CurseManifest manifest = null;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -86,12 +86,12 @@ public final class CurseCompletionTask extends Task {
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@@ -140,14 +140,14 @@ public final class CurseCompletionTask extends Task {
for (CurseManifestFile file : newManifest.getFiles())
if (StringUtils.isNotBlank(file.getFileName())) {
if (!modManager.hasSimpleMod(file.getFileName())) {
dependencies.add(new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile()).setCaching(true));
dependencies.add(new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile()));
}
}
// Let this task fail if the curse manifest has not been completed.
// But continue other downloads.
if (!flag.get() || notFound.get())
dependencies.add(Task.of(() -> {
dependencies.add(Task.runAsync(() -> {
if (notFound.get())
throw new CurseCompletionException(new FileNotFoundException());
else
@@ -38,7 +38,7 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class CurseInstallTask extends Task {
public final class CurseInstallTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final DefaultGameRepository repository;
@@ -48,8 +48,8 @@ public final class CurseInstallTask extends Task {
private final String name;
private final File run;
private final ModpackConfiguration<CurseManifest> config;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -99,12 +99,12 @@ public final class CurseInstallTask extends Task {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -24,8 +24,7 @@ import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jackhuang.hmcl.util.io.CompressingUtils;
@@ -129,7 +128,7 @@ public class Datapack {
isMultiple = false;
try {
PackMcMeta pack = JsonUtils.fromNonNullJson(FileUtils.readText(mcmeta), PackMcMeta.class);
Platform.runLater(() -> info.add(new Pack(path, FileUtils.getNameWithoutExtension(path), pack.getPackInfo().getDescription(), this)));
info.add(new Pack(path, FileUtils.getNameWithoutExtension(path), pack.getPackInfo().getDescription(), this));
} catch (IOException | JsonParseException e) {
Logging.LOG.log(Level.WARNING, "Failed to read datapack " + path, e);
}
@@ -193,7 +192,7 @@ public class Datapack {
}
}
Platform.runLater(() -> this.info.setAll(info));
this.info.setAll(info);
}
public static class Pack {
@@ -33,7 +33,7 @@ import java.util.List;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
public final class MinecraftInstanceTask<T> extends Task {
public final class MinecraftInstanceTask<T> extends Task<Void> {
private final File zipFile;
private final Charset encoding;
@@ -31,7 +31,7 @@ import java.util.function.Predicate;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
public class ModpackInstallTask<T> extends Task {
public class ModpackInstallTask<T> extends Task<Void> {
private final File modpackFile;
private final File dest;
@@ -26,14 +26,14 @@ import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
public class ModpackUpdateTask extends Task {
public class ModpackUpdateTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String id;
private final Task updateTask;
private final Task<?> updateTask;
private final Path backupFolder;
public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) {
public ModpackUpdateTask(DefaultGameRepository repository, String id, Task<?> updateTask) {
this.repository = repository;
this.id = id;
this.updateTask = updateTask;
@@ -49,7 +49,7 @@ public class ModpackUpdateTask extends Task {
}
@Override
public Collection<? extends Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return Collections.singleton(updateTask);
}
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff. Show more