Modpack updates UI

This commit is contained in:
huangyuhui
2018-01-27 12:56:45 +08:00
parent e8316de160
commit 1a572be52e
39 changed files with 617 additions and 117 deletions

View File

@@ -41,6 +41,14 @@ public class DefaultGameBuilder extends GameBuilder {
this.downloadProvider = dependencyManager.getDownloadProvider();
}
public DefaultDependencyManager getDependencyManager() {
return dependencyManager;
}
public DownloadProvider getDownloadProvider() {
return downloadProvider;
}
@Override
public Task buildAsync() {
return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> {
@@ -50,7 +58,7 @@ public class DefaultGameBuilder extends GameBuilder {
Task result = new ParallelTask(
new GameAssetDownloadTask(dependencyManager, version),
new GameLoggingDownloadTask(dependencyManager, version),
new GameDownloadTask(dependencyManager, version),
downloadGameAsync(gameVersion, version),
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.
@@ -68,4 +76,8 @@ public class DefaultGameBuilder extends GameBuilder {
return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId));
}
protected Task downloadGameAsync(String gameVersion, Version version) {
return new GameDownloadTask(dependencyManager, version);
}
}

View File

@@ -310,4 +310,12 @@ public class DefaultGameRepository implements GameRepository {
return loaded;
}
public File getModpackConfiguration(String version) {
return new File(getRunDirectory(version), "modpack.json");
}
public boolean isModpack(String version) {
return getModpackConfiguration(version).exists();
}
}

View File

@@ -23,7 +23,6 @@ import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.GameBuilder;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.CompressingUtils;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.FileUtils;
@@ -32,7 +31,6 @@ import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;
/**
* Install a downloaded CurseForge modpack.
@@ -68,7 +66,7 @@ public final class CurseInstallTask extends Task {
this.repository = dependencyManager.getGameRepository();
this.run = repository.getRunDirectory(name);
File json = new File(run, "modpack.json");
File json = repository.getModpackConfiguration(name);
if (repository.hasVersion(name) && !json.exists())
throw new IllegalArgumentException("Version " + name + " already exists.");
@@ -80,8 +78,13 @@ public final class CurseInstallTask extends Task {
ModpackConfiguration<CurseManifest> config = null;
try {
if (json.exists())
config = Constants.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<CurseManifest>>(){}.getType());
if (json.exists()) {
config = Constants.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<CurseManifest>>() {
}.getType());
if (!MODPACK_TYPE.equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a Curse modpack. Cannot update this version.");
}
} catch (JsonParseException | IOException ignore) {
}
this.config = config;
@@ -110,7 +113,8 @@ public final class CurseInstallTask extends Task {
}
dependencies.add(new CurseCompletionTask(dependencyManager, name));
dependencies.add(new MinecraftInstanceTask<>(zipFile, manifest.getOverrides(), false, manifest, new File(run, "modpack.json")));
dependencies.add(new MinecraftInstanceTask<>(zipFile, manifest.getOverrides(), manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
}
public static final String MODPACK_TYPE = "Curse";
}

View File

@@ -23,11 +23,12 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Constants;
import org.jackhuang.hmcl.util.DigestUtils;
import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public final class MinecraftInstanceTask<T> extends Task {
@@ -36,12 +37,14 @@ public final class MinecraftInstanceTask<T> extends Task {
private final String subDirectory;
private final File jsonFile;
private final T manifest;
private final String type;
public MinecraftInstanceTask(File zipFile, String subDirectory, T manifest, File jsonFile) {
public MinecraftInstanceTask(File zipFile, String subDirectory, T manifest, String type, File jsonFile) {
this.zipFile = zipFile;
this.subDirectory = subDirectory;
this.manifest = manifest;
this.jsonFile = jsonFile;
this.type = type;
if (!zipFile.exists())
throw new IllegalArgumentException("File " + zipFile + " does not exist. Cannot parse this modpack.");
@@ -49,9 +52,8 @@ public final class MinecraftInstanceTask<T> extends Task {
@Override
public void execute() throws Exception {
Map<String, ModpackConfiguration.FileInformation> overrides = new HashMap<>();
List<ModpackConfiguration.FileInformation> overrides = new LinkedList<>();
byte[] buf = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
try (ZipArchiveInputStream zip = new ZipArchiveInputStream(new FileInputStream(zipFile), null, true, true)) {
ArchiveEntry entry;
while ((entry = zip.getNextEntry()) != null) {
@@ -62,12 +64,10 @@ public final class MinecraftInstanceTask<T> extends Task {
if (path.startsWith("/") || path.startsWith("\\"))
path = path.substring(1);
overrides.put(path, new ModpackConfiguration.FileInformation(
path, DigestUtils.sha1Hex(zip)
));
overrides.add(new ModpackConfiguration.FileInformation(path, DigestUtils.sha1Hex(zip)));
}
}
FileUtils.writeText(jsonFile, Constants.GSON.toJson(new ModpackConfiguration<>(manifest, overrides)));
FileUtils.writeText(jsonFile, Constants.GSON.toJson(new ModpackConfiguration<>(manifest, type, overrides)));
}
}

View File

@@ -0,0 +1,30 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.mod;
public class MismatchedModpackTypeException extends Exception {
private final String required;
private final String found;
public MismatchedModpackTypeException(String required, String found) {
super("Required " + required + ", but found " + found);
this.required = required;
this.found = found;
}
}

View File

@@ -21,50 +21,56 @@ import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.util.Immutable;
import org.jackhuang.hmcl.util.Validation;
import java.util.Collections;
import java.util.Map;
import java.util.*;
@Immutable
public final class ModpackConfiguration<T> implements Validation {
private final T manifest;
private final Map<String, FileInformation> overrides;
private final String type;
private final List<FileInformation> overrides;
public ModpackConfiguration() {
this(null, Collections.emptyMap());
this(null, null, Collections.emptyList());
}
public ModpackConfiguration(T manifest, Map<String, FileInformation> overrides) {
public ModpackConfiguration(T manifest, String type, List<FileInformation> overrides) {
this.manifest = manifest;
this.overrides = overrides;
this.type = type;
this.overrides = new ArrayList<>(overrides);
}
public T getManifest() {
return manifest;
}
public String getType() {
return type;
}
public ModpackConfiguration<T> setManifest(T manifest) {
return new ModpackConfiguration<>(manifest, overrides);
return new ModpackConfiguration<>(manifest, type, overrides);
}
public ModpackConfiguration<T> setOverrides(Map<String, FileInformation> overrides) {
return new ModpackConfiguration<>(manifest, overrides);
public ModpackConfiguration<T> setOverrides(List<FileInformation> overrides) {
return new ModpackConfiguration<>(manifest, type, overrides);
}
public Map<String, FileInformation> getOverrides() {
return Collections.unmodifiableMap(overrides);
public List<FileInformation> getOverrides() {
return Collections.unmodifiableList(overrides);
}
@Override
public void validate() throws JsonParseException {
if (manifest == null)
throw new JsonParseException("MinecraftInstanceConfiguration missing `manifest`");
if (type == null)
throw new JsonParseException("MinecraftInstanceConfiguration missing `type`");
}
@Immutable
public static class FileInformation implements Validation {
private final String location; // relative
private final String path; // relative
private final String hash;
private final String downloadURL;
@@ -72,18 +78,22 @@ public final class ModpackConfiguration<T> implements Validation {
this(null, null);
}
public FileInformation(String location, String hash) {
this(location, hash, null);
public FileInformation(String path, String hash) {
this(path, hash, null);
}
public FileInformation(String location, String hash, String downloadURL) {
this.location = location;
public FileInformation(String path, String hash, String downloadURL) {
this.path = path;
this.hash = hash;
this.downloadURL = downloadURL;
}
public String getLocation() {
return location;
/**
* The relative path to Minecraft run directory
* @return
*/
public String getPath() {
return path;
}
public String getDownloadURL() {
@@ -96,8 +106,8 @@ public final class ModpackConfiguration<T> implements Validation {
@Override
public void validate() throws JsonParseException {
if (location == null)
throw new JsonParseException("FileInformation missing `location`.");
if (path == null)
throw new JsonParseException("FileInformation missing `path`.");
if (hash == null)
throw new JsonParseException("FileInformation missing file hash code.");
}

View File

@@ -25,10 +25,7 @@ import org.jackhuang.hmcl.util.FileUtils;
import org.jackhuang.hmcl.util.IOUtils;
import java.io.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Predicate;
public class ModpackInstallTask<T> extends Task {
@@ -36,7 +33,7 @@ public class ModpackInstallTask<T> extends Task {
private final File modpackFile;
private final File dest;
private final String subDirectory;
private final Map<String, ModpackConfiguration.FileInformation> overrides;
private final List<ModpackConfiguration.FileInformation> overrides;
private final Predicate<String> callback;
public ModpackInstallTask(File modpackFile, File dest, String subDirectory, Predicate<String> callback, ModpackConfiguration<T> oldConfiguration) {
@@ -46,7 +43,7 @@ public class ModpackInstallTask<T> extends Task {
this.callback = callback;
if (oldConfiguration == null)
overrides = Collections.emptyMap();
overrides = Collections.emptyList();
else
overrides = oldConfiguration.getOverrides();
}
@@ -57,6 +54,11 @@ public class ModpackInstallTask<T> extends Task {
byte[] buf = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
if (!FileUtils.makeDirectory(dest))
throw new IOException("Unable to make directory " + dest);
HashSet<String> files = new HashSet<>();
for (ModpackConfiguration.FileInformation file : overrides)
files.add(file.getPath());
try (ZipArchiveInputStream zipStream = new ZipArchiveInputStream(new FileInputStream(modpackFile), null, true, true)) {
ArchiveEntry entry;
while ((entry = zipStream.getNextEntry()) != null) {
@@ -86,7 +88,7 @@ public class ModpackInstallTask<T> extends Task {
IOUtils.copyTo(zipStream, os, buf);
byte[] data = os.toByteArray();
if (!overrides.containsKey(path) || entryFile.exists()) {
if (files.contains(path) && entryFile.exists()) {
String oldHash = DigestUtils.sha1Hex(new FileInputStream(entryFile));
String newHash = DigestUtils.sha1Hex(new ByteArrayInputStream(data));
if (!oldHash.equals(newHash)) {
@@ -94,15 +96,19 @@ public class ModpackInstallTask<T> extends Task {
IOUtils.copyTo(new ByteArrayInputStream(data), fos, buf);
}
}
} else if (!files.contains(path)) {
try (FileOutputStream fos = new FileOutputStream(entryFile)) {
IOUtils.copyTo(new ByteArrayInputStream(data), fos, buf);
}
}
}
}
}
for (String path : overrides.keySet()) {
File original = new File(dest, path);
if (original.exists() && !entries.contains(path))
for (ModpackConfiguration.FileInformation file : overrides) {
File original = new File(dest, file.getPath());
if (original.exists() && !entries.contains(file.getPath()))
original.delete();
}
}

View File

@@ -58,7 +58,7 @@ public final class MultiMCModpackInstallTask extends Task {
this.repository = dependencyManager.getGameRepository();
this.run = repository.getRunDirectory(name);
File json = new File(run, "modpack.json");
File json = repository.getModpackConfiguration(name);
if (repository.hasVersion(name) && !json.exists())
throw new IllegalArgumentException("Version " + name + " already exists.");
dependents.add(dependencyManager.gameBuilder().name(name).gameVersion(manifest.getGameVersion()).buildAsync());
@@ -69,8 +69,13 @@ public final class MultiMCModpackInstallTask extends Task {
ModpackConfiguration<MultiMCInstanceConfiguration> config = null;
try {
if (json.exists())
config = Constants.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<MultiMCInstanceConfiguration>>(){}.getType());
if (json.exists()) {
config = Constants.GSON.fromJson(FileUtils.readText(json), new TypeToken<ModpackConfiguration<MultiMCInstanceConfiguration>>() {
}.getType());
if (!MODPACK_TYPE.equals(config.getType()))
throw new IllegalArgumentException("Version " + name + " is not a MultiMC modpack. Cannot update this version.");
}
} catch (JsonParseException | IOException ignore) {
}
@@ -110,7 +115,8 @@ public final class MultiMCModpackInstallTask extends Task {
}
dependencies.add(new VersionJsonSaveTask(repository, version));
dependencies.add(new MinecraftInstanceTask<>(zipFile, manifest.getName() + "/minecraft/", manifest, new File(run, "modpack.json")));
dependencies.add(new MinecraftInstanceTask<>(zipFile, manifest.getName() + "/minecraft/", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name)));
}
public static final String MODPACK_TYPE = "MultiMC";
}

View File

@@ -0,0 +1,35 @@
/*
* Hello Minecraft! Launcher.
* Copyright (C) 2017 huangyuhui <huanghongxun2008@126.com>
*
* 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 {http://www.gnu.org/licenses/}.
*/
package org.jackhuang.hmcl.mod;
public class UnsupportedModpackException extends Exception {
public UnsupportedModpackException() {
}
public UnsupportedModpackException(String message) {
super(message);
}
public UnsupportedModpackException(String message, Throwable cause) {
super(message, cause);
}
public UnsupportedModpackException(Throwable cause) {
super(cause);
}
}

View File

@@ -114,7 +114,7 @@ public class FileDownloadTask extends Task {
@Override
public void execute() throws Exception {
URL currentURL = url;
Logging.LOG.log(Level.FINER, "Downloading {0}, to {1}", new Object[] { currentURL, file });
Logging.LOG.log(Level.FINER, "Downloading {0} to {1}", new Object[] { currentURL, file });
Exception exception = null;
for (int repeat = 0; repeat < retry; repeat++) {
@@ -193,7 +193,7 @@ public class FileDownloadTask extends Task {
Thread.currentThread().interrupt();
break;
} else {
if (file.exists() || !file.delete())
if (file.exists() && !file.delete())
throw new IOException("Unable to delete existent file " + file);
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
throw new IOException("Unable to make parent directory " + file);