mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-04-19 21:16:30 +02:00
Commit WIP downloading process
This commit is contained in:
parent
917d10c448
commit
81c1ebaa15
@ -7,7 +7,18 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CompletionService;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorCompletionService;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonIOException;
|
import com.google.gson.JsonIOException;
|
||||||
@ -15,6 +26,7 @@ import com.google.gson.JsonSyntaxException;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.moandjiezana.toml.Toml;
|
import com.moandjiezana.toml.Toml;
|
||||||
|
|
||||||
|
import link.infra.packwiz.installer.metadata.IndexFile;
|
||||||
import link.infra.packwiz.installer.metadata.ManifestFile;
|
import link.infra.packwiz.installer.metadata.ManifestFile;
|
||||||
import link.infra.packwiz.installer.metadata.PackFile;
|
import link.infra.packwiz.installer.metadata.PackFile;
|
||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
import link.infra.packwiz.installer.metadata.hash.Hash;
|
||||||
@ -35,10 +47,8 @@ public class UpdateManager {
|
|||||||
|
|
||||||
public static enum Side {
|
public static enum Side {
|
||||||
@SerializedName("client")
|
@SerializedName("client")
|
||||||
CLIENT("client"),
|
CLIENT("client"), @SerializedName("server")
|
||||||
@SerializedName("server")
|
SERVER("server"), @SerializedName("both")
|
||||||
SERVER("server"),
|
|
||||||
@SerializedName("both")
|
|
||||||
BOTH("both", new Side[] { CLIENT, SERVER });
|
BOTH("both", new Side[] { CLIENT, SERVER });
|
||||||
|
|
||||||
private final String sideName;
|
private final String sideName;
|
||||||
@ -138,6 +148,8 @@ public class UpdateManager {
|
|||||||
|
|
||||||
System.out.println(pf.name);
|
System.out.println(pf.name);
|
||||||
|
|
||||||
|
processIndex(HandlerManager.getNewLoc(opts.downloadURI, pf.index.file),
|
||||||
|
new Hash(pf.index.hash, pf.index.hashFormat), manifest);
|
||||||
|
|
||||||
// When successfully updated
|
// When successfully updated
|
||||||
manifest.packFileHash = packFileHash;
|
manifest.packFileHash = packFileHash;
|
||||||
@ -155,4 +167,155 @@ public class UpdateManager {
|
|||||||
protected void checkOptions() {
|
protected void checkOptions() {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void processIndex(URI indexUri, Hash indexHash, ManifestFile manifest) {
|
||||||
|
Hash.HashInputStream indexFileStream;
|
||||||
|
try {
|
||||||
|
InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI);
|
||||||
|
if (stream == null) {
|
||||||
|
throw new Exception("Index file URI is invalid, is it supported?");
|
||||||
|
}
|
||||||
|
indexFileStream = new Hash.HashInputStream(stream, indexHash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO: still launch the game if updating doesn't work?
|
||||||
|
// TODO: ask user if they want to launch the game, exit(1) if they don't
|
||||||
|
ui.handleExceptionAndExit(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IndexFile indexFile;
|
||||||
|
try {
|
||||||
|
indexFile = new Toml().read(indexFileStream).to(IndexFile.class);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
ui.handleExceptionAndExit(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!indexFileStream.hashIsEqual()) {
|
||||||
|
System.out.println("Hash problems!!!!!!!");
|
||||||
|
// TODO: throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: progress bar
|
||||||
|
ConcurrentLinkedQueue<Exception> exceptionQueue = new ConcurrentLinkedQueue<Exception>();
|
||||||
|
List<IndexFile.File> newFiles = indexFile.files.stream().filter(f -> {
|
||||||
|
ManifestFile.File cachedFile = manifest.cachedFiles.get(f.file);
|
||||||
|
Hash newHash = new Hash(f.hash, f.hashFormat);
|
||||||
|
return cachedFile == null || newHash.equals(cachedFile.hash);
|
||||||
|
}).parallel().map(f -> {
|
||||||
|
try {
|
||||||
|
f.downloadMeta(indexFile, indexUri);
|
||||||
|
} catch (Exception e) {
|
||||||
|
exceptionQueue.add(e);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (Exception e : exceptionQueue) {
|
||||||
|
// TODO: collect all exceptions, present in one dialog
|
||||||
|
ui.handleException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: present options
|
||||||
|
// TODO: all options should be presented, not just new files!!!!!!!
|
||||||
|
// and options should be readded to newFiles after option -> true
|
||||||
|
newFiles.stream().filter(f -> f.linkedFile != null).filter(f -> f.linkedFile.option != null).map(f -> {
|
||||||
|
return "option: " + (f.linkedFile.option.description == null ? "null" : f.linkedFile.option.description);
|
||||||
|
}).forEachOrdered(desc -> {
|
||||||
|
System.out.println(desc);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: different thread pool type?
|
||||||
|
ExecutorService threadPool = Executors.newFixedThreadPool(10);
|
||||||
|
CompletionService<DownloadCompletion> completionService = new ExecutorCompletionService<DownloadCompletion>(
|
||||||
|
threadPool);
|
||||||
|
|
||||||
|
for (IndexFile.File f : newFiles) {
|
||||||
|
ManifestFile.File cachedFile = manifest.cachedFiles.get(f.file);
|
||||||
|
completionService.submit(new Callable<DownloadCompletion>() {
|
||||||
|
public DownloadCompletion call() {
|
||||||
|
DownloadCompletion dc = new DownloadCompletion();
|
||||||
|
dc.file = f;
|
||||||
|
|
||||||
|
if (cachedFile.linkedFileHash != null && f.linkedFile != null) {
|
||||||
|
try {
|
||||||
|
if (cachedFile.linkedFileHash.equals(f.linkedFile.getHash())) {
|
||||||
|
// Do nothing, the file didn't change
|
||||||
|
// TODO: but if the hash of the metafile changed, what did change?????
|
||||||
|
// should this be checked somehow??
|
||||||
|
return dc;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream stream = f.getInputStream(indexUri);
|
||||||
|
if (stream == null) {
|
||||||
|
throw new Exception("Failed to open download stream");
|
||||||
|
}
|
||||||
|
Hash.HashInputStream fileStream = new Hash.HashInputStream(stream, f.getHash());
|
||||||
|
// UGHHHHHH if you're reading this point in history
|
||||||
|
// this is the point where i change EVERYTHING to okio because it's 1000000000000 times nicer for this
|
||||||
|
byte[] data = fileStream.readAllBytes();
|
||||||
|
Files.copy(fileStream, Paths.get(opts.packFolder, f.getDestURI().toString()), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
return dc;
|
||||||
|
} catch (Exception e) {
|
||||||
|
dc.err = e;
|
||||||
|
return dc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < newFiles.size(); i++) {
|
||||||
|
DownloadCompletion ret;
|
||||||
|
try {
|
||||||
|
ret = completionService.take().get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
// TODO: collect all exceptions, present in one dialog
|
||||||
|
ui.handleException(e);
|
||||||
|
ret = null;
|
||||||
|
}
|
||||||
|
// Update manifest
|
||||||
|
if (ret != null && ret.err == null && ret.file != null) {
|
||||||
|
ManifestFile.File newCachedFile = new ManifestFile.File();
|
||||||
|
try {
|
||||||
|
newCachedFile.hash = ret.file.getHash();
|
||||||
|
if (newCachedFile.hash == null) {
|
||||||
|
throw new Exception("Invalid hash!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
ret.err = e;
|
||||||
|
}
|
||||||
|
if (ret.file.metafile && ret.file.linkedFile != null) {
|
||||||
|
newCachedFile.isOptional = ret.file.linkedFile.isOptional();
|
||||||
|
if (newCachedFile.isOptional) {
|
||||||
|
newCachedFile.optionValue = ret.file.optionValue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
newCachedFile.linkedFileHash = ret.file.linkedFile.getHash();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ret.err = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: show errors properly?
|
||||||
|
String progress;
|
||||||
|
if (ret != null && ret.file != null) {
|
||||||
|
progress = "Downloaded " + ret.file.getName();
|
||||||
|
} else if (ret.err != null) {
|
||||||
|
progress = "Failed to download: " + ret.err.getMessage();
|
||||||
|
} else {
|
||||||
|
progress = "Failed to download, unknown reason";
|
||||||
|
}
|
||||||
|
ui.submitProgress(new InstallProgress(progress, i + 1, newFiles.size()));
|
||||||
|
}
|
||||||
|
// option = false file hashes should be stored to disk, but not downloaded
|
||||||
|
// TODO: don't include optional files in progress????
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DownloadCompletion {
|
||||||
|
Exception err;
|
||||||
|
IndexFile.File file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
package link.infra.packwiz.installer.metadata;
|
package link.infra.packwiz.installer.metadata;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.moandjiezana.toml.Toml;
|
||||||
|
|
||||||
|
import link.infra.packwiz.installer.metadata.hash.Hash;
|
||||||
|
import link.infra.packwiz.installer.request.HandlerManager;
|
||||||
|
|
||||||
public class IndexFile {
|
public class IndexFile {
|
||||||
@SerializedName("hash-format")
|
@SerializedName("hash-format")
|
||||||
@ -10,12 +17,88 @@ public class IndexFile {
|
|||||||
public List<File> files;
|
public List<File> files;
|
||||||
|
|
||||||
public static class File {
|
public static class File {
|
||||||
public String file;
|
public URI file;
|
||||||
@SerializedName("hash-format")
|
@SerializedName("hash-format")
|
||||||
public String hashFormat;
|
public String hashFormat;
|
||||||
public String hash;
|
public String hash;
|
||||||
|
// TODO: implement
|
||||||
public String alias;
|
public String alias;
|
||||||
public boolean metafile;
|
public boolean metafile;
|
||||||
|
// TODO: implement
|
||||||
public boolean preserve;
|
public boolean preserve;
|
||||||
|
|
||||||
|
public transient ModFile linkedFile;
|
||||||
|
public transient URI linkedFileURI;
|
||||||
|
public transient boolean optionValue = true;
|
||||||
|
|
||||||
|
public void downloadMeta(IndexFile parentIndexFile, URI indexUri) throws Exception {
|
||||||
|
if (!metafile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hashFormat == null || hashFormat.length() == 0) {
|
||||||
|
hashFormat = parentIndexFile.hashFormat;
|
||||||
|
}
|
||||||
|
Hash fileHash = new Hash(hash, hashFormat);
|
||||||
|
linkedFileURI = HandlerManager.getNewLoc(indexUri, file);
|
||||||
|
InputStream stream = HandlerManager.getFileInputStream(linkedFileURI);
|
||||||
|
if (stream == null) {
|
||||||
|
throw new Exception("Index file URI is invalid, is it supported?");
|
||||||
|
}
|
||||||
|
Hash.HashInputStream fileStream = new Hash.HashInputStream(stream, fileHash);
|
||||||
|
|
||||||
|
linkedFile = new Toml().read(fileStream).to(ModFile.class);
|
||||||
|
if (!fileStream.hashIsEqual()) {
|
||||||
|
throw new Exception("Invalid mod file hash");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream(URI indexUri) throws Exception {
|
||||||
|
if (metafile) {
|
||||||
|
if (linkedFile == null) {
|
||||||
|
throw new Exception("Linked file doesn't exist!");
|
||||||
|
}
|
||||||
|
return linkedFile.getInputStream(linkedFileURI);
|
||||||
|
} else {
|
||||||
|
URI newLoc = HandlerManager.getNewLoc(indexUri, file);
|
||||||
|
if (newLoc == null) {
|
||||||
|
throw new Exception("Index file URI is invalid");
|
||||||
|
}
|
||||||
|
return HandlerManager.getFileInputStream(newLoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hash getHash() throws Exception {
|
||||||
|
if (hash == null) {
|
||||||
|
throw new Exception("Index file doesn't have a hash");
|
||||||
|
}
|
||||||
|
if (hashFormat == null) {
|
||||||
|
throw new Exception("Index file doesn't have a hash format");
|
||||||
|
}
|
||||||
|
return new Hash(hash, hashFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
if (metafile) {
|
||||||
|
if (linkedFile != null) {
|
||||||
|
if (linkedFile.name != null) {
|
||||||
|
return linkedFile.name;
|
||||||
|
} else if (linkedFile.filename != null) {
|
||||||
|
return linkedFile.filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file != null) {
|
||||||
|
return Paths.get(file.getPath()).getFileName().toString();
|
||||||
|
}
|
||||||
|
return file.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI getDestURI() {
|
||||||
|
if (metafile && linkedFile != null) {
|
||||||
|
return file.resolve(linkedFile.filename);
|
||||||
|
} else {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,20 @@
|
|||||||
package link.infra.packwiz.installer.metadata;
|
package link.infra.packwiz.installer.metadata;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
import link.infra.packwiz.installer.metadata.hash.Hash;
|
||||||
|
|
||||||
public class ManifestFile {
|
public class ManifestFile {
|
||||||
|
|
||||||
public Hash packFileHash = null;
|
public Hash packFileHash = null;
|
||||||
public Hash indexFileHash = null;
|
public Hash indexFileHash = null;
|
||||||
|
public Map<URI, File> cachedFiles;
|
||||||
|
|
||||||
public static class File {
|
public static class File {
|
||||||
public Hash hash = null;
|
public Hash hash = null;
|
||||||
|
public boolean isOptional = false;
|
||||||
|
public boolean optionValue = true;
|
||||||
|
public Hash linkedFileHash = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,16 @@
|
|||||||
package link.infra.packwiz.installer.metadata;
|
package link.infra.packwiz.installer.metadata;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import link.infra.packwiz.installer.UpdateManager.Options.Side;
|
import link.infra.packwiz.installer.UpdateManager.Options.Side;
|
||||||
|
import link.infra.packwiz.installer.metadata.hash.Hash;
|
||||||
|
import link.infra.packwiz.installer.request.HandlerManager;
|
||||||
|
|
||||||
class ModFile {
|
public class ModFile {
|
||||||
public String name;
|
public String name;
|
||||||
public String filename;
|
public String filename;
|
||||||
public Side side;
|
public Side side;
|
||||||
@ -30,4 +33,39 @@ class ModFile {
|
|||||||
public boolean defaultValue;
|
public boolean defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream(URI baseLoc) throws Exception {
|
||||||
|
if (download == null) {
|
||||||
|
throw new Exception("Metadata file doesn't have download");
|
||||||
|
}
|
||||||
|
if (download.url == null) {
|
||||||
|
throw new Exception("Metadata file doesn't have a download URI");
|
||||||
|
}
|
||||||
|
URI newLoc = HandlerManager.getNewLoc(baseLoc, download.url);
|
||||||
|
if (newLoc == null) {
|
||||||
|
throw new Exception("Metadata file URI is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
return HandlerManager.getFileInputStream(newLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hash getHash() throws Exception {
|
||||||
|
if (download == null) {
|
||||||
|
throw new Exception("Metadata file doesn't have download");
|
||||||
|
}
|
||||||
|
if (download.hash == null) {
|
||||||
|
throw new Exception("Metadata file doesn't have a hash");
|
||||||
|
}
|
||||||
|
if (download.hashFormat == null) {
|
||||||
|
throw new Exception("Metadata file doesn't have a hash format");
|
||||||
|
}
|
||||||
|
return new Hash(download.hash, download.hashFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOptional() {
|
||||||
|
if (option != null) {
|
||||||
|
return option.optional;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package link.infra.packwiz.installer.metadata;
|
package link.infra.packwiz.installer.metadata;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
@ -9,7 +10,7 @@ public class PackFile {
|
|||||||
|
|
||||||
public IndexFileLoc index;
|
public IndexFileLoc index;
|
||||||
public static class IndexFileLoc {
|
public static class IndexFileLoc {
|
||||||
public String file;
|
public URI file;
|
||||||
@SerializedName("hash-format")
|
@SerializedName("hash-format")
|
||||||
public String hashFormat;
|
public String hashFormat;
|
||||||
public String hash;
|
public String hash;
|
||||||
|
@ -104,11 +104,11 @@ public class Hash {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNew() {
|
public boolean hashIsEqual() {
|
||||||
if (output != null) {
|
if (output == null) {
|
||||||
return !output.equals(compare);
|
get();
|
||||||
}
|
}
|
||||||
return false;
|
return !output.equals(compare);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ public abstract class HandlerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static URI getNewLoc(URI base, URI loc) {
|
public static URI getNewLoc(URI base, URI loc) {
|
||||||
|
if (loc == null) return null;
|
||||||
if (base != null) {
|
if (base != null) {
|
||||||
loc = base.resolve(loc);
|
loc = base.resolve(loc);
|
||||||
}
|
}
|
||||||
if (loc == null) return null;
|
|
||||||
|
|
||||||
for (IRequestHandler handler : handlers) {
|
for (IRequestHandler handler : handlers) {
|
||||||
if (handler.matchesHandler(loc)) {
|
if (handler.matchesHandler(loc)) {
|
||||||
@ -38,10 +38,16 @@ public abstract class HandlerManager {
|
|||||||
public static InputStream getFileInputStream(URI loc) throws Exception {
|
public static InputStream getFileInputStream(URI loc) throws Exception {
|
||||||
for (IRequestHandler handler : handlers) {
|
for (IRequestHandler handler : handlers) {
|
||||||
if (handler.matchesHandler(loc)) {
|
if (handler.matchesHandler(loc)) {
|
||||||
return handler.getFileInputStream(loc);
|
InputStream stream = handler.getFileInputStream(loc);
|
||||||
|
if (stream == null) {
|
||||||
|
throw new Exception("Couldn't find URI: " + loc.toString());
|
||||||
|
} else {
|
||||||
|
return stream;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
// TODO: specialised exception classes??
|
||||||
|
throw new Exception("No handler available for URI: " + loc.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// To enqueue stuff:
|
// To enqueue stuff:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user