refactor(task): fix stage progress.

This commit is contained in:
huanghongxun
2021-10-04 23:49:03 +08:00
parent 31327d685b
commit ba0a7ddfa8
34 changed files with 511 additions and 194 deletions

View File

@@ -65,6 +65,7 @@ public final class GameAssetDownloadTask extends Task<Void> {
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
this.integrityCheck = integrityCheck;
setStage("hmcl.install.assets");
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version, forceDownloadingIndex));
}
@@ -109,7 +110,7 @@ public final class GameAssetDownloadTask extends Task<Void> {
.resolve("assets").resolve("objects").resolve(assetObject.getLocation()));
task.setCacheRepository(dependencyManager.getCacheRepository());
task.setCaching(true);
dependencies.add(task.withCounter());
dependencies.add(task.withCounter("hmcl.install.assets"));
} else {
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
}
@@ -119,6 +120,7 @@ public final class GameAssetDownloadTask extends Task<Void> {
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
notifyPropertiesChanged();
}
}

View File

@@ -74,7 +74,7 @@ public class GameInstallTask extends Task<Version> {
Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true),
new GameLibrariesTask(dependencyManager, version, true)
).withStage("hmcl.install.assets").withRunAsync(() -> {
).withRunAsync(() -> {
// ignore failure
})
).thenComposeAsync(gameRepository.saveAsync(version)));

View File

@@ -26,10 +26,9 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
/**
*
@@ -141,6 +140,17 @@ public final class LocalMod implements Comparable<LocalMod> {
return fileName;
}
public ModUpdate checkUpdates(String gameVersion, RemoteModRepository repository) throws IOException {
Optional<RemoteMod.Version> currentVersion = repository.getRemoteVersionByLocalFile(this, file);
if (!currentVersion.isPresent()) return null;
List<RemoteMod.Version> remoteVersions = repository.getRemoteVersionsById(currentVersion.get().getModid())
.filter(version -> version.getGameVersions().contains(gameVersion))
.filter(version -> version.getLoaders().contains(modLoaderType))
.sorted(Comparator.comparing(RemoteMod.Version::getDatePublished).reversed())
.collect(Collectors.toList());
return new ModUpdate(this, currentVersion.get(), remoteVersions);
}
@Override
public int compareTo(LocalMod o) {
return getFileName().compareTo(o.getFileName());
@@ -156,6 +166,30 @@ public final class LocalMod implements Comparable<LocalMod> {
return Objects.hash(getFileName());
}
public static class ModUpdate {
private final LocalMod localMod;
private final RemoteMod.Version currentVersion;
private final List<RemoteMod.Version> candidates;
public ModUpdate(LocalMod localMod, RemoteMod.Version currentVersion, List<RemoteMod.Version> candidates) {
this.localMod = localMod;
this.currentVersion = currentVersion;
this.candidates = candidates;
}
public LocalMod getLocalMod() {
return localMod;
}
public RemoteMod.Version getCurrentVersion() {
return currentVersion;
}
public List<RemoteMod.Version> getCandidates() {
return candidates;
}
}
public static class Description {
private final List<Part> parts;

View File

@@ -90,6 +90,7 @@ public class RemoteMod {
public static class Version {
private final Object self;
private final String modid;
private final String name;
private final String version;
private final String changelog;
@@ -98,10 +99,11 @@ public class RemoteMod {
private final File file;
private final List<String> dependencies;
private final List<String> gameVersions;
private final List<String> loaders;
private final List<ModLoaderType> loaders;
public Version(Object self, String name, String version, String changelog, Instant datePublished, VersionType versionType, File file, List<String> dependencies, List<String> gameVersions, List<String> loaders) {
public Version(Object self, String modid, String name, String version, String changelog, Instant datePublished, VersionType versionType, File file, List<String> dependencies, List<String> gameVersions, List<ModLoaderType> loaders) {
this.self = self;
this.modid = modid;
this.name = name;
this.version = version;
this.changelog = changelog;
@@ -117,6 +119,10 @@ public class RemoteMod {
return self;
}
public String getModid() {
return modid;
}
public String getName() {
return name;
}
@@ -149,7 +155,7 @@ public class RemoteMod {
return gameVersions;
}
public List<String> getLoaders() {
public List<ModLoaderType> getLoaders() {
return loaders;
}
}

View File

@@ -28,7 +28,11 @@ public interface RemoteModRepository {
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort)
throws IOException;
Optional<RemoteMod.Version> getRemoteVersionByLocalFile(Path file) throws IOException;
Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) throws IOException;
RemoteMod getModById(String id) throws IOException;
Stream<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException;
Stream<Category> getCategories() throws IOException;

View File

@@ -176,15 +176,14 @@ public class CurseAddon implements RemoteMod.IMod {
.collect(Collectors.toSet());
List<RemoteMod> mods = new ArrayList<>();
for (int dependencyId : dependencies) {
mods.add(CurseForgeRemoteModRepository.MODS.getAddon(dependencyId).toMod());
mods.add(CurseForgeRemoteModRepository.MODS.getModById(Integer.toString(dependencyId)));
}
return mods;
}
@Override
public Stream<RemoteMod.Version> loadVersions() throws IOException {
return CurseForgeRemoteModRepository.MODS.getFiles(this).stream()
.map(CurseAddon.LatestFile::toVersion);
return CurseForgeRemoteModRepository.MODS.getRemoteVersionsById(Integer.toString(id));
}
public RemoteMod toMod() {
@@ -500,6 +499,7 @@ public class CurseAddon implements RemoteMod.IMod {
return new RemoteMod.Version(
this,
Integer.toString(projectId),
getDisplayName(),
null,
null,

View File

@@ -90,6 +90,8 @@ public final class CurseCompletionTask extends Task<Void> {
} catch (Exception e) {
Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e);
}
setStage("hmcl.modpack.download");
}
@Override
@@ -158,12 +160,13 @@ public final class CurseCompletionTask extends Task<Void> {
FileDownloadTask task = new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile());
task.setCacheRepository(dependency.getCacheRepository());
task.setCaching(true);
dependencies.add(task.withCounter());
dependencies.add(task.withCounter("hmcl.modpack.download"));
}
}
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
notifyPropertiesChanged();
}
}

View File

@@ -18,6 +18,7 @@
package org.jackhuang.hmcl.mod.curse;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.util.MurmurHash;
@@ -73,7 +74,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
}
@Override
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(Path file) throws IOException {
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(file)))) {
int b;
@@ -97,15 +98,18 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
return Optional.of(response.getExactMatches().get(0).getFile().toVersion());
}
public CurseAddon getAddon(int id) throws IOException {
@Override
public RemoteMod getModById(String id) throws IOException {
String response = NetworkUtils.doGet(NetworkUtils.toURL(PREFIX + "/addon/" + id));
return JsonUtils.fromNonNullJson(response, CurseAddon.class);
return JsonUtils.fromNonNullJson(response, CurseAddon.class).toMod();
}
public List<CurseAddon.LatestFile> getFiles(CurseAddon addon) throws IOException {
String response = NetworkUtils.doGet(NetworkUtils.toURL(PREFIX + "/addon/" + addon.getId() + "/files"));
return JsonUtils.fromNonNullJson(response, new TypeToken<List<CurseAddon.LatestFile>>() {
@Override
public Stream<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException {
String response = NetworkUtils.doGet(NetworkUtils.toURL(PREFIX + "/addon/" + id + "/files"));
List<CurseAddon.LatestFile> files = JsonUtils.fromNonNullJson(response, new TypeToken<List<CurseAddon.LatestFile>>() {
}.getType());
return files.stream().map(CurseAddon.LatestFile::toVersion);
}
public List<Category> getCategoriesImpl() throws IOException {

View File

@@ -36,8 +36,6 @@ import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Install a downloaded CurseForge modpack.
@@ -111,7 +109,7 @@ public final class CurseInstallTask extends Task<Void> {
dependents.add(new ModpackInstallTask<>(zipFile, run, modpack.getEncoding(), manifest.getOverrides(), any -> true, config).withStage("hmcl.modpack"));
dependents.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), manifest.getOverrides(), manifest, MODPACK_TYPE, manifest.getName(), manifest.getVersion(), repository.getModpackConfiguration(name)).withStage("hmcl.modpack"));
dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest).withStage("hmcl.modpack.download"));
dependencies.add(new CurseCompletionTask(dependencyManager, name, manifest));
}
@Override
@@ -142,13 +140,5 @@ public final class CurseInstallTask extends Task<Void> {
FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(manifest));
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack", "hmcl.modpack.download")
).collect(Collectors.toList());
}
public static final String MODPACK_TYPE = "Curse";
}

View File

@@ -81,6 +81,8 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
this.version = version;
this.configurationFile = repository.getModpackConfiguration(version);
this.configuration = configuration;
setStage("hmcl.modpack.download");
}
@Override
@@ -253,13 +255,14 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
FileDownloadTask task = new FileDownloadTask(curseFile.getUrl(), modManager.getSimpleModPath(curseFile.getFileName()).toFile());
task.setCacheRepository(dependency.getCacheRepository());
task.setCaching(true);
dependencies.add(task.withCounter());
dependencies.add(task.withCounter("hmcl.modpack.download"));
}
}
}
if (!dependencies.isEmpty()) {
getProperties().put("total", dependencies.size());
notifyPropertiesChanged();
}
return executor.all(dependencies);

View File

@@ -36,8 +36,6 @@ import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class McbbsModpackLocalInstallTask extends Task<Void> {
@@ -120,15 +118,7 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
// TODO: maintain libraries.
}
dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult()).withStage("hmcl.modpack.download"));
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack")
).collect(Collectors.toList());
dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult()));
}
private static final String PATCH_NAME = "mcbbs";

View File

@@ -19,6 +19,8 @@ package org.jackhuang.hmcl.mod.modrinth;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.mod.LocalMod;
import org.jackhuang.hmcl.mod.ModLoaderType;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.util.DigestUtils;
@@ -36,6 +38,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.jackhuang.hmcl.util.Lang.mapOf;
@@ -71,7 +74,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
}
@Override
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(Path file) throws IOException {
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) throws IOException {
String sha1 = Hex.encodeHex(DigestUtils.digest("SHA-1", file));
try {
@@ -88,11 +91,18 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
}
}
public List<ModVersion> getFiles(ModResult mod) throws IOException {
String id = StringUtils.removePrefix(mod.getModId(), "local-");
return HttpRequest.GET("https://api.modrinth.com/api/v1/mod/" + id + "/version")
@Override
public RemoteMod getModById(String id) {
throw new UnsupportedOperationException();
}
@Override
public Stream<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException {
id = StringUtils.removePrefix(id, "local-");
List<ModVersion> versions = HttpRequest.GET("https://api.modrinth.com/api/v1/mod/" + id + "/version")
.getJson(new TypeToken<List<ModVersion>>() {
}.getType());
return versions.stream().map(ModVersion::toVersion).flatMap(Lang::toStream);
}
public List<String> getCategoriesImpl() throws IOException {
@@ -307,6 +317,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
return Optional.of(new RemoteMod.Version(
this,
modId,
name,
versionNumber,
changelog,
@@ -315,7 +326,11 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
files.get(0).toFile(),
dependencies,
gameVersions,
loaders
loaders.stream().flatMap(loader -> {
if ("fabric".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FABRIC);
else if ("forge".equalsIgnoreCase(loader)) return Stream.of(ModLoaderType.FORGE);
else return Stream.empty();
}).collect(Collectors.toList())
));
}
}
@@ -464,9 +479,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
@Override
public Stream<RemoteMod.Version> loadVersions() throws IOException {
return ModrinthRemoteModRepository.INSTANCE.getFiles(this).stream()
.map(ModVersion::toVersion)
.flatMap(Lang::toStream);
return ModrinthRemoteModRepository.INSTANCE.getRemoteVersionsById(getModId());
}
public RemoteMod toMod() {

View File

@@ -39,9 +39,10 @@ import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
/**
*
@@ -179,13 +180,5 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
dependencies.add(repository.saveAsync(version));
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack")
).collect(Collectors.toList());
}
public static final String MODPACK_TYPE = "MultiMC";
}

View File

@@ -77,6 +77,8 @@ public class ServerModpackCompletionTask extends Task<Void> {
} else {
this.manifest = manifest;
}
setStage("hmcl.modpack.download");
}
@Override
@@ -133,6 +135,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)
.collect(Collectors.toSet());
int total = 0;
// for files in new modpack
for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) {
Path actualPath = rootPath.resolve(file.getPath());
@@ -152,10 +155,12 @@ public class ServerModpackCompletionTask extends Task<Void> {
}
if (download) {
total++;
dependencies.add(new FileDownloadTask(
new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(file.getPath())),
actualPath.toFile(),
new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash())));
new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash()))
.withCounter("hmcl.modpack.download"));
}
}
@@ -165,6 +170,9 @@ public class ServerModpackCompletionTask extends Task<Void> {
if (Files.exists(actualPath) && !remoteFiles.contains(file.getPath()))
Files.deleteIfExists(actualPath);
}
getProperties().put("total", dependencies.size());
notifyPropertiesChanged();
}
@Override

View File

@@ -34,8 +34,6 @@ import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ServerModpackLocalInstallTask extends Task<Void> {
@@ -99,13 +97,5 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
public void execute() throws Exception {
}
@Override
public List<String> getStages() {
return Stream.concat(
dependents.stream().flatMap(task -> task.getStages().stream()),
Stream.of("hmcl.modpack")
).collect(Collectors.toList());
}
public static final String MODPACK_TYPE = "Server";
}

View File

@@ -97,7 +97,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
cancelled.set(true);
}
private CompletableFuture<?> executeTasksExceptionally(Task<?> parentTask, Collection<Task<?>> tasks) {
private CompletableFuture<?> executeTasksExceptionally(Task<?> parentTask, Collection<? extends Task<?>> tasks) {
if (tasks == null || tasks.isEmpty())
return CompletableFuture.completedFuture(null);
@@ -117,7 +117,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
});
}
private CompletableFuture<Exception> executeTasks(Task<?> parentTask, Collection<Task<?>> tasks) {
private CompletableFuture<Exception> executeTasks(Task<?> parentTask, Collection<? extends Task<?>> tasks) {
return executeTasksExceptionally(parentTask, tasks)
.thenApplyAsync(unused -> (Exception) null)
.exceptionally(throwable -> {
@@ -208,8 +208,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
task.setCancelled(this::isCancelled);
task.setState(Task.TaskState.READY);
if (parentTask != null && task.getStage() == null)
task.setStage(parentTask.getStage());
task.setNotifyPropertiesChanged(() -> taskListeners.forEach(it -> it.onPropertiesUpdate(task)));
if (task.getSignificance().shouldLog())
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());

View File

@@ -24,7 +24,6 @@ import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import org.jackhuang.hmcl.event.EventManager;
import org.jackhuang.hmcl.util.InvocationDispatcher;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.ReflectionHelper;
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
@@ -39,10 +38,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Disposable task.
@@ -98,21 +95,30 @@ public abstract class Task<T> {
* You must initialize stage in constructor.
* @param stage the stage
*/
final void setStage(String stage) {
protected final void setStage(String stage) {
this.stage = stage;
}
public List<String> getStages() {
return getStage() == null ? Collections.emptyList() : Collections.singletonList(getStage());
}
// properties
Map<String, Object> properties;
protected Map<String, Object> getProperties() {
public Map<String, Object> getProperties() {
if (properties == null) properties = new HashMap<>();
return properties;
}
private Runnable notifyPropertiesChanged;
void setNotifyPropertiesChanged(Runnable runnable) {
this.notifyPropertiesChanged = runnable;
}
protected void notifyPropertiesChanged() {
if (notifyPropertiesChanged != null) {
notifyPropertiesChanged.run();
}
}
// state
private TaskState state = TaskState.READY;
@@ -283,7 +289,7 @@ public abstract class Task<T> {
/**
* The collection of sub-tasks that should execute **before** this task running.
*/
public Collection<Task<?>> getDependents() {
public Collection<? extends Task<?>> getDependents() {
return Collections.emptySet();
}
@@ -291,7 +297,7 @@ public abstract class Task<T> {
* The collection of sub-tasks that should execute **after** this task running.
* Will not be executed if execution fails.
*/
public Collection<Task<?>> getDependencies() {
public Collection<? extends Task<?>> getDependencies() {
return Collections.emptySet();
}
@@ -715,11 +721,6 @@ public abstract class Task<T> {
public boolean isRelyingOnDependents() {
return false;
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}.setExecutor(executor).setName(getCaller()).setSignificance(TaskSignificance.MODERATE);
}
@@ -801,27 +802,33 @@ public abstract class Task<T> {
}
public Task<T> withStagesHint(List<String> stages) {
return new Task<T>() {
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public void execute() throws Exception {
setResult(Task.this.getResult());
}
@Override
public List<String> getStages() {
return stages;
}
};
return new StagesHintTask(stages);
}
public Task<T> withCounter() {
return new CountTask();
public class StagesHintTask extends Task<T> {
private final List<String> stages;
public StagesHintTask(List<String> stages) {
this.stages = stages;
}
@Override
public Collection<Task<?>> getDependents() {
return Collections.singleton(Task.this);
}
@Override
public void execute() {
setResult(Task.this.getResult());
}
public List<String> getStages() {
return stages;
}
}
public Task<T> withCounter(String countStage) {
return new CountTask(countStage);
}
public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
@@ -859,11 +866,6 @@ public abstract class Task<T> {
public Collection<Task<?>> getDependencies() {
return then == null ? Collections.emptySet() : Collections.singleton(then);
}
@Override
public List<String> getStages() {
return Lang.merge(super.getStages(), then == null ? null : then.getStages());
}
}.setName(name);
}
@@ -932,11 +934,6 @@ public abstract class Task<T> {
public Collection<Task<?>> getDependents() {
return tasks;
}
@Override
public List<String> getStages() {
return tasks.stream().flatMap(task -> task.getStages().stream()).collect(Collectors.toList());
}
};
}
@@ -1032,11 +1029,6 @@ public abstract class Task<T> {
public void execute() throws Exception {
setResult(callable.apply(Task.this.getResult()));
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}
/**
@@ -1096,13 +1088,6 @@ public abstract class Task<T> {
public boolean isRelyingOnDependents() {
return relyingOnDependents;
}
@Override
public List<String> getStages() {
return Stream.of(Task.this.getStages(), super.getStages(), succ == null ? Collections.<String>emptyList() : succ.getStages())
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
}
public class StageTask extends Task<T> {
@@ -1116,19 +1101,19 @@ public abstract class Task<T> {
public void execute() throws Exception {
setResult(Task.this.getResult());
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
}
}
private class CountTask extends Task<T> {
private final UnaryOperator<Integer> COUNTER = a -> {
int result = 0;
if (a != null) result += a;
return result + 1;
};
public class CountTask extends Task<T> {
private final String countStage;
private CountTask(String countStage) {
this.countStage = countStage;
setSignificance(TaskSignificance.MINOR);
}
public String getCountStage() {
return countStage;
}
@Override
public Collection<Task<?>> getDependents() {
@@ -1146,13 +1131,8 @@ public abstract class Task<T> {
}
@Override
public void postExecute() {
getProperties().put("count", COUNTER);
}
@Override
public List<String> getStages() {
return Lang.merge(Task.this.getStages(), super.getStages());
public void postExecute() throws Exception {
notifyPropertiesChanged();
}
}
}

View File

@@ -19,10 +19,7 @@ package org.jackhuang.hmcl.task;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -33,11 +30,10 @@ public abstract class TaskExecutor {
protected final AtomicBoolean cancelled = new AtomicBoolean(false);
protected Exception exception;
private final List<String> stages;
protected final Map<String, Map<String, Object>> stageProperties = new HashMap<>();
public TaskExecutor(Task<?> task) {
this.firstTask = task;
this.stages = task.getStages();
this.stages = task instanceof Task.StagesHintTask ? ((Task<?>.StagesHintTask) task).getStages() : Collections.emptyList();
}
public void addTaskListener(TaskListener taskListener) {

View File

@@ -18,7 +18,6 @@
package org.jackhuang.hmcl.task;
import java.util.EventListener;
import java.util.Map;
/**
*
@@ -80,6 +79,6 @@ public abstract class TaskListener implements EventListener {
public void onStop(boolean success, TaskExecutor executor) {
}
public void onPropertiesUpdate(Map<String, Map<String, Object>> stageProperties) {
public void onPropertiesUpdate(Task<?> task) {
}
}

View File

@@ -1,6 +1,6 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* 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
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.util;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
@@ -53,6 +54,7 @@ public class CacheRepository {
private Path cacheDirectory;
private Path indexFile;
private Map<String, ETagItem> index;
private Map<String, Storage> storages = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public void changeDirectory(Path commonDir) {
@@ -62,6 +64,10 @@ public class CacheRepository {
lock.writeLock().lock();
try {
for (Storage storage : storages.values()) {
storage.changeDirectory(cacheDirectory);
}
if (Files.isRegularFile(indexFile)) {
ETagIndex raw = JsonUtils.GSON.fromJson(FileUtils.readText(indexFile.toFile()), ETagIndex.class);
if (raw == null)
@@ -76,6 +82,7 @@ public class CacheRepository {
} finally {
lock.writeLock().unlock();
}
}
public Path getCommonDirectory() {
@@ -86,6 +93,15 @@ public class CacheRepository {
return cacheDirectory;
}
public Storage getStorage(String key) {
lock.readLock().lock();
try {
return storages.computeIfAbsent(key, Storage::new);
} finally {
lock.readLock().unlock();
}
}
protected Path getFile(String algorithm, String hash) {
return getCacheDirectory().resolve(algorithm).resolve(hash.substring(0, 2)).resolve(hash);
}
@@ -353,6 +369,78 @@ public class CacheRepository {
}
}
/**
* Universal cache
*/
public static class Storage {
private final String name;
private Map<String, Object> storage;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private Path indexFile;
public Storage(String name) {
this.name = name;
}
public Object getEntry(String key) {
lock.readLock().lock();
try {
return storage.get(key);
} finally {
lock.readLock().unlock();
}
}
public void putEntry(String key, Object value) {
lock.writeLock().lock();
try {
storage.put(key, value);
saveToFile();
} finally {
lock.writeLock().unlock();
}
}
private void joinEntries(Map<String, Object> storage) {
this.storage.putAll(storage);
}
private void changeDirectory(Path cacheDirectory) {
lock.writeLock().lock();
try {
indexFile = cacheDirectory.resolve(name + ".json");
if (Files.isRegularFile(indexFile)) {
joinEntries(JsonUtils.fromNonNullJson(FileUtils.readText(indexFile.toFile()), new TypeToken<Map<String, Object>>() {
}.getType()));
}
} catch (IOException | JsonParseException e) {
LOG.log(Level.WARNING, "Unable to read storage {" + name + "} file");
} finally {
lock.writeLock().unlock();
}
}
public void saveToFile() {
try (RandomAccessFile file = new RandomAccessFile(indexFile.toFile(), "rw"); FileChannel channel = file.getChannel()) {
FileLock lock = channel.lock();
try {
Map<String, Object> indexOnDisk = JsonUtils.fromMaybeMalformedJson(new String(IOUtils.readFullyWithoutClosing(Channels.newInputStream(channel)), UTF_8), new TypeToken<Map<String, Object>>() {
}.getType());
if (indexOnDisk == null) indexOnDisk = new HashMap<>();
indexOnDisk.putAll(storage);
channel.truncate(0);
OutputStream os = Channels.newOutputStream(channel);
IOUtils.write(JsonUtils.GSON.toJson(storage).getBytes(UTF_8), os);
this.storage = indexOnDisk;
} finally {
lock.release();
}
} catch (IOException e) {
LOG.log(Level.WARNING, "Unable to write storage {" + name + "} file");
}
}
}
private static CacheRepository instance = new CacheRepository();
public static CacheRepository getInstance() {

View File

@@ -66,6 +66,14 @@ public final class JsonUtils {
}
}
public static <T> T fromMaybeMalformedJson(String json, Type type) throws JsonParseException {
try {
return GSON.fromJson(json, type);
} catch (JsonSyntaxException e) {
return null;
}
}
public static GsonBuilder defaultGsonBuilder() {
return new GsonBuilder()
.enableComplexMapKeySerialization()