diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java index 2ffdf8edb..9d5c7c2a6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/TaskListPane.java @@ -31,10 +31,7 @@ import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.game.HMCLModpackExportTask; import org.jackhuang.hmcl.game.HMCLModpackInstallTask; -import org.jackhuang.hmcl.mod.CurseCompletionTask; -import org.jackhuang.hmcl.mod.CurseInstallTask; -import org.jackhuang.hmcl.mod.MinecraftInstanceTask; -import org.jackhuang.hmcl.mod.MultiMCModpackInstallTask; +import org.jackhuang.hmcl.mod.*; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskListener; @@ -60,7 +57,7 @@ public final class TaskListPane extends StackPane { } @Override - public void onReady(Task task) { + public void onRunning(Task task) { if (!task.getSignificance().shouldShow()) return; @@ -76,6 +73,8 @@ public final class TaskListPane extends StackPane { task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine"))); } else if (task instanceof CurseCompletionTask) { task.setName(Main.i18n("modpack.type.curse.completion")); + } else if (task instanceof ModpackInstallTask) { + task.setName(Main.i18n("modpack.installing")); } else if (task instanceof CurseInstallTask) { task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse"))); } else if (task instanceof MultiMCModpackInstallTask) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java index b4023f303..78128fa9e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java @@ -52,7 +52,7 @@ public class DefaultGameBuilder extends GameBuilder { public Task buildAsync() { return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> { Version version = Constants.GSON.fromJson(variables.get(VersionJsonDownloadTask.ID), Version.class); - version = version.setId(name).setJar(null).resolve(new SimpleVersionProvider()); + version = version.setId(name).setJar(null); variables.set("version", version); Task result = new ParallelTask( new GameAssetDownloadTask(dependencyManager, version), diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index 2bd5aa57e..be65124e9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -55,7 +55,7 @@ public final class GameAssetDownloadTask extends Task { */ public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) { this.dependencyManager = dependencyManager; - this.version = version.requireResolved(); + this.version = version; this.refreshTask = new GameAssetRefreshTask(dependencyManager, version); this.dependents.add(refreshTask); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java index f180a4a80..9e1878024 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetIndexDownloadTask.java @@ -49,7 +49,7 @@ public final class GameAssetIndexDownloadTask extends Task { */ public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) { this.dependencyManager = dependencyManager; - this.version = version.requireResolved(); + this.version = version; setSignificance(TaskSignificance.MODERATE); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetRefreshTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetRefreshTask.java index d0cf4095a..cc2d8f743 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetRefreshTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetRefreshTask.java @@ -54,7 +54,7 @@ public final class GameAssetRefreshTask extends TaskResult { for (Library library : libraries) { String id = library.getGroupId() + ":" + library.getArtifactId(); VersionNumber number = VersionNumber.asVersion(library.getVersion()); + String serialized = Constants.GSON.toJson(library); if (versionMap.containsKey(id)) { VersionNumber otherNumber = versionMap.get(id); @@ -61,8 +63,15 @@ public class LibrariesUniqueTask extends TaskResult { // prevent from duplicated libraries for (Library otherLibrary : multimap.get(id)) if (library.equals(otherLibrary)) { - flag = true; - break; + String otherSerialized = Constants.GSON.toJson(otherLibrary); + // A trick, the library that has more information is better, which can be + // considered whose serialized JSON text will be longer. + if (serialized.length() <= otherSerialized.length()) { + flag = true; + break; + } else { + multimap.removeValue(id, otherLibrary); + } } if (!flag) multimap.put(id, library); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index 86052d1a7..4648b5c6c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -24,22 +24,32 @@ public final class LibraryDownloadTask extends Task { private final File xzFile; private final Library library; + private boolean downloaded = false; + public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) { + if (library.is("net.minecraftforge", "forge")) + library = library.setClassifier("universal"); + + this.library = library; + String url = dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl()); jar = file; - this.library = library; + xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz"); xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"), xzFile, dependencyManager.getProxy(), null, 1); + xzTask.setSignificance(TaskSignificance.MINOR); - task = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"), + setSignificance(TaskSignificance.MODERATE); + + task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), library.getDownload().getSha1()); } @Override public Collection getDependents() { - return Collections.singleton(xzTask); + return library.getChecksums() != null ? Collections.singleton(xzTask) : Collections.emptySet(); } @Override @@ -47,23 +57,20 @@ public final class LibraryDownloadTask extends Task { return false; } - @Override - public Scheduler getScheduler() { - return Schedulers.io(); - } - @Override public void execute() throws Exception { - if (isDependentsSucceeded()) { + if (isDependentsSucceeded() && library.getChecksums() != null) { unpackLibrary(jar, FileUtils.readBytes(xzFile)); if (!checksumValid(jar, library.getChecksums())) throw new IOException("Checksum failed for " + library); + + downloaded = true; } } @Override public Collection getDependencies() { - return isDependentsSucceeded() ? Collections.emptySet() : Collections.singleton(task); + return downloaded ? Collections.emptySet() : Collections.singleton(task); } private static boolean checksumValid(File libPath, List checksums) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java index fe8374ea4..13bd180c3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java @@ -40,11 +40,11 @@ public final class VersionJsonSaveTask extends Task { * Constructor. * * @param repository the game repository - * @param version the **resolved** version + * @param version the game version */ public VersionJsonSaveTask(DefaultGameRepository repository, Version version) { this.repository = repository; - this.version = version.requireResolved(); + this.version = version; setSignificance(TaskSignificance.MODERATE); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java index 1de840576..ef17af4cb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Library.java @@ -147,6 +147,10 @@ public class Library implements Comparable { return checksums; } + public boolean is(String groupId, String artifactId) { + return this.groupId.equals(groupId) && this.artifactId.equals(artifactId); + } + @Override public String toString() { return new ToStringBuilder(this).append("name", getName()).toString(); @@ -174,6 +178,10 @@ public class Library implements Comparable { return Objects.hash(getName(), isNative()); } + public Library setClassifier(String classifier) { + return new Library(groupId, artifactId, version, classifier, url, downloads, lateload, checksums, extract, natives, rules); + } + public static Library fromName(String name) { return fromName(name, null, null, null, null, null, null); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java index f3b5b041c..06202d055 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java @@ -143,13 +143,6 @@ public class Version implements Comparable, Validation { return false; } - public Version requireResolved() { - if (!resolved) - throw new RuntimeException("Version not resolved"); - - return this; - } - /** * Resolve given version */ diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 56310f86d..6887466c8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -220,7 +220,6 @@ public class FileDownloadTask extends Task { } catch (IOException | IllegalStateException e) { if (temp != null) temp.delete(); - Logging.LOG.log(Level.WARNING, "Unable to download file " + currentURL, e); exception = e; } finally { closeFiles(); @@ -228,7 +227,7 @@ public class FileDownloadTask extends Task { } if (exception != null) - throw exception; + throw new IOException("Unable to download file " + currentURL, exception); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index 6d1875e17..f19a17629 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -133,6 +133,7 @@ public abstract class Task { /** * The collection of sub-tasks that should execute **after** this task running. + * Will not be executed if execution fails. */ public Collection getDependencies() { return Collections.emptySet(); @@ -350,6 +351,7 @@ public abstract class Task { public enum TaskState { READY, RUNNING, + EXECUTED, SUCCEEDED, FAILED } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java index a77a4d74a..c85ea57b6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java @@ -131,7 +131,7 @@ public final class TaskExecutor { return false; } - task.setState(Task.TaskState.RUNNING); + task.setState(Task.TaskState.READY); if (task.getSignificance().shouldLog()) Logging.LOG.log(Level.FINE, "Executing task: {0}", task.getName()); @@ -150,6 +150,9 @@ public final class TaskExecutor { task.setVariables(variables); + task.setState(Task.TaskState.RUNNING); + + taskListeners.forEach(it -> it.onRunning(task)); try { task.getScheduler().schedule(task::execute).get(); } catch (ExecutionException e) { @@ -157,6 +160,8 @@ public final class TaskExecutor { throw (Exception) e.getCause(); else throw e; + } finally { + task.setState(Task.TaskState.EXECUTED); } if (task instanceof TaskResult) { @@ -172,22 +177,24 @@ public final class TaskExecutor { flag = true; if (task.getSignificance().shouldLog()) { Logging.LOG.log(Level.FINER, "Task finished: {0}", task.getName()); - - task.onDone().fireEvent(new TaskEvent(this, task, false)); - taskListeners.forEach(it -> it.onFinished(task)); } + + task.onDone().fireEvent(new TaskEvent(this, task, false)); + taskListeners.forEach(it -> it.onFinished(task)); } catch (InterruptedException e) { if (task.getSignificance().shouldLog()) { Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName()); - task.onDone().fireEvent(new TaskEvent(this, task, true)); - taskListeners.forEach(it -> it.onFailed(task, e)); } + task.onDone().fireEvent(new TaskEvent(this, task, true)); + taskListeners.forEach(it -> it.onFailed(task, e)); } catch (SilentException | RejectedExecutionException e) { // do nothing } catch (Exception e) { lastException = e; variables.set(LAST_EXCEPTION_ID, e); - Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e); + if (task.getSignificance().shouldLog()) { + Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e); + } task.onDone().fireEvent(new TaskEvent(this, task, true)); taskListeners.forEach(it -> it.onFailed(task, e)); } finally { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java index 742b2e805..977ecf515 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskListener.java @@ -31,6 +31,9 @@ public abstract class TaskListener implements EventListener { public void onReady(Task task) { } + public void onRunning(Task task) { + } + public void onFinished(Task task) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/SimpleMultimap.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/SimpleMultimap.java index a90060172..111f6c59e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/SimpleMultimap.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/SimpleMultimap.java @@ -74,9 +74,15 @@ public final class SimpleMultimap { return map.remove(key); } - public void removeValue(V value) { + public boolean removeValue(V value) { + boolean flag = false; for (Collection c : map.values()) - c.remove(value); + flag |= c.remove(value); + return flag; + } + + public boolean removeValue(K key, V value) { + return get(key).remove(value); } public void clear() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ToStringBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ToStringBuilder.java index bc9243ab9..335a45d67 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ToStringBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ToStringBuilder.java @@ -13,7 +13,7 @@ public class ToStringBuilder { if (!first) stringBuilder.append(", "); first = false; - stringBuilder.append(name).append('=').append(content.toString()); + stringBuilder.append(name).append('=').append(content); return this; }