Merge branch 'javafx' of github.com:huanghongxun/HMCL into javafx
This commit is contained in:
@@ -122,14 +122,14 @@ public final class LauncherHelper {
|
|||||||
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
|
||||||
|
|
||||||
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
|
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
|
||||||
.then(variables -> {
|
.then(() -> {
|
||||||
if (setting.isNotCheckGame())
|
if (setting.isNotCheckGame())
|
||||||
return null;
|
return null;
|
||||||
else
|
else
|
||||||
return dependencyManager.checkGameCompletionAsync(version);
|
return dependencyManager.checkGameCompletionAsync(version);
|
||||||
})
|
})
|
||||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
|
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
|
||||||
.then(var -> {
|
.then(() -> {
|
||||||
try {
|
try {
|
||||||
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
|
||||||
if ("Curse".equals(configuration.getType()))
|
if ("Curse".equals(configuration.getType()))
|
||||||
@@ -141,44 +141,42 @@ public final class LauncherHelper {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
|
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
|
||||||
.then(Task.of(i18n("account.methods"), variables -> {
|
.thenTaskResult(() -> Task.ofResult(i18n("account.methods"), () -> {
|
||||||
try {
|
try {
|
||||||
variables.set("account", account.logIn());
|
return account.logIn();
|
||||||
} catch (CredentialExpiredException e) {
|
} catch (CredentialExpiredException e) {
|
||||||
LOG.info("Credential has expired: " + e);
|
LOG.info("Credential has expired: " + e);
|
||||||
variables.set("account", DialogController.logIn(account));
|
return DialogController.logIn(account);
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
LOG.warning("Authentication failed, try playing offline: " + e);
|
LOG.warning("Authentication failed, try playing offline: " + e);
|
||||||
variables.set("account",
|
return account.playOffline().orElseThrow(() -> e);
|
||||||
account.playOffline().orElseThrow(() -> e));
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LAUNCHING)))
|
.thenResult(Schedulers.javafx(), authInfo -> {
|
||||||
.then(Task.of(variables -> {
|
emitStatus(LoadingState.LAUNCHING);
|
||||||
variables.set("launcher", new HMCLGameLauncher(
|
return authInfo;
|
||||||
|
})
|
||||||
|
.thenResult(authInfo -> new HMCLGameLauncher(
|
||||||
repository,
|
repository,
|
||||||
selectedVersion,
|
selectedVersion,
|
||||||
variables.get("account"),
|
authInfo,
|
||||||
setting.toLaunchOptions(profile.getGameDir()),
|
setting.toLaunchOptions(profile.getGameDir()),
|
||||||
launcherVisibility == LauncherVisibility.CLOSE
|
launcherVisibility == LauncherVisibility.CLOSE
|
||||||
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
|
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
|
||||||
: new HMCLProcessListener(variables.get("account"), setting, gameVersion.isPresent())
|
: new HMCLProcessListener(authInfo, setting, gameVersion.isPresent())
|
||||||
));
|
))
|
||||||
}))
|
.thenTaskResult(launcher -> { // launcher is prev task's result
|
||||||
.then(variables -> {
|
|
||||||
DefaultLauncher launcher = variables.get("launcher");
|
|
||||||
if (scriptFile == null) {
|
if (scriptFile == null) {
|
||||||
return new LaunchTask<>(launcher::launch).setName(i18n("version.launch"));
|
return new LaunchTask<>(launcher::launch).setName(i18n("version.launch"));
|
||||||
} else {
|
} else {
|
||||||
return new LaunchTask<>(() -> {
|
return new LaunchTask<ManagedProcess>(() -> {
|
||||||
launcher.makeLaunchScript(scriptFile);
|
launcher.makeLaunchScript(scriptFile);
|
||||||
return null;
|
return null;
|
||||||
}).setName(i18n("version.launch_script"));
|
}).setName(i18n("version.launch_script"));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(Task.of(variables -> {
|
.thenVoid(process -> { // process is LaunchTask's result
|
||||||
if (scriptFile == null) {
|
if (scriptFile == null) {
|
||||||
ManagedProcess process = variables.get(LaunchTask.LAUNCH_ID);
|
|
||||||
PROCESSES.add(process);
|
PROCESSES.add(process);
|
||||||
if (launcherVisibility == LauncherVisibility.CLOSE)
|
if (launcherVisibility == LauncherVisibility.CLOSE)
|
||||||
Launcher.stopApplication();
|
Launcher.stopApplication();
|
||||||
@@ -187,13 +185,13 @@ public final class LauncherHelper {
|
|||||||
process.stop();
|
process.stop();
|
||||||
it.fireEvent(new DialogCloseEvent());
|
it.fireEvent(new DialogCloseEvent());
|
||||||
});
|
});
|
||||||
} else
|
} else {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
launchingStepsPane.fireEvent(new DialogCloseEvent());
|
launchingStepsPane.fireEvent(new DialogCloseEvent());
|
||||||
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}))
|
})
|
||||||
.executor();
|
.executor();
|
||||||
|
|
||||||
launchingStepsPane.setExecutor(executor, false);
|
launchingStepsPane.setExecutor(executor, false);
|
||||||
@@ -429,13 +427,6 @@ public final class LauncherHelper {
|
|||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
setResult(supplier.get());
|
setResult(supplier.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return LAUNCH_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static final String LAUNCH_ID = "launch";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -110,13 +110,13 @@ public final class ModpackHelper {
|
|||||||
|
|
||||||
if (modpack.getManifest() instanceof CurseManifest)
|
if (modpack.getManifest() instanceof CurseManifest)
|
||||||
return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name)
|
return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name)
|
||||||
.finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure);
|
.finalized(Schedulers.defaultScheduler(), success, failure);
|
||||||
else if (modpack.getManifest() instanceof HMCLModpackManifest)
|
else if (modpack.getManifest() instanceof HMCLModpackManifest)
|
||||||
return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
|
return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
|
||||||
.finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure);
|
.finalized(Schedulers.defaultScheduler(), success, failure);
|
||||||
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
|
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
|
||||||
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
|
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
|
||||||
.finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure)
|
.finalized(Schedulers.defaultScheduler(), success, failure)
|
||||||
.then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
|
.then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
|
||||||
else throw new IllegalStateException("Unrecognized modpack: " + modpack);
|
else throw new IllegalStateException("Unrecognized modpack: " + modpack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,13 +121,12 @@ public final class LeftPaneController extends AdvancedListBox {
|
|||||||
if (modpackFile.exists()) {
|
if (modpackFile.exists()) {
|
||||||
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
|
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
|
||||||
.thenResult(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
|
.thenResult(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
|
||||||
.thenResult(modpack -> {
|
.thenVoid(modpack -> {
|
||||||
AtomicReference<Region> region = new AtomicReference<>();
|
AtomicReference<Region> region = new AtomicReference<>();
|
||||||
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
|
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
|
||||||
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor();
|
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor();
|
||||||
region.set(Controllers.taskDialog(executor, i18n("modpack.installing")));
|
region.set(Controllers.taskDialog(executor, i18n("modpack.installing")));
|
||||||
executor.start();
|
executor.start();
|
||||||
return null;
|
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class AccountLoginPane extends StackPane {
|
|||||||
progressBar.setVisible(true);
|
progressBar.setVisible(true);
|
||||||
lblCreationWarning.setText("");
|
lblCreationWarning.setText("");
|
||||||
Task.ofResult(() -> oldAccount.logInWithPassword(password))
|
Task.ofResult(() -> oldAccount.logInWithPassword(password))
|
||||||
.finalizedResult(Schedulers.javafx(), authInfo -> {
|
.finalized(Schedulers.javafx(), authInfo -> {
|
||||||
success.accept(authInfo);
|
success.accept(authInfo);
|
||||||
fireEvent(new DialogCloseEvent());
|
fireEvent(new DialogCloseEvent());
|
||||||
progressBar.setVisible(false);
|
progressBar.setVisible(false);
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public class AddAccountPane extends StackPane {
|
|||||||
Object additionalData = getAuthAdditionalData();
|
Object additionalData = getAuthAdditionalData();
|
||||||
|
|
||||||
Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData))
|
Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData))
|
||||||
.finalizedResult(Schedulers.javafx(), account -> {
|
.finalized(Schedulers.javafx(), account -> {
|
||||||
int oldIndex = Accounts.getAccounts().indexOf(account);
|
int oldIndex = Accounts.getAccounts().indexOf(account);
|
||||||
if (oldIndex == -1) {
|
if (oldIndex == -1) {
|
||||||
Accounts.getAccounts().add(account);
|
Accounts.getAccounts().add(account);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
|
|||||||
|
|
||||||
Task.of(() -> {
|
Task.of(() -> {
|
||||||
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
|
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
|
||||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded, exception) -> {
|
}).finalized(Schedulers.javafx(), (isDependentsSucceeded, exception) -> {
|
||||||
addServerPane.setDisable(false);
|
addServerPane.setDisable(false);
|
||||||
nextPane.hideSpinner();
|
nextPane.hideSpinner();
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.game.Version;
|
|||||||
import org.jackhuang.hmcl.setting.Profile;
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
import org.jackhuang.hmcl.task.DownloadException;
|
import org.jackhuang.hmcl.task.DownloadException;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
@@ -89,16 +90,16 @@ public final class InstallerWizardProvider implements WizardProvider {
|
|||||||
settings.put("success_message", i18n("install.success"));
|
settings.put("success_message", i18n("install.success"));
|
||||||
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next));
|
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next));
|
||||||
|
|
||||||
Task ret = Task.ofResult("version", () -> version);
|
TaskResult<Version> ret = Task.ofResult(() -> version);
|
||||||
|
|
||||||
if (settings.containsKey("forge"))
|
if (settings.containsKey("forge"))
|
||||||
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
|
ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
|
||||||
|
|
||||||
if (settings.containsKey("liteloader"))
|
if (settings.containsKey("liteloader"))
|
||||||
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
|
ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
|
||||||
|
|
||||||
if (settings.containsKey("optifine"))
|
if (settings.containsKey("optifine"))
|
||||||
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
|
ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
|
||||||
|
|
||||||
return ret.then(profile.getRepository().refreshVersionsAsync());
|
return ret.then(profile.getRepository().refreshVersionsAsync());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
|
|||||||
spinnerPane.showSpinner();
|
spinnerPane.showSpinner();
|
||||||
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
|
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
|
||||||
.thenResult(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
|
.thenResult(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
|
||||||
.finalizedResult(Schedulers.javafx(), manifest -> {
|
.finalized(Schedulers.javafx(), manifest -> {
|
||||||
spinnerPane.hideSpinner();
|
spinnerPane.hideSpinner();
|
||||||
controller.getSettings().put(MODPACK_MANIFEST, manifest);
|
controller.getSettings().put(MODPACK_MANIFEST, manifest);
|
||||||
lblName.setText(manifest.getName());
|
lblName.setText(manifest.getName());
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
|
|||||||
if (settings.containsKey("optifine"))
|
if (settings.containsKey("optifine"))
|
||||||
builder.version((RemoteVersion) settings.get("optifine"));
|
builder.version((RemoteVersion) settings.get("optifine"));
|
||||||
|
|
||||||
return builder.buildAsync().finalized((a, b, c) -> profile.getRepository().refreshVersions())
|
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions())
|
||||||
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
|
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
|
|||||||
@Override
|
@Override
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
|
||||||
executor = versionList.refreshAsync(gameVersion, downloadProvider).finalized((variables, isDependentsSucceeded, exception) -> {
|
executor = versionList.refreshAsync(gameVersion, downloadProvider).finalized((isDependentsSucceeded, exception) -> {
|
||||||
if (isDependentsSucceeded) {
|
if (isDependentsSucceeded) {
|
||||||
List<VersionsPageItem> items = loadVersions();
|
List<VersionsPageItem> items = loadVersions();
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class InstallerListPage extends ListPage<InstallerItem> {
|
|||||||
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
|
||||||
newList.remove(library);
|
newList.remove(library);
|
||||||
new MaintainTask(version.setLibraries(newList))
|
new MaintainTask(version.setLibraries(newList))
|
||||||
.then(variables -> new VersionJsonSaveTask(profile.getRepository(), variables.get(MaintainTask.ID)))
|
.then(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion))
|
||||||
.with(profile.getRepository().refreshVersionsAsync())
|
.with(profile.getRepository().refreshVersionsAsync())
|
||||||
.with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)))
|
.with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)))
|
||||||
.start();
|
.start();
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.jackhuang.hmcl.task.Schedulers;
|
|||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.ListPage;
|
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
@@ -86,13 +85,13 @@ public final class ModListPage extends Control {
|
|||||||
|
|
||||||
public void loadMods(ModManager modManager) {
|
public void loadMods(ModManager modManager) {
|
||||||
this.modManager = modManager;
|
this.modManager = modManager;
|
||||||
Task.ofResult("list", variables -> {
|
Task.ofResult(() -> {
|
||||||
synchronized (ModListPage.this) {
|
synchronized (ModListPage.this) {
|
||||||
JFXUtilities.runInFX(() -> loadingProperty().set(true));
|
JFXUtilities.runInFX(() -> loadingProperty().set(true));
|
||||||
modManager.refreshMods();
|
modManager.refreshMods();
|
||||||
return new LinkedList<>(modManager.getMods());
|
return new LinkedList<>(modManager.getMods());
|
||||||
}
|
}
|
||||||
}).finalizedResult(Schedulers.javafx(), (list, isDependentsSucceeded, exception) -> {
|
}).finalized(Schedulers.javafx(), (list, isDependentsSucceeded, exception) -> {
|
||||||
loadingProperty().set(false);
|
loadingProperty().set(false);
|
||||||
if (isDependentsSucceeded)
|
if (isDependentsSucceeded)
|
||||||
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
|
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
|
||||||
@@ -112,7 +111,7 @@ public final class ModListPage extends Control {
|
|||||||
List<String> succeeded = new LinkedList<>();
|
List<String> succeeded = new LinkedList<>();
|
||||||
List<String> failed = new LinkedList<>();
|
List<String> failed = new LinkedList<>();
|
||||||
if (res == null) return;
|
if (res == null) return;
|
||||||
Task.of(variables -> {
|
Task.of(() -> {
|
||||||
for (File file : res) {
|
for (File file : res) {
|
||||||
try {
|
try {
|
||||||
modManager.addMod(file);
|
modManager.addMod(file);
|
||||||
@@ -124,7 +123,7 @@ public final class ModListPage extends Control {
|
|||||||
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
// Actually addMod will not throw exceptions because FileChooser has already filtered files.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).with(Task.of(Schedulers.javafx(), variables -> {
|
}).with(Task.of(Schedulers.javafx(), () -> {
|
||||||
List<String> prompt = new LinkedList<>();
|
List<String> prompt = new LinkedList<>();
|
||||||
if (!succeeded.isEmpty())
|
if (!succeeded.isEmpty())
|
||||||
prompt.add(i18n("mods.add.success", String.join(", ", succeeded)));
|
prompt.add(i18n("mods.add.success", String.join(", ", succeeded)));
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -117,14 +118,13 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
|||||||
|
|
||||||
FXUtils.smoothScrolling(scroll);
|
FXUtils.smoothScrolling(scroll);
|
||||||
|
|
||||||
Task.ofResult(JavaVersion::getJavas).thenResult(Schedulers.javafx(), list -> {
|
Task.ofResult(JavaVersion::getJavas).thenVoid(Schedulers.javafx(), list -> {
|
||||||
javaItem.loadChildren(list.stream()
|
javaItem.loadChildren(list.stream()
|
||||||
.map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit",
|
.map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit",
|
||||||
javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion))
|
javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion))
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
javaItemsLoaded = true;
|
javaItemsLoaded = true;
|
||||||
initializeSelectedJava();
|
initializeSelectedJava();
|
||||||
return null;
|
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
javaItem.setSelectedData(null);
|
javaItem.setSelectedData(null);
|
||||||
@@ -273,10 +273,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
|
|||||||
VersionSetting versionSetting = lastVersionSetting;
|
VersionSetting versionSetting = lastVersionSetting;
|
||||||
if (versionSetting == null)
|
if (versionSetting == null)
|
||||||
return;
|
return;
|
||||||
Task.of(variables -> variables.set("java", versionSetting.getJavaVersion()))
|
Task.ofResult(versionSetting::getJavaVersion)
|
||||||
.subscribe(Task.of(Schedulers.javafx(),
|
.thenVoid(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion)
|
||||||
variables -> javaItem.setSubtitle(variables.<JavaVersion>getOptional("java")
|
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))
|
||||||
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path"))));
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import org.jackhuang.hmcl.util.Logging;
|
|||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -52,7 +51,7 @@ public class WorldListPage extends ListPage<WorldListItem> {
|
|||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
|
Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
|
||||||
.finalizedResult(Schedulers.javafx(), (result, isDependentsSucceeded, exception) -> {
|
.finalized(Schedulers.javafx(), (result, isDependentsSucceeded, exception) -> {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
if (isDependentsSucceeded)
|
if (isDependentsSucceeded)
|
||||||
itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList()));
|
itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList()));
|
||||||
@@ -74,10 +73,10 @@ public class WorldListPage extends ListPage<WorldListItem> {
|
|||||||
// Only accept one world file because user is required to confirm the new world name
|
// Only accept one world file because user is required to confirm the new world name
|
||||||
// Or too many input dialogs are popped.
|
// Or too many input dialogs are popped.
|
||||||
Task.ofResult(() -> new World(zipFile.toPath()))
|
Task.ofResult(() -> new World(zipFile.toPath()))
|
||||||
.finalizedResult(Schedulers.javafx(), world -> {
|
.finalized(Schedulers.javafx(), world -> {
|
||||||
Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> {
|
Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> {
|
||||||
Task.of(() -> world.install(savesDir, name))
|
Task.of(() -> world.install(savesDir, name))
|
||||||
.finalized(Schedulers.javafx(), var -> {
|
.finalized(Schedulers.javafx(), () -> {
|
||||||
itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name))));
|
itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name))));
|
||||||
resolve.run();
|
resolve.run();
|
||||||
}, e -> {
|
}, e -> {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import org.jackhuang.hmcl.game.DefaultGameRepository;
|
|||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.ParallelTask;
|
import org.jackhuang.hmcl.task.ParallelTask;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +71,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
@Override
|
@Override
|
||||||
public Task checkGameCompletionAsync(Version version) {
|
public Task checkGameCompletionAsync(Version version) {
|
||||||
return new ParallelTask(
|
return new ParallelTask(
|
||||||
Task.ofThen(var -> {
|
Task.ofThen(() -> {
|
||||||
if (!repository.getVersionJar(version).exists())
|
if (!repository.getVersionJar(version).exists())
|
||||||
return new GameDownloadTask(this, null, version);
|
return new GameDownloadTask(this, null, version);
|
||||||
else
|
else
|
||||||
@@ -88,36 +88,32 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
|
public TaskResult<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
|
||||||
VersionList<?> versionList = getVersionList(libraryId);
|
VersionList<?> versionList = getVersionList(libraryId);
|
||||||
return versionList.loadAsync(gameVersion, getDownloadProvider())
|
return versionList.loadAsync(gameVersion, getDownloadProvider())
|
||||||
.then(variables -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
|
.thenTaskResult(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
|
||||||
.orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
|
.orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task installLibraryAsync(Version version, RemoteVersion libraryVersion) {
|
public TaskResult<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
|
||||||
|
TaskResult<Version> task;
|
||||||
if (libraryVersion instanceof ForgeRemoteVersion)
|
if (libraryVersion instanceof ForgeRemoteVersion)
|
||||||
return new ForgeInstallTask(this, version, (ForgeRemoteVersion) libraryVersion)
|
task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion);
|
||||||
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
|
|
||||||
.then(variables -> new MaintainTask(variables.get("version")))
|
|
||||||
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
|
|
||||||
else if (libraryVersion instanceof LiteLoaderRemoteVersion)
|
else if (libraryVersion instanceof LiteLoaderRemoteVersion)
|
||||||
return new LiteLoaderInstallTask(this, version, (LiteLoaderRemoteVersion) libraryVersion)
|
task = new LiteLoaderInstallTask(this, oldVersion, (LiteLoaderRemoteVersion) libraryVersion);
|
||||||
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
|
|
||||||
.then(variables -> new MaintainTask(variables.get("version")))
|
|
||||||
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
|
|
||||||
else if (libraryVersion instanceof OptiFineRemoteVersion)
|
else if (libraryVersion instanceof OptiFineRemoteVersion)
|
||||||
return new OptiFineInstallTask(this, version, (OptiFineRemoteVersion) libraryVersion)
|
task = new OptiFineInstallTask(this, oldVersion, (OptiFineRemoteVersion) libraryVersion);
|
||||||
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
|
|
||||||
.then(variables -> new MaintainTask(variables.get("version")))
|
|
||||||
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
|
|
||||||
else
|
else
|
||||||
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
|
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
|
||||||
|
return task
|
||||||
|
.thenTaskResult(LibrariesUniqueTask::new)
|
||||||
|
.thenTaskResult(MaintainTask::new)
|
||||||
|
.thenTaskResult(newVersion -> new VersionJsonSaveTask(repository, newVersion));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ExceptionalFunction<AutoTypingMap<String>, Task, ?> installLibraryAsync(RemoteVersion libraryVersion) {
|
public ExceptionalFunction<Version, TaskResult<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
|
||||||
return var -> installLibraryAsync(var.get("version"), libraryVersion);
|
return version -> installLibraryAsync(version, libraryVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.game.*;
|
|||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.ParallelTask;
|
import org.jackhuang.hmcl.task.ParallelTask;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
@@ -49,34 +50,35 @@ public class DefaultGameBuilder extends GameBuilder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task buildAsync() {
|
public Task buildAsync() {
|
||||||
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
|
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenTaskResult(rawJson -> {
|
||||||
Version version = JsonUtils.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class);
|
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
|
||||||
version = version.setId(name).setJar(null);
|
Version version = original.setId(name).setJar(null);
|
||||||
variables.set("version", version);
|
Task vanillaTask = downloadGameAsync(gameVersion, version).then(new ParallelTask(
|
||||||
Task result = downloadGameAsync(gameVersion, version).then(new ParallelTask(
|
|
||||||
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
|
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.
|
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.
|
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
|
||||||
|
|
||||||
|
TaskResult<Version> libraryTask = vanillaTask.thenResult(() -> version);
|
||||||
|
|
||||||
if (toolVersions.containsKey("forge"))
|
if (toolVersions.containsKey("forge"))
|
||||||
result = result.then(libraryTaskHelper(gameVersion, "forge"));
|
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "forge"));
|
||||||
if (toolVersions.containsKey("liteloader"))
|
if (toolVersions.containsKey("liteloader"))
|
||||||
result = result.then(libraryTaskHelper(gameVersion, "liteloader"));
|
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "liteloader"));
|
||||||
if (toolVersions.containsKey("optifine"))
|
if (toolVersions.containsKey("optifine"))
|
||||||
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
|
libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "optifine"));
|
||||||
|
|
||||||
for (RemoteVersion remoteVersion : remoteVersions)
|
for (RemoteVersion remoteVersion : remoteVersions)
|
||||||
result = result.then(dependencyManager.installLibraryAsync(remoteVersion));
|
libraryTask = libraryTask.thenTaskResult(dependencyManager.installLibraryAsync(remoteVersion));
|
||||||
|
|
||||||
return result;
|
return libraryTask;
|
||||||
}).finalized((variables, isDependentsSucceeded, exception) -> {
|
}).finalized((isDependentsSucceeded, exception) -> {
|
||||||
if (!isDependentsSucceeded)
|
if (!isDependentsSucceeded)
|
||||||
dependencyManager.getGameRepository().getVersionRoot(name).delete();
|
dependencyManager.getGameRepository().getVersionRoot(name).delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExceptionalFunction<AutoTypingMap<String>, Task, ?> libraryTaskHelper(String gameVersion, String libraryId) {
|
private ExceptionalFunction<Version, TaskResult<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId) {
|
||||||
return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId));
|
return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, toolVersions.get(libraryId));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Task downloadGameAsync(String gameVersion, Version version) {
|
protected Task downloadGameAsync(String gameVersion, Version version) {
|
||||||
|
|||||||
@@ -23,15 +23,9 @@ import org.jackhuang.hmcl.task.TaskResult;
|
|||||||
public class MaintainTask extends TaskResult<Version> {
|
public class MaintainTask extends TaskResult<Version> {
|
||||||
|
|
||||||
private final Version version;
|
private final Version version;
|
||||||
private final String id;
|
|
||||||
|
|
||||||
public MaintainTask(Version version) {
|
public MaintainTask(Version version) {
|
||||||
this(version, ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MaintainTask(Version version, String id) {
|
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,11 +70,4 @@ public class MaintainTask extends TaskResult<Version> {
|
|||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String ID = "version";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public abstract class VersionList<T extends RemoteVersion> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task loadAsync(DownloadProvider downloadProvider) {
|
public Task loadAsync(DownloadProvider downloadProvider) {
|
||||||
return Task.ofThen(variables -> {
|
return Task.ofThen(() -> {
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
boolean loaded;
|
boolean loaded;
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ public abstract class VersionList<T extends RemoteVersion> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task loadAsync(String gameVersion, DownloadProvider downloadProvider) {
|
public Task loadAsync(String gameVersion, DownloadProvider downloadProvider) {
|
||||||
return Task.ofThen(variables -> {
|
return Task.ofThen(() -> {
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
boolean loaded;
|
boolean loaded;
|
||||||
|
|
||||||
|
|||||||
@@ -82,11 +82,6 @@ public final class ForgeInstallTask extends TaskResult<Version> {
|
|||||||
return Collections.singleton(dependency);
|
return Collections.singleton(dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "version";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRelyingOnDependencies() {
|
public boolean isRelyingOnDependencies() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -89,11 +89,6 @@ public class ForgeNewInstallTask extends TaskResult<Version> {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "version";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doPreExecute() {
|
public boolean doPreExecute() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -49,11 +49,6 @@ public class ForgeOldInstallTask extends TaskResult<Version> {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "version";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doPreExecute() {
|
public boolean doPreExecute() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -33,15 +33,9 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
public class LibrariesUniqueTask extends TaskResult<Version> {
|
public class LibrariesUniqueTask extends TaskResult<Version> {
|
||||||
private final Version version;
|
private final Version version;
|
||||||
private final String id;
|
|
||||||
|
|
||||||
public LibrariesUniqueTask(Version version) {
|
public LibrariesUniqueTask(Version version) {
|
||||||
this(version, "version");
|
|
||||||
}
|
|
||||||
|
|
||||||
public LibrariesUniqueTask(Version version, String id) {
|
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,9 +93,4 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
|
|||||||
|
|
||||||
setResult(version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList())));
|
setResult(version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,16 +225,16 @@ public class LibraryDownloadTask extends Task {
|
|||||||
int x = decompressed.length;
|
int x = decompressed.length;
|
||||||
int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24;
|
int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24;
|
||||||
|
|
||||||
File temp = FileUtils.createTempFile("minecraft", ".pack");
|
Path temp = Files.createTempFile("minecraft", ".pack");
|
||||||
|
|
||||||
byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8);
|
byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8);
|
||||||
|
|
||||||
OutputStream out = new FileOutputStream(temp);
|
try (OutputStream out = Files.newOutputStream(temp)) {
|
||||||
out.write(decompressed, 0, decompressed.length - len - 8);
|
out.write(decompressed, 0, decompressed.length - len - 8);
|
||||||
out.close();
|
}
|
||||||
|
|
||||||
try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) {
|
try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) {
|
||||||
Pack200.newUnpacker().unpack(temp, jos);
|
Pack200.newUnpacker().unpack(temp.toFile(), jos);
|
||||||
|
|
||||||
JarEntry checksumsFile = new JarEntry("checksums.sha1");
|
JarEntry checksumsFile = new JarEntry("checksums.sha1");
|
||||||
checksumsFile.setTime(0L);
|
checksumsFile.setTime(0L);
|
||||||
@@ -243,6 +243,6 @@ public class LibraryDownloadTask extends Task {
|
|||||||
jos.closeEntry();
|
jos.closeEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
temp.delete();
|
Files.delete(temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.jackhuang.hmcl.download.RemoteVersion;
|
|||||||
import org.jackhuang.hmcl.download.VersionList;
|
import org.jackhuang.hmcl.download.VersionList;
|
||||||
import org.jackhuang.hmcl.task.GetTask;
|
import org.jackhuang.hmcl.task.GetTask;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -32,7 +33,7 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class VersionJsonDownloadTask extends Task {
|
public final class VersionJsonDownloadTask extends TaskResult<String> {
|
||||||
private final String gameVersion;
|
private final String gameVersion;
|
||||||
private final DefaultDependencyManager dependencyManager;
|
private final DefaultDependencyManager dependencyManager;
|
||||||
private final List<Task> dependents = new LinkedList<>();
|
private final List<Task> dependents = new LinkedList<>();
|
||||||
@@ -65,8 +66,6 @@ public final class VersionJsonDownloadTask extends Task {
|
|||||||
RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
|
RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
|
||||||
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
|
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
|
||||||
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
|
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
|
||||||
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), ID));
|
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL)).storeTo(this::setResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String ID = "raw_version_json";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.download.game;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
import org.jackhuang.hmcl.game.DefaultGameRepository;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.TaskResult;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ import java.io.File;
|
|||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class VersionJsonSaveTask extends Task {
|
public final class VersionJsonSaveTask extends TaskResult<Version> {
|
||||||
|
|
||||||
private final DefaultGameRepository repository;
|
private final DefaultGameRepository repository;
|
||||||
private final Version version;
|
private final Version version;
|
||||||
@@ -46,6 +46,7 @@ public final class VersionJsonSaveTask extends Task {
|
|||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
||||||
setSignificance(TaskSignificance.MODERATE);
|
setSignificance(TaskSignificance.MODERATE);
|
||||||
|
setResult(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package org.jackhuang.hmcl.download.liteloader;
|
package org.jackhuang.hmcl.download.liteloader;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
|
||||||
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.Library;
|
||||||
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
|
||||||
@@ -61,11 +60,6 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "version";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
Library library = new Library(
|
Library library = new Library(
|
||||||
|
|||||||
@@ -59,11 +59,6 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "version";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRelyingOnDependencies() {
|
public boolean isRelyingOnDependencies() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ public class DefaultGameRepository implements GameRepository {
|
|||||||
|
|
||||||
versions.remove(id);
|
versions.remove(id);
|
||||||
|
|
||||||
if (FileUtils.isMovingToTrashSupported()) {
|
if (FileUtils.isMovingToTrashSupported() && FileUtils.moveToTrash(removedFile)) {
|
||||||
return FileUtils.moveToTrash(removedFile);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove json files first to ensure HMCL will not recognize this folder as a valid version.
|
// remove json files first to ensure HMCL will not recognize this folder as a valid version.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
import org.jackhuang.hmcl.util.*;
|
import org.jackhuang.hmcl.util.*;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -119,7 +119,7 @@ public final class ForgeModMetadata {
|
|||||||
Path mcmod = fs.getPath("mcmod.info");
|
Path mcmod = fs.getPath("mcmod.info");
|
||||||
if (Files.notExists(mcmod))
|
if (Files.notExists(mcmod))
|
||||||
throw new IOException("File " + modFile + " is not a Forge mod.");
|
throw new IOException("File " + modFile + " is not a Forge mod.");
|
||||||
List<ForgeModMetadata> modList = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(Files.newInputStream(mcmod)),
|
List<ForgeModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
|
||||||
new TypeToken<List<ForgeModMetadata>>() {
|
new TypeToken<List<ForgeModMetadata>>() {
|
||||||
}.getType());
|
}.getType());
|
||||||
if (modList == null || modList.isEmpty())
|
if (modList == null || modList.isEmpty())
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ public final class MultiMCModpackInstallTask extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies.add(new MaintainTask(version).then(var -> new VersionJsonSaveTask(repository, var.get(MaintainTask.ID))));
|
dependencies.add(new MaintainTask(version).thenTaskResult(maintainedVersion -> new VersionJsonSaveTask(repository, maintainedVersion)));
|
||||||
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,57 +18,53 @@
|
|||||||
package org.jackhuang.hmcl.task;
|
package org.jackhuang.hmcl.task;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A task that combines two tasks and make sure [pred] runs before succ.
|
* A task that combines two tasks and make sure [pred] runs before succ.
|
||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
final class CoupleTask<P extends Task> extends Task {
|
final class CoupleTask extends Task {
|
||||||
|
|
||||||
private final boolean relyingOnDependents;
|
private final boolean relyingOnDependents;
|
||||||
private final Collection<Task> dependents;
|
private final Task pred;
|
||||||
private final List<Task> dependencies = new LinkedList<>();
|
private Task succ;
|
||||||
private final ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ;
|
private final ExceptionalSupplier<Task, ?> supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A task that combines two tasks and make sure pred runs before succ.
|
* A task that combines two tasks and make sure pred runs before succ.
|
||||||
*
|
*
|
||||||
* @param pred the task that runs before succ.
|
* @param pred the task that runs before supplier.
|
||||||
* @param succ a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
|
* @param supplier a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
|
||||||
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
|
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
|
||||||
*/
|
*/
|
||||||
public CoupleTask(P pred, ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ, boolean relyingOnDependents) {
|
CoupleTask(Task pred, ExceptionalSupplier<Task, ?> supplier, boolean relyingOnDependents) {
|
||||||
this.dependents = pred == null ? Collections.emptySet() : Collections.singleton(pred);
|
this.pred = pred;
|
||||||
this.succ = succ;
|
this.supplier = supplier;
|
||||||
this.relyingOnDependents = relyingOnDependents;
|
this.relyingOnDependents = relyingOnDependents;
|
||||||
|
|
||||||
setSignificance(TaskSignificance.MODERATE);
|
setSignificance(TaskSignificance.MODERATE);
|
||||||
setName(succ.toString());
|
setName(supplier.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
setName(succ.toString());
|
setName(supplier.toString());
|
||||||
Task task = succ.apply(getVariables());
|
succ = supplier.get();
|
||||||
if (task != null)
|
|
||||||
dependencies.add(task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Task> getDependents() {
|
public Collection<Task> getDependents() {
|
||||||
return dependents;
|
return pred == null ? Collections.emptySet() : Collections.singleton(pred);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Task> getDependencies() {
|
public Collection<Task> getDependencies() {
|
||||||
return dependencies;
|
return succ == null ? Collections.emptySet() : Collections.singleton(succ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.io.RandomAccessFile;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -212,7 +213,7 @@ public class FileDownloadTask extends Task {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
File temp = null;
|
Path temp = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateProgress(0);
|
updateProgress(0);
|
||||||
@@ -242,15 +243,15 @@ public class FileDownloadTask extends Task {
|
|||||||
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
|
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
|
||||||
throw new IOException("Could not make directory " + file.getAbsoluteFile().getParent());
|
throw new IOException("Could not make directory " + file.getAbsoluteFile().getParent());
|
||||||
|
|
||||||
temp = FileUtils.createTempFile();
|
temp = Files.createTempFile(null, null);
|
||||||
rFile = new RandomAccessFile(temp, "rw");
|
rFile = new RandomAccessFile(temp.toFile(), "rw");
|
||||||
|
|
||||||
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
|
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
|
||||||
|
|
||||||
stream = con.getInputStream();
|
stream = con.getInputStream();
|
||||||
int lastDownloaded = 0, downloaded = 0;
|
int lastDownloaded = 0, downloaded = 0;
|
||||||
long lastTime = System.currentTimeMillis();
|
long lastTime = System.currentTimeMillis();
|
||||||
byte buffer[] = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
|
byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
@@ -283,16 +284,15 @@ public class FileDownloadTask extends Task {
|
|||||||
|
|
||||||
// Restore temp file to original name.
|
// Restore temp file to original name.
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
temp.delete();
|
temp.toFile().delete();
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (file.exists() && !file.delete())
|
Files.deleteIfExists(file.toPath());
|
||||||
throw new IOException("Unable to delete existent file " + file);
|
|
||||||
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
|
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
|
||||||
throw new IOException("Unable to make parent directory " + file);
|
throw new IOException("Unable to make parent directory " + file);
|
||||||
try {
|
try {
|
||||||
FileUtils.moveFile(temp, file);
|
FileUtils.moveFile(temp.toFile(), file);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IOException("Unable to move temp file from " + temp + " to " + file, e);
|
throw new IOException("Unable to move temp file from " + temp + " to " + file, e);
|
||||||
}
|
}
|
||||||
@@ -321,7 +321,7 @@ public class FileDownloadTask extends Task {
|
|||||||
return;
|
return;
|
||||||
} catch (IOException | IllegalStateException e) {
|
} catch (IOException | IllegalStateException e) {
|
||||||
if (temp != null)
|
if (temp != null)
|
||||||
temp.delete();
|
temp.toFile().delete();
|
||||||
exception = e;
|
exception = e;
|
||||||
} finally {
|
} finally {
|
||||||
closeFiles();
|
closeFiles();
|
||||||
|
|||||||
@@ -1,24 +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.task;
|
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
|
||||||
|
|
||||||
public interface FinalizedCallback {
|
|
||||||
void execute(AutoTypingMap<String> variables, boolean isDependentsSucceeded, Exception exception) throws Exception;
|
|
||||||
}
|
|
||||||
@@ -37,13 +37,12 @@ final class FinalizedTask extends Task {
|
|||||||
* @param pred the task that runs before succ.
|
* @param pred the task that runs before succ.
|
||||||
* @param callback a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
|
* @param callback a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
|
||||||
*/
|
*/
|
||||||
public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback, String name) {
|
public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback) {
|
||||||
this.pred = pred;
|
this.pred = pred;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
setSignificance(TaskSignificance.MODERATE);
|
setSignificance(TaskSignificance.MODERATE);
|
||||||
setName(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,7 +52,7 @@ final class FinalizedTask extends Task {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
callback.execute(getVariables(), isDependentsSucceeded(), pred.getLastException());
|
callback.execute(isDependentsSucceeded(), pred.getLastException());
|
||||||
|
|
||||||
if (!isDependentsSucceeded())
|
if (!isDependentsSucceeded())
|
||||||
throw new SilentException();
|
throw new SilentException();
|
||||||
|
|||||||
@@ -72,11 +72,6 @@ public final class GetTask extends TaskResult<String> {
|
|||||||
return Schedulers.io();
|
return Schedulers.io();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GetTask setCacheRepository(CacheRepository repository) {
|
public GetTask setCacheRepository(CacheRepository repository) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task;
|
package org.jackhuang.hmcl.task;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -26,16 +25,16 @@ import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
|||||||
*/
|
*/
|
||||||
class SimpleTask extends Task {
|
class SimpleTask extends Task {
|
||||||
|
|
||||||
private final ExceptionalConsumer<AutoTypingMap<String>, ?> consumer;
|
private final ExceptionalRunnable<?> closure;
|
||||||
private final Scheduler scheduler;
|
private final Scheduler scheduler;
|
||||||
|
|
||||||
public SimpleTask(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> consumer, Scheduler scheduler) {
|
public SimpleTask(String name, ExceptionalRunnable<?> closure, Scheduler scheduler) {
|
||||||
this.consumer = consumer;
|
this.closure = closure;
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
setSignificance(TaskSignificance.MINOR);
|
setSignificance(TaskSignificance.MINOR);
|
||||||
setName(consumer.toString());
|
setName(closure.toString());
|
||||||
} else {
|
} else {
|
||||||
setName(name);
|
setName(name);
|
||||||
}
|
}
|
||||||
@@ -48,6 +47,6 @@ class SimpleTask extends Task {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
consumer.accept(getVariables());
|
closure.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,24 +17,30 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task;
|
package org.jackhuang.hmcl.task;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.AutoTypingMap;
|
||||||
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||||
|
|
||||||
public final class SimpleTaskResult<V> extends TaskResult<V> {
|
import java.util.concurrent.Callable;
|
||||||
private final String id;
|
|
||||||
private final ExceptionalSupplier<V, ?> supplier;
|
|
||||||
|
|
||||||
public SimpleTaskResult(String id, ExceptionalSupplier<V, ?> supplier) {
|
/**
|
||||||
this.id = id;
|
*
|
||||||
this.supplier = supplier;
|
* @author huangyuhui
|
||||||
|
*/
|
||||||
|
class SimpleTaskResult<V> extends TaskResult<V> {
|
||||||
|
|
||||||
|
private final Callable<V> callable;
|
||||||
|
|
||||||
|
public SimpleTaskResult(Callable<V> callable) {
|
||||||
|
this.callable = callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleTaskResult(ExceptionalSupplier<V, ?> supplier) {
|
||||||
|
this.callable = supplier.toCallable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
setResult(supplier.get());
|
setResult(callable.call());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,12 @@ import javafx.beans.property.ReadOnlyDoubleWrapper;
|
|||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||||
import org.jackhuang.hmcl.event.EventManager;
|
import org.jackhuang.hmcl.event.EventManager;
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
|
||||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.ReflectionHelper;
|
import org.jackhuang.hmcl.util.ReflectionHelper;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
import org.jackhuang.hmcl.util.function.ExceptionalRunnable;
|
||||||
|
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -134,16 +133,6 @@ public abstract class Task {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AutoTypingMap<String> variables = null;
|
|
||||||
|
|
||||||
public AutoTypingMap<String> getVariables() {
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setVariables(AutoTypingMap<String> variables) {
|
|
||||||
this.variables = variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean doPreExecute() {
|
public boolean doPreExecute() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -280,16 +269,8 @@ public abstract class Task {
|
|||||||
new TaskExecutor(then(subscriber)).start();
|
new TaskExecutor(then(subscriber)).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void subscribe(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
|
||||||
subscribe(of(scheduler, closure));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void subscribe(Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
public final void subscribe(Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
||||||
subscribe(of(scheduler, ExceptionalConsumer.fromRunnable(closure)));
|
subscribe(of(scheduler, closure));
|
||||||
}
|
|
||||||
|
|
||||||
public final void subscribe(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
|
||||||
subscribe(of(closure));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void subscribe(ExceptionalRunnable<?> closure) {
|
public final void subscribe(ExceptionalRunnable<?> closure) {
|
||||||
@@ -300,16 +281,41 @@ public abstract class Task {
|
|||||||
return then(convert(b));
|
return then(convert(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Task then(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
|
public final Task then(ExceptionalSupplier<Task, ?> b) {
|
||||||
return new CoupleTask<>(this, b, true);
|
return new CoupleTask(this, b, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final <R> TaskResult<R> thenResult(Callable<R> supplier) {
|
||||||
|
return thenTaskResult(() -> Task.ofResult(supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final <R> TaskResult<R> thenTaskResult(ExceptionalSupplier<TaskResult<R>, ?> taskSupplier) {
|
||||||
|
return new TaskResult<R>() {
|
||||||
|
TaskResult<R> then;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends Task> getDependents() {
|
||||||
|
return Collections.singleton(Task.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
then = taskSupplier.get().storeTo(this::setResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends Task> getDependencies() {
|
||||||
|
return then == null ? Collections.emptyList() : Collections.singleton(then);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Task with(Task b) {
|
public final Task with(Task b) {
|
||||||
return with(convert(b));
|
return with(convert(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final <E extends Exception> Task with(ExceptionalFunction<AutoTypingMap<String>, Task, E> b) {
|
public final <E extends Exception> Task with(ExceptionalSupplier<Task, E> b) {
|
||||||
return new CoupleTask<>(this, b, false);
|
return new CoupleTask(this, b, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Task finalized(FinalizedCallback b) {
|
public final Task finalized(FinalizedCallback b) {
|
||||||
@@ -317,15 +323,16 @@ public abstract class Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final Task finalized(Scheduler scheduler, FinalizedCallback b) {
|
public final Task finalized(Scheduler scheduler, FinalizedCallback b) {
|
||||||
return new FinalizedTask(this, scheduler, b, ReflectionHelper.getCaller().toString());
|
return new FinalizedTask(this, scheduler, b).setName(getCaller());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final <T extends Exception, K extends Exception> Task finalized(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, T> success, ExceptionalConsumer<Exception, K> failure) {
|
// T, K here is necessary, or javac cannot infer type of failure
|
||||||
return finalized(scheduler, (variables, isDependentsSucceeded, exception) -> {
|
public final <T extends Exception, K extends Exception> Task finalized(Scheduler scheduler, ExceptionalRunnable<T> success, ExceptionalConsumer<Exception, K> failure) {
|
||||||
|
return finalized(scheduler, (isDependentsSucceeded, exception) -> {
|
||||||
if (isDependentsSucceeded) {
|
if (isDependentsSucceeded) {
|
||||||
if (success != null)
|
if (success != null)
|
||||||
try {
|
try {
|
||||||
success.accept(variables);
|
success.run();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.LOG.log(Level.WARNING, "Failed to execute " + success, e);
|
Logging.LOG.log(Level.WARNING, "Failed to execute " + success, e);
|
||||||
if (failure != null)
|
if (failure != null)
|
||||||
@@ -338,58 +345,38 @@ public abstract class Task {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task of(String name, ExceptionalRunnable<?> runnable) {
|
public static Task of(ExceptionalRunnable<?> closure) {
|
||||||
return of(name, ExceptionalConsumer.fromRunnable(runnable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task of(ExceptionalRunnable<?> runnable) {
|
|
||||||
return of(ExceptionalConsumer.fromRunnable(runnable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task of(String name, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
|
||||||
return of(name, Schedulers.defaultScheduler(), closure);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task of(ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
|
||||||
return of(Schedulers.defaultScheduler(), closure);
|
return of(Schedulers.defaultScheduler(), closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task of(String name, Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
public static Task of(String name, ExceptionalRunnable<?> closure) {
|
||||||
return new SimpleTask(name, closure, scheduler);
|
return of(name, Schedulers.defaultScheduler(), closure);
|
||||||
}
|
|
||||||
|
|
||||||
public static Task of(Scheduler scheduler, ExceptionalConsumer<AutoTypingMap<String>, ?> closure) {
|
|
||||||
return of(ReflectionHelper.getCaller().toString(), scheduler, closure);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task of(String name, Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
|
||||||
return new SimpleTask(name, ExceptionalConsumer.fromRunnable(closure), scheduler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task of(Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
public static Task of(Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
||||||
return of(ReflectionHelper.getCaller().toString(), scheduler, closure);
|
return of(getCaller(), scheduler, closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task ofThen(ExceptionalFunction<AutoTypingMap<String>, Task, ?> b) {
|
public static Task of(String name, Scheduler scheduler, ExceptionalRunnable<?> closure) {
|
||||||
return new CoupleTask<>(null, b, true);
|
return new SimpleTask(name, closure, scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task ofThen(ExceptionalSupplier<Task, ?> b) {
|
||||||
|
return new CoupleTask(null, b, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> TaskResult<V> ofResult(Callable<V> callable) {
|
public static <V> TaskResult<V> ofResult(Callable<V> callable) {
|
||||||
return ofResult("", callable);
|
return ofResult(getCaller(), callable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> TaskResult<V> ofResult(String id, Callable<V> callable) {
|
public static <V> TaskResult<V> ofResult(String name, Callable<V> callable) {
|
||||||
return new TaskCallable<>(id, callable);
|
return new SimpleTaskResult<>(callable).setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <V> TaskResult<V> ofResult(String id, ExceptionalFunction<AutoTypingMap<String>, V, ?> closure) {
|
private static ExceptionalSupplier<Task, ?> convert(Task t) {
|
||||||
return new TaskCallable<>(id, closure);
|
return new ExceptionalSupplier<Task, Exception>() {
|
||||||
}
|
|
||||||
|
|
||||||
private static ExceptionalFunction<AutoTypingMap<String>, Task, ?> convert(Task t) {
|
|
||||||
return new ExceptionalFunction<AutoTypingMap<String>, Task, Exception>() {
|
|
||||||
@Override
|
@Override
|
||||||
public Task apply(AutoTypingMap<String> autoTypingMap) {
|
public Task get() {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,4 +408,12 @@ public abstract class Task {
|
|||||||
SUCCEEDED,
|
SUCCEEDED,
|
||||||
FAILED
|
FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface FinalizedCallback {
|
||||||
|
void execute(boolean isDependentsSucceeded, Exception exception) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getCaller() {
|
||||||
|
return ReflectionHelper.getCaller(packageName -> !"org.jackhuang.hmcl.task".equals(packageName)).toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +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.task;
|
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.AutoTypingMap;
|
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author huangyuhui
|
|
||||||
*/
|
|
||||||
class TaskCallable<V> extends TaskResult<V> {
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final ExceptionalFunction<AutoTypingMap<String>, V, ?> callable;
|
|
||||||
|
|
||||||
public TaskCallable(String id, Callable<V> callable) {
|
|
||||||
this(id, variables -> callable.call());
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskCallable(String id, ExceptionalFunction<AutoTypingMap<String>, V, ?> callable) {
|
|
||||||
this.id = id;
|
|
||||||
this.callable = callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute() throws Exception {
|
|
||||||
setResult(callable.apply(getVariables()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,7 +38,6 @@ public final class TaskExecutor {
|
|||||||
private Exception lastException;
|
private Exception lastException;
|
||||||
private final AtomicInteger totTask = new AtomicInteger(0);
|
private final AtomicInteger totTask = new AtomicInteger(0);
|
||||||
private final ConcurrentLinkedQueue<Future<?>> workerQueue = new ConcurrentLinkedQueue<>();
|
private final ConcurrentLinkedQueue<Future<?>> workerQueue = new ConcurrentLinkedQueue<>();
|
||||||
private final AutoTypingMap<String> variables = new AutoTypingMap<>(new HashMap<>());
|
|
||||||
private Scheduler scheduler = Schedulers.newThread();
|
private Scheduler scheduler = Schedulers.newThread();
|
||||||
|
|
||||||
public TaskExecutor(Task task) {
|
public TaskExecutor(Task task) {
|
||||||
@@ -147,8 +146,6 @@ public final class TaskExecutor {
|
|||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
task.setVariables(variables);
|
|
||||||
|
|
||||||
if (task.doPreExecute()) {
|
if (task.doPreExecute()) {
|
||||||
try {
|
try {
|
||||||
task.getScheduler().schedule(task::preExecute).get();
|
task.getScheduler().schedule(task::preExecute).get();
|
||||||
@@ -186,11 +183,6 @@ public final class TaskExecutor {
|
|||||||
task.setState(Task.TaskState.EXECUTED);
|
task.setState(Task.TaskState.EXECUTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task instanceof TaskResult<?>) {
|
|
||||||
TaskResult<?> taskResult = (TaskResult<?>) task;
|
|
||||||
variables.set(taskResult.getId(), taskResult.getResult());
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<? extends Task> dependencies = task.getDependencies();
|
Collection<? extends Task> dependencies = task.getDependencies();
|
||||||
boolean doDependenciesSucceeded = executeTasks(dependencies);
|
boolean doDependenciesSucceeded = executeTasks(dependencies);
|
||||||
Exception dependenciesException = dependencies.stream().map(Task::getLastException).filter(Objects::nonNull).findAny().orElse(null);
|
Exception dependenciesException = dependencies.stream().map(Task::getLastException).filter(Objects::nonNull).findAny().orElse(null);
|
||||||
@@ -238,8 +230,6 @@ public final class TaskExecutor {
|
|||||||
}
|
}
|
||||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||||
} finally {
|
|
||||||
task.setVariables(null);
|
|
||||||
}
|
}
|
||||||
task.setState(flag ? Task.TaskState.SUCCEEDED : Task.TaskState.FAILED);
|
task.setState(flag ? Task.TaskState.SUCCEEDED : Task.TaskState.FAILED);
|
||||||
return flag;
|
return flag;
|
||||||
@@ -249,10 +239,6 @@ public final class TaskExecutor {
|
|||||||
return totTask.get();
|
return totTask.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AutoTypingMap<String> getVariables() {
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Invoker implements ExceptionalRunnable<Exception> {
|
private class Invoker implements ExceptionalRunnable<Exception> {
|
||||||
|
|
||||||
private final Task task;
|
private final Task task;
|
||||||
|
|||||||
@@ -17,12 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.task;
|
package org.jackhuang.hmcl.task;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.ReflectionHelper;
|
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A task that has a result.
|
* A task that has a result.
|
||||||
@@ -32,6 +32,13 @@ import java.util.Collections;
|
|||||||
public abstract class TaskResult<V> extends Task {
|
public abstract class TaskResult<V> extends Task {
|
||||||
|
|
||||||
private V result;
|
private V result;
|
||||||
|
private Consumer<V> resultConsumer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskResult<V> setName(String name) {
|
||||||
|
super.setName(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public V getResult() {
|
public V getResult() {
|
||||||
return result;
|
return result;
|
||||||
@@ -39,42 +46,18 @@ public abstract class TaskResult<V> extends Task {
|
|||||||
|
|
||||||
public void setResult(V result) {
|
public void setResult(V result) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
|
if (resultConsumer != null)
|
||||||
|
resultConsumer.accept(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract String getId();
|
public TaskResult<V> storeTo(Consumer<V> resultConsumer) {
|
||||||
|
this.resultConsumer = resultConsumer;
|
||||||
public <R, E extends Exception> TaskResult<R> thenResult(ExceptionalFunction<V, R, E> task) {
|
return this;
|
||||||
return thenResult(Schedulers.defaultScheduler(), task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R, E extends Exception> TaskResult<R> thenResult(Scheduler scheduler, ExceptionalFunction<V, R, E> task) {
|
public <R, E extends Exception> TaskResult<R> thenTaskResult(ExceptionalFunction<V, TaskResult<R>, E> taskSupplier) {
|
||||||
return thenResult(ReflectionHelper.getCaller().toString(), scheduler, task);
|
return new TaskResult<R>() {
|
||||||
}
|
TaskResult<R> then;
|
||||||
|
|
||||||
public <R, E extends Exception> TaskResult<R> thenResult(String id, Scheduler scheduler, ExceptionalFunction<V, R, E> task) {
|
|
||||||
return new Subtask<>(id, scheduler, task);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Exception, K extends Exception> Task finalizedResult(Scheduler scheduler, ExceptionalConsumer<V, T> success, ExceptionalConsumer<Exception, K> failure) {
|
|
||||||
return finalized(scheduler, variables -> success.accept(getResult()), failure);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task finalizedResult(Scheduler scheduler, FinalizedCallback<V> callback) {
|
|
||||||
return new FinalizedTask(this, scheduler,
|
|
||||||
(variables, isDependentsSucceeded, exception) -> callback.execute(getResult(), isDependentsSucceeded, exception),
|
|
||||||
ReflectionHelper.getCaller().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Subtask<R> extends TaskResult<R> {
|
|
||||||
private final String id;
|
|
||||||
private final Scheduler scheduler;
|
|
||||||
private final ExceptionalFunction<V, R, ?> callable;
|
|
||||||
|
|
||||||
public Subtask(String id, Scheduler scheduler, ExceptionalFunction<V, R, ?> callable) {
|
|
||||||
this.id = id;
|
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.callable = callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends Task> getDependents() {
|
public Collection<? extends Task> getDependents() {
|
||||||
@@ -82,8 +65,64 @@ public abstract class TaskResult<V> extends Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public void execute() throws Exception {
|
||||||
return id;
|
then = taskSupplier.apply(TaskResult.this.getResult()).storeTo(this::setResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends Task> getDependencies() {
|
||||||
|
return then == null ? Collections.emptyList() : Collections.singleton(then);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R, E extends Exception> Task then(ExceptionalFunction<V, Task, E> taskSupplier) {
|
||||||
|
return new CoupleTask(this, () -> taskSupplier.apply(getResult()), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R, E extends Exception> TaskResult<R> thenResult(ExceptionalFunction<V, R, E> task) {
|
||||||
|
return thenResult(Schedulers.defaultScheduler(), task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R, E extends Exception> TaskResult<R> thenResult(Scheduler scheduler, ExceptionalFunction<V, R, E> task) {
|
||||||
|
return thenResult(getCaller(), scheduler, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <R, E extends Exception> TaskResult<R> thenResult(String name, Scheduler scheduler, ExceptionalFunction<V, R, E> task) {
|
||||||
|
return new Subtask<>(name, scheduler, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stupid javac stop us from renaming thenVoid to thenResult
|
||||||
|
public <E extends Exception> Task thenVoid(ExceptionalConsumer<V, E> task) {
|
||||||
|
return thenVoid(Schedulers.defaultScheduler(), task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Exception> Task thenVoid(Scheduler scheduler, ExceptionalConsumer<V, E> task) {
|
||||||
|
return new CoupleTask(this, () -> Task.of(scheduler, () -> task.accept(getResult())), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Exception, K extends Exception> Task finalized(Scheduler scheduler, ExceptionalConsumer<V, T> success, ExceptionalConsumer<Exception, K> failure) {
|
||||||
|
return finalized(scheduler, () -> success.accept(getResult()), failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task finalized(Scheduler scheduler, FinalizedCallback<V> callback) {
|
||||||
|
return finalized(scheduler, ((isDependentsSucceeded, exception) -> callback.execute(getResult(), isDependentsSucceeded, exception)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Subtask<R> extends TaskResult<R> {
|
||||||
|
private final Scheduler scheduler;
|
||||||
|
private final ExceptionalFunction<V, R, ?> callable;
|
||||||
|
|
||||||
|
public Subtask(String name, Scheduler scheduler, ExceptionalFunction<V, R, ?> callable) {
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.callable = callable;
|
||||||
|
|
||||||
|
setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends Task> getDependents() {
|
||||||
|
return Collections.singleton(TaskResult.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,17 +17,28 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.util;
|
package org.jackhuang.hmcl.util;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class ReflectionHelper {
|
public final class ReflectionHelper {
|
||||||
|
|
||||||
public static StackTraceElement getCaller() {
|
/**
|
||||||
|
* Get caller, this method is caller sensitive.
|
||||||
|
* @param packageFilter returns false if we consider the given package is internal calls, not the caller
|
||||||
|
* @return the caller, method name, source file, line number
|
||||||
|
*/
|
||||||
|
public static StackTraceElement getCaller(Predicate<String> packageFilter) {
|
||||||
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
|
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
|
||||||
|
// element[0] is Thread.currentThread().getStackTrace()
|
||||||
|
// element[1] is ReflectionHelper.getCaller(packageFilter)
|
||||||
|
// so element[2] is caller of this method.
|
||||||
StackTraceElement caller = elements[2];
|
StackTraceElement caller = elements[2];
|
||||||
for (int i = 3; i < elements.length; ++i) {
|
for (int i = 3; i < elements.length; ++i) {
|
||||||
if (!caller.getClassName().equals(elements[i].getClassName()))
|
if (packageFilter.test(StringUtils.substringBeforeLast(elements[i].getClassName(), '.')) &&
|
||||||
|
!caller.getClassName().equals(elements[i].getClassName()))
|
||||||
return elements[i];
|
return elements[i];
|
||||||
}
|
}
|
||||||
return caller;
|
return caller;
|
||||||
|
|||||||
@@ -116,14 +116,43 @@ public final class FileUtils {
|
|||||||
return new String(Files.readAllBytes(file), charset);
|
return new String(Files.readAllBytes(file), charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write plain text to file. Characters are encoded into bytes using UTF-8.
|
||||||
|
*
|
||||||
|
* We don't care about platform difference of line separator. Because readText accept all possibilities of line separator.
|
||||||
|
* It will create the file if it does not exist, or truncate the existing file to empty for rewriting.
|
||||||
|
* All characters in text will be written into the file in binary format. Existing data will be erased.
|
||||||
|
* @param file the path to the file
|
||||||
|
* @param text the text being written to file
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
|
*/
|
||||||
public static void writeText(File file, String text) throws IOException {
|
public static void writeText(File file, String text) throws IOException {
|
||||||
writeText(file, text, UTF_8);
|
writeText(file, text, UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write plain text to file.
|
||||||
|
*
|
||||||
|
* We don't care about platform difference of line separator. Because readText accept all possibilities of line separator.
|
||||||
|
* It will create the file if it does not exist, or truncate the existing file to empty for rewriting.
|
||||||
|
* All characters in text will be written into the file in binary format. Existing data will be erased.
|
||||||
|
* @param file the path to the file
|
||||||
|
* @param text the text being written to file
|
||||||
|
* @param charset the charset to use for encoding
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
|
*/
|
||||||
public static void writeText(File file, String text, Charset charset) throws IOException {
|
public static void writeText(File file, String text, Charset charset) throws IOException {
|
||||||
writeBytes(file, text.getBytes(charset));
|
writeBytes(file, text.getBytes(charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write byte array to file.
|
||||||
|
* It will create the file if it does not exist, or truncate the existing file to empty for rewriting.
|
||||||
|
* All bytes in byte array will be written into the file in binary format. Existing data will be erased.
|
||||||
|
* @param file the path to the file
|
||||||
|
* @param array the data being written to file
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
|
*/
|
||||||
public static void writeBytes(File file, byte[] array) throws IOException {
|
public static void writeBytes(File file, byte[] array) throws IOException {
|
||||||
Files.createDirectories(file.toPath().getParent());
|
Files.createDirectories(file.toPath().getParent());
|
||||||
Files.write(file.toPath(), array);
|
Files.write(file.toPath(), array);
|
||||||
@@ -153,6 +182,14 @@ public final class FileUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy directory.
|
||||||
|
* Paths of all files relative to source directory will be the same as the ones relative to destination directory.
|
||||||
|
*
|
||||||
|
* @param src the source directory.
|
||||||
|
* @param dest the destination directory, which will be created if not existing.
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
*/
|
||||||
public static void copyDirectory(Path src, Path dest) throws IOException {
|
public static void copyDirectory(Path src, Path dest) throws IOException {
|
||||||
Files.walkFileTree(src, new SimpleFileVisitor<Path>(){
|
Files.walkFileTree(src, new SimpleFileVisitor<Path>(){
|
||||||
@Override
|
@Override
|
||||||
@@ -173,6 +210,20 @@ public final class FileUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move file to trash.
|
||||||
|
*
|
||||||
|
* This method is only implemented in Java 9. Please check we are using Java 9 by invoking isMovingToTrashSupported.
|
||||||
|
* Example:
|
||||||
|
* <pre>{@code
|
||||||
|
* if (FileUtils.isMovingToTrashSupported()) {
|
||||||
|
* FileUtils.moveToTrash(file);
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* @param file the file being moved to trash.
|
||||||
|
* @see FileUtils#isMovingToTrashSupported()
|
||||||
|
* @return false if moveToTrash does not exist, or platform does not support Desktop.Action.MOVE_TO_TRASH
|
||||||
|
*/
|
||||||
public static boolean moveToTrash(File file) {
|
public static boolean moveToTrash(File file) {
|
||||||
try {
|
try {
|
||||||
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
|
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
|
||||||
@@ -314,20 +365,4 @@ public final class FileUtils {
|
|||||||
result.add(it);
|
result.add(it);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File createTempFile() throws IOException {
|
|
||||||
return createTempFile("tmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File createTempFile(String prefix) throws IOException {
|
|
||||||
return createTempFile(prefix, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File createTempFile(String prefix, String suffix) throws IOException {
|
|
||||||
return createTempFile(prefix, suffix, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File createTempFile(String prefix, String suffix, File directory) throws IOException {
|
|
||||||
return File.createTempFile(prefix, suffix, directory);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ import java.io.*;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This utility class consists of some util methods operating on InputStream/OutputStream.
|
||||||
*
|
*
|
||||||
* @author huang
|
* @author huangyuhui
|
||||||
*/
|
*/
|
||||||
public final class IOUtils {
|
public final class IOUtils {
|
||||||
|
|
||||||
@@ -31,12 +32,26 @@ public final class IOUtils {
|
|||||||
|
|
||||||
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
|
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all bytes to a buffer from given input stream. The stream will not be closed.
|
||||||
|
*
|
||||||
|
* @param stream the InputStream being read.
|
||||||
|
* @return all bytes read from the stream
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
*/
|
||||||
public static byte[] readFullyWithoutClosing(InputStream stream) throws IOException {
|
public static byte[] readFullyWithoutClosing(InputStream stream) throws IOException {
|
||||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||||
copyTo(stream, result);
|
copyTo(stream, result);
|
||||||
return result.toByteArray();
|
return result.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all bytes to a buffer from given input stream, and close the input stream finally.
|
||||||
|
*
|
||||||
|
* @param stream the InputStream being read, closed finally.
|
||||||
|
* @return all bytes read from the stream
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
*/
|
||||||
public static ByteArrayOutputStream readFully(InputStream stream) throws IOException {
|
public static ByteArrayOutputStream readFully(InputStream stream) throws IOException {
|
||||||
try (InputStream is = stream) {
|
try (InputStream is = stream) {
|
||||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||||
|
|||||||
Reference in New Issue
Block a user