Compare commits

...
Author SHA1 Message Date
huanghongxun 32d0d6a7ee Add some task test 2019-02-26 15:28:40 +08:00
huanghongxun d9a336e50b Remove redundant codes 2019-02-26 13:29:09 +08:00
huanghongxun 3c958d06c8 Replace scheduler to executor 2019-02-26 11:39:38 +08:00
huanghongxun 15a75a69b3 Fix exception handling 2019-02-26 08:17:46 +08:00
huanghongxun cae32cff4c remove isDependentSucceeded in whenComplete 2019-02-25 23:59:21 +08:00
huanghongxun eeef76594f Rename task.lastException -> task.exception 2019-02-25 22:17:30 +08:00
huanghongxun 8f2a1030bc Refactor Task 2019-02-24 23:27:53 +08:00
huanghongxun dc1df98277 Fix compilation error 2019-02-24 23:13:31 +08:00
huanghongxun def45cc3e4 Fix NumberFormatException when parsing forge version list json 2019-02-24 23:11:09 +08:00
huanghongxun 8007f1958e Rename isDependentsSucceeded to isDependentSucceeded 2019-02-24 17:00:17 +08:00
huanghongxun b22e724a3f Remove SilentException 2019-02-24 12:39:40 +08:00
huanghongxun ed66b016db Rename then-series functions 2019-02-24 12:39:39 +08:00
yushijinhun 34890f21b3 Update issue template 2019-02-24 12:29:58 +08:00
yushijinhun 9f85e4a63b Do not run GameVersion.minecraftVersion on UI thread 2019-02-24 12:25:19 +08:00
yushijinhun 7fcd4d94a6 Merge branch 'javafx' of github.com:huanghongxun/HMCL into javafx 2019-02-23 23:52:26 +08:00
yushijinhun 84fbca7905 Remove unused code 2019-02-23 23:48:23 +08:00
huanghongxun 9add2387b9 Add comments to FileUtils and IOUtils 2019-02-23 23:42:05 +08:00
huanghongxun a388450f94 Remove AutoTypingMap 2019-02-23 22:50:01 +08:00
huanghongxun fff7abc2ac finalized no longer passes AutoTypingMap as callback parameter 2019-02-23 22:50:00 +08:00
yushijinhun e5587418c7 Remove SafeIntStringConverter 2019-02-23 22:36:38 +08:00
yushijinhun 7c89fac1e9 Rename MultiStepBinding->BindingMapping 2019-02-23 22:14:29 +08:00
yushijinhun bc9a9ffb72 rewrite MultiStepBinding.asyncMap 2019-02-23 21:58:44 +08:00
yushijinhun 46d27a04a2 Fix auth server list in add account pane 2019-02-23 21:49:38 +08:00
huanghongxun 0f33981c96 Fix #534 2019-02-23 19:13:24 +08:00
yushijinhun b396e0b208 Fix compilation error 2019-02-23 18:41:39 +08:00
yushijinhun 1bf2f17538 Fix #533 2019-02-23 18:38:43 +08:00
huanghongxun 608a531655 Remove AtomicReference 2019-02-20 20:18:34 +08:00
huanghongxun e57180eb15 remove lastException in task.variables 2019-02-20 20:07:43 +08:00
huanghongxun 3ae826d9ca Fix task.getLastException does not receive subtasks' value 2019-02-20 20:07:11 +08:00
huanghongxun e940845c60 Fix not correct mod installation 2019-02-20 16:40:26 +08:00
huanghongxun d8aa989cc4 Fix #532 2019-02-19 23:15:45 +08:00
129 changed files with 1649 additions and 2270 deletions

No files matched your search

+3 -3
View File
@@ -6,7 +6,7 @@
我们发现,对于不经常检查邮箱的人来说,GitHub issues 的反馈效率太低,时隔几个小时甚至一天的情况很多。为了改善反馈效率,并将积极为 HMCL 提供问题反馈和建议的人聚集起来,我们希望在这里反馈的人可以在 HMCL 3 用户 QQ 群:219177735 中反馈信息,**而不在 GitHub Issues 中反馈问题**。
我们发现,对于不经常检查邮箱的人来说,GitHub Issues 的反馈效率太低,时隔几个小时甚至一天的情况很多。为了改善反馈效率,并将积极为 HMCL 提供问题反馈和建议的人聚集起来,我们希望在这里反馈的人可以在 HMCL 3 用户 QQ 群:219177735 中反馈信息,**而不在 GitHub Issues 中反馈问题**。
如果你希望在 QQ 群中反馈问题,也请将下面的信息表填好直接发在群中,加快我们的沟通速度。
@@ -24,5 +24,5 @@
* 对游戏做的修改(是否自行通过安装器安装 Rift 等 API,以及 mod):
* 游戏崩溃报告(如果有):
* 启动器崩溃报告(如果有):
* 启动器日志文件(在启动器设置中打开日志文件夹并将文件夹内所有文件打包发在这里):
* 问题描述(如何触发问题):
* 启动器日志文件(出现问题后点击启动器设置中的【导出启动器日志】按钮):
* 问题描述(如何触发问题):
@@ -20,9 +20,6 @@ package org.jackhuang.hmcl.game;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javafx.scene.image.Image;
import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.setting.EnumGameDirectory;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
@@ -30,7 +30,7 @@ import java.util.List;
/**
* Export the game to a mod pack file.
*/
public class HMCLModpackExportTask extends Task {
public class HMCLModpackExportTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String version;
private final List<String> whitelist;
@@ -36,13 +36,13 @@ import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public final class HMCLModpackInstallTask extends Task {
public final class HMCLModpackInstallTask extends Task<Void> {
private final File zipFile;
private final String name;
private final HMCLGameRepository repository;
private final Modpack modpack;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
public HMCLModpackInstallTask(Profile profile, File zipFile, Modpack modpack, String name) {
DependencyManager dependency = profile.getDependency();
@@ -77,12 +77,12 @@ public final class HMCLModpackInstallTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@Override
public List<Task> getDependents() {
public List<Task<?>> getDependents() {
return dependents;
}
@@ -38,7 +38,7 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.DialogController;
import org.jackhuang.hmcl.ui.LogWindow;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.util.Log4jLevel;
import org.jackhuang.hmcl.util.Logging;
@@ -102,7 +102,7 @@ public final class LauncherHelper {
try {
checkGameState(profile, setting, version, () -> {
Controllers.dialog(launchingStepsPane);
Schedulers.newThread().schedule(this::launch0);
Schedulers.newThread().execute(this::launch0);
});
} catch (InterruptedException ignore) {
}
@@ -121,15 +121,15 @@ public final class LauncherHelper {
Version version = MaintainTask.maintain(repository.getResolvedVersion(selectedVersion));
Optional<String> gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version));
TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
.then(variables -> {
TaskExecutor executor = Task.runAsync(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES))
.thenCompose(() -> {
if (setting.isNotCheckGame())
return null;
else
return dependencyManager.checkGameCompletionAsync(version);
})
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS)))
.then(var -> {
.thenRun(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))
.thenCompose(() -> {
try {
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
if ("Curse".equals(configuration.getType()))
@@ -140,45 +140,43 @@ public final class LauncherHelper {
return null;
}
})
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN)))
.then(Task.of(i18n("account.methods"), variables -> {
.thenRun(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN))
.thenSupply(i18n("account.methods"), () -> {
try {
variables.set("account", account.logIn());
return account.logIn();
} catch (CredentialExpiredException e) {
LOG.info("Credential has expired: " + e);
variables.set("account", DialogController.logIn(account));
return DialogController.logIn(account);
} catch (AuthenticationException e) {
LOG.warning("Authentication failed, try playing offline: " + e);
variables.set("account",
account.playOffline().orElseThrow(() -> e));
return account.playOffline().orElseThrow(() -> e);
}
}))
.then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LAUNCHING)))
.then(Task.of(variables -> {
variables.set("launcher", new HMCLGameLauncher(
repository,
selectedVersion,
variables.get("account"),
setting.toLaunchOptions(profile.getGameDir()),
launcherVisibility == LauncherVisibility.CLOSE
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
: new HMCLProcessListener(variables.get("account"), setting, gameVersion.isPresent())
));
}))
.then(variables -> {
DefaultLauncher launcher = variables.get("launcher");
})
.thenApply(Schedulers.javafx(), authInfo -> {
emitStatus(LoadingState.LAUNCHING);
return authInfo;
})
.thenApply(authInfo -> new HMCLGameLauncher(
repository,
selectedVersion,
authInfo,
setting.toLaunchOptions(profile.getGameDir()),
launcherVisibility == LauncherVisibility.CLOSE
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
: new HMCLProcessListener(authInfo, gameVersion.isPresent())
))
.thenCompose(launcher -> { // launcher is prev task's result
if (scriptFile == null) {
return new LaunchTask<>(launcher::launch).setName(i18n("version.launch"));
} else {
return new LaunchTask<>(() -> {
return new LaunchTask<ManagedProcess>(() -> {
launcher.makeLaunchScript(scriptFile);
return null;
}).setName(i18n("version.launch_script"));
}
})
.then(Task.of(variables -> {
.thenAccept(process -> { // process is LaunchTask's result
if (scriptFile == null) {
ManagedProcess process = variables.get(LaunchTask.LAUNCH_ID);
PROCESSES.add(process);
if (launcherVisibility == LauncherVisibility.CLOSE)
Launcher.stopApplication();
@@ -187,13 +185,13 @@ public final class LauncherHelper {
process.stop();
it.fireEvent(new DialogCloseEvent());
});
} else
} else {
Platform.runLater(() -> {
launchingStepsPane.fireEvent(new DialogCloseEvent());
Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath()));
});
}))
}
})
.executor();
launchingStepsPane.setExecutor(executor, false);
@@ -201,7 +199,7 @@ public final class LauncherHelper {
final AtomicInteger finished = new AtomicInteger(0);
@Override
public void onFinished(Task task) {
public void onFinished(Task<?> task) {
finished.incrementAndGet();
int runningTasks = executor.getRunningTasks();
Platform.runLater(() -> launchingStepsPane.setProgress(1.0 * finished.get() / runningTasks));
@@ -215,7 +213,7 @@ public final class LauncherHelper {
// because onStop will be invoked if tasks fail when the executor service shut down.
if (!Controllers.isStopped()) {
launchingStepsPane.fireEvent(new DialogCloseEvent());
Exception ex = executor.getLastException();
Exception ex = executor.getException();
if (ex != null) {
String message;
if (ex instanceof CurseCompletionException) {
@@ -236,7 +234,7 @@ public final class LauncherHelper {
}
Controllers.dialog(message,
scriptFile == null ? i18n("launch.failed") : i18n("version.launch_script.failed"),
MessageBox.ERROR_MESSAGE);
MessageType.ERROR);
}
}
});
@@ -263,7 +261,7 @@ public final class LauncherHelper {
VersionNumber gameVersion = VersionNumber.asVersion(GameVersion.minecraftVersion(profile.getRepository().getVersionJar(version)).orElse("Unknown"));
JavaVersion java = setting.getJavaVersion();
if (java == null) {
Controllers.dialog(i18n("launch.wrong_javadir"), i18n("message.warning"), MessageBox.WARNING_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.wrong_javadir"), i18n("message.warning"), MessageType.WARNING, onAccept);
setting.setJava(null);
setting.setDefaultJavaPath(null);
java = JavaVersion.fromCurrentEnvironment();
@@ -282,10 +280,10 @@ public final class LauncherHelper {
if (gameVersion.compareTo(VersionNumber.asVersion("1.13")) >= 0) {
// Minecraft 1.13 and later versions only support Java 8 or later.
// Terminate launching operation.
Controllers.dialog(i18n("launch.advice.java8_1_13"), i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
Controllers.dialog(i18n("launch.advice.java8_1_13"), i18n("message.error"), MessageType.ERROR, null);
} else {
// Most mods require Java 8 or later version.
Controllers.dialog(i18n("launch.advice.newer_java"), i18n("message.warning"), MessageBox.WARNING_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.newer_java"), i18n("message.warning"), MessageType.WARNING, onAccept);
}
flag = true;
}
@@ -301,10 +299,10 @@ public final class LauncherHelper {
if (java8.isPresent()) {
java8required = true;
setting.setJavaVersion(java8.get());
Controllers.dialog(i18n("launch.advice.java9") + "\n" + i18n("launch.advice.corrected"), i18n("message.info"), MessageBox.INFORMATION_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.java9") + "\n" + i18n("launch.advice.corrected"), i18n("message.info"), MessageType.INFORMATION, onAccept);
flag = true;
} else {
Controllers.dialog(i18n("launch.advice.java9") + "\n" + i18n("launch.advice.uncorrected"), i18n("message.error"), MessageBox.ERROR_MESSAGE, null);
Controllers.dialog(i18n("launch.advice.java9") + "\n" + i18n("launch.advice.uncorrected"), i18n("message.error"), MessageType.ERROR, null);
flag = true;
}
}
@@ -319,7 +317,7 @@ public final class LauncherHelper {
newJavaRequired = true;
setting.setJavaVersion(java8.get());
} else {
Controllers.dialog(i18n("launch.advice.java8_51_1_13"), i18n("message.warning"), MessageBox.WARNING_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.java8_51_1_13"), i18n("message.warning"), MessageType.WARNING, onAccept);
flag = true;
}
}
@@ -351,7 +349,7 @@ public final class LauncherHelper {
if (java64.isPresent()) {
setting.setJavaVersion(java64.get());
} else {
Controllers.dialog(i18n("launch.advice.different_platform"), i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.different_platform"), i18n("message.error"), MessageType.ERROR, onAccept);
flag = true;
}
}
@@ -361,13 +359,13 @@ public final class LauncherHelper {
setting.getMaxMemory() > 1.5 * 1024) {
// 1.5 * 1024 is an inaccurate number.
// Actual memory limit depends on operating system and memory.
Controllers.dialog(i18n("launch.advice.too_large_memory_for_32bit"), i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.too_large_memory_for_32bit"), i18n("message.error"), MessageType.ERROR, onAccept);
flag = true;
}
// Cannot allocate too much memory exceeding free space.
if (!flag && OperatingSystem.TOTAL_MEMORY > 0 && OperatingSystem.TOTAL_MEMORY < setting.getMaxMemory()) {
Controllers.dialog(i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), i18n("message.error"), MessageType.ERROR, onAccept);
flag = true;
}
@@ -379,7 +377,7 @@ public final class LauncherHelper {
VersionNumber.VERSION_COMPARATOR.compare(it.getVersion(), "1.12.2-14.23.5.2773") < 0);
boolean hasLiteLoader = version.getLibraries().stream().anyMatch(it -> it.is("com.mumfrey", "liteloader"));
if (hasForge2760 && hasLiteLoader && gameVersion.compareTo(VersionNumber.asVersion("1.12.2")) == 0) {
Controllers.dialog(i18n("launch.advice.forge2760_liteloader"), i18n("message.error"), MessageBox.ERROR_MESSAGE, onAccept);
Controllers.dialog(i18n("launch.advice.forge2760_liteloader"), i18n("message.error"), MessageType.ERROR, onAccept);
flag = true;
}
}
@@ -418,7 +416,7 @@ public final class LauncherHelper {
}
}
private static class LaunchTask<T> extends TaskResult<T> {
private static class LaunchTask<T> extends Task<T> {
private final ExceptionalSupplier<T, Exception> supplier;
public LaunchTask(ExceptionalSupplier<T, Exception> supplier) {
@@ -429,13 +427,6 @@ public final class LauncherHelper {
public void execute() throws Exception {
setResult(supplier.get());
}
@Override
public String getId() {
return LAUNCH_ID;
}
static final String LAUNCH_ID = "launch";
}
/**
@@ -445,7 +436,6 @@ public final class LauncherHelper {
*/
class HMCLProcessListener implements ProcessListener {
private final VersionSetting setting;
private final Map<String, String> forbiddenTokens;
private ManagedProcess process;
private boolean lwjgl;
@@ -454,8 +444,7 @@ public final class LauncherHelper {
private final LinkedList<Pair<String, Log4jLevel>> logs;
private final CountDownLatch latch = new CountDownLatch(1);
public HMCLProcessListener(AuthInfo authInfo, VersionSetting setting, boolean detectWindow) {
this.setting = setting;
public HMCLProcessListener(AuthInfo authInfo, boolean detectWindow) {
this.detectWindow = detectWindow;
if (authInfo == null)
@@ -86,7 +86,7 @@ public final class ModpackHelper {
throw new UnsupportedModpackException();
}
public static Task getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
public static Task<Void> getInstallTask(Profile profile, File zipFile, String name, Modpack modpack) {
profile.getRepository().markVersionAsModpack(name);
ExceptionalRunnable<?> success = () -> {
@@ -110,18 +110,18 @@ public final class ModpackHelper {
if (modpack.getManifest() instanceof CurseManifest)
return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name)
.finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure);
.whenComplete(Schedulers.defaultScheduler(), success, failure);
else if (modpack.getManifest() instanceof HMCLModpackManifest)
return new HMCLModpackInstallTask(profile, zipFile, modpack, name)
.finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure);
.whenComplete(Schedulers.defaultScheduler(), success, failure);
else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration)
return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)
.finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure)
.then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
.whenComplete(Schedulers.defaultScheduler(), success, failure)
.thenCompose(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name));
else throw new IllegalStateException("Unrecognized modpack: " + modpack);
}
public static Task getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
public static Task<Void> getUpdateTask(Profile profile, File zipFile, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException, MismatchedModpackTypeException {
Modpack modpack = ModpackHelper.readModpackManifest(zipFile.toPath(), charset);
switch (configuration.getType()) {
@@ -20,13 +20,12 @@ package org.jackhuang.hmcl.game;
import org.jackhuang.hmcl.mod.MultiMCInstanceConfiguration;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.VersionSetting;
import org.jackhuang.hmcl.task.Scheduler;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import java.util.Objects;
public final class MultiMCInstallVersionSettingTask extends Task {
public final class MultiMCInstallVersionSettingTask extends Task<Void> {
private final Profile profile;
private final MultiMCInstanceConfiguration manifest;
private final String version;
@@ -35,11 +34,8 @@ public final class MultiMCInstallVersionSettingTask extends Task {
this.profile = profile;
this.manifest = manifest;
this.version = version;
}
@Override
public Scheduler getScheduler() {
return Schedulers.javafx();
setExecutor(Schedulers.javafx());
}
@Override
@@ -17,7 +17,9 @@
*/
package org.jackhuang.hmcl.game;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static java.util.Objects.requireNonNull;
import static org.jackhuang.hmcl.util.Lang.threadPool;
import static org.jackhuang.hmcl.util.Logging.LOG;
@@ -33,6 +35,7 @@ import java.util.EnumMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -48,7 +51,8 @@ import org.jackhuang.hmcl.auth.yggdrasil.TextureType;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.util.javafx.MultiStepBinding;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
@@ -69,8 +73,8 @@ public final class TexturesLoader {
private final Map<String, String> metadata;
public LoadedTexture(BufferedImage image, Map<String, String> metadata) {
this.image = image;
this.metadata = metadata;
this.image = requireNonNull(image);
this.metadata = requireNonNull(metadata);
}
public BufferedImage getImage() {
@@ -98,6 +102,10 @@ public final class TexturesLoader {
}
public static LoadedTexture loadTexture(Texture texture) throws IOException {
if (StringUtils.isBlank(texture.getUrl())) {
throw new IOException("Texture url is empty");
}
Path file = getTexturePath(texture);
if (!Files.isRegularFile(file)) {
// download it
@@ -118,7 +126,11 @@ public final class TexturesLoader {
try (InputStream in = Files.newInputStream(file)) {
img = ImageIO.read(in);
}
return new LoadedTexture(img, texture.getMetadata());
Map<String, String> metadata = texture.getMetadata();
if (metadata == null) {
metadata = emptyMap();
}
return new LoadedTexture(img, metadata);
}
// ====
@@ -142,7 +154,7 @@ public final class TexturesLoader {
public static ObjectBinding<LoadedTexture> skinBinding(YggdrasilService service, UUID uuid) {
LoadedTexture uuidFallback = getDefaultSkin(TextureModel.detectUUID(uuid));
return MultiStepBinding.of(service.getProfileRepository().binding(uuid))
return BindingMapping.of(service.getProfileRepository().binding(uuid))
.map(profile -> profile
.flatMap(it -> {
try {
@@ -152,20 +164,23 @@ public final class TexturesLoader {
return Optional.empty();
}
})
.flatMap(it -> Optional.ofNullable(it.get(TextureType.SKIN))))
.flatMap(it -> Optional.ofNullable(it.get(TextureType.SKIN)))
.filter(it -> StringUtils.isNotBlank(it.getUrl())))
.asyncMap(it -> {
if (it.isPresent()) {
Texture texture = it.get();
try {
return loadTexture(texture);
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
return getDefaultSkin(TextureModel.detectModelName(texture.getMetadata()));
}
return CompletableFuture.supplyAsync(() -> {
try {
return loadTexture(texture);
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to load texture " + texture.getUrl() + ", using fallback texture", e);
return uuidFallback;
}
}, POOL);
} else {
return uuidFallback;
return CompletableFuture.completedFuture(uuidFallback);
}
}, uuidFallback, POOL);
}, uuidFallback);
}
// ====
@@ -189,7 +204,7 @@ public final class TexturesLoader {
}
public static ObjectBinding<Image> fxAvatarBinding(YggdrasilService service, UUID uuid, int size) {
return MultiStepBinding.of(skinBinding(service, uuid))
return BindingMapping.of(skinBinding(service, uuid))
.map(it -> toAvatar(it.image, size))
.map(img -> SwingFXUtils.toFXImage(img, null));
}
@@ -197,7 +197,7 @@ public final class Accounts {
Account selected = selectedAccount.get();
if (selected != null) {
Schedulers.io().schedule(() -> {
Schedulers.io().execute(() -> {
try {
selected.logIn();
} catch (AuthenticationException e) {
@@ -209,7 +209,7 @@ public final class Accounts {
for (AuthlibInjectorServer server : config().getAuthlibInjectorServers()) {
if (selected instanceof AuthlibInjectorAccount && ((AuthlibInjectorAccount) selected).getServer() == server)
continue;
Schedulers.io().schedule(() -> {
Schedulers.io().execute(() -> {
try {
server.fetchMetadataResponse();
} catch (IOException e) {
@@ -33,8 +33,6 @@ import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.ui.WeakListenerHolder;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.javafx.ImmediateObjectProperty;
import org.jackhuang.hmcl.util.javafx.ImmediateStringProperty;
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
import java.io.File;
@@ -90,9 +88,9 @@ public final class Profile implements Observable {
return global.get();
}
private final ImmediateStringProperty name;
private final SimpleStringProperty name;
public ImmediateStringProperty nameProperty() {
public StringProperty nameProperty() {
return name;
}
@@ -131,8 +129,8 @@ public final class Profile implements Observable {
}
public Profile(String name, File initialGameDir, VersionSetting global, String selectedVersion, boolean useRelativePath) {
this.name = new ImmediateStringProperty(this, "name", name);
gameDir = new ImmediateObjectProperty<>(this, "gameDir", initialGameDir);
this.name = new SimpleStringProperty(this, "name", name);
gameDir = new SimpleObjectProperty<>(this, "gameDir", initialGameDir);
repository = new HMCLGameRepository(this, initialGameDir);
this.global.set(global == null ? new VersionSetting() : global);
this.selectedVersion.set(selectedVersion);
@@ -27,6 +27,7 @@ import javafx.scene.paint.Color;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import java.io.File;
import java.io.IOException;
@@ -132,7 +133,8 @@ public class Theme {
}
public static ObjectBinding<Color> foregroundFillBinding() {
return Bindings.createObjectBinding(() -> config().getTheme().getForegroundColor(), config().themeProperty());
return BindingMapping.of(config().themeProperty())
.map(Theme::getForegroundColor);
}
public static ObjectBinding<Color> blackFillBinding() {
@@ -21,13 +21,18 @@ import com.google.gson.*;
import com.google.gson.annotations.JsonAdapter;
import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.game.LaunchOptions;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.javafx.ImmediateBooleanProperty;
import org.jackhuang.hmcl.util.javafx.ImmediateIntegerProperty;
import org.jackhuang.hmcl.util.javafx.ImmediateObjectProperty;
import org.jackhuang.hmcl.util.javafx.ImmediateStringProperty;
import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@@ -60,9 +65,9 @@ public final class VersionSetting {
this.global = global;
}
private final ImmediateBooleanProperty usesGlobalProperty = new ImmediateBooleanProperty(this, "usesGlobal", false);
private final BooleanProperty usesGlobalProperty = new SimpleBooleanProperty(this, "usesGlobal", false);
public ImmediateBooleanProperty usesGlobalProperty() {
public BooleanProperty usesGlobalProperty() {
return usesGlobalProperty;
}
@@ -84,9 +89,9 @@ public final class VersionSetting {
// java
private final ImmediateStringProperty javaProperty = new ImmediateStringProperty(this, "java", "");
private final StringProperty javaProperty = new SimpleStringProperty(this, "java", "");
public ImmediateStringProperty javaProperty() {
public StringProperty javaProperty() {
return javaProperty;
}
@@ -110,7 +115,7 @@ public final class VersionSetting {
setDefaultJavaPath(null);
}
private final ImmediateStringProperty defaultJavaPathProperty = new ImmediateStringProperty(this, "defaultJavaPath", "");
private final StringProperty defaultJavaPathProperty = new SimpleStringProperty(this, "defaultJavaPath", "");
/**
* Path to Java executable, or null if user customizes java directory.
@@ -124,9 +129,9 @@ public final class VersionSetting {
defaultJavaPathProperty.set(defaultJavaPath);
}
private final ImmediateStringProperty javaDirProperty = new ImmediateStringProperty(this, "javaDir", "");
private final StringProperty javaDirProperty = new SimpleStringProperty(this, "javaDir", "");
public ImmediateStringProperty javaDirProperty() {
public StringProperty javaDirProperty() {
return javaDirProperty;
}
@@ -141,9 +146,9 @@ public final class VersionSetting {
javaDirProperty.set(javaDir);
}
private final ImmediateStringProperty wrapperProperty = new ImmediateStringProperty(this, "wrapper", "");
private final StringProperty wrapperProperty = new SimpleStringProperty(this, "wrapper", "");
public ImmediateStringProperty wrapperProperty() {
public StringProperty wrapperProperty() {
return wrapperProperty;
}
@@ -158,9 +163,9 @@ public final class VersionSetting {
wrapperProperty.set(wrapper);
}
private final ImmediateStringProperty permSizeProperty = new ImmediateStringProperty(this, "permSize", "");
private final StringProperty permSizeProperty = new SimpleStringProperty(this, "permSize", "");
public ImmediateStringProperty permSizeProperty() {
public StringProperty permSizeProperty() {
return permSizeProperty;
}
@@ -175,9 +180,9 @@ public final class VersionSetting {
permSizeProperty.set(permSize);
}
private final ImmediateIntegerProperty maxMemoryProperty = new ImmediateIntegerProperty(this, "maxMemory", OperatingSystem.SUGGESTED_MEMORY);
private final IntegerProperty maxMemoryProperty = new SimpleIntegerProperty(this, "maxMemory", OperatingSystem.SUGGESTED_MEMORY);
public ImmediateIntegerProperty maxMemoryProperty() {
public IntegerProperty maxMemoryProperty() {
return maxMemoryProperty;
}
@@ -195,9 +200,9 @@ public final class VersionSetting {
/**
* The minimum memory that JVM can allocate for heap.
*/
private final ImmediateObjectProperty<Integer> minMemoryProperty = new ImmediateObjectProperty<>(this, "minMemory", null);
private final ObjectProperty<Integer> minMemoryProperty = new SimpleObjectProperty<>(this, "minMemory", null);
public ImmediateObjectProperty<Integer> minMemoryProperty() {
public ObjectProperty<Integer> minMemoryProperty() {
return minMemoryProperty;
}
@@ -209,9 +214,9 @@ public final class VersionSetting {
minMemoryProperty.set(minMemory);
}
private final ImmediateStringProperty preLaunchCommandProperty = new ImmediateStringProperty(this, "precalledCommand", "");
private final StringProperty preLaunchCommandProperty = new SimpleStringProperty(this, "precalledCommand", "");
public ImmediateStringProperty preLaunchCommandProperty() {
public StringProperty preLaunchCommandProperty() {
return preLaunchCommandProperty;
}
@@ -229,9 +234,9 @@ public final class VersionSetting {
// options
private final ImmediateStringProperty javaArgsProperty = new ImmediateStringProperty(this, "javaArgs", "");
private final StringProperty javaArgsProperty = new SimpleStringProperty(this, "javaArgs", "");
public ImmediateStringProperty javaArgsProperty() {
public StringProperty javaArgsProperty() {
return javaArgsProperty;
}
@@ -246,9 +251,9 @@ public final class VersionSetting {
javaArgsProperty.set(javaArgs);
}
private final ImmediateStringProperty minecraftArgsProperty = new ImmediateStringProperty(this, "minecraftArgs", "");
private final StringProperty minecraftArgsProperty = new SimpleStringProperty(this, "minecraftArgs", "");
public ImmediateStringProperty minecraftArgsProperty() {
public StringProperty minecraftArgsProperty() {
return minecraftArgsProperty;
}
@@ -263,9 +268,9 @@ public final class VersionSetting {
minecraftArgsProperty.set(minecraftArgs);
}
private final ImmediateBooleanProperty noJVMArgsProperty = new ImmediateBooleanProperty(this, "noJVMArgs", false);
private final BooleanProperty noJVMArgsProperty = new SimpleBooleanProperty(this, "noJVMArgs", false);
public ImmediateBooleanProperty noJVMArgsProperty() {
public BooleanProperty noJVMArgsProperty() {
return noJVMArgsProperty;
}
@@ -280,9 +285,9 @@ public final class VersionSetting {
noJVMArgsProperty.set(noJVMArgs);
}
private final ImmediateBooleanProperty notCheckJVMProperty = new ImmediateBooleanProperty(this, "notCheckJVM", false);
private final BooleanProperty notCheckJVMProperty = new SimpleBooleanProperty(this, "notCheckJVM", false);
public ImmediateBooleanProperty notCheckJVMProperty() {
public BooleanProperty notCheckJVMProperty() {
return notCheckJVMProperty;
}
@@ -297,9 +302,9 @@ public final class VersionSetting {
notCheckJVMProperty.set(notCheckJVM);
}
private final ImmediateBooleanProperty notCheckGameProperty = new ImmediateBooleanProperty(this, "notCheckGame", false);
private final BooleanProperty notCheckGameProperty = new SimpleBooleanProperty(this, "notCheckGame", false);
public ImmediateBooleanProperty notCheckGameProperty() {
public BooleanProperty notCheckGameProperty() {
return notCheckGameProperty;
}
@@ -314,9 +319,9 @@ public final class VersionSetting {
notCheckGameProperty.set(notCheckGame);
}
private final ImmediateBooleanProperty showLogsProperty = new ImmediateBooleanProperty(this, "showLogs", false);
private final BooleanProperty showLogsProperty = new SimpleBooleanProperty(this, "showLogs", false);
public ImmediateBooleanProperty showLogsProperty() {
public BooleanProperty showLogsProperty() {
return showLogsProperty;
}
@@ -333,14 +338,14 @@ public final class VersionSetting {
// Minecraft settings.
private final ImmediateStringProperty serverIpProperty = new ImmediateStringProperty(this, "serverIp", "");
private final StringProperty serverIpProperty = new SimpleStringProperty(this, "serverIp", "");
public ImmediateStringProperty serverIpProperty() {
public StringProperty serverIpProperty() {
return serverIpProperty;
}
/**
* The server ip that will be entered after Minecraft successfully loaded immediately.
* The server ip that will be entered after Minecraft successfully loaded ly.
*
* Format: ip:port or without port.
*/
@@ -353,9 +358,9 @@ public final class VersionSetting {
}
private final ImmediateBooleanProperty fullscreenProperty = new ImmediateBooleanProperty(this, "fullscreen", false);
private final BooleanProperty fullscreenProperty = new SimpleBooleanProperty(this, "fullscreen", false);
public ImmediateBooleanProperty fullscreenProperty() {
public BooleanProperty fullscreenProperty() {
return fullscreenProperty;
}
@@ -370,9 +375,9 @@ public final class VersionSetting {
fullscreenProperty.set(fullscreen);
}
private final ImmediateIntegerProperty widthProperty = new ImmediateIntegerProperty(this, "width", 854);
private final IntegerProperty widthProperty = new SimpleIntegerProperty(this, "width", 854);
public ImmediateIntegerProperty widthProperty() {
public IntegerProperty widthProperty() {
return widthProperty;
}
@@ -392,9 +397,9 @@ public final class VersionSetting {
}
private final ImmediateIntegerProperty heightProperty = new ImmediateIntegerProperty(this, "height", 480);
private final IntegerProperty heightProperty = new SimpleIntegerProperty(this, "height", 480);
public ImmediateIntegerProperty heightProperty() {
public IntegerProperty heightProperty() {
return heightProperty;
}
@@ -417,9 +422,9 @@ public final class VersionSetting {
* 0 - .minecraft<br/>
* 1 - .minecraft/versions/&lt;version&gt;/<br/>
*/
private final ImmediateObjectProperty<EnumGameDirectory> gameDirTypeProperty = new ImmediateObjectProperty<>(this, "gameDirType", EnumGameDirectory.ROOT_FOLDER);
private final ObjectProperty<EnumGameDirectory> gameDirTypeProperty = new SimpleObjectProperty<>(this, "gameDirType", EnumGameDirectory.ROOT_FOLDER);
public ImmediateObjectProperty<EnumGameDirectory> gameDirTypeProperty() {
public ObjectProperty<EnumGameDirectory> gameDirTypeProperty() {
return gameDirTypeProperty;
}
@@ -434,9 +439,9 @@ public final class VersionSetting {
/**
* Your custom gameDir
*/
private final ImmediateStringProperty gameDirProperty = new ImmediateStringProperty(this, "gameDir", "");
private final StringProperty gameDirProperty = new SimpleStringProperty(this, "gameDir", "");
public ImmediateStringProperty gameDirProperty() {
public StringProperty gameDirProperty() {
return gameDirProperty;
}
@@ -455,9 +460,9 @@ public final class VersionSetting {
* 1 - Hide the launcher when the game starts.<br/>
* 2 - Keep the launcher open.<br/>
*/
private final ImmediateObjectProperty<LauncherVisibility> launcherVisibilityProperty = new ImmediateObjectProperty<>(this, "launcherVisibility", LauncherVisibility.HIDE);
private final ObjectProperty<LauncherVisibility> launcherVisibilityProperty = new SimpleObjectProperty<>(this, "launcherVisibility", LauncherVisibility.HIDE);
public ImmediateObjectProperty<LauncherVisibility> launcherVisibilityProperty() {
public ObjectProperty<LauncherVisibility> launcherVisibilityProperty() {
return launcherVisibilityProperty;
}
@@ -35,6 +35,7 @@ import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.account.AccountList;
import org.jackhuang.hmcl.ui.account.AuthlibInjectorServersPage;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.decorator.DecoratorController;
import org.jackhuang.hmcl.ui.download.ModpackInstallWizardProvider;
import org.jackhuang.hmcl.ui.profile.ProfileList;
@@ -45,7 +46,7 @@ import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.FutureCallback;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.javafx.MultiStepBinding;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -158,7 +159,7 @@ public final class Controllers {
});
mainPage.showUpdateProperty().bind(UpdateChecker.outdatedProperty());
mainPage.latestVersionProperty().bind(
MultiStepBinding.of(UpdateChecker.latestVersionProperty())
BindingMapping.of(UpdateChecker.latestVersionProperty())
.map(version -> version == null ? "" : i18n("update.bubble.title", version.getVersion())));
Profiles.registerVersionsListener(profile -> {
@@ -204,7 +205,7 @@ public final class Controllers {
dialog(i18n("launcher.cache_directory.invalid"));
}
Task.of(JavaVersion::initialize).start();
Task.runAsync(JavaVersion::initialize).start();
scene = new Scene(decorator.getDecorator(), 800, 519);
scene.getStylesheets().setAll(config().getTheme().getStylesheets());
@@ -223,14 +224,14 @@ public final class Controllers {
}
public static void dialog(String text, String title) {
dialog(text, title, MessageBox.INFORMATION_MESSAGE);
dialog(text, title, MessageType.INFORMATION);
}
public static void dialog(String text, String title, int type) {
public static void dialog(String text, String title, MessageType type) {
dialog(text, title, type, null);
}
public static void dialog(String text, String title, int type, Runnable onAccept) {
public static void dialog(String text, String title, MessageType type, Runnable onAccept) {
dialog(new MessageDialogPane(text, title, type, onAccept));
}
@@ -20,17 +20,18 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.utils.JFXUtilities;
import org.jackhuang.hmcl.auth.Account;
import org.jackhuang.hmcl.auth.AuthInfo;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
import org.jackhuang.hmcl.task.SilentException;
import org.jackhuang.hmcl.ui.account.AccountLoginPane;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public final class DialogController {
public static AuthInfo logIn(Account account) throws Exception {
public static AuthInfo logIn(Account account) throws CancellationException, AuthenticationException, InterruptedException {
if (account instanceof YggdrasilAccount) {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<AuthInfo> res = new AtomicReference<>(null);
@@ -42,7 +43,7 @@ public final class DialogController {
Controllers.dialog(pane);
});
latch.await();
return Optional.ofNullable(res.get()).orElseThrow(SilentException::new);
return Optional.ofNullable(res.get()).orElseThrow(CancellationException::new);
}
return account.logIn();
}
@@ -48,6 +48,7 @@ import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.javafx.ExtendedProperties;
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.File;
@@ -331,7 +332,7 @@ public final class FXUtils {
}
public static void bindInt(JFXTextField textField, Property<Number> property) {
textField.textProperty().bindBidirectional(property, SafeIntStringConverter.INSTANCE);
textField.textProperty().bindBidirectional(property, SafeStringConverter.fromInteger());
}
public static void unbindInt(JFXTextField textField, Property<Number> property) {
@@ -372,7 +373,7 @@ public final class FXUtils {
*/
@SuppressWarnings("unchecked")
@Deprecated
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum> property) {
public static void bindEnum(JFXComboBox<?> comboBox, Property<? extends Enum<?>> property) {
unbindEnum(comboBox);
ChangeListener<Number> listener = (a, b, newValue) ->
((Property) property).setValue(property.getValue().getClass().getEnumConstants()[newValue.intValue()]);
@@ -510,6 +511,7 @@ public final class FXUtils {
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(graphicBuilder.apply(item));
}
}
@@ -25,7 +25,6 @@ import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshedVersionsEvent;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.ModpackHelper;
import org.jackhuang.hmcl.mod.Modpack;
import org.jackhuang.hmcl.setting.Accounts;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
@@ -119,15 +118,14 @@ public final class LeftPaneController extends AdvancedListBox {
if (repository.getVersionCount() == 0) {
File modpackFile = new File("modpack.zip").getAbsoluteFile();
if (modpackFile.exists()) {
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
.thenResult(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenResult(modpack -> {
Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath()))
.thenApply(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding))
.thenAccept(modpack -> {
AtomicReference<Region> region = new AtomicReference<>();
TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack)
.with(Task.of(Schedulers.javafx(), this::checkAccount)).executor();
.withRun(Schedulers.javafx(), this::checkAccount).executor();
region.set(Controllers.taskDialog(executor, i18n("modpack.installing")));
executor.start();
return null;
}).start();
}
}
@@ -201,11 +201,11 @@ public final class LogWindow extends Stage {
if (!flag)
cboLines.getSelectionModel().select(0);
btnFatals.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(fatal.get()) + " fatals", fatal));
btnErrors.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(error.get()) + " errors", error));
btnWarns.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(warn.get()) + " warns", warn));
btnInfos.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(info.get()) + " infos", info));
btnDebugs.textProperty().bind(Bindings.createStringBinding(() -> Integer.toString(debug.get()) + " debugs", debug));
btnFatals.textProperty().bind(Bindings.concat(fatal, " fatals"));
btnErrors.textProperty().bind(Bindings.concat(error, " errors"));
btnWarns.textProperty().bind(Bindings.concat(warn, " warns"));
btnInfos.textProperty().bind(Bindings.concat(info, " infos"));
btnDebugs.textProperty().bind(Bindings.concat(debug, " debugs"));
btnFatals.selectedProperty().addListener(o -> specificChanged());
btnErrors.selectedProperty().addListener(o -> specificChanged());
@@ -33,7 +33,6 @@ import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import org.jackhuang.hmcl.setting.Profile;
@@ -1,43 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui;
import javafx.util.StringConverter;
import org.jackhuang.hmcl.util.Lang;
import java.util.Optional;
/**
* @author huangyuhui
*/
public final class SafeIntStringConverter extends StringConverter<Number> {
public static final SafeIntStringConverter INSTANCE = new SafeIntStringConverter();
private SafeIntStringConverter() {
}
@Override
public Number fromString(String string) {
return Optional.ofNullable(string).map(Lang::toIntOrNull).orElse(null);
}
@Override
public String toString(Number object) {
return Optional.ofNullable(object).map(Object::toString).orElse("");
}
}
@@ -31,7 +31,7 @@ import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.setting.*;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.upgrade.RemoteVersion;
@@ -228,7 +228,7 @@ public final class SettingsPage extends SettingsView implements DecoratorPage {
try {
Files.write(logFile, Logging.getRawLogs());
} catch (IOException e) {
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.failed") + "\n" + e, null, MessageBox.ERROR_MESSAGE));
Platform.runLater(() -> Controllers.dialog(i18n("settings.launcher.launcher_log.export.failed") + "\n" + e, null, MessageType.ERROR));
LOG.log(Level.WARNING, "Failed to export logs", e);
return;
}
@@ -37,6 +37,7 @@ import static org.jackhuang.hmcl.util.Lang.thread;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
public class AccountListItem extends RadioButton {
@@ -84,6 +85,8 @@ public class AccountListItem extends RadioButton {
} catch (CredentialExpiredException e) {
try {
DialogController.logIn(account);
} catch (CancellationException e1) {
// ignore cancellation
} catch (Exception e1) {
LOG.log(Level.WARNING, "Failed to refresh " + account + " with password", e1);
}
@@ -22,7 +22,6 @@ import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXRadioButton;
import com.jfoenix.effects.JFXDepthManager;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
@@ -35,6 +34,7 @@ import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -75,7 +75,7 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
if (skinnable.getAccount() instanceof AuthlibInjectorAccount) {
Tooltip tooltip = new Tooltip();
AuthlibInjectorServer server = ((AuthlibInjectorAccount) skinnable.getAccount()).getServer();
tooltip.textProperty().bind(Bindings.createStringBinding(server::toString, server));
tooltip.textProperty().bind(BindingMapping.of(server, AuthlibInjectorServer::toString));
FXUtils.installSlowTooltip(subtitle, tooltip);
}
VBox item = new VBox(title, subtitle);
@@ -61,8 +61,8 @@ public class AccountLoginPane extends StackPane {
String password = txtPassword.getText();
progressBar.setVisible(true);
lblCreationWarning.setText("");
Task.ofResult(() -> oldAccount.logInWithPassword(password))
.finalizedResult(Schedulers.javafx(), authInfo -> {
Task.supplyAsync(() -> oldAccount.logInWithPassword(password))
.whenComplete(Schedulers.javafx(), authInfo -> {
success.accept(authInfo);
fireEvent(new DialogCloseEvent());
progressBar.setVisible(false);
@@ -47,7 +47,7 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.util.javafx.MultiStepBinding;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import java.util.ArrayList;
import java.util.List;
@@ -126,7 +126,7 @@ public class AddAccountPane extends StackPane {
cboServers.getSelectionModel().selectedItemProperty(), cboServers.visibleProperty()));
// authlib-injector links
links.bind(MultiStepBinding.of(cboServers.getSelectionModel().selectedItemProperty())
links.bind(BindingMapping.of(cboServers.getSelectionModel().selectedItemProperty())
.map(AddAccountPane::createHyperlinks)
.map(FXCollections::observableList));
Bindings.bindContent(linksContainer.getChildren(), links);
@@ -195,8 +195,8 @@ public class AddAccountPane extends StackPane {
AccountFactory<?> factory = cboType.getSelectionModel().getSelectedItem();
Object additionalData = getAuthAdditionalData();
Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData))
.finalizedResult(Schedulers.javafx(), account -> {
Task.supplyAsync(() -> factory.create(new Selector(), username, password, additionalData))
.whenComplete(Schedulers.javafx(), account -> {
int oldIndex = Accounts.getAccounts().indexOf(account);
if (oldIndex == -1) {
Accounts.getAccounts().add(account);
@@ -104,13 +104,13 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
nextPane.showSpinner();
addServerPane.setDisable(true);
Task.of(() -> {
Task.runAsync(() -> {
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
}).whenComplete(Schedulers.javafx(), exception -> {
addServerPane.setDisable(false);
nextPane.hideSpinner();
if (isDependentsSucceeded) {
if (exception == null) {
lblServerName.setText(serverBeingAdded.getName());
lblServerUrl.setText(serverBeingAdded.getUrl());
@@ -118,7 +118,6 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
transitionHandler.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT.getAnimationProducer());
} else {
Exception exception = variables.get("lastException");
LOG.log(Level.WARNING, "Failed to resolve auth server: " + url, exception);
lblCreationWarning.setText(resolveFetchExceptionMessage(exception));
}
@@ -21,7 +21,7 @@ import static javafx.collections.FXCollections.emptyObservableList;
import static javafx.collections.FXCollections.observableList;
import static javafx.collections.FXCollections.singletonObservableList;
import org.jackhuang.hmcl.util.javafx.MultiStepBinding;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXListCell;
@@ -49,7 +49,7 @@ public class FontComboBox extends JFXComboBox<String> {
}
});
itemsProperty().bind(MultiStepBinding.of(valueProperty())
itemsProperty().bind(BindingMapping.of(valueProperty())
.map(value -> value == null ? emptyObservableList() : singletonObservableList(value)));
setOnMouseClicked(e -> {
@@ -17,7 +17,6 @@
*/
package org.jackhuang.hmcl.ui.construct;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
@@ -1,172 +0,0 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2019 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.construct;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextInputDialog;
import org.jackhuang.hmcl.ui.FXUtils;
import javax.swing.*;
import java.util.Optional;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class MessageBox {
private MessageBox() {
}
private static final String TITLE = i18n("message.info");
/**
* User Operation: Yes
*/
public static final int YES_OPTION = 0;
/**
* User Operation: No
*/
public static final int NO_OPTION = 1;
/**
* User Operation: Cancel
*/
public static final int CANCEL_OPTION = 2;
/**
* User Operation: OK
*/
public static final int OK_OPTION = 0;
/**
* User Operation: Closed Message Box
*/
public static final int CLOSED_OPTION = -1;
/**
* Buttons: Yes No
*/
public static final int YES_NO_OPTION = 10;
/**
* Buttons: Yes No Cancel
*/
public static final int YES_NO_CANCEL_OPTION = 11;
/**
* Buttons: OK Cancel
*/
public static final int OK_CANCEL_OPTION = 12;
/**
* Message Box Type: Error
*/
public static final int ERROR_MESSAGE = 0;
/**
* Message Box Type: Info
*/
public static final int INFORMATION_MESSAGE = 1;
/**
* Message Box Type: Warning
*/
public static final int WARNING_MESSAGE = 2;
/**
* Message Box Type: Question
*/
public static final int QUESTION_MESSAGE = 3;
/**
* Message Box Type: Fine
*/
public static final int FINE_MESSAGE = 4;
/**
* Message Box Type: Plain
*/
public static final int PLAIN_MESSAGE = -1;
public static void show(String message) {
show(message, TITLE);
}
/**
* Show MsgBox with title and options
*
* @param message The Message
* @param title The title of MsgBox.
* @return user operation.
*/
public static Optional<ButtonType> show(String message, String title) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(title);
alert.setHeaderText(title);
alert.setContentText(message);
return alert.showAndWait();
}
public static int confirm(String message, String title) {
return confirm(message, title, -1);
}
public static int confirm(String message, int option) {
return confirm(message, TITLE, option);
}
public static int confirm(String message, String title, int option) {
return FXUtils.runInUIThread(() -> {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle(title);
alert.setHeaderText(title);
alert.setContentText(message);
switch (option) {
case YES_NO_OPTION:
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);
break;
case YES_NO_CANCEL_OPTION:
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);
break;
case OK_CANCEL_OPTION:
alert.getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
break;
default:
throw new IllegalArgumentException("Unrecognized message box option " + option);
}
Optional<ButtonType> buttonType = alert.showAndWait();
if (!buttonType.isPresent()) return CLOSED_OPTION;
else if (buttonType.get() == ButtonType.OK) return OK_OPTION;
else if (buttonType.get() == ButtonType.YES) return YES_OPTION;
else if (buttonType.get() == ButtonType.NO) return NO_OPTION;
else if (buttonType.get() == ButtonType.CANCEL) return CANCEL_OPTION;
else throw new IllegalStateException("Unexpected button type:" + buttonType.get());
});
}
public static Optional<String> input(String message) {
return input(message, UIManager.getString("OptionPane.inputDialogTitle"));
}
public static Optional<String> input(String message, String title) {
return input(message, title, "");
}
public static Optional<String> input(String message, String title, String initialValue) {
TextInputDialog dialog = new TextInputDialog(initialValue);
dialog.setTitle(title);
dialog.setHeaderText(message);
dialog.setContentText(message);
return dialog.showAndWait();
}
}
@@ -32,6 +32,15 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class MessageDialogPane extends StackPane {
public static enum MessageType {
ERROR,
INFORMATION,
WARNING,
QUESTION,
FINE,
PLAIN,
}
@FXML
private JFXButton acceptButton;
@FXML
@@ -45,7 +54,7 @@ public final class MessageDialogPane extends StackPane {
@FXML
private HBox actions;
public MessageDialogPane(String text, String title, int type, Runnable onAccept) {
public MessageDialogPane(String text, String title, MessageType type, Runnable onAccept) {
FXUtils.loadFXML(this, "/assets/fxml/message-dialog.fxml");
if (title != null)
@@ -60,19 +69,19 @@ public final class MessageDialogPane extends StackPane {
actions.getChildren().remove(cancelButton);
switch (type) {
case MessageBox.INFORMATION_MESSAGE:
case INFORMATION:
graphic.setGraphic(SVG.infoCircle(Theme.blackFillBinding(), 40, 40));
break;
case MessageBox.ERROR_MESSAGE:
case ERROR:
graphic.setGraphic(SVG.closeCircle(Theme.blackFillBinding(), 40, 40));
break;
case MessageBox.FINE_MESSAGE:
case FINE:
graphic.setGraphic(SVG.checkCircle(Theme.blackFillBinding(), 40, 40));
break;
case MessageBox.WARNING_MESSAGE:
case WARNING:
graphic.setGraphic(SVG.alert(Theme.blackFillBinding(), 40, 40));
break;
case MessageBox.QUESTION_MESSAGE:
case QUESTION:
graphic.setGraphic(SVG.helpCircle(Theme.blackFillBinding(), 40, 40));
break;
default:
@@ -81,7 +90,7 @@ public final class MessageDialogPane extends StackPane {
}
public MessageDialogPane(String text, String title, Runnable onAccept, Runnable onCancel) {
this(text, title, MessageBox.QUESTION_MESSAGE, onAccept);
this(text, title, MessageType.QUESTION, onAccept);
cancelButton.setVisible(true);
cancelButton.setOnMouseClicked(e -> {
@@ -42,7 +42,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public final class TaskListPane extends StackPane {
private final AdvancedListBox listBox = new AdvancedListBox();
private final Map<Task, ProgressListNode> nodes = new HashMap<>();
private final Map<Task<?>, ProgressListNode> nodes = new HashMap<>();
private final ReadOnlyIntegerWrapper finishedTasks = new ReadOnlyIntegerWrapper();
private final ReadOnlyIntegerWrapper totTasks = new ReadOnlyIntegerWrapper();
@@ -72,12 +72,12 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onReady(Task task) {
public void onReady(Task<?> task) {
Platform.runLater(() -> totTasks.set(totTasks.getValue() + 1));
}
@Override
public void onRunning(Task task) {
public void onRunning(Task<?> task) {
if (!task.getSignificance().shouldShow())
return;
@@ -113,7 +113,7 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onFinished(Task task) {
public void onFinished(Task<?> task) {
ProgressListNode node = nodes.remove(task);
if (node == null)
return;
@@ -125,7 +125,7 @@ public final class TaskListPane extends StackPane {
}
@Override
public void onFailed(Task task, Throwable throwable) {
public void onFailed(Task<?> task, Throwable throwable) {
ProgressListNode node = nodes.remove(task);
if (node == null)
return;
@@ -142,7 +142,7 @@ public final class TaskListPane extends StackPane {
private final Label title = new Label();
private final Label state = new Label();
public ProgressListNode(Task task) {
public ProgressListNode(Task<?> task) {
bar.progressProperty().bind(task.progressProperty());
title.setText(task.getName());
state.textProperty().bind(task.messageProperty());
@@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.collections.ListChangeListener;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -56,7 +55,6 @@ public class DecoratorSkin extends SkinBase<Decorator> {
private double xOffset, yOffset, newX, newY, initX, initY;
private boolean allowMove, isDragging;
private BoundingBox originalBox, maximizedBox;
/**
* Constructor for all SkinBase instances.
@@ -28,7 +28,7 @@ import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.DownloadException;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
import org.jackhuang.hmcl.util.StringUtils;
@@ -89,18 +89,18 @@ public final class InstallerWizardProvider implements WizardProvider {
settings.put("success_message", i18n("install.success"));
settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next));
Task ret = Task.ofResult("version", () -> version);
Task<Version> ret = Task.supplyAsync(() -> version);
if (settings.containsKey("forge"))
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge")));
if (settings.containsKey("liteloader"))
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader")));
if (settings.containsKey("optifine"))
ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
ret = ret.thenCompose(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine")));
return ret.then(profile.getRepository().refreshVersionsAsync());
return ret.thenCompose(profile.getRepository().refreshVersionsAsync());
}
@Override
@@ -121,11 +121,11 @@ public final class InstallerWizardProvider implements WizardProvider {
public static void alertFailureMessage(Exception exception, Runnable next) {
if (exception instanceof LibraryDownloadException) {
Controllers.dialog(i18n("launch.failed.download_library", ((LibraryDownloadException) exception).getLibrary().getName()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageBox.ERROR_MESSAGE, next);
Controllers.dialog(i18n("launch.failed.download_library", ((LibraryDownloadException) exception).getLibrary().getName()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageType.ERROR, next);
} else if (exception instanceof DownloadException) {
Controllers.dialog(i18n("install.failed.downloading.detail", ((DownloadException) exception).getUrl()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageBox.ERROR_MESSAGE, next);
Controllers.dialog(i18n("install.failed.downloading.detail", ((DownloadException) exception).getUrl()) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageType.ERROR, next);
} else {
Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageBox.ERROR_MESSAGE, next);
Controllers.dialog(StringUtils.getStackTrace(exception), i18n("install.failed"), MessageType.ERROR, next);
}
}
}
@@ -27,7 +27,7 @@ import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.wizard.WizardController;
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
@@ -71,7 +71,7 @@ public class ModpackInstallWizardProvider implements WizardProvider {
settings.put(PROFILE, profile);
}
private Task finishModpackInstallingAsync(Map<String, Object> settings) {
private Task<Void> finishModpackInstallingAsync(Map<String, Object> settings) {
if (!settings.containsKey(ModpackPage.MODPACK_FILE))
return null;
@@ -84,16 +84,16 @@ public class ModpackInstallWizardProvider implements WizardProvider {
try {
return ModpackHelper.getUpdateTask(profile, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name)));
} catch (UnsupportedModpackException e) {
Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageType.ERROR);
} catch (MismatchedModpackTypeException e) {
Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(i18n("modpack.mismatched_type"), i18n("message.error"), MessageType.ERROR);
} catch (IOException e) {
Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(i18n("modpack.invalid"), i18n("message.error"), MessageType.ERROR);
}
return null;
} else {
return ModpackHelper.getInstallTask(profile, selected, name, modpack)
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
.thenRun(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
}
}
@@ -105,9 +105,9 @@ public class ModpackInstallWizardProvider implements WizardProvider {
public void onFail(Map<String, Object> settings, Exception exception, Runnable next) {
if (exception instanceof CurseCompletionException) {
if (exception.getCause() instanceof FileNotFoundException) {
Controllers.dialog(i18n("modpack.type.curse.not_found"), i18n("install.failed"), MessageBox.ERROR_MESSAGE, next);
Controllers.dialog(i18n("modpack.type.curse.not_found"), i18n("install.failed"), MessageType.ERROR, next);
} else {
Controllers.dialog(i18n("modpack.type.curse.tolerable_error"), i18n("install.success"), MessageBox.INFORMATION_MESSAGE, next);
Controllers.dialog(i18n("modpack.type.curse.tolerable_error"), i18n("install.success"), MessageType.INFORMATION, next);
}
} else {
InstallerWizardProvider.alertFailureMessage(exception, next);
@@ -33,7 +33,7 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.WebStage;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.Validator;
import org.jackhuang.hmcl.ui.wizard.WizardController;
@@ -109,9 +109,9 @@ public final class ModpackPage extends StackPane implements WizardPage {
}
spinnerPane.showSpinner();
Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
.thenResult(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
.finalizedResult(Schedulers.javafx(), manifest -> {
Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath()))
.thenApply(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding))
.whenComplete(Schedulers.javafx(), manifest -> {
spinnerPane.hideSpinner();
controller.getSettings().put(MODPACK_MANIFEST, manifest);
lblName.setText(manifest.getName());
@@ -129,7 +129,7 @@ public final class ModpackPage extends StackPane implements WizardPage {
txtModpackName.textProperty().addListener(e -> btnInstall.setDisable(!txtModpackName.validate()));
}
}, e -> {
Controllers.dialog(i18n("modpack.task.install.error"), i18n("message.error"), MessageBox.ERROR_MESSAGE);
Controllers.dialog(i18n("modpack.task.install.error"), i18n("message.error"), MessageType.ERROR);
Platform.runLater(controller::onEnd);
}).start();
}
@@ -43,7 +43,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
settings.put(PROFILE, profile);
}
private Task finishVersionDownloadingAsync(Map<String, Object> settings) {
private Task<Void> finishVersionDownloadingAsync(Map<String, Object> settings) {
GameBuilder builder = profile.getDependency().gameBuilder();
String name = (String) settings.get("name");
@@ -59,8 +59,8 @@ public final class VanillaInstallWizardProvider implements WizardProvider {
if (settings.containsKey("optifine"))
builder.version((RemoteVersion) settings.get("optifine"));
return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions())
.then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name)));
return builder.buildAsync().whenComplete(any -> profile.getRepository().refreshVersions())
.thenRun(Schedulers.javafx(), () -> profile.setSelectedVersion(name));
}
@Override
@@ -128,8 +128,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
@Override
public void refresh() {
transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer());
executor = versionList.refreshAsync(gameVersion, downloadProvider).finalized((variables, isDependentsSucceeded) -> {
if (isDependentsSucceeded) {
executor = versionList.refreshAsync(gameVersion, downloadProvider).whenComplete(exception -> {
if (exception == null) {
List<VersionsPageItem> items = loadVersions();
Platform.runLater(() -> {
@@ -147,7 +147,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
}
});
} else {
LOG.log(Level.WARNING, "Failed to fetch versions list", (Throwable) variables.get("lastException"));
LOG.log(Level.WARNING, "Failed to fetch versions list", exception);
Platform.runLater(() -> {
transitionHandler.setContent(failedPane, ContainerAnimations.FADE.getAnimationProducer());
});
@@ -60,8 +60,8 @@ public final class ExportWizardProvider implements WizardProvider {
List<File> launcherJar = Launcher.getCurrentJarFiles();
boolean includeLauncher = (Boolean) settings.get(ModpackInfoPage.MODPACK_INCLUDE_LAUNCHER) && launcherJar != null;
return new Task() {
Task dependency = null;
return new Task<Void>() {
Task<?> dependency = null;
@Override
public void execute() throws Exception {
@@ -80,7 +80,7 @@ public final class ExportWizardProvider implements WizardProvider {
), tempModpack);
if (includeLauncher) {
dependency = dependency.then(Task.of(() -> {
dependency = dependency.thenRun(() -> {
try (Zipper zip = new Zipper(modpackFile.toPath())) {
Config exported = new Config();
@@ -109,12 +109,12 @@ public final class ExportWizardProvider implements WizardProvider {
for (File jar : launcherJar)
zip.putFile(jar, jar.getName());
}
}));
});
}
}
@Override
public Collection<? extends Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency);
}
};
@@ -72,7 +72,7 @@ public class ProfileListItemSkin extends SkinBase<ProfileListItem> {
right.getChildren().add(btnRemove);
root.setRight(right);
root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 0;");
root.setStyle("-fx-background-color: white; -fx-padding: 8 8 8 12;");
JFXDepthManager.setDepth(root, 1);
item.titleProperty().bind(skinnable.titleProperty());
item.subtitleProperty().bind(skinnable.subtitleProperty());
@@ -26,11 +26,9 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.FileItem;
import org.jackhuang.hmcl.ui.construct.PageCloseEvent;
@@ -38,7 +36,6 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.File;
import java.nio.file.Paths;
import java.util.Optional;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
@@ -25,11 +26,20 @@ import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.setting.Profile;
import static org.jackhuang.hmcl.util.Lang.handleUncaught;
import static org.jackhuang.hmcl.util.Lang.threadPool;
import static org.jackhuang.hmcl.util.StringUtils.removePrefix;
import static org.jackhuang.hmcl.util.StringUtils.removeSuffix;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class GameItem extends Control {
private static final ThreadPoolExecutor POOL_VERSION_RESOLVE = threadPool("VersionResolve", true, 1, 1, TimeUnit.SECONDS);
private final Profile profile;
private final String version;
private final StringProperty title = new SimpleStringProperty();
@@ -40,16 +50,19 @@ public class GameItem extends Control {
this.profile = profile;
this.version = id;
String game = GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse("Unknown");
StringBuilder libraries = new StringBuilder(game);
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getVersion(id));
analyzer.getForge().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.forge")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)forge", ""))));
analyzer.getLiteLoader().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.liteloader")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)liteloader", ""))));
analyzer.getOptiFine().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.optifine")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)optifine", ""))));
// GameVersion.minecraftVersion() is a time-costing job (up to ~200 ms)
CompletableFuture.supplyAsync(() -> GameVersion.minecraftVersion(profile.getRepository().getVersionJar(id)).orElse("Unknown"), POOL_VERSION_RESOLVE)
.thenAcceptAsync(game -> {
StringBuilder libraries = new StringBuilder(game);
LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(profile.getRepository().getVersion(id));
analyzer.getForge().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.forge")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)forge", ""))));
analyzer.getLiteLoader().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.liteloader")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)liteloader", ""))));
analyzer.getOptiFine().ifPresent(library -> libraries.append(", ").append(i18n("install.installer.optifine")).append(": ").append(modifyVersion(game, library.getVersion().replaceAll("(?i)optifine", ""))));
subtitle.set(libraries.toString());
}, Platform::runLater)
.exceptionally(handleUncaught);
title.set(id);
subtitle.set(libraries.toString());
image.set(profile.getRepository().getVersionIconImage(version));
}
@@ -25,7 +25,6 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.InstallerItem;
import org.jackhuang.hmcl.ui.ListPage;
@@ -54,9 +53,9 @@ public class InstallerListPage extends ListPage<InstallerItem> {
LinkedList<Library> newList = new LinkedList<>(version.getLibraries());
newList.remove(library);
new MaintainTask(version.setLibraries(newList))
.then(variables -> new VersionJsonSaveTask(profile.getRepository(), variables.get(MaintainTask.ID)))
.with(profile.getRepository().refreshVersionsAsync())
.with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId)))
.thenCompose(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion))
.withCompose(profile.getRepository().refreshVersionsAsync())
.withRun(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))
.start();
};
@@ -36,7 +36,6 @@ import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.ListPage;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -86,15 +85,15 @@ public final class ModListPage extends Control {
public void loadMods(ModManager modManager) {
this.modManager = modManager;
Task.ofResult("list", variables -> {
Task.supplyAsync(() -> {
synchronized (ModListPage.this) {
JFXUtilities.runInFX(() -> loadingProperty().set(true));
modManager.refreshMods();
return new LinkedList<>(modManager.getMods());
}
}).finalizedResult(Schedulers.javafx(), (list, isDependentsSucceeded) -> {
}).whenComplete(Schedulers.javafx(), (list, exception) -> {
loadingProperty().set(false);
if (isDependentsSucceeded)
if (exception == null)
FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> {
if (newValue != null && newValue.getUserData() == ModListPage.this)
itemsProperty().setAll(list.stream().map(ModListPageSkin.ModInfoObject::new).collect(Collectors.toList()));
@@ -112,7 +111,7 @@ public final class ModListPage extends Control {
List<String> succeeded = new LinkedList<>();
List<String> failed = new LinkedList<>();
if (res == null) return;
Task.of(variables -> {
Task.runAsync(() -> {
for (File file : res) {
try {
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.
}
}
}).with(Task.of(Schedulers.javafx(), variables -> {
}).withRun(Schedulers.javafx(), () -> {
List<String> prompt = new LinkedList<>();
if (!succeeded.isEmpty())
prompt.add(i18n("mods.add.success", String.join(", ", succeeded)));
@@ -132,7 +131,7 @@ public final class ModListPage extends Control {
prompt.add(i18n("mods.add.failed", String.join(", ", failed)));
Controllers.dialog(String.join("\n", prompt), i18n("mods.add"));
loadMods(modManager);
})).start();
}).start();
}
public void setParentTab(JFXTabPane parentTab) {
@@ -57,7 +57,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -71,7 +71,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private Profile profile;
private String versionId;
private boolean javaItemsLoaded;
private InvalidationListener specificSettingsListener;
@FXML private VBox rootPane;
@FXML private ScrollPane scroll;
@@ -100,6 +99,12 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
@FXML private JFXCheckBox chkEnableSpecificSettings;
@FXML private BorderPane settingsTypePane;
private InvalidationListener specificSettingsListener = any -> {
chkEnableSpecificSettings.setSelected(!lastVersionSetting.isUsesGlobal());
};
private InvalidationListener javaListener = any -> initJavaSubtitle();
public VersionSettingsPage() {
FXUtils.loadFXML(this, "/assets/fxml/version/version-settings.fxml");
@@ -113,14 +118,13 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.smoothScrolling(scroll);
Task.ofResult(JavaVersion::getJavas).thenResult(Schedulers.javafx(), list -> {
Task.supplyAsync(JavaVersion::getJavas).thenAccept(Schedulers.javafx(), list -> {
javaItem.loadChildren(list.stream()
.map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit",
javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion))
.collect(Collectors.toList()));
javaItemsLoaded = true;
initializeSelectedJava();
return null;
}).start();
javaItem.setSelectedData(null);
@@ -148,10 +152,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
Platform.runLater(() -> loadVersion(profile, versionId));
});
specificSettingsListener = o -> {
chkEnableSpecificSettings.setSelected(!lastVersionSetting.isUsesGlobal());
};
componentList.disableProperty().bind(chkEnableSpecificSettings.selectedProperty().not());
advancedSettingsPane.disableProperty().bind(chkEnableSpecificSettings.selectedProperty().not());
}
@@ -193,6 +193,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.unbindEnum(cboLauncherVisibility);
lastVersionSetting.usesGlobalProperty().removeListener(specificSettingsListener);
lastVersionSetting.javaDirProperty().removeListener(javaListener);
lastVersionSetting.javaProperty().removeListener(javaListener);
gameDirItem.selectedDataProperty().unbindBidirectional(lastVersionSetting.gameDirTypeProperty());
gameDirItem.subtitleProperty().unbind();
@@ -232,9 +234,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
}
});
versionSetting.javaDirProperty().setChangedListener(it -> initJavaSubtitle(versionSetting));
versionSetting.javaProperty().setChangedListener(it -> initJavaSubtitle(versionSetting));
initJavaSubtitle(versionSetting);
versionSetting.javaDirProperty().addListener(javaListener);
versionSetting.javaProperty().addListener(javaListener);
gameDirItem.selectedDataProperty().bindBidirectional(versionSetting.gameDirTypeProperty());
gameDirItem.subtitleProperty().bind(Bindings.createStringBinding(() -> Paths.get(profile.getRepository().getRunDirectory(versionId).getAbsolutePath()).normalize().toString(),
@@ -243,6 +244,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
lastVersionSetting = versionSetting;
initializeSelectedJava();
initJavaSubtitle();
loadIcon();
}
@@ -267,11 +269,14 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
}
}
private void initJavaSubtitle(VersionSetting versionSetting) {
Task.of(variables -> variables.set("java", versionSetting.getJavaVersion()))
.subscribe(Task.of(Schedulers.javafx(),
variables -> javaItem.setSubtitle(variables.<JavaVersion>getOptional("java")
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Directory"))));
private void initJavaSubtitle() {
VersionSetting versionSetting = lastVersionSetting;
if (versionSetting == null)
return;
Task.supplyAsync(versionSetting::getJavaVersion)
.thenAccept(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion)
.map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))
.start();
}
@FXML
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.versions;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.download.game.GameAssetDownloadTask;
import org.jackhuang.hmcl.download.game.GameAssetIndexDownloadTask;
import org.jackhuang.hmcl.game.GameRepository;
import org.jackhuang.hmcl.game.LauncherHelper;
import org.jackhuang.hmcl.game.Version;
@@ -74,6 +74,6 @@ public class WorldExportPage extends WizardSinglePage {
@Override
protected Object finish() {
return Task.of(i18n("world.export.wizard", worldName.get()), () -> world.export(Paths.get(path.get()), worldName.get()));
return Task.runAsync(i18n("world.export.wizard", worldName.get()), () -> world.export(Paths.get(path.get()), worldName.get()));
}
}
@@ -29,7 +29,6 @@ import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.util.List;
@@ -51,10 +50,10 @@ public class WorldListPage extends ListPage<WorldListItem> {
this.savesDir = profile.getRepository().getRunDirectory(id).toPath().resolve("saves");
setLoading(true);
Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
.finalizedResult(Schedulers.javafx(), (result, isDependentsSucceeded) -> {
Task.supplyAsync(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList()))
.whenComplete(Schedulers.javafx(), (result, exception) -> {
setLoading(false);
if (isDependentsSucceeded)
if (exception == null)
itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList()));
}).start();
}
@@ -73,11 +72,11 @@ public class WorldListPage extends ListPage<WorldListItem> {
private void installWorld(File zipFile) {
// Only accept one world file because user is required to confirm the new world name
// Or too many input dialogs are popped.
Task.ofResult(() -> new World(zipFile.toPath()))
.finalizedResult(Schedulers.javafx(), world -> {
Task.supplyAsync(() -> new World(zipFile.toPath()))
.whenComplete(Schedulers.javafx(), world -> {
Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> {
Task.of(() -> world.install(savesDir, name))
.finalized(Schedulers.javafx(), var -> {
Task.runAsync(() -> world.install(savesDir, name))
.whenComplete(Schedulers.javafx(), () -> {
itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name))));
resolve.run();
}, e -> {
@@ -32,8 +32,8 @@ public interface AbstractWizardDisplayer extends WizardDisplayer {
Queue<Object> getCancelQueue();
@Override
default void handleTask(Map<String, Object> settings, Task task) {
TaskExecutor executor = task.with(Task.of(Schedulers.javafx(), this::navigateToSuccess)).executor();
default void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutor executor = task.withRun(Schedulers.javafx(), this::navigateToSuccess).executor();
TaskListPane pane = new TaskListPane();
pane.setExecutor(executor);
navigateTo(pane, Navigation.NavigationDirection.FINISH);
@@ -23,7 +23,6 @@ import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.animation.TransitionHandler;
import org.jackhuang.hmcl.util.StringUtils;
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.util.StringUtils;
@@ -35,7 +35,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplayer {
@Override
default void handleTask(Map<String, Object> settings, Task task) {
default void handleTask(Map<String, Object> settings, Task<?> task) {
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(it -> {
it.fireEvent(new DialogCloseEvent());
onEnd();
@@ -67,19 +67,19 @@ public interface TaskExecutorDialogWizardDisplayer extends AbstractWizardDisplay
pane.fireEvent(new DialogCloseEvent());
if (success) {
if (settings.containsKey("success_message") && settings.get("success_message") instanceof String)
Controllers.dialog((String) settings.get("success_message"), null, MessageBox.FINE_MESSAGE, () -> onEnd());
Controllers.dialog((String) settings.get("success_message"), null, MessageType.FINE, () -> onEnd());
else if (!settings.containsKey("forbid_success_message"))
Controllers.dialog(i18n("message.success"), null, MessageBox.FINE_MESSAGE, () -> onEnd());
Controllers.dialog(i18n("message.success"), null, MessageType.FINE, () -> onEnd());
} else {
if (executor.getLastException() == null)
if (executor.getException() == null)
return;
String appendix = StringUtils.getStackTrace(executor.getLastException());
String appendix = StringUtils.getStackTrace(executor.getException());
if (settings.get("failure_callback") instanceof WizardProvider.FailureCallback)
((WizardProvider.FailureCallback)settings.get("failure_callback")).onFail(settings, executor.getLastException(), () -> onEnd());
((WizardProvider.FailureCallback)settings.get("failure_callback")).onFail(settings, executor.getException(), () -> onEnd());
else if (settings.get("failure_message") instanceof String)
Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageBox.ERROR_MESSAGE, () -> onEnd());
Controllers.dialog(appendix, (String) settings.get("failure_message"), MessageType.ERROR, () -> onEnd());
else if (!settings.containsKey("forbid_failure_message"))
Controllers.dialog(appendix, i18n("wizard.failed"), MessageBox.ERROR_MESSAGE, () -> onEnd());
Controllers.dialog(appendix, i18n("wizard.failed"), MessageType.ERROR, () -> onEnd());
}
});
@@ -106,7 +106,7 @@ public class WizardController implements Navigation {
public void onFinish() {
Object result = provider.finish(settings);
if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT);
else if (result instanceof Task) displayer.handleTask(settings, ((Task) result));
else if (result instanceof Task<?>) displayer.handleTask(settings, ((Task<?>) result));
else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result);
}
@@ -27,5 +27,5 @@ public interface WizardDisplayer {
void onEnd();
void onCancel();
void navigateTo(Node page, Navigation.NavigationDirection nav);
void handleTask(Map<String, Object> settings, Task task);
void handleTask(Map<String, Object> settings, Task<?> task);
}
@@ -20,13 +20,13 @@ package org.jackhuang.hmcl.upgrade;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import javafx.application.Platform;
import javafx.scene.layout.Region;
import org.jackhuang.hmcl.Main;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.JarUtils;
@@ -99,10 +99,10 @@ public final class UpdateHandler {
return;
}
Task task = new HMCLDownloadTask(version, downloaded);
Task<?> task = new HMCLDownloadTask(version, downloaded);
TaskExecutor executor = task.executor();
Region dialog = Controllers.taskDialog(executor, i18n("message.downloading"));
Controllers.taskDialog(executor, i18n("message.downloading"));
thread(() -> {
boolean success = executor.test();
@@ -116,14 +116,14 @@ public final class UpdateHandler {
System.exit(0);
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to update to " + version, e);
Platform.runLater(() -> Controllers.dialog(StringUtils.getStackTrace(e), i18n("update.failed"), MessageBox.ERROR_MESSAGE));
Platform.runLater(() -> Controllers.dialog(StringUtils.getStackTrace(e), i18n("update.failed"), MessageType.ERROR));
return;
}
} else {
Throwable e = task.getLastException();
Exception e = executor.getException();
LOG.log(Level.WARNING, "Failed to update to " + version, e);
Platform.runLater(() -> Controllers.dialog(e.toString(), i18n("update.failed"), MessageBox.ERROR_MESSAGE));
Platform.runLater(() -> Controllers.dialog(e.toString(), i18n("update.failed"), MessageType.ERROR));
}
});
}
@@ -18,9 +18,11 @@
package org.jackhuang.hmcl.util;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import org.jackhuang.hmcl.Metadata;
import org.jackhuang.hmcl.ui.CrashWindow;
import org.jackhuang.hmcl.ui.construct.MessageBox;
import org.jackhuang.hmcl.upgrade.IntegrityChecker;
import org.jackhuang.hmcl.upgrade.UpdateChecker;
import org.jackhuang.hmcl.util.io.NetworkUtils;
@@ -69,7 +71,10 @@ public class CrashReporter implements Thread.UncaughtExceptionHandler {
String info = entry.getValue();
LOG.severe(info);
try {
MessageBox.show(info);
Alert alert = new Alert(AlertType.INFORMATION, info);
alert.setTitle(i18n("message.info"));
alert.setHeaderText(i18n("message.info"));
alert.showAndWait();
} catch (Throwable t) {
LOG.log(Level.SEVERE, "Unable to show message", t);
}
@@ -58,10 +58,6 @@ public final class Locales {
public static final List<SupportedLocale> LOCALES = Lang.immutableListOf(DEFAULT, EN, ZH_CN, ZH);
public static SupportedLocale getLocale(int index) {
return Lang.get(LOCALES, index).orElse(DEFAULT);
}
public static SupportedLocale getLocaleByName(String name) {
if (name == null) return DEFAULT;
switch (name.toLowerCase()) {
@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.auth.yggdrasil;
import org.jackhuang.hmcl.util.Immutable;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -36,10 +37,12 @@ public final class Texture {
this.metadata = metadata;
}
@Nullable
public String getUrl() {
return url;
}
@Nullable
public Map<String, String> getMetadata() {
return metadata;
}
@@ -21,6 +21,7 @@ import com.google.gson.JsonParseException;
import java.util.Map;
import com.google.gson.annotations.JsonAdapter;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.Validation;
@@ -36,13 +37,14 @@ public final class User implements Validation {
private final String id;
@Nullable
@JsonAdapter(PropertyMapSerializer.class)
private final Map<String, String> properties;
public User(String id) {
this(id, null);
}
public User(String id, Map<String, String> properties) {
public User(String id, @Nullable Map<String, String> properties) {
this.id = id;
this.properties = properties;
}
@@ -51,6 +53,7 @@ public final class User implements Validation {
return id;
}
@Nullable
public Map<String, String> getProperties() {
return properties;
}
@@ -17,8 +17,6 @@
*/
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.util.CacheRepository;
/**
*
* @author huangyuhui
@@ -26,9 +26,7 @@ import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
/**
@@ -69,9 +67,9 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
@Override
public Task checkGameCompletionAsync(Version version) {
return new ParallelTask(
Task.ofThen(var -> {
public Task<?> checkGameCompletionAsync(Version version) {
return Task.allOf(
Task.composeAsync(() -> {
if (!repository.getVersionJar(version).exists())
return new GameDownloadTask(this, null, version);
else
@@ -83,41 +81,37 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
}
@Override
public Task checkLibraryCompletionAsync(Version version) {
public Task<?> checkLibraryCompletionAsync(Version version) {
return new GameLibrariesTask(this, version);
}
@Override
public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
public Task<Version> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) {
VersionList<?> versionList = getVersionList(libraryId);
return versionList.loadAsync(gameVersion, getDownloadProvider())
.then(variables -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
.thenCompose(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion)
.orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion))));
}
@Override
public Task installLibraryAsync(Version version, RemoteVersion libraryVersion) {
public Task<Version> installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) {
Task<Version> task;
if (libraryVersion instanceof ForgeRemoteVersion)
return new ForgeInstallTask(this, version, (ForgeRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion);
else if (libraryVersion instanceof LiteLoaderRemoteVersion)
return new LiteLoaderInstallTask(this, version, (LiteLoaderRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
task = new LiteLoaderInstallTask(this, oldVersion, (LiteLoaderRemoteVersion) libraryVersion);
else if (libraryVersion instanceof OptiFineRemoteVersion)
return new OptiFineInstallTask(this, version, (OptiFineRemoteVersion) libraryVersion)
.then(variables -> new LibrariesUniqueTask(variables.get("version")))
.then(variables -> new MaintainTask(variables.get("version")))
.then(variables -> new VersionJsonSaveTask(repository, variables.get("version")));
task = new OptiFineInstallTask(this, oldVersion, (OptiFineRemoteVersion) libraryVersion);
else
throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized.");
return task
.thenCompose(LibrariesUniqueTask::new)
.thenCompose(MaintainTask::new)
.thenCompose(newVersion -> new VersionJsonSaveTask(repository, newVersion));
}
public ExceptionalFunction<AutoTypingMap<String>, Task, ?> installLibraryAsync(RemoteVersion libraryVersion) {
return var -> installLibraryAsync(var.get("version"), libraryVersion);
public ExceptionalFunction<Version, Task<Version>, ?> installLibraryAsync(RemoteVersion libraryVersion) {
return version -> installLibraryAsync(version, libraryVersion);
}
}
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.game.*;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.ParallelTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.AutoTypingMap;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -48,38 +46,39 @@ public class DefaultGameBuilder extends GameBuilder {
}
@Override
public Task buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
Version version = JsonUtils.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class);
version = version.setId(name).setJar(null);
variables.set("version", version);
Task result = downloadGameAsync(gameVersion, version).then(new ParallelTask(
public Task<?> buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenCompose(rawJson -> {
Version original = JsonUtils.GSON.fromJson(rawJson, Version.class);
Version version = original.setId(name).setJar(null);
Task<?> vanillaTask = downloadGameAsync(gameVersion, version).thenCompose(Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY),
new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries.
).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
).withCompose(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant.
Task<Version> libraryTask = vanillaTask.thenSupply(() -> version);
if (toolVersions.containsKey("forge"))
result = result.then(libraryTaskHelper(gameVersion, "forge"));
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "forge"));
if (toolVersions.containsKey("liteloader"))
result = result.then(libraryTaskHelper(gameVersion, "liteloader"));
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "liteloader"));
if (toolVersions.containsKey("optifine"))
result = result.then(libraryTaskHelper(gameVersion, "optifine"));
libraryTask = libraryTask.thenCompose(libraryTaskHelper(gameVersion, "optifine"));
for (RemoteVersion remoteVersion : remoteVersions)
result = result.then(dependencyManager.installLibraryAsync(remoteVersion));
libraryTask = libraryTask.thenCompose(dependencyManager.installLibraryAsync(remoteVersion));
return result;
}).finalized((variables, isDependentsSucceeded) -> {
if (!isDependentsSucceeded)
return libraryTask;
}).whenComplete(exception -> {
if (exception != null)
dependencyManager.getGameRepository().getVersionRoot(name).delete();
});
}
private ExceptionalFunction<AutoTypingMap<String>, Task, ?> libraryTaskHelper(String gameVersion, String libraryId) {
return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId));
private ExceptionalFunction<Version, Task<Version>, ?> libraryTaskHelper(String gameVersion, String libraryId) {
return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, toolVersions.get(libraryId));
}
protected Task downloadGameAsync(String gameVersion, Version version) {
protected Task<Void> downloadGameAsync(String gameVersion, Version version) {
return new GameDownloadTask(dependencyManager, gameVersion, version);
}
@@ -46,7 +46,7 @@ public interface DependencyManager {
*
* @return the task to check game completion.
*/
Task checkGameCompletionAsync(Version version);
Task<?> checkGameCompletionAsync(Version version);
/**
* Check if the game is complete.
@@ -54,7 +54,7 @@ public interface DependencyManager {
*
* @return the task to check game completion.
*/
Task checkLibraryCompletionAsync(Version version);
Task<?> checkLibraryCompletionAsync(Version version);
/**
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.
@@ -71,7 +71,7 @@ public interface DependencyManager {
* @param libraryVersion the version of being installed library.
* @return the task to install the specific library.
*/
Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
Task<?> installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion);
/**
* Install a library to a version.
@@ -81,7 +81,7 @@ public interface DependencyManager {
* @param libraryVersion the remote version of being installed library.
* @return the task to install the specific library.
*/
Task installLibraryAsync(Version version, RemoteVersion libraryVersion);
Task<?> installLibraryAsync(Version version, RemoteVersion libraryVersion);
/**
* Get registered version list.
@@ -72,5 +72,5 @@ public abstract class GameBuilder {
/**
* @return the task that can build thw whole Minecraft environment
*/
public abstract Task buildAsync();
public abstract Task<?> buildAsync();
}
@@ -18,20 +18,14 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.task.Task;
public class MaintainTask extends TaskResult<Version> {
public class MaintainTask extends Task<Version> {
private final Version version;
private final String id;
public MaintainTask(Version version) {
this(version, ID);
}
public MaintainTask(Version version, String id) {
this.version = version;
this.id = id;
}
@Override
@@ -76,11 +70,4 @@ public class MaintainTask extends TaskResult<Version> {
return builder.build();
}
@Override
public String getId() {
return id;
}
public static final String ID = "version";
}
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.download.forge.ForgeBMCLVersionList;
import org.jackhuang.hmcl.download.forge.ForgeVersionList;
import org.jackhuang.hmcl.download.game.GameVersionList;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderVersionList;
import org.jackhuang.hmcl.download.optifine.OptiFineBMCLVersionList;
@@ -62,19 +62,19 @@ public abstract class VersionList<T extends RemoteVersion> {
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
public abstract Task refreshAsync(DownloadProvider downloadProvider);
public abstract Task<?> refreshAsync(DownloadProvider downloadProvider);
/**
* @param gameVersion the remote version depends on
* @param downloadProvider DownloadProvider
* @return the task to reload the remote version list.
*/
public Task refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
public Task<?> refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
return refreshAsync(downloadProvider);
}
public Task loadAsync(DownloadProvider downloadProvider) {
return Task.ofThen(variables -> {
public Task<?> loadAsync(DownloadProvider downloadProvider) {
return Task.composeAsync(() -> {
lock.readLock().lock();
boolean loaded;
@@ -87,8 +87,8 @@ public abstract class VersionList<T extends RemoteVersion> {
});
}
public Task loadAsync(String gameVersion, DownloadProvider downloadProvider) {
return Task.ofThen(variables -> {
public Task<?> loadAsync(String gameVersion, DownloadProvider downloadProvider) {
return Task.composeAsync(() -> {
lock.readLock().lock();
boolean loaded;
@@ -46,21 +46,21 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
}
@Override
public Task loadAsync(DownloadProvider downloadProvider) {
public Task<?> loadAsync(DownloadProvider downloadProvider) {
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
throw new UnsupportedOperationException("ForgeBMCLVersionList does not support loading the entire Forge remote version list.");
}
@Override
public Task refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
public Task<?> refreshAsync(String gameVersion, DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL("https://bmclapi2.bangbang93.com/forge/minecraft/" + gameVersion));
return new Task() {
return new Task<Void>() {
@Override
public Collection<? extends Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -104,7 +104,6 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
private final String branch;
private final String mcversion;
private final String version;
private final int build;
private final List<File> files;
/**
@@ -112,14 +111,13 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
*/
@SuppressWarnings("unused")
public ForgeVersion() {
this(null, null, null, 0, null);
this(null, null, null, null);
}
public ForgeVersion(String branch, String mcversion, String version, int build, List<File> files) {
public ForgeVersion(String branch, String mcversion, String version, List<File> files) {
this.branch = branch;
this.mcversion = mcversion;
this.version = version;
this.build = build;
this.files = files;
}
@@ -135,10 +133,6 @@ public final class ForgeBMCLVersionList extends VersionList<ForgeRemoteVersion>
return version;
}
public int getBuild() {
return build;
}
public List<File> getFiles() {
return files;
}
@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -34,14 +33,14 @@ import java.util.Collections;
*
* @author huangyuhui
*/
public final class ForgeInstallTask extends TaskResult<Version> {
public final class ForgeInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private Path installer;
private final ForgeRemoteVersion remote;
private Task dependent;
private TaskResult<Version> dependency;
private Task<Void> dependent;
private Task<Version> dependency;
public ForgeInstallTask(DefaultDependencyManager dependencyManager, Version version, ForgeRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -73,25 +72,15 @@ public final class ForgeInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(dependent);
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return Collections.singleton(dependency);
}
@Override
public String getId() {
return "version";
}
@Override
public boolean isRelyingOnDependencies() {
return false;
}
@Override
public void execute() {
if (VersionNumber.VERSION_COMPARATOR.compare("1.13", remote.getGameVersion()) <= 0)
@@ -21,7 +21,6 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@@ -49,19 +48,19 @@ import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
import static org.jackhuang.hmcl.util.Logging.LOG;
public class ForgeNewInstallTask extends TaskResult<Version> {
public class ForgeNewInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final DefaultGameRepository gameRepository;
private final Version version;
private final Path installer;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private ForgeNewInstallProfile profile;
private Version forgeVersion;
public ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
ForgeNewInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
this.dependencyManager = dependencyManager;
this.gameRepository = dependencyManager.getGameRepository();
this.version = version;
@@ -80,20 +79,15 @@ public class ForgeNewInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public List<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public boolean doPreExecute() {
return true;
@@ -20,7 +20,6 @@ package org.jackhuang.hmcl.download.forge;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
@@ -31,29 +30,24 @@ import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ForgeOldInstallTask extends TaskResult<Version> {
public class ForgeOldInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final Path installer;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
ForgeOldInstallTask(DefaultDependencyManager dependencyManager, Version version, Path installer) {
this.dependencyManager = dependencyManager;
this.version = version;
this.installer = installer;
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public boolean doPreExecute() {
return true;
@@ -45,12 +45,12 @@ public final class ForgeVersionList extends VersionList<ForgeRemoteVersion> {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
final GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(FORGE_LIST)));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -38,14 +38,14 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameAssetDownloadTask extends Task {
public final class GameAssetDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final AssetIndexInfo assetIndexInfo;
private final File assetIndexFile;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -64,12 +64,12 @@ public final class GameAssetDownloadTask extends Task {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -22,7 +22,6 @@ import org.jackhuang.hmcl.game.AssetIndexInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File;
@@ -34,11 +33,11 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameAssetIndexDownloadTask extends Task {
public final class GameAssetIndexDownloadTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -53,7 +52,7 @@ public final class GameAssetIndexDownloadTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@@ -26,6 +26,7 @@ import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import java.io.File;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
@@ -33,11 +34,11 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameDownloadTask extends Task {
public final class GameDownloadTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final String gameVersion;
private final Version version;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public GameDownloadTask(DefaultDependencyManager dependencyManager, String gameVersion, Version version) {
this.dependencyManager = dependencyManager;
@@ -48,7 +49,7 @@ public final class GameDownloadTask extends Task {
}
@Override
public List<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -32,12 +32,12 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class GameLibrariesTask extends Task {
public final class GameLibrariesTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager;
private final Version version;
private final List<Library> libraries;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -64,7 +64,7 @@ public final class GameLibrariesTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@@ -55,11 +55,11 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.getVersionListURL()));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -20,7 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.CompatibilityRule;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
@@ -31,17 +31,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class LibrariesUniqueTask extends TaskResult<Version> {
public class LibrariesUniqueTask extends Task<Version> {
private final Version version;
private final String id;
public LibrariesUniqueTask(Version version) {
this(version, "version");
}
public LibrariesUniqueTask(Version version, String id) {
this.version = version;
this.id = id;
}
@Override
@@ -99,9 +93,4 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
setResult(version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList())));
}
@Override
public String getId() {
return id;
}
}
@@ -45,7 +45,7 @@ import java.util.logging.Level;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
public class LibraryDownloadTask extends Task {
public class LibraryDownloadTask extends Task<Void> {
private FileDownloadTask task;
protected final File jar;
protected final DefaultCacheRepository cacheRepository;
@@ -74,7 +74,7 @@ public class LibraryDownloadTask extends Task {
}
@Override
public Collection<? extends Task> getDependents() {
public Collection<Task<?>> getDependents() {
if (cached) return Collections.emptyList();
else return Collections.singleton(task);
}
@@ -91,7 +91,7 @@ public class LibraryDownloadTask extends Task {
if (!isDependentsSucceeded()) {
// Since FileDownloadTask wraps the actual exception with DownloadException.
// We should extract it letting the error message clearer.
Throwable t = task.getLastException();
Exception t = task.getException();
if (t instanceof DownloadException)
throw new LibraryDownloadException(library, t.getCause());
else
@@ -225,16 +225,16 @@ public class LibraryDownloadTask extends Task {
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;
File temp = FileUtils.createTempFile("minecraft", ".pack");
Path temp = Files.createTempFile("minecraft", ".pack");
byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8);
OutputStream out = new FileOutputStream(temp);
out.write(decompressed, 0, decompressed.length - len - 8);
out.close();
try (OutputStream out = Files.newOutputStream(temp)) {
out.write(decompressed, 0, decompressed.length - len - 8);
}
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");
checksumsFile.setTime(0L);
@@ -243,6 +243,6 @@ public class LibraryDownloadTask extends Task {
jos.closeEntry();
}
temp.delete();
Files.delete(temp);
}
}
@@ -32,11 +32,11 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class VersionJsonDownloadTask extends Task {
public final class VersionJsonDownloadTask extends Task<String> {
private final String gameVersion;
private final DefaultDependencyManager dependencyManager;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final VersionList<?> gameVersionList;
public VersionJsonDownloadTask(String gameVersion, DefaultDependencyManager dependencyManager) {
@@ -51,12 +51,12 @@ public final class VersionJsonDownloadTask extends Task {
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@@ -65,8 +65,6 @@ public final class VersionJsonDownloadTask extends Task {
RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository"));
String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl());
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), ID));
dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL)).storeTo(this::setResult));
}
public static final String ID = "raw_version_json";
}
@@ -24,14 +24,13 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
/**
* This task is to save the version json.
*
* @author huangyuhui
*/
public final class VersionJsonSaveTask extends Task {
public final class VersionJsonSaveTask extends Task<Version> {
private final DefaultGameRepository repository;
private final Version version;
@@ -47,13 +46,12 @@ public final class VersionJsonSaveTask extends Task {
this.version = version;
setSignificance(TaskSignificance.MODERATE);
setResult(version);
}
@Override
public void execute() throws Exception {
File json = repository.getVersionJson(version.getId()).getAbsoluteFile();
if (!FileUtils.makeFile(json))
throw new IOException("Cannot create file " + json);
FileUtils.writeText(json, JsonUtils.GSON.toJson(version));
}
}
@@ -33,7 +33,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
*
@@ -52,11 +51,11 @@ public final class LiteLoaderBMCLVersionList extends VersionList<LiteLoaderRemot
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -18,13 +18,11 @@
package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.game.GameLibrariesTask;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
import java.util.Collection;
@@ -37,13 +35,13 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class LiteLoaderInstallTask extends TaskResult<Version> {
public final class LiteLoaderInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final LiteLoaderRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public LiteLoaderInstallTask(DefaultDependencyManager dependencyManager, Version version, LiteLoaderRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -52,20 +50,15 @@ public final class LiteLoaderInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public void execute() {
Library library = new Library(
@@ -33,7 +33,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
*
@@ -52,11 +51,11 @@ public final class LiteLoaderVersionList extends VersionList<LiteLoaderRemoteVer
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL(downloadProvider.injectURL(LITELOADER_LIST)));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -46,11 +46,11 @@ public final class OptiFineBMCLVersionList extends VersionList<OptiFineRemoteVer
}
@Override
public Task refreshAsync(DownloadProvider downloadProvider) {
public Task<?> refreshAsync(DownloadProvider downloadProvider) {
GetTask task = new GetTask(NetworkUtils.toURL("http://bmclapi2.bangbang93.com/optifine/versionlist"));
return new Task() {
return new Task<Void>() {
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return Collections.singleton(task);
}
@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Lang;
import java.util.Collection;
@@ -35,13 +34,13 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class OptiFineInstallTask extends TaskResult<Version> {
public final class OptiFineInstallTask extends Task<Version> {
private final DefaultDependencyManager dependencyManager;
private final Version version;
private final OptiFineRemoteVersion remote;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
public OptiFineInstallTask(DefaultDependencyManager dependencyManager, Version version, OptiFineRemoteVersion remoteVersion) {
this.dependencyManager = dependencyManager;
@@ -50,20 +49,15 @@ public final class OptiFineInstallTask extends TaskResult<Version> {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public String getId() {
return "version";
}
@Override
public boolean isRelyingOnDependencies() {
return false;
@@ -168,8 +168,8 @@ public class DefaultGameRepository implements GameRepository {
versions.remove(id);
if (FileUtils.isMovingToTrashSupported()) {
return FileUtils.moveToTrash(removedFile);
if (FileUtils.isMovingToTrashSupported() && FileUtils.moveToTrash(removedFile)) {
return true;
}
// remove json files first to ensure HMCL will not recognize this folder as a valid version.
@@ -77,8 +77,8 @@ public interface GameRepository extends VersionProvider {
*/
void refreshVersions();
default Task refreshVersionsAsync() {
return Task.of(this::refreshVersions);
default Task<Void> refreshVersionsAsync() {
return Task.runAsync(this::refreshVersions);
}
/**
@@ -36,7 +36,6 @@ import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -43,14 +43,14 @@ import java.util.stream.Collectors;
*
* @author huangyuhui
*/
public final class CurseCompletionTask extends Task {
public final class CurseCompletionTask extends Task<Void> {
private final DefaultGameRepository repository;
private final ModManager modManager;
private final String version;
private CurseManifest manifest = null;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -86,12 +86,12 @@ public final class CurseCompletionTask extends Task {
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@@ -147,7 +147,7 @@ public final class CurseCompletionTask extends Task {
// Let this task fail if the curse manifest has not been completed.
// But continue other downloads.
if (!flag.get() || notFound.get())
dependencies.add(Task.of(() -> {
dependencies.add(Task.runAsync(() -> {
if (notFound.get())
throw new CurseCompletionException(new FileNotFoundException());
else
@@ -38,7 +38,7 @@ import java.util.List;
*
* @author huangyuhui
*/
public final class CurseInstallTask extends Task {
public final class CurseInstallTask extends Task<Void> {
private final DefaultDependencyManager dependencyManager;
private final DefaultGameRepository repository;
@@ -48,8 +48,8 @@ public final class CurseInstallTask extends Task {
private final String name;
private final File run;
private final ModpackConfiguration<CurseManifest> config;
private final List<Task> dependents = new LinkedList<>();
private final List<Task> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
/**
* Constructor.
@@ -99,12 +99,12 @@ public final class CurseInstallTask extends Task {
}
@Override
public Collection<Task> getDependents() {
public Collection<Task<?>> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return dependencies;
}
@@ -23,7 +23,7 @@ import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.gson.JsonUtils;
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.IOException;
@@ -119,7 +119,7 @@ public final class ForgeModMetadata {
Path mcmod = fs.getPath("mcmod.info");
if (Files.notExists(mcmod))
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>>() {
}.getType());
if (modList == null || modList.isEmpty())
@@ -33,7 +33,7 @@ import java.util.List;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
public final class MinecraftInstanceTask<T> extends Task {
public final class MinecraftInstanceTask<T> extends Task<Void> {
private final File zipFile;
private final Charset encoding;
@@ -20,7 +20,9 @@ package org.jackhuang.hmcl.mod;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.javafx.ImmediateBooleanProperty;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.io.File;
import java.io.IOException;
@@ -42,7 +44,7 @@ public final class ModInfo implements Comparable<ModInfo> {
private final String gameVersion;
private final String url;
private final String fileName;
private final ImmediateBooleanProperty activeProperty;
private final BooleanProperty activeProperty;
public ModInfo(ModManager modManager, File file, String name, String description) {
this(modManager, file, name, description, "", "", "", "");
@@ -57,7 +59,7 @@ public final class ModInfo implements Comparable<ModInfo> {
this.gameVersion = gameVersion;
this.url = url;
activeProperty = new ImmediateBooleanProperty(this, "active", !modManager.isDisabled(file)) {
activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) {
@Override
protected void invalidated() {
Path path = ModInfo.this.file.toAbsolutePath();
@@ -104,7 +106,7 @@ public final class ModInfo implements Comparable<ModInfo> {
return url;
}
public ImmediateBooleanProperty activeProperty() {
public BooleanProperty activeProperty() {
return activeProperty;
}
@@ -85,13 +85,15 @@ public final class ModManager {
public void refreshMods() throws IOException {
modInfos.clear();
for (Path subitem : Files.newDirectoryStream(getModsDirectory())) {
if (Files.isDirectory(subitem) && VersionNumber.isIntVersionNumber(FileUtils.getName(subitem))) {
// If the folder name is game version, forge will search mod in this subdirectory
for (Path subsubitem : Files.newDirectoryStream(subitem))
addModInfo(subsubitem.toFile());
} else {
addModInfo(subitem.toFile());
if (Files.isDirectory(getModsDirectory())) {
for (Path subitem : Files.newDirectoryStream(getModsDirectory())) {
if (Files.isDirectory(subitem) && VersionNumber.isIntVersionNumber(FileUtils.getName(subitem))) {
// If the folder name is game version, forge will search mod in this subdirectory
for (Path subsubitem : Files.newDirectoryStream(subitem))
addModInfo(subsubitem.toFile());
} else {
addModInfo(subitem.toFile());
}
}
}
loaded = true;
@@ -31,7 +31,7 @@ import java.util.function.Predicate;
import static org.jackhuang.hmcl.util.DigestUtils.digest;
import static org.jackhuang.hmcl.util.Hex.encodeHex;
public class ModpackInstallTask<T> extends Task {
public class ModpackInstallTask<T> extends Task<Void> {
private final File modpackFile;
private final File dest;
@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.mod;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.task.SilentException;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -27,14 +26,14 @@ import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
public class ModpackUpdateTask extends Task {
public class ModpackUpdateTask extends Task<Void> {
private final DefaultGameRepository repository;
private final String id;
private final Task updateTask;
private final Task<?> updateTask;
private final Path backupFolder;
public ModpackUpdateTask(DefaultGameRepository repository, String id, Task updateTask) {
public ModpackUpdateTask(DefaultGameRepository repository, String id, Task<?> updateTask) {
this.repository = repository;
this.id = id;
this.updateTask = updateTask;
@@ -50,12 +49,7 @@ public class ModpackUpdateTask extends Task {
}
@Override
public boolean isRelyingOnDependencies() {
return false;
}
@Override
public Collection<? extends Task> getDependencies() {
public Collection<Task<?>> getDependencies() {
return Collections.singleton(updateTask);
}
@@ -78,8 +72,6 @@ public class ModpackUpdateTask extends Task {
repository.removeVersionFromDisk(id);
FileUtils.copyDirectory(backupFolder, repository.getVersionRoot(id).toPath());
throw new SilentException();
}
}
}
@@ -46,15 +46,15 @@ import java.util.Optional;
*
* @author huangyuhui
*/
public final class MultiMCModpackInstallTask extends Task {
public final class MultiMCModpackInstallTask extends Task<Void> {
private final File zipFile;
private final Modpack modpack;
private final MultiMCInstanceConfiguration manifest;
private final String name;
private final DefaultGameRepository repository;
private final List<Task> dependencies = new LinkedList<>();
private final List<Task> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>();
private final List<Task<?>> dependents = new LinkedList<>();
public MultiMCModpackInstallTask(DefaultDependencyManager dependencyManager, File zipFile, Modpack modpack, MultiMCInstanceConfiguration manifest, String name) {
this.zipFile = zipFile;
@@ -91,7 +91,7 @@ public final class MultiMCModpackInstallTask extends Task {
}
@Override
public List<Task> getDependencies() {
public List<Task<?>> getDependencies() {
return dependencies;
}
@@ -126,7 +126,7 @@ public final class MultiMCModpackInstallTask extends Task {
}
@Override
public List<Task> getDependents() {
public List<Task<?>> getDependents() {
return dependents;
}
@@ -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).thenCompose(maintainedVersion -> new VersionJsonSaveTask(repository, maintainedVersion)));
dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
}
@@ -1,78 +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.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
final class CoupleTask<P extends Task> extends Task {
private final boolean relyingOnDependents;
private final Collection<Task> dependents;
private final List<Task> dependencies = new LinkedList<>();
private final ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param pred the task that runs before succ.
* @param succ a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
* @param relyingOnDependents true if this task chain will be broken when task pred fails.
*/
public CoupleTask(P pred, ExceptionalFunction<AutoTypingMap<String>, Task, ?> succ, boolean relyingOnDependents) {
this.dependents = pred == null ? Collections.emptySet() : Collections.singleton(pred);
this.succ = succ;
this.relyingOnDependents = relyingOnDependents;
setSignificance(TaskSignificance.MODERATE);
setName(succ.toString());
}
@Override
public void execute() throws Exception {
setName(succ.toString());
Task task = succ.apply(getVariables());
if (task != null)
dependencies.add(task);
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public Collection<Task> getDependencies() {
return dependencies;
}
@Override
public boolean isRelyingOnDependents() {
return relyingOnDependents;
}
}
@@ -33,9 +33,11 @@ import java.io.RandomAccessFile;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import static java.util.Objects.requireNonNull;
@@ -46,7 +48,7 @@ import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
*
* @author huangyuhui
*/
public class FileDownloadTask extends Task {
public class FileDownloadTask extends Task<Void> {
public static class IntegrityCheck {
private String algorithm;
@@ -123,6 +125,7 @@ public class FileDownloadTask extends Task {
this.retry = retry;
setName(file.getName());
setExecutor(Schedulers.io());
}
private void closeFiles() {
@@ -144,11 +147,6 @@ public class FileDownloadTask extends Task {
stream = null;
}
@Override
public Scheduler getScheduler() {
return Schedulers.io();
}
public EventManager<FailedEvent<URL>> getOnFailed() {
return onFailed;
}
@@ -212,7 +210,7 @@ public class FileDownloadTask extends Task {
break;
}
File temp = null;
Path temp = null;
try {
updateProgress(0);
@@ -242,15 +240,15 @@ public class FileDownloadTask extends Task {
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
throw new IOException("Could not make directory " + file.getAbsoluteFile().getParent());
temp = FileUtils.createTempFile();
rFile = new RandomAccessFile(temp, "rw");
temp = Files.createTempFile(null, null);
rFile = new RandomAccessFile(temp.toFile(), "rw");
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
stream = con.getInputStream();
int lastDownloaded = 0, downloaded = 0;
long lastTime = System.currentTimeMillis();
byte buffer[] = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
while (true) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
@@ -283,16 +281,15 @@ public class FileDownloadTask extends Task {
// Restore temp file to original name.
if (Thread.interrupted()) {
temp.delete();
temp.toFile().delete();
Thread.currentThread().interrupt();
break;
} else {
if (file.exists() && !file.delete())
throw new IOException("Unable to delete existent file " + file);
Files.deleteIfExists(file.toPath());
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
throw new IOException("Unable to make parent directory " + file);
try {
FileUtils.moveFile(temp, file);
FileUtils.moveFile(temp.toFile(), file);
} catch (Exception e) {
throw new IOException("Unable to move temp file from " + temp + " to " + file, e);
}
@@ -321,7 +318,7 @@ public class FileDownloadTask extends Task {
return;
} catch (IOException | IllegalStateException e) {
if (temp != null)
temp.delete();
temp.toFile().delete();
exception = e;
} finally {
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) throws Exception;
}
@@ -1,72 +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 java.util.Collection;
import java.util.Collections;
/**
* A task that combines two tasks and make sure [pred] runs before succ.
*
* @author huangyuhui
*/
final class FinalizedTask extends Task {
private final Collection<Task> dependents;
private final FinalizedCallback callback;
private final Scheduler scheduler;
/**
* A task that combines two tasks and make sure pred runs before succ.
*
* @param pred the task that runs before succ.
* @param callback a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred.
*/
public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback, String name) {
this.dependents = Collections.singleton(pred);
this.scheduler = scheduler;
this.callback = callback;
setSignificance(TaskSignificance.MODERATE);
setName(name);
}
@Override
public Scheduler getScheduler() {
return scheduler;
}
@Override
public void execute() throws Exception {
callback.execute(getVariables(), isDependentsSucceeded());
if (!isDependentsSucceeded())
throw new SilentException();
}
@Override
public Collection<Task> getDependents() {
return dependents;
}
@Override
public boolean isRelyingOnDependents() {
return false;
}
}
@@ -30,51 +30,37 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
*
* @author huang
* @author huangyuhui
*/
public final class GetTask extends TaskResult<String> {
public final class GetTask extends Task<String> {
private final URL url;
private final Charset charset;
private final int retry;
private final String id;
private CacheRepository repository = CacheRepository.getInstance();
public GetTask(URL url) {
this(url, ID);
this(url, UTF_8);
}
public GetTask(URL url, String id) {
this(url, id, UTF_8);
public GetTask(URL url, Charset charset) {
this(url, charset, 5);
}
public GetTask(URL url, String id, Charset charset) {
this(url, id, charset, 5);
}
public GetTask(URL url, String id, Charset charset, int retry) {
public GetTask(URL url, Charset charset, int retry) {
this.url = url;
this.charset = charset;
this.retry = retry;
this.id = id;
setName(url.toString());
}
@Override
public Scheduler getScheduler() {
return Schedulers.io();
}
@Override
public String getId() {
return id;
setExecutor(Schedulers.io());
}
public GetTask setCacheRepository(CacheRepository repository) {
@@ -143,9 +129,4 @@ public final class GetTask extends TaskResult<String> {
throw new DownloadException(url, exception);
}
/**
* The default task result ID.
*/
public static final String ID = "http_get";
}
Loaded 100 of 129 files, more files were not shown because too many files have changed in this diff. Show more