library download optimization

This commit is contained in:
huangyuhui
2018-02-26 19:56:06 +08:00
parent cbb2a1b575
commit 1404a080ad
18 changed files with 77 additions and 44 deletions

View File

@@ -31,10 +31,7 @@ import org.jackhuang.hmcl.download.liteloader.LiteLoaderInstallTask;
import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.HMCLModpackExportTask; import org.jackhuang.hmcl.game.HMCLModpackExportTask;
import org.jackhuang.hmcl.game.HMCLModpackInstallTask; import org.jackhuang.hmcl.game.HMCLModpackInstallTask;
import org.jackhuang.hmcl.mod.CurseCompletionTask; import org.jackhuang.hmcl.mod.*;
import org.jackhuang.hmcl.mod.CurseInstallTask;
import org.jackhuang.hmcl.mod.MinecraftInstanceTask;
import org.jackhuang.hmcl.mod.MultiMCModpackInstallTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener; import org.jackhuang.hmcl.task.TaskListener;
@@ -60,7 +57,7 @@ public final class TaskListPane extends StackPane {
} }
@Override @Override
public void onReady(Task task) { public void onRunning(Task task) {
if (!task.getSignificance().shouldShow()) if (!task.getSignificance().shouldShow())
return; return;
@@ -76,6 +73,8 @@ public final class TaskListPane extends StackPane {
task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine"))); task.setName(Main.i18n("install.installer.install", Main.i18n("install.installer.optifine")));
} else if (task instanceof CurseCompletionTask) { } else if (task instanceof CurseCompletionTask) {
task.setName(Main.i18n("modpack.type.curse.completion")); task.setName(Main.i18n("modpack.type.curse.completion"));
} else if (task instanceof ModpackInstallTask) {
task.setName(Main.i18n("modpack.installing"));
} else if (task instanceof CurseInstallTask) { } else if (task instanceof CurseInstallTask) {
task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse"))); task.setName(Main.i18n("modpack.install", Main.i18n("modpack.type.curse")));
} else if (task instanceof MultiMCModpackInstallTask) { } else if (task instanceof MultiMCModpackInstallTask) {

View File

@@ -52,7 +52,7 @@ public class DefaultGameBuilder extends GameBuilder {
public Task buildAsync() { public Task buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> { return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
Version version = Constants.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class); Version version = Constants.GSON.fromJson(variables.<String>get(VersionJsonDownloadTask.ID), Version.class);
version = version.setId(name).setJar(null).resolve(new SimpleVersionProvider()); version = version.setId(name).setJar(null);
variables.set("version", version); variables.set("version", version);
Task result = new ParallelTask( Task result = new ParallelTask(
new GameAssetDownloadTask(dependencyManager, version), new GameAssetDownloadTask(dependencyManager, version),

View File

@@ -55,7 +55,7 @@ public final class GameAssetDownloadTask extends Task {
*/ */
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) { public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version.requireResolved(); this.version = version;
this.refreshTask = new GameAssetRefreshTask(dependencyManager, version); this.refreshTask = new GameAssetRefreshTask(dependencyManager, version);
this.dependents.add(refreshTask); this.dependents.add(refreshTask);
} }

View File

@@ -49,7 +49,7 @@ public final class GameAssetIndexDownloadTask extends Task {
*/ */
public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) { public GameAssetIndexDownloadTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version.requireResolved(); this.version = version;
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
} }

View File

@@ -54,7 +54,7 @@ public final class GameAssetRefreshTask extends TaskResult<Collection<Pair<File,
*/ */
public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) { public GameAssetRefreshTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version.requireResolved(); this.version = version;
this.assetIndexInfo = version.getAssetIndex(); this.assetIndexInfo = version.getAssetIndex();
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId()); this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());

View File

@@ -48,7 +48,7 @@ public final class GameLibrariesTask extends Task {
*/ */
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) { public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version.requireResolved(); this.version = version;
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
} }

View File

@@ -49,7 +49,7 @@ public final class GameLoggingDownloadTask extends Task {
*/ */
public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) { public GameLoggingDownloadTask(DependencyManager dependencyManager, Version version) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version.requireResolved(); this.version = version;
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
} }

View File

@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.task.TaskResult;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.SimpleMultimap; import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.VersionNumber; import org.jackhuang.hmcl.util.VersionNumber;
@@ -49,6 +50,7 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
for (Library library : libraries) { for (Library library : libraries) {
String id = library.getGroupId() + ":" + library.getArtifactId(); String id = library.getGroupId() + ":" + library.getArtifactId();
VersionNumber number = VersionNumber.asVersion(library.getVersion()); VersionNumber number = VersionNumber.asVersion(library.getVersion());
String serialized = Constants.GSON.toJson(library);
if (versionMap.containsKey(id)) { if (versionMap.containsKey(id)) {
VersionNumber otherNumber = versionMap.get(id); VersionNumber otherNumber = versionMap.get(id);
@@ -61,8 +63,15 @@ public class LibrariesUniqueTask extends TaskResult<Version> {
// prevent from duplicated libraries // prevent from duplicated libraries
for (Library otherLibrary : multimap.get(id)) for (Library otherLibrary : multimap.get(id))
if (library.equals(otherLibrary)) { if (library.equals(otherLibrary)) {
flag = true; String otherSerialized = Constants.GSON.toJson(otherLibrary);
break; // 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) if (!flag)
multimap.put(id, library); multimap.put(id, library);

View File

@@ -24,22 +24,32 @@ public final class LibraryDownloadTask extends Task {
private final File xzFile; private final File xzFile;
private final Library library; private final Library library;
private boolean downloaded = false;
public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) { 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()); String url = dependencyManager.getDownloadProvider().injectURL(library.getDownload().getUrl());
jar = file; jar = file;
this.library = library;
xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz"); xzFile = new File(file.getAbsoluteFile().getParentFile(), file.getName() + ".pack.xz");
xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"), xzTask = new FileDownloadTask(NetworkUtils.toURL(url + ".pack.xz"),
xzFile, dependencyManager.getProxy(), null, 1); 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()); file, dependencyManager.getProxy(), library.getDownload().getSha1());
} }
@Override @Override
public Collection<? extends Task> getDependents() { public Collection<? extends Task> getDependents() {
return Collections.singleton(xzTask); return library.getChecksums() != null ? Collections.singleton(xzTask) : Collections.emptySet();
} }
@Override @Override
@@ -47,23 +57,20 @@ public final class LibraryDownloadTask extends Task {
return false; return false;
} }
@Override
public Scheduler getScheduler() {
return Schedulers.io();
}
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
if (isDependentsSucceeded()) { if (isDependentsSucceeded() && library.getChecksums() != null) {
unpackLibrary(jar, FileUtils.readBytes(xzFile)); unpackLibrary(jar, FileUtils.readBytes(xzFile));
if (!checksumValid(jar, library.getChecksums())) if (!checksumValid(jar, library.getChecksums()))
throw new IOException("Checksum failed for " + library); throw new IOException("Checksum failed for " + library);
downloaded = true;
} }
} }
@Override @Override
public Collection<? extends Task> getDependencies() { public Collection<? extends Task> getDependencies() {
return isDependentsSucceeded() ? Collections.emptySet() : Collections.singleton(task); return downloaded ? Collections.emptySet() : Collections.singleton(task);
} }
private static boolean checksumValid(File libPath, List<String> checksums) { private static boolean checksumValid(File libPath, List<String> checksums) {

View File

@@ -40,11 +40,11 @@ public final class VersionJsonSaveTask extends Task {
* Constructor. * Constructor.
* *
* @param repository the game repository * @param repository the game repository
* @param version the **resolved** version * @param version the game version
*/ */
public VersionJsonSaveTask(DefaultGameRepository repository, Version version) { public VersionJsonSaveTask(DefaultGameRepository repository, Version version) {
this.repository = repository; this.repository = repository;
this.version = version.requireResolved(); this.version = version;
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
} }

View File

@@ -147,6 +147,10 @@ public class Library implements Comparable<Library> {
return checksums; return checksums;
} }
public boolean is(String groupId, String artifactId) {
return this.groupId.equals(groupId) && this.artifactId.equals(artifactId);
}
@Override @Override
public String toString() { public String toString() {
return new ToStringBuilder(this).append("name", getName()).toString(); return new ToStringBuilder(this).append("name", getName()).toString();
@@ -174,6 +178,10 @@ public class Library implements Comparable<Library> {
return Objects.hash(getName(), isNative()); 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) { public static Library fromName(String name) {
return fromName(name, null, null, null, null, null, null); return fromName(name, null, null, null, null, null, null);
} }

View File

@@ -143,13 +143,6 @@ public class Version implements Comparable<Version>, Validation {
return false; return false;
} }
public Version requireResolved() {
if (!resolved)
throw new RuntimeException("Version not resolved");
return this;
}
/** /**
* Resolve given version * Resolve given version
*/ */

View File

@@ -220,7 +220,6 @@ public class FileDownloadTask extends Task {
} catch (IOException | IllegalStateException e) { } catch (IOException | IllegalStateException e) {
if (temp != null) if (temp != null)
temp.delete(); temp.delete();
Logging.LOG.log(Level.WARNING, "Unable to download file " + currentURL, e);
exception = e; exception = e;
} finally { } finally {
closeFiles(); closeFiles();
@@ -228,7 +227,7 @@ public class FileDownloadTask extends Task {
} }
if (exception != null) if (exception != null)
throw exception; throw new IOException("Unable to download file " + currentURL, exception);
} }
} }

View File

@@ -133,6 +133,7 @@ public abstract class Task {
/** /**
* The collection of sub-tasks that should execute **after** this task running. * The collection of sub-tasks that should execute **after** this task running.
* Will not be executed if execution fails.
*/ */
public Collection<? extends Task> getDependencies() { public Collection<? extends Task> getDependencies() {
return Collections.emptySet(); return Collections.emptySet();
@@ -350,6 +351,7 @@ public abstract class Task {
public enum TaskState { public enum TaskState {
READY, READY,
RUNNING, RUNNING,
EXECUTED,
SUCCEEDED, SUCCEEDED,
FAILED FAILED
} }

View File

@@ -131,7 +131,7 @@ public final class TaskExecutor {
return false; return false;
} }
task.setState(Task.TaskState.RUNNING); task.setState(Task.TaskState.READY);
if (task.getSignificance().shouldLog()) if (task.getSignificance().shouldLog())
Logging.LOG.log(Level.FINE, "Executing task: {0}", task.getName()); Logging.LOG.log(Level.FINE, "Executing task: {0}", task.getName());
@@ -150,6 +150,9 @@ public final class TaskExecutor {
task.setVariables(variables); task.setVariables(variables);
task.setState(Task.TaskState.RUNNING);
taskListeners.forEach(it -> it.onRunning(task));
try { try {
task.getScheduler().schedule(task::execute).get(); task.getScheduler().schedule(task::execute).get();
} catch (ExecutionException e) { } catch (ExecutionException e) {
@@ -157,6 +160,8 @@ public final class TaskExecutor {
throw (Exception) e.getCause(); throw (Exception) e.getCause();
else else
throw e; throw e;
} finally {
task.setState(Task.TaskState.EXECUTED);
} }
if (task instanceof TaskResult<?>) { if (task instanceof TaskResult<?>) {
@@ -172,22 +177,24 @@ public final class TaskExecutor {
flag = true; flag = true;
if (task.getSignificance().shouldLog()) { if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINER, "Task finished: {0}", task.getName()); 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) { } catch (InterruptedException e) {
if (task.getSignificance().shouldLog()) { if (task.getSignificance().shouldLog()) {
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName()); 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) { } catch (SilentException | RejectedExecutionException e) {
// do nothing // do nothing
} catch (Exception e) { } catch (Exception e) {
lastException = e; lastException = e;
variables.set(LAST_EXCEPTION_ID, 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)); task.onDone().fireEvent(new TaskEvent(this, task, true));
taskListeners.forEach(it -> it.onFailed(task, e)); taskListeners.forEach(it -> it.onFailed(task, e));
} finally { } finally {

View File

@@ -31,6 +31,9 @@ public abstract class TaskListener implements EventListener {
public void onReady(Task task) { public void onReady(Task task) {
} }
public void onRunning(Task task) {
}
public void onFinished(Task task) { public void onFinished(Task task) {
} }

View File

@@ -74,9 +74,15 @@ public final class SimpleMultimap<K, V> {
return map.remove(key); return map.remove(key);
} }
public void removeValue(V value) { public boolean removeValue(V value) {
boolean flag = false;
for (Collection<V> c : map.values()) for (Collection<V> 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() { public void clear() {

View File

@@ -13,7 +13,7 @@ public class ToStringBuilder {
if (!first) if (!first)
stringBuilder.append(", "); stringBuilder.append(", ");
first = false; first = false;
stringBuilder.append(name).append('=').append(content.toString()); stringBuilder.append(name).append('=').append(content);
return this; return this;
} }