mirror of
https://github.com/packwiz/packwiz-installer.git
synced 2025-04-19 21:16:30 +02:00
Start rewrite of downloading system, THIS IS SCARY HELP
This commit is contained in:
parent
bd95bc15ad
commit
320e56e74e
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
@ -1,5 +1,126 @@
|
|||||||
package link.infra.packwiz.installer;
|
package link.infra.packwiz.installer;
|
||||||
|
|
||||||
class DownloadTask {
|
import link.infra.packwiz.installer.metadata.IndexFile;
|
||||||
|
import link.infra.packwiz.installer.metadata.ManifestFile;
|
||||||
|
import link.infra.packwiz.installer.metadata.hash.Hash;
|
||||||
|
import link.infra.packwiz.installer.metadata.hash.HashUtils;
|
||||||
|
import link.infra.packwiz.installer.ui.IOptionDetails;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class DownloadTask implements IOptionDetails {
|
||||||
|
final IndexFile.File metadata;
|
||||||
|
private Exception failure = null;
|
||||||
|
private boolean complete = false;
|
||||||
|
private boolean invalidated = false;
|
||||||
|
private boolean optionValue = true;
|
||||||
|
// If file is new or isOptional changed to true, the option needs to be presented again
|
||||||
|
private boolean newOptional = true;
|
||||||
|
|
||||||
|
public DownloadTask(IndexFile.File metadata) {
|
||||||
|
this.metadata = metadata;
|
||||||
|
if (this.metadata.linkedFile != null) {
|
||||||
|
if (this.metadata.linkedFile.option != null) {
|
||||||
|
// Set option to it's default value
|
||||||
|
optionValue = this.metadata.linkedFile.option.defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultHashFormat(String format) {
|
||||||
|
if (failure != null || complete) return;
|
||||||
|
if (metadata.hashFormat == null || metadata.hashFormat.length() == 0) {
|
||||||
|
metadata.hashFormat = format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
invalidated = true;
|
||||||
|
complete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFromCache(ManifestFile.File cachedFile) {
|
||||||
|
if (failure != null || complete) return;
|
||||||
|
if (cachedFile == null) return;
|
||||||
|
|
||||||
|
if (!invalidated) {
|
||||||
|
Hash currHash = null;
|
||||||
|
try {
|
||||||
|
currHash = HashUtils.getHash(metadata.hashFormat, metadata.hash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
failure = e;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currHash != null && currHash.equals(cachedFile.hash)) {
|
||||||
|
// Already up to date
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cachedFile.isOptional) {
|
||||||
|
// Set option to the cached value
|
||||||
|
optionValue = cachedFile.optionValue;
|
||||||
|
if (isOptional()) {
|
||||||
|
// isOptional didn't change
|
||||||
|
newOptional = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void downloadMetadata(IndexFile parentIndexFile, URI indexUri) {
|
||||||
|
if (failure != null || complete) return;
|
||||||
|
try {
|
||||||
|
metadata.downloadMeta(parentIndexFile, indexUri);
|
||||||
|
} catch (Exception e) {
|
||||||
|
failure = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exception getException() {
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOptional() {
|
||||||
|
if (metadata.linkedFile != null) {
|
||||||
|
return metadata.linkedFile.isOptional();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNewOptional() {
|
||||||
|
return isOptional() && this.newOptional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return metadata.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getOptionValue() {
|
||||||
|
return this.optionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOptionDescription() {
|
||||||
|
if (metadata.linkedFile != null) {
|
||||||
|
return metadata.linkedFile.option.description;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOptionValue(boolean value) {
|
||||||
|
// TODO: if this is false, ensure the file is deleted in the actual download stage
|
||||||
|
this.optionValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<DownloadTask> createTasksFromIndex(IndexFile index) {
|
||||||
|
ArrayList<DownloadTask> tasks = new ArrayList<>();
|
||||||
|
for (IndexFile.File file : index.files) {
|
||||||
|
tasks.add(new DownloadTask(file));
|
||||||
|
}
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -13,6 +13,7 @@ import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
|
|||||||
import link.infra.packwiz.installer.metadata.hash.Hash;
|
import link.infra.packwiz.installer.metadata.hash.Hash;
|
||||||
import link.infra.packwiz.installer.metadata.hash.HashUtils;
|
import link.infra.packwiz.installer.metadata.hash.HashUtils;
|
||||||
import link.infra.packwiz.installer.request.HandlerManager;
|
import link.infra.packwiz.installer.request.HandlerManager;
|
||||||
|
import link.infra.packwiz.installer.ui.IOptionDetails;
|
||||||
import link.infra.packwiz.installer.ui.IUserInterface;
|
import link.infra.packwiz.installer.ui.IUserInterface;
|
||||||
import link.infra.packwiz.installer.ui.InstallProgress;
|
import link.infra.packwiz.installer.ui.InstallProgress;
|
||||||
import okio.Buffer;
|
import okio.Buffer;
|
||||||
@ -133,16 +134,27 @@ public class UpdateManager {
|
|||||||
|
|
||||||
ui.submitProgress(new InstallProgress("Checking local files..."));
|
ui.submitProgress(new InstallProgress("Checking local files..."));
|
||||||
|
|
||||||
|
// Invalidation checking must be done here, as it must happen before pack/index hashes are checked
|
||||||
List<URI> invalidatedUris = new ArrayList<>();
|
List<URI> invalidatedUris = new ArrayList<>();
|
||||||
if (manifest.cachedFiles != null) {
|
if (manifest.cachedFiles != null) {
|
||||||
for (Map.Entry<URI, ManifestFile.File> entry : manifest.cachedFiles.entrySet()) {
|
for (Map.Entry<URI, ManifestFile.File> entry : manifest.cachedFiles.entrySet()) {
|
||||||
if (entry.getValue().cachedLocation != null) {
|
boolean invalid = false;
|
||||||
if (!Files.exists(Paths.get(opts.packFolder, entry.getValue().cachedLocation))) {
|
// if isn't optional, or is optional but optionValue == true
|
||||||
URI fileUri = entry.getKey();
|
if (!entry.getValue().isOptional || entry.getValue().optionValue) {
|
||||||
System.out.println("File " + fileUri.toString() + " invalidated, marked for redownloading");
|
if (entry.getValue().cachedLocation != null) {
|
||||||
invalidatedUris.add(fileUri);
|
if (!Files.exists(Paths.get(opts.packFolder, entry.getValue().cachedLocation))) {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if cachedLocation == null, should probably be installed!!
|
||||||
|
invalid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (invalid) {
|
||||||
|
URI fileUri = entry.getKey();
|
||||||
|
System.out.println("File " + fileUri.toString() + " invalidated, marked for redownloading");
|
||||||
|
invalidatedUris.add(fileUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +167,7 @@ public class UpdateManager {
|
|||||||
System.out.println("Modpack name: " + pf.name);
|
System.out.println("Modpack name: " + pf.name);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// This is badly written, I'll probably heavily refactor it at some point
|
||||||
processIndex(HandlerManager.getNewLoc(opts.downloadURI, pf.index.file),
|
processIndex(HandlerManager.getNewLoc(opts.downloadURI, pf.index.file),
|
||||||
HashUtils.getHash(pf.index.hashFormat, pf.index.hash), pf.index.hashFormat, manifest, invalidatedUris);
|
HashUtils.getHash(pf.index.hashFormat, pf.index.hash), pf.index.hashFormat, manifest, invalidatedUris);
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
@ -216,60 +229,57 @@ public class UpdateManager {
|
|||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Map.Entry<URI, ManifestFile.File> entry = it.next();
|
Map.Entry<URI, ManifestFile.File> entry = it.next();
|
||||||
if (entry.getValue().cachedLocation != null) {
|
if (entry.getValue().cachedLocation != null) {
|
||||||
if (indexFile.files.stream().noneMatch(f -> f.file.equals(entry.getKey()))) {
|
boolean alreadyDeleted = false;
|
||||||
// File has been removed from the index
|
// Delete if option value has been set to false
|
||||||
|
if (entry.getValue().isOptional && !entry.getValue().optionValue) {
|
||||||
try {
|
try {
|
||||||
Files.deleteIfExists(Paths.get(opts.packFolder, entry.getValue().cachedLocation));
|
Files.deleteIfExists(Paths.get(opts.packFolder, entry.getValue().cachedLocation));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO: should this be shown to the user in some way?
|
// TODO: should this be shown to the user in some way?
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
// Set to null, as it doesn't exist anymore
|
||||||
|
entry.getValue().cachedLocation = null;
|
||||||
|
alreadyDeleted = true;
|
||||||
|
}
|
||||||
|
if (indexFile.files.stream().noneMatch(f -> f.file.equals(entry.getKey()))) {
|
||||||
|
// File has been removed from the index
|
||||||
|
if (!alreadyDeleted) {
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(Paths.get(opts.packFolder, entry.getValue().cachedLocation));
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO: should this be shown to the user in some way?
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.submitProgress(new InstallProgress("Comparing new files..."));
|
ui.submitProgress(new InstallProgress("Comparing new files..."));
|
||||||
|
|
||||||
// TODO: progress bar
|
// TODO: progress bar, parallelify
|
||||||
ConcurrentLinkedQueue<Exception> exceptionQueue = new ConcurrentLinkedQueue<>();
|
List<DownloadTask> tasks = DownloadTask.createTasksFromIndex(indexFile);
|
||||||
List<IndexFile.File> newFiles = indexFile.files.stream().peek(f -> {
|
tasks.forEach(f -> f.setDefaultHashFormat(indexFile.hashFormat));
|
||||||
if (f.hashFormat == null || f.hashFormat.length() == 0) {
|
tasks.forEach(f -> {
|
||||||
f.hashFormat = indexFile.hashFormat;
|
// TODO: should linkedfile be checked as well? should a getter be used?
|
||||||
|
if (invalidatedUris.contains(f.metadata.file)) {
|
||||||
|
f.invalidate();
|
||||||
|
} else {
|
||||||
|
f.updateFromCache(manifest.cachedFiles.get(f.metadata.file));
|
||||||
}
|
}
|
||||||
}).filter(f -> {
|
|
||||||
if (invalidatedUris.contains(f.file)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ManifestFile.File cachedFile = manifest.cachedFiles.get(f.file);
|
|
||||||
Hash newHash;
|
|
||||||
try {
|
|
||||||
newHash = HashUtils.getHash(f.hashFormat, f.hash);
|
|
||||||
} catch (Exception e) {
|
|
||||||
exceptionQueue.add(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return cachedFile == null || !newHash.equals(cachedFile.hash);
|
|
||||||
}).parallel().peek(f -> {
|
|
||||||
try {
|
|
||||||
f.downloadMeta(indexFile, indexUri);
|
|
||||||
} catch (Exception e) {
|
|
||||||
exceptionQueue.add(e);
|
|
||||||
}
|
|
||||||
}).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 ->
|
|
||||||
"option: " + (f.linkedFile.option.description == null ? "null" : f.linkedFile.option.description)
|
|
||||||
).forEachOrdered(desc -> {
|
|
||||||
System.out.println(desc);
|
|
||||||
});
|
});
|
||||||
|
tasks.forEach(f -> f.downloadMetadata(indexFile, indexUri));
|
||||||
|
|
||||||
|
// TODO: collect all exceptions, present in one dialog
|
||||||
|
// TODO: quit if there are exceptions or just remove failed tasks before presenting options
|
||||||
|
List<DownloadTask> failedTasks = tasks.stream().filter(t -> t.getException() != null).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// If options changed, present all options again
|
||||||
|
if (tasks.stream().filter(DownloadTask::isNewOptional).count() > 0) {
|
||||||
|
List<IOptionDetails> opts = tasks.stream().filter(DownloadTask::isOptional).collect(Collectors.toList());
|
||||||
|
// TODO: present options
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: different thread pool type?
|
// TODO: different thread pool type?
|
||||||
ExecutorService threadPool = Executors.newFixedThreadPool(10);
|
ExecutorService threadPool = Executors.newFixedThreadPool(10);
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package link.infra.packwiz.installer.ui;
|
||||||
|
|
||||||
|
public interface IOptionDetails {
|
||||||
|
String getName();
|
||||||
|
boolean getOptionValue();
|
||||||
|
String getOptionDescription();
|
||||||
|
void setOptionValue(boolean value);
|
||||||
|
}
|
@ -1,23 +1,27 @@
|
|||||||
package link.infra.packwiz.installer.ui;
|
package link.infra.packwiz.installer.ui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface IUserInterface {
|
public interface IUserInterface {
|
||||||
|
|
||||||
public void show();
|
void show();
|
||||||
|
|
||||||
public void handleException(Exception e);
|
void handleException(Exception e);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This might not exit straight away, return after calling this!
|
* This might not exit straight away, return after calling this!
|
||||||
*/
|
*/
|
||||||
public default void handleExceptionAndExit(Exception e) {
|
default void handleExceptionAndExit(Exception e) {
|
||||||
handleException(e);
|
handleException(e);
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
};
|
}
|
||||||
|
|
||||||
public default void setTitle(String title) {};
|
default void setTitle(String title) {}
|
||||||
|
|
||||||
public void submitProgress(InstallProgress progress);
|
void submitProgress(InstallProgress progress);
|
||||||
|
|
||||||
public void executeManager(Runnable task);
|
void executeManager(Runnable task);
|
||||||
|
|
||||||
|
void showOptions(List<IOptionDetails> option);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user