mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-17 07:47:57 -04:00
refactor(task): fix stage progress.
This commit is contained in:
parent
31327d685b
commit
ba0a7ddfa8
@ -35,8 +35,6 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public final class HMCLModpackInstallTask extends Task<Void> {
|
public final class HMCLModpackInstallTask extends Task<Void> {
|
||||||
private final File zipFile;
|
private final File zipFile;
|
||||||
@ -107,13 +105,5 @@ public final class HMCLModpackInstallTask extends Task<Void> {
|
|||||||
dependencies.add(libraryTask.thenComposeAsync(repository::saveAsync));
|
dependencies.add(libraryTask.thenComposeAsync(repository::saveAsync));
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 = "HMCL";
|
public static final String MODPACK_TYPE = "HMCL";
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ import java.nio.file.FileSystem;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -167,7 +168,8 @@ public final class ModpackHelper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return new ServerModpackRemoteInstallTask(profile.getDependency(), manifest, name)
|
return new ServerModpackRemoteInstallTask(profile.getDependency(), manifest, name)
|
||||||
.whenComplete(Schedulers.defaultScheduler(), success, failure);
|
.whenComplete(Schedulers.defaultScheduler(), success, failure)
|
||||||
|
.withStagesHint(Arrays.asList("hmcl.modpack", "hmcl.modpack.download"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExternalGameNameConflicts(String name) {
|
public static boolean isExternalGameNameConflicts(String name) {
|
||||||
@ -223,7 +225,8 @@ public final class ModpackHelper {
|
|||||||
public static Task<Void> getUpdateTask(Profile profile, ServerModpackManifest manifest, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException {
|
public static Task<Void> getUpdateTask(Profile profile, ServerModpackManifest manifest, Charset charset, String name, ModpackConfiguration<?> configuration) throws UnsupportedModpackException {
|
||||||
switch (configuration.getType()) {
|
switch (configuration.getType()) {
|
||||||
case ServerModpackRemoteInstallTask.MODPACK_TYPE:
|
case ServerModpackRemoteInstallTask.MODPACK_TYPE:
|
||||||
return new ModpackUpdateTask(profile.getRepository(), name, new ServerModpackRemoteInstallTask(profile.getDependency(), manifest, name));
|
return new ModpackUpdateTask(profile.getRepository(), name, new ServerModpackRemoteInstallTask(profile.getDependency(), manifest, name))
|
||||||
|
.withStagesHint(Arrays.asList("hmcl.modpack", "hmcl.modpack.download"));
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedModpackException();
|
throw new UnsupportedModpackException();
|
||||||
}
|
}
|
||||||
|
@ -275,6 +275,16 @@ public final class Controllers {
|
|||||||
return pane;
|
return pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TaskExecutorDialogPane taskDialog(Task<?> task, String title, Consumer<Region> onCancel) {
|
||||||
|
TaskExecutor executor = task.executor();
|
||||||
|
TaskExecutorDialogPane pane = new TaskExecutorDialogPane(onCancel);
|
||||||
|
pane.setTitle(title);
|
||||||
|
pane.setExecutor(executor);
|
||||||
|
dialog(pane);
|
||||||
|
executor.start();
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
|
||||||
public static void navigate(Node node) {
|
public static void navigate(Node node) {
|
||||||
decorator.getNavigator().navigate(node, ContainerAnimations.FADE.getAnimationProducer());
|
decorator.getNavigator().navigate(node, ContainerAnimations.FADE.getAnimationProducer());
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ public final class TaskListPane extends StackPane {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReady(Task<?> task) {
|
public void onReady(Task<?> task) {
|
||||||
if (task instanceof Task.StageTask) {
|
if (task.getStage() != null) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::begin);
|
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::begin);
|
||||||
});
|
});
|
||||||
@ -150,7 +151,7 @@ public final class TaskListPane extends StackPane {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFinished(Task<?> task) {
|
public void onFinished(Task<?> task) {
|
||||||
if (task instanceof Task.StageTask) {
|
if (task.getStage() != null) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::succeed);
|
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::succeed);
|
||||||
});
|
});
|
||||||
@ -167,7 +168,7 @@ public final class TaskListPane extends StackPane {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailed(Task<?> task, Throwable throwable) {
|
public void onFailed(Task<?> task, Throwable throwable) {
|
||||||
if (task instanceof Task.StageTask) {
|
if (task.getStage() != null) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::fail);
|
stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().ifPresent(StageNode::fail);
|
||||||
});
|
});
|
||||||
@ -181,14 +182,29 @@ public final class TaskListPane extends StackPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPropertiesUpdate(Map<String, Map<String, Object>> stageProperties) {
|
public void onPropertiesUpdate(Task<?> task) {
|
||||||
stageProperties.forEach((stage, properties) -> {
|
if (task instanceof Task.CountTask) {
|
||||||
int count = tryCast(properties.get("count"), Integer.class).orElse(0),
|
runInFX(() -> {
|
||||||
total = tryCast(properties.get("total"), Integer.class).orElse(0);
|
stageNodes.stream()
|
||||||
if (total > 0)
|
.filter(x -> x.stage.equals(((Task.CountTask) task).getCountStage()))
|
||||||
Platform.runLater(() ->
|
.findAny()
|
||||||
stageNodes.stream().filter(x -> x.stage.equals(stage)).findAny().ifPresent(stageNode -> stageNode.updateCounter(count, total)));
|
.ifPresent(StageNode::count);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.getStage() != null) {
|
||||||
|
int total = tryCast(task.getProperties().get("total"), Integer.class).orElse(0);
|
||||||
|
runInFX(() -> {
|
||||||
|
stageNodes.stream()
|
||||||
|
.filter(x -> x.stage.equals(task.getStage()))
|
||||||
|
.findAny()
|
||||||
|
.ifPresent(stageNode -> {
|
||||||
|
stageNode.setTotal(total);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -197,6 +213,8 @@ public final class TaskListPane extends StackPane {
|
|||||||
private final String stage;
|
private final String stage;
|
||||||
private final Label title = new Label();
|
private final Label title = new Label();
|
||||||
private final String message;
|
private final String message;
|
||||||
|
private int count = 0;
|
||||||
|
private int total = 0;
|
||||||
private boolean started = false;
|
private boolean started = false;
|
||||||
|
|
||||||
public StageNode(String stage) {
|
public StageNode(String stage) {
|
||||||
@ -242,6 +260,15 @@ public final class TaskListPane extends StackPane {
|
|||||||
setLeft(FXUtils.limitingSize(SVG.check(Theme.blackFillBinding(), 14, 14), 14, 14));
|
setLeft(FXUtils.limitingSize(SVG.check(Theme.blackFillBinding(), 14, 14), 14, 14));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void count() {
|
||||||
|
updateCounter(++count, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotal(int total) {
|
||||||
|
this.total = total;
|
||||||
|
updateCounter(count, total);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateCounter(int count, int total) {
|
public void updateCounter(int count, int total) {
|
||||||
if (total > 0)
|
if (total > 0)
|
||||||
title.setText(String.format("%s - %d/%d", message, count, total));
|
title.setText(String.format("%s - %d/%d", message, count, total));
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.versions;
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.mod.LocalMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
||||||
@ -76,11 +77,6 @@ public class ModDownloadListPage extends DownloadListPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(Path file) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<Category> getCategories() throws IOException {
|
public Stream<Category> getCategories() throws IOException {
|
||||||
if ("mods.modrinth".equals(downloadSource.get())) {
|
if ("mods.modrinth".equals(downloadSource.get())) {
|
||||||
@ -89,6 +85,21 @@ public class ModDownloadListPage extends DownloadListPage {
|
|||||||
return CurseForgeRemoteModRepository.MODS.getCategories();
|
return CurseForgeRemoteModRepository.MODS.getCategories();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalMod localMod, Path file) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteMod getModById(String id) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,16 +32,14 @@ import org.jackhuang.hmcl.task.Task;
|
|||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.ListPageBase;
|
import org.jackhuang.hmcl.ui.ListPageBase;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -176,6 +174,27 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
|
|||||||
FXUtils.openFolder(new File(profile.getRepository().getRunDirectory(versionId), "mods"));
|
FXUtils.openFolder(new File(profile.getRepository().getRunDirectory(versionId), "mods"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void checkUpdates() {
|
||||||
|
Controllers.taskDialog(
|
||||||
|
Task.composeAsync(() -> {
|
||||||
|
Optional<String> gameVersion = profile.getRepository().getGameVersion(versionId);
|
||||||
|
if (gameVersion.isPresent()) {
|
||||||
|
return new ModUpdateTask(gameVersion.get(), modManager.getMods());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||||
|
if (exception != null) {
|
||||||
|
Controllers.dialog("Failed to check updates", "failed", MessageDialogPane.MessageType.ERROR);
|
||||||
|
} else {
|
||||||
|
Controllers.dialog(new ModUpdatesDialog(result));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withStagesHint(Collections.singletonList("mods.check_updates"))
|
||||||
|
, i18n("update.checking"), pane -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isModded() {
|
public boolean isModded() {
|
||||||
return modded.get();
|
return modded.get();
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,9 @@ class ModListPageSkin extends SkinBase<ModListPage> {
|
|||||||
createToolbarButton2(i18n("mods.disable"), SVG::close, () ->
|
createToolbarButton2(i18n("mods.disable"), SVG::close, () ->
|
||||||
skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())),
|
skinnable.disableSelected(listView.getSelectionModel().getSelectedItems())),
|
||||||
createToolbarButton2(i18n("folder.mod"), SVG::folderOpen, () ->
|
createToolbarButton2(i18n("folder.mod"), SVG::folderOpen, () ->
|
||||||
skinnable.openModFolder()));
|
skinnable.openModFolder()),
|
||||||
|
createToolbarButton2(i18n("mods.check_updates"), SVG::update, () ->
|
||||||
|
skinnable.checkUpdates()));
|
||||||
root.getContent().add(toolbar);
|
root.getContent().add(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* 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
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.mod.LocalMod;
|
||||||
|
import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ModUpdateTask extends Task<List<LocalMod.ModUpdate>> {
|
||||||
|
|
||||||
|
private final String gameVersion;
|
||||||
|
private final Collection<LocalMod> mods;
|
||||||
|
private final Collection<Task<LocalMod.ModUpdate>> dependents;
|
||||||
|
|
||||||
|
public ModUpdateTask(String gameVersion, Collection<LocalMod> mods) {
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.mods = mods;
|
||||||
|
|
||||||
|
dependents = mods.stream()
|
||||||
|
.map(mod -> Task.supplyAsync(() -> {
|
||||||
|
return mod.checkUpdates(gameVersion, CurseForgeRemoteModRepository.MODS);
|
||||||
|
}).setSignificance(TaskSignificance.MAJOR).withCounter("mods.check_updates"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
setStage("mods.check_updates");
|
||||||
|
getProperties().put("total", dependents.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doPreExecute() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preExecute() throws Exception {
|
||||||
|
notifyPropertiesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends Task<?>> getDependents() {
|
||||||
|
return dependents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRelyingOnDependents() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws Exception {
|
||||||
|
setResult(dependents.stream()
|
||||||
|
.filter(task -> task.getResult() != null)
|
||||||
|
.map(Task::getResult)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* 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
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXListView;
|
||||||
|
import org.jackhuang.hmcl.mod.LocalMod;
|
||||||
|
import org.jackhuang.hmcl.mod.curse.CurseAddon;
|
||||||
|
import org.jackhuang.hmcl.mod.modrinth.ModrinthRemoteModRepository;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.DialogPane;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.MDListCell;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
|
public class ModUpdatesDialog extends DialogPane {
|
||||||
|
|
||||||
|
public ModUpdatesDialog(List<LocalMod.ModUpdate> updates) {
|
||||||
|
setTitle(i18n("mods.check_updates"));
|
||||||
|
|
||||||
|
JFXListView<LocalMod.ModUpdate> listView = new JFXListView<>();
|
||||||
|
listView.getItems().setAll(updates);
|
||||||
|
listView.setCellFactory(l -> new ModUpdateCell(listView));
|
||||||
|
setBody(listView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ModUpdateCell extends MDListCell<LocalMod.ModUpdate> {
|
||||||
|
TwoLineListItem content = new TwoLineListItem();
|
||||||
|
|
||||||
|
public ModUpdateCell(JFXListView<LocalMod.ModUpdate> listView) {
|
||||||
|
super(listView);
|
||||||
|
|
||||||
|
getContainer().getChildren().setAll(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateControl(LocalMod.ModUpdate item, boolean empty) {
|
||||||
|
if (empty) return;
|
||||||
|
ModTranslations.Mod mod = ModTranslations.getModById(item.getLocalMod().getId());
|
||||||
|
content.setTitle(mod != null ? mod.getDisplayName() : item.getCurrentVersion().getName());
|
||||||
|
content.setSubtitle(item.getLocalMod().getFileName());
|
||||||
|
content.getTags().setAll();
|
||||||
|
|
||||||
|
if (item.getCurrentVersion().getSelf() instanceof CurseAddon.LatestFile) {
|
||||||
|
content.getTags().add("Curseforge");
|
||||||
|
} else if (item.getCurrentVersion().getSelf() instanceof ModrinthRemoteModRepository.ModVersion) {
|
||||||
|
content.getTags().add("Modrinth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ public class JavaRuntimeDownloadTask extends Task<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Task<?>> getDependencies() {
|
public Collection<? extends Task<?>> getDependencies() {
|
||||||
return super.getDependencies();
|
return super.getDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,6 +580,7 @@ mods.add=Install mods
|
|||||||
mods.add.failed=Failed to install mods %s.
|
mods.add.failed=Failed to install mods %s.
|
||||||
mods.add.success=Successfully installed mods %s.
|
mods.add.success=Successfully installed mods %s.
|
||||||
mods.category=Category
|
mods.category=Category
|
||||||
|
mods.check_updates=Check updates
|
||||||
mods.choose_mod=Choose your mods
|
mods.choose_mod=Choose your mods
|
||||||
mods.curseforge=CurseForge
|
mods.curseforge=CurseForge
|
||||||
mods.dependencies=Dependencies
|
mods.dependencies=Dependencies
|
||||||
|
@ -580,6 +580,7 @@ mods.add=新增模組
|
|||||||
mods.add.failed=新增模組 %s 失敗。
|
mods.add.failed=新增模組 %s 失敗。
|
||||||
mods.add.success=成功新增模組 %s。
|
mods.add.success=成功新增模組 %s。
|
||||||
mods.category=類別
|
mods.category=類別
|
||||||
|
mods.check_updates=檢查模組更新
|
||||||
mods.choose_mod=選擇模組
|
mods.choose_mod=選擇模組
|
||||||
mods.curseforge=CurseForge
|
mods.curseforge=CurseForge
|
||||||
mods.dependencies=前置 Mod
|
mods.dependencies=前置 Mod
|
||||||
|
@ -580,6 +580,7 @@ mods.add=添加模组
|
|||||||
mods.add.failed=添加模组 %s 失败。
|
mods.add.failed=添加模组 %s 失败。
|
||||||
mods.add.success=成功添加模组 %s。
|
mods.add.success=成功添加模组 %s。
|
||||||
mods.category=类别
|
mods.category=类别
|
||||||
|
mods.check_updates=检查模组更新
|
||||||
mods.choose_mod=选择模组
|
mods.choose_mod=选择模组
|
||||||
mods.curseforge=CurseForge
|
mods.curseforge=CurseForge
|
||||||
mods.dependencies=前置 Mod
|
mods.dependencies=前置 Mod
|
||||||
|
@ -65,6 +65,7 @@ public final class GameAssetDownloadTask extends Task<Void> {
|
|||||||
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
|
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
|
||||||
this.integrityCheck = integrityCheck;
|
this.integrityCheck = integrityCheck;
|
||||||
|
|
||||||
|
setStage("hmcl.install.assets");
|
||||||
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version, forceDownloadingIndex));
|
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()));
|
.resolve("assets").resolve("objects").resolve(assetObject.getLocation()));
|
||||||
task.setCacheRepository(dependencyManager.getCacheRepository());
|
task.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
dependencies.add(task.withCounter());
|
dependencies.add(task.withCounter("hmcl.install.assets"));
|
||||||
} else {
|
} else {
|
||||||
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
|
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
|
||||||
}
|
}
|
||||||
@ -119,6 +120,7 @@ public final class GameAssetDownloadTask extends Task<Void> {
|
|||||||
|
|
||||||
if (!dependencies.isEmpty()) {
|
if (!dependencies.isEmpty()) {
|
||||||
getProperties().put("total", dependencies.size());
|
getProperties().put("total", dependencies.size());
|
||||||
|
notifyPropertiesChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ public class GameInstallTask extends Task<Version> {
|
|||||||
Task.allOf(
|
Task.allOf(
|
||||||
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true),
|
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true),
|
||||||
new GameLibrariesTask(dependencyManager, version, true)
|
new GameLibrariesTask(dependencyManager, version, true)
|
||||||
).withStage("hmcl.install.assets").withRunAsync(() -> {
|
).withRunAsync(() -> {
|
||||||
// ignore failure
|
// ignore failure
|
||||||
})
|
})
|
||||||
).thenComposeAsync(gameRepository.saveAsync(version)));
|
).thenComposeAsync(gameRepository.saveAsync(version)));
|
||||||
|
@ -26,10 +26,9 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -141,6 +140,17 @@ public final class LocalMod implements Comparable<LocalMod> {
|
|||||||
return fileName;
|
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
|
@Override
|
||||||
public int compareTo(LocalMod o) {
|
public int compareTo(LocalMod o) {
|
||||||
return getFileName().compareTo(o.getFileName());
|
return getFileName().compareTo(o.getFileName());
|
||||||
@ -156,6 +166,30 @@ public final class LocalMod implements Comparable<LocalMod> {
|
|||||||
return Objects.hash(getFileName());
|
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 {
|
public static class Description {
|
||||||
private final List<Part> parts;
|
private final List<Part> parts;
|
||||||
|
|
||||||
|
@ -90,6 +90,7 @@ public class RemoteMod {
|
|||||||
|
|
||||||
public static class Version {
|
public static class Version {
|
||||||
private final Object self;
|
private final Object self;
|
||||||
|
private final String modid;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String version;
|
private final String version;
|
||||||
private final String changelog;
|
private final String changelog;
|
||||||
@ -98,10 +99,11 @@ public class RemoteMod {
|
|||||||
private final File file;
|
private final File file;
|
||||||
private final List<String> dependencies;
|
private final List<String> dependencies;
|
||||||
private final List<String> gameVersions;
|
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.self = self;
|
||||||
|
this.modid = modid;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.changelog = changelog;
|
this.changelog = changelog;
|
||||||
@ -117,6 +119,10 @@ public class RemoteMod {
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getModid() {
|
||||||
|
return modid;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -149,7 +155,7 @@ public class RemoteMod {
|
|||||||
return gameVersions;
|
return gameVersions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getLoaders() {
|
public List<ModLoaderType> getLoaders() {
|
||||||
return loaders;
|
return loaders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,11 @@ public interface RemoteModRepository {
|
|||||||
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort)
|
Stream<RemoteMod> search(String gameVersion, Category category, int pageOffset, int pageSize, String searchFilter, int sort)
|
||||||
throws IOException;
|
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;
|
Stream<Category> getCategories() throws IOException;
|
||||||
|
|
||||||
|
@ -176,15 +176,14 @@ public class CurseAddon implements RemoteMod.IMod {
|
|||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
List<RemoteMod> mods = new ArrayList<>();
|
List<RemoteMod> mods = new ArrayList<>();
|
||||||
for (int dependencyId : dependencies) {
|
for (int dependencyId : dependencies) {
|
||||||
mods.add(CurseForgeRemoteModRepository.MODS.getAddon(dependencyId).toMod());
|
mods.add(CurseForgeRemoteModRepository.MODS.getModById(Integer.toString(dependencyId)));
|
||||||
}
|
}
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<RemoteMod.Version> loadVersions() throws IOException {
|
public Stream<RemoteMod.Version> loadVersions() throws IOException {
|
||||||
return CurseForgeRemoteModRepository.MODS.getFiles(this).stream()
|
return CurseForgeRemoteModRepository.MODS.getRemoteVersionsById(Integer.toString(id));
|
||||||
.map(CurseAddon.LatestFile::toVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoteMod toMod() {
|
public RemoteMod toMod() {
|
||||||
@ -500,6 +499,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
|||||||
|
|
||||||
return new RemoteMod.Version(
|
return new RemoteMod.Version(
|
||||||
this,
|
this,
|
||||||
|
Integer.toString(projectId),
|
||||||
getDisplayName(),
|
getDisplayName(),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
@ -90,6 +90,8 @@ public final class CurseCompletionTask extends Task<Void> {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e);
|
Logging.LOG.log(Level.WARNING, "Unable to read CurseForge modpack manifest.json", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setStage("hmcl.modpack.download");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -158,12 +160,13 @@ public final class CurseCompletionTask extends Task<Void> {
|
|||||||
FileDownloadTask task = new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile());
|
FileDownloadTask task = new FileDownloadTask(file.getUrl(), modManager.getSimpleModPath(file.getFileName()).toFile());
|
||||||
task.setCacheRepository(dependency.getCacheRepository());
|
task.setCacheRepository(dependency.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
dependencies.add(task.withCounter());
|
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dependencies.isEmpty()) {
|
if (!dependencies.isEmpty()) {
|
||||||
getProperties().put("total", dependencies.size());
|
getProperties().put("total", dependencies.size());
|
||||||
|
notifyPropertiesChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package org.jackhuang.hmcl.mod.curse;
|
package org.jackhuang.hmcl.mod.curse;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.jackhuang.hmcl.mod.LocalMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
import org.jackhuang.hmcl.util.MurmurHash;
|
import org.jackhuang.hmcl.util.MurmurHash;
|
||||||
@ -73,7 +74,7 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(file)))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(file)))) {
|
||||||
int b;
|
int b;
|
||||||
@ -97,15 +98,18 @@ public final class CurseForgeRemoteModRepository implements RemoteModRepository
|
|||||||
return Optional.of(response.getExactMatches().get(0).getFile().toVersion());
|
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));
|
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 {
|
@Override
|
||||||
String response = NetworkUtils.doGet(NetworkUtils.toURL(PREFIX + "/addon/" + addon.getId() + "/files"));
|
public Stream<RemoteMod.Version> getRemoteVersionsById(String id) throws IOException {
|
||||||
return JsonUtils.fromNonNullJson(response, new TypeToken<List<CurseAddon.LatestFile>>() {
|
String response = NetworkUtils.doGet(NetworkUtils.toURL(PREFIX + "/addon/" + id + "/files"));
|
||||||
|
List<CurseAddon.LatestFile> files = JsonUtils.fromNonNullJson(response, new TypeToken<List<CurseAddon.LatestFile>>() {
|
||||||
}.getType());
|
}.getType());
|
||||||
|
return files.stream().map(CurseAddon.LatestFile::toVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Category> getCategoriesImpl() throws IOException {
|
public List<Category> getCategoriesImpl() throws IOException {
|
||||||
|
@ -36,8 +36,6 @@ import java.io.IOException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install a downloaded CurseForge modpack.
|
* 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 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"));
|
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
|
@Override
|
||||||
@ -142,13 +140,5 @@ public final class CurseInstallTask extends Task<Void> {
|
|||||||
FileUtils.writeText(new File(root, "manifest.json"), JsonUtils.GSON.toJson(manifest));
|
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";
|
public static final String MODPACK_TYPE = "Curse";
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,8 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
this.version = version;
|
this.version = version;
|
||||||
this.configurationFile = repository.getModpackConfiguration(version);
|
this.configurationFile = repository.getModpackConfiguration(version);
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
|
|
||||||
|
setStage("hmcl.modpack.download");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -253,13 +255,14 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
FileDownloadTask task = new FileDownloadTask(curseFile.getUrl(), modManager.getSimpleModPath(curseFile.getFileName()).toFile());
|
FileDownloadTask task = new FileDownloadTask(curseFile.getUrl(), modManager.getSimpleModPath(curseFile.getFileName()).toFile());
|
||||||
task.setCacheRepository(dependency.getCacheRepository());
|
task.setCacheRepository(dependency.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
dependencies.add(task.withCounter());
|
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dependencies.isEmpty()) {
|
if (!dependencies.isEmpty()) {
|
||||||
getProperties().put("total", dependencies.size());
|
getProperties().put("total", dependencies.size());
|
||||||
|
notifyPropertiesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
return executor.all(dependencies);
|
return executor.all(dependencies);
|
||||||
|
@ -36,8 +36,6 @@ import java.io.IOException;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class McbbsModpackLocalInstallTask extends Task<Void> {
|
public class McbbsModpackLocalInstallTask extends Task<Void> {
|
||||||
|
|
||||||
@ -120,15 +118,7 @@ public class McbbsModpackLocalInstallTask extends Task<Void> {
|
|||||||
// TODO: maintain libraries.
|
// TODO: maintain libraries.
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult()).withStage("hmcl.modpack.download"));
|
dependencies.add(new McbbsModpackCompletionTask(dependencyManager, name, instanceTask.getResult()));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getStages() {
|
|
||||||
return Stream.concat(
|
|
||||||
dependents.stream().flatMap(task -> task.getStages().stream()),
|
|
||||||
Stream.of("hmcl.modpack")
|
|
||||||
).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String PATCH_NAME = "mcbbs";
|
private static final String PATCH_NAME = "mcbbs";
|
||||||
|
@ -19,6 +19,8 @@ package org.jackhuang.hmcl.mod.modrinth;
|
|||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
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.RemoteMod;
|
||||||
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||||
import org.jackhuang.hmcl.util.DigestUtils;
|
import org.jackhuang.hmcl.util.DigestUtils;
|
||||||
@ -36,6 +38,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
@ -71,7 +74,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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));
|
String sha1 = Hex.encodeHex(DigestUtils.digest("SHA-1", file));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -88,11 +91,18 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ModVersion> getFiles(ModResult mod) throws IOException {
|
@Override
|
||||||
String id = StringUtils.removePrefix(mod.getModId(), "local-");
|
public RemoteMod getModById(String id) {
|
||||||
return HttpRequest.GET("https://api.modrinth.com/api/v1/mod/" + id + "/version")
|
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>>() {
|
.getJson(new TypeToken<List<ModVersion>>() {
|
||||||
}.getType());
|
}.getType());
|
||||||
|
return versions.stream().map(ModVersion::toVersion).flatMap(Lang::toStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getCategoriesImpl() throws IOException {
|
public List<String> getCategoriesImpl() throws IOException {
|
||||||
@ -307,6 +317,7 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
|
|
||||||
return Optional.of(new RemoteMod.Version(
|
return Optional.of(new RemoteMod.Version(
|
||||||
this,
|
this,
|
||||||
|
modId,
|
||||||
name,
|
name,
|
||||||
versionNumber,
|
versionNumber,
|
||||||
changelog,
|
changelog,
|
||||||
@ -315,7 +326,11 @@ public final class ModrinthRemoteModRepository implements RemoteModRepository {
|
|||||||
files.get(0).toFile(),
|
files.get(0).toFile(),
|
||||||
dependencies,
|
dependencies,
|
||||||
gameVersions,
|
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
|
@Override
|
||||||
public Stream<RemoteMod.Version> loadVersions() throws IOException {
|
public Stream<RemoteMod.Version> loadVersions() throws IOException {
|
||||||
return ModrinthRemoteModRepository.INSTANCE.getFiles(this).stream()
|
return ModrinthRemoteModRepository.INSTANCE.getRemoteVersionsById(getModId());
|
||||||
.map(ModVersion::toVersion)
|
|
||||||
.flatMap(Lang::toStream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoteMod toMod() {
|
public RemoteMod toMod() {
|
||||||
|
@ -39,9 +39,10 @@ import java.nio.file.DirectoryStream;
|
|||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
import java.util.stream.Collectors;
|
import java.util.LinkedList;
|
||||||
import java.util.stream.Stream;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -179,13 +180,5 @@ public final class MultiMCModpackInstallTask extends Task<Void> {
|
|||||||
dependencies.add(repository.saveAsync(version));
|
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";
|
public static final String MODPACK_TYPE = "MultiMC";
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,8 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
|||||||
} else {
|
} else {
|
||||||
this.manifest = manifest;
|
this.manifest = manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setStage("hmcl.modpack.download");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -133,6 +135,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
|||||||
Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)
|
Set<String> remoteFiles = remoteManifest.getFiles().stream().map(ModpackConfiguration.FileInformation::getPath)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
int total = 0;
|
||||||
// for files in new modpack
|
// for files in new modpack
|
||||||
for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) {
|
for (ModpackConfiguration.FileInformation file : remoteManifest.getFiles()) {
|
||||||
Path actualPath = rootPath.resolve(file.getPath());
|
Path actualPath = rootPath.resolve(file.getPath());
|
||||||
@ -152,10 +155,12 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (download) {
|
if (download) {
|
||||||
|
total++;
|
||||||
dependencies.add(new FileDownloadTask(
|
dependencies.add(new FileDownloadTask(
|
||||||
new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(file.getPath())),
|
new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(file.getPath())),
|
||||||
actualPath.toFile(),
|
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()))
|
if (Files.exists(actualPath) && !remoteFiles.contains(file.getPath()))
|
||||||
Files.deleteIfExists(actualPath);
|
Files.deleteIfExists(actualPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProperties().put("total", dependencies.size());
|
||||||
|
notifyPropertiesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -34,8 +34,6 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class ServerModpackLocalInstallTask extends Task<Void> {
|
public class ServerModpackLocalInstallTask extends Task<Void> {
|
||||||
|
|
||||||
@ -99,13 +97,5 @@ public class ServerModpackLocalInstallTask extends Task<Void> {
|
|||||||
public void execute() throws Exception {
|
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";
|
public static final String MODPACK_TYPE = "Server";
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
cancelled.set(true);
|
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())
|
if (tasks == null || tasks.isEmpty())
|
||||||
return CompletableFuture.completedFuture(null);
|
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)
|
return executeTasksExceptionally(parentTask, tasks)
|
||||||
.thenApplyAsync(unused -> (Exception) null)
|
.thenApplyAsync(unused -> (Exception) null)
|
||||||
.exceptionally(throwable -> {
|
.exceptionally(throwable -> {
|
||||||
@ -208,8 +208,7 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
|
|
||||||
task.setCancelled(this::isCancelled);
|
task.setCancelled(this::isCancelled);
|
||||||
task.setState(Task.TaskState.READY);
|
task.setState(Task.TaskState.READY);
|
||||||
if (parentTask != null && task.getStage() == null)
|
task.setNotifyPropertiesChanged(() -> taskListeners.forEach(it -> it.onPropertiesUpdate(task)));
|
||||||
task.setStage(parentTask.getStage());
|
|
||||||
|
|
||||||
if (task.getSignificance().shouldLog())
|
if (task.getSignificance().shouldLog())
|
||||||
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());
|
Logging.LOG.log(Level.FINE, "Executing task: " + task.getName());
|
||||||
|
@ -24,7 +24,6 @@ import javafx.beans.property.ReadOnlyStringProperty;
|
|||||||
import javafx.beans.property.ReadOnlyStringWrapper;
|
import javafx.beans.property.ReadOnlyStringWrapper;
|
||||||
import org.jackhuang.hmcl.event.EventManager;
|
import org.jackhuang.hmcl.event.EventManager;
|
||||||
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
import org.jackhuang.hmcl.util.InvocationDispatcher;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
import org.jackhuang.hmcl.util.ReflectionHelper;
|
import org.jackhuang.hmcl.util.ReflectionHelper;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
import org.jackhuang.hmcl.util.function.ExceptionalConsumer;
|
||||||
@ -39,10 +38,8 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposable task.
|
* Disposable task.
|
||||||
@ -98,21 +95,30 @@ public abstract class Task<T> {
|
|||||||
* You must initialize stage in constructor.
|
* You must initialize stage in constructor.
|
||||||
* @param stage the stage
|
* @param stage the stage
|
||||||
*/
|
*/
|
||||||
final void setStage(String stage) {
|
protected final void setStage(String stage) {
|
||||||
this.stage = stage;
|
this.stage = stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getStages() {
|
// properties
|
||||||
return getStage() == null ? Collections.emptyList() : Collections.singletonList(getStage());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> properties;
|
Map<String, Object> properties;
|
||||||
|
|
||||||
protected Map<String, Object> getProperties() {
|
public Map<String, Object> getProperties() {
|
||||||
if (properties == null) properties = new HashMap<>();
|
if (properties == null) properties = new HashMap<>();
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Runnable notifyPropertiesChanged;
|
||||||
|
|
||||||
|
void setNotifyPropertiesChanged(Runnable runnable) {
|
||||||
|
this.notifyPropertiesChanged = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyPropertiesChanged() {
|
||||||
|
if (notifyPropertiesChanged != null) {
|
||||||
|
notifyPropertiesChanged.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// state
|
// state
|
||||||
private TaskState state = TaskState.READY;
|
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.
|
* The collection of sub-tasks that should execute **before** this task running.
|
||||||
*/
|
*/
|
||||||
public Collection<Task<?>> getDependents() {
|
public Collection<? extends Task<?>> getDependents() {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +297,7 @@ public abstract class Task<T> {
|
|||||||
* 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.
|
* Will not be executed if execution fails.
|
||||||
*/
|
*/
|
||||||
public Collection<Task<?>> getDependencies() {
|
public Collection<? extends Task<?>> getDependencies() {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,11 +721,6 @@ public abstract class Task<T> {
|
|||||||
public boolean isRelyingOnDependents() {
|
public boolean isRelyingOnDependents() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getStages() {
|
|
||||||
return Lang.merge(Task.this.getStages(), super.getStages());
|
|
||||||
}
|
|
||||||
}.setExecutor(executor).setName(getCaller()).setSignificance(TaskSignificance.MODERATE);
|
}.setExecutor(executor).setName(getCaller()).setSignificance(TaskSignificance.MODERATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,27 +802,33 @@ public abstract class Task<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> withStagesHint(List<String> stages) {
|
public Task<T> withStagesHint(List<String> stages) {
|
||||||
return new Task<T>() {
|
return new StagesHintTask(stages);
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> withCounter() {
|
public class StagesHintTask extends Task<T> {
|
||||||
return new CountTask();
|
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) {
|
public static Task<Void> runAsync(ExceptionalRunnable<?> closure) {
|
||||||
@ -859,11 +866,6 @@ public abstract class Task<T> {
|
|||||||
public Collection<Task<?>> getDependencies() {
|
public Collection<Task<?>> getDependencies() {
|
||||||
return then == null ? Collections.emptySet() : Collections.singleton(then);
|
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);
|
}.setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -932,11 +934,6 @@ public abstract class Task<T> {
|
|||||||
public Collection<Task<?>> getDependents() {
|
public Collection<Task<?>> getDependents() {
|
||||||
return tasks;
|
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 {
|
public void execute() throws Exception {
|
||||||
setResult(callable.apply(Task.this.getResult()));
|
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() {
|
public boolean isRelyingOnDependents() {
|
||||||
return relyingOnDependents;
|
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> {
|
public class StageTask extends Task<T> {
|
||||||
@ -1116,19 +1101,19 @@ public abstract class Task<T> {
|
|||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
setResult(Task.this.getResult());
|
setResult(Task.this.getResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getStages() {
|
|
||||||
return Lang.merge(Task.this.getStages(), super.getStages());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CountTask extends Task<T> {
|
public class CountTask extends Task<T> {
|
||||||
private final UnaryOperator<Integer> COUNTER = a -> {
|
private final String countStage;
|
||||||
int result = 0;
|
|
||||||
if (a != null) result += a;
|
private CountTask(String countStage) {
|
||||||
return result + 1;
|
this.countStage = countStage;
|
||||||
};
|
setSignificance(TaskSignificance.MINOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCountStage() {
|
||||||
|
return countStage;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Task<?>> getDependents() {
|
public Collection<Task<?>> getDependents() {
|
||||||
@ -1146,13 +1131,8 @@ public abstract class Task<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postExecute() {
|
public void postExecute() throws Exception {
|
||||||
getProperties().put("count", COUNTER);
|
notifyPropertiesChanged();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getStages() {
|
|
||||||
return Lang.merge(Task.this.getStages(), super.getStages());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,7 @@ package org.jackhuang.hmcl.task;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@ -33,11 +30,10 @@ public abstract class TaskExecutor {
|
|||||||
protected final AtomicBoolean cancelled = new AtomicBoolean(false);
|
protected final AtomicBoolean cancelled = new AtomicBoolean(false);
|
||||||
protected Exception exception;
|
protected Exception exception;
|
||||||
private final List<String> stages;
|
private final List<String> stages;
|
||||||
protected final Map<String, Map<String, Object>> stageProperties = new HashMap<>();
|
|
||||||
|
|
||||||
public TaskExecutor(Task<?> task) {
|
public TaskExecutor(Task<?> task) {
|
||||||
this.firstTask = 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) {
|
public void addTaskListener(TaskListener taskListener) {
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package org.jackhuang.hmcl.task;
|
package org.jackhuang.hmcl.task;
|
||||||
|
|
||||||
import java.util.EventListener;
|
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 onStop(boolean success, TaskExecutor executor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPropertiesUpdate(Map<String, Map<String, Object>> stageProperties) {
|
public void onPropertiesUpdate(Task<?> task) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Hello Minecraft! Launcher
|
* 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
|
* 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
|
* 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.JsonParseException;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
@ -53,6 +54,7 @@ public class CacheRepository {
|
|||||||
private Path cacheDirectory;
|
private Path cacheDirectory;
|
||||||
private Path indexFile;
|
private Path indexFile;
|
||||||
private Map<String, ETagItem> index;
|
private Map<String, ETagItem> index;
|
||||||
|
private Map<String, Storage> storages = new HashMap<>();
|
||||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
public void changeDirectory(Path commonDir) {
|
public void changeDirectory(Path commonDir) {
|
||||||
@ -62,6 +64,10 @@ public class CacheRepository {
|
|||||||
|
|
||||||
lock.writeLock().lock();
|
lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
|
for (Storage storage : storages.values()) {
|
||||||
|
storage.changeDirectory(cacheDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
if (Files.isRegularFile(indexFile)) {
|
if (Files.isRegularFile(indexFile)) {
|
||||||
ETagIndex raw = JsonUtils.GSON.fromJson(FileUtils.readText(indexFile.toFile()), ETagIndex.class);
|
ETagIndex raw = JsonUtils.GSON.fromJson(FileUtils.readText(indexFile.toFile()), ETagIndex.class);
|
||||||
if (raw == null)
|
if (raw == null)
|
||||||
@ -76,6 +82,7 @@ public class CacheRepository {
|
|||||||
} finally {
|
} finally {
|
||||||
lock.writeLock().unlock();
|
lock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getCommonDirectory() {
|
public Path getCommonDirectory() {
|
||||||
@ -86,6 +93,15 @@ public class CacheRepository {
|
|||||||
return cacheDirectory;
|
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) {
|
protected Path getFile(String algorithm, String hash) {
|
||||||
return getCacheDirectory().resolve(algorithm).resolve(hash.substring(0, 2)).resolve(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();
|
private static CacheRepository instance = new CacheRepository();
|
||||||
|
|
||||||
public static CacheRepository getInstance() {
|
public static CacheRepository getInstance() {
|
||||||
|
@ -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() {
|
public static GsonBuilder defaultGsonBuilder() {
|
||||||
return new GsonBuilder()
|
return new GsonBuilder()
|
||||||
.enableComplexMapKeySerialization()
|
.enableComplexMapKeySerialization()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user