diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScanner.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScanner.java index 62b979f..3a5bb14 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScanner.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/ModsDirScanner.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds; +import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer; import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta; import io.gitlab.jfronny.inceptum.launcher.system.mds.flow.FlowMds; import io.gitlab.jfronny.inceptum.launcher.system.mds.noop.NoopMds; @@ -10,7 +11,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; -import java.util.function.BiConsumer; public interface ModsDirScanner extends Closeable { static ModsDirScanner get(Path modsDir, InstanceMeta meta) throws IOException { @@ -46,5 +46,5 @@ public interface ModsDirScanner extends Closeable { return hasScanned(mod.getMetadataPath()); } - void runOnce(ScanStage targetStage, BiConsumer discovered); + void runOnce(ScanStage targetStage, ThrowingBiConsumer discovered); } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java index 6bba830..4cd6ff9 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java @@ -2,6 +2,7 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds.flow; import io.gitlab.jfronny.commons.io.JFiles; import io.gitlab.jfronny.commons.ref.R; +import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta; @@ -15,7 +16,7 @@ import java.io.IOException; import java.nio.file.*; import java.util.*; import java.util.concurrent.*; -import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -111,17 +112,92 @@ public class FlowMds implements ModsDirScanner { return scannedPaths.contains(path) || scannedPaths.contains(ModPath.appendImod(path)); } + private final Set savedTasks = new HashSet<>(); + record SavedTask(ScanStage at, ThrowingBiConsumer discovered, CompletableFuture future) implements ThrowingBiConsumer { + @Override + public void accept(Path var1, Mod var2) throws IOException { + try { + discovered.accept(var1, var2); + future.complete(null); + } catch (Throwable e) { + future.completeExceptionally(e); + } + } + } + + private void waitUntilAfterScan(ScanStage at, ThrowingBiConsumer discovered) { + CompletableFuture future = new CompletableFuture<>(); + synchronized (savedTasks) { + savedTasks.add(new SavedTask(at, discovered, future)); + } + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + Utils.LOGGER.error("Could not wait for scan to finish", e); + } + } + private void scanTaskInternal() { while (!disposed) { - runOnce(ScanStage.ALL, R::nop); + Map tasks; + synchronized (savedTasks) { + tasks = savedTasks.stream().collect(Collectors.toMap( + SavedTask::at, + Function.identity() + )); + savedTasks.clear(); + } try { + performScanTask(Map.of(ScanStage.ALL, R::nop)); Thread.sleep(1000); - } catch (InterruptedException e) { + } catch (IOException | InterruptedException e) { + for (SavedTask task : tasks.values()) { + task.future.completeExceptionally(e); + } throw new RuntimeException(e); } } } + private void performScanTask(Map> discovered) throws IOException { + if (!Files.isDirectory(instance.modsDir())) { + return; + } + MdsPipeline pipeline = new MdsPipeline(); + pipeline.addTask(ScanStage.NONE, (path, mod) -> { + descriptions.put(path, mod); + scannedPaths.add(path); + }); + ScanStage targetStage = discovered.keySet().stream() + .max(Comparator.naturalOrder()) + .orElse(ScanStage.DISCOVER); + pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.NONE)); + pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.DISCOVER)); + if (targetStage.contains(ScanStage.DOWNLOAD)) { + pipeline.addTask(ScanStage.DOWNLOAD, new MdsDownloadTask(instance)); + pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.DOWNLOAD)); + } + if (targetStage.contains(ScanStage.CROSSREFERENCE)) { + pipeline.addTask(ScanStage.CROSSREFERENCE, new MdsCrossReferenceTask(instance)); + pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.CROSSREFERENCE)); + } + if (targetStage.contains(ScanStage.UPDATECHECK)) { + pipeline.addTask(ScanStage.UPDATECHECK, new MdsUpdateTask(instance, getGameVersion())); + pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.UPDATECHECK)); + } + pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.ALL)); + var futures1 = pipeline.run(factory, getToScan(), new MdsDiscoverTask(instance, getGameVersion())); + var futures2 = pipeline.run(factory, descriptions.entrySet().stream().map(Tuple::from).collect(Collectors.toSet())); + for (CompletableFuture future : Stream.concat(futures1.stream(), futures2.stream()).toList()) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + Utils.LOGGER.error("Could not scan file for mod info", e); + } + } + runOnce(ScanStage.ALL, R::nop); + } + private Set getToScan() throws IOException { if (descriptions.isEmpty()) return Set.copyOf(JFiles.list(instance.modsDir())); Set toScan = new HashSet<>(); @@ -141,34 +217,12 @@ public class FlowMds implements ModsDirScanner { } @Override - public void runOnce(ScanStage targetStage, BiConsumer discovered) { + public void runOnce(ScanStage targetStage, ThrowingBiConsumer discovered) { try { - if (!Files.isDirectory(instance.modsDir())) { - return; - } - MdsPipeline pipeline = new MdsPipeline(); - if (targetStage.contains(ScanStage.DOWNLOAD)) { - pipeline.addTask(ScanStage.DOWNLOAD, new MdsDownloadTask(instance)); - } - if (targetStage.contains(ScanStage.CROSSREFERENCE)) { - pipeline.addTask(ScanStage.CROSSREFERENCE, new MdsCrossReferenceTask(instance)); - } - if (targetStage.contains(ScanStage.UPDATECHECK)) { - pipeline.addTask(ScanStage.UPDATECHECK, new MdsUpdateTask(instance, getGameVersion())); - } - pipeline.addTask(ScanStage.NONE, (path, mod) -> { - scannedPaths.add(path); - descriptions.put(path, mod); - if (mod != null) discovered.accept(path, mod); - }); - var futures1 = pipeline.run(factory, getToScan(), new MdsDiscoverTask(instance, getGameVersion())); - var futures2 = pipeline.run(factory, descriptions.entrySet().stream().map(Tuple::from).collect(Collectors.toSet())); - for (CompletableFuture future : Stream.concat(futures1.stream(), futures2.stream()).toList()) { - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - Utils.LOGGER.error("Could not scan file for mod info", e); - } + if (!th.isAlive()) { + performScanTask(Map.of(targetStage, discovered)); + } else { + waitUntilAfterScan(targetStage, discovered); } } catch (IOException e) { Utils.LOGGER.error("Could not scan file for mod info", e); diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java index c6a6bd0..63af2af 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java @@ -3,23 +3,27 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds.flow; import io.gitlab.jfronny.commons.throwable.*; import io.gitlab.jfronny.commons.tuple.Tuple; import io.gitlab.jfronny.inceptum.launcher.system.mds.*; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import java.util.function.Consumer; public class MdsPipeline { private final List, IOException>>> functions = new ArrayList<>(); - public void addTask(ScanStage stage, ThrowingConsumer task) { + public void addTask(ScanStage stage, @Nullable ThrowingConsumer task) { + if (task == null) return; functions.add(Tuple.of(stage, tuple -> { if (tuple.right() instanceof MdsMod mmod) task.accept(mmod); })); } - public void addTask(ScanStage stage, ThrowingBiConsumer task) { + public void addTask(ScanStage stage, @Nullable ThrowingBiConsumer task) { + if (task == null) return; functions.add(Tuple.of(stage, tuple -> { if (tuple.right() instanceof MdsMod mmod) task.accept(tuple.left(), mmod); })); diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/noop/NoopMds.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/noop/NoopMds.java index 78f701d..570b4cb 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/noop/NoopMds.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/noop/NoopMds.java @@ -1,6 +1,7 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds.noop; import io.gitlab.jfronny.commons.ref.R; +import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer; import io.gitlab.jfronny.inceptum.launcher.system.mds.*; import io.gitlab.jfronny.inceptum.launcher.util.VoidClaimPool; @@ -54,6 +55,6 @@ public record NoopMds(String gameVersion) implements ModsDirScanner { } @Override - public void runOnce(ScanStage targetStage, BiConsumer discovered) { + public void runOnce(ScanStage targetStage, ThrowingBiConsumer discovered) { } } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/FileScanTask.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/FileScanTask.java index 13b6561..b7fa46b 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/FileScanTask.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/FileScanTask.java @@ -1,5 +1,6 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds.threaded; +import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer; import io.gitlab.jfronny.inceptum.common.GsonPreset; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson; @@ -14,9 +15,8 @@ import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.*; -import java.util.function.BiConsumer; -public record FileScanTask(ProtoInstance instance, Path file, BiConsumer discovered, String gameVersion) implements Runnable { +public record FileScanTask(ProtoInstance instance, Path file, ThrowingBiConsumer discovered, String gameVersion) implements Runnable { @Override public void run() { if (!Files.exists(file)) return; diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/ThreadedMds.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/ThreadedMds.java index 65814b4..62b99e2 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/ThreadedMds.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/threaded/ThreadedMds.java @@ -2,6 +2,7 @@ package io.gitlab.jfronny.inceptum.launcher.system.mds.threaded; import io.gitlab.jfronny.commons.io.JFiles; import io.gitlab.jfronny.commons.ref.R; +import io.gitlab.jfronny.commons.throwable.ThrowingBiConsumer; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta; import io.gitlab.jfronny.inceptum.launcher.system.mds.*; @@ -112,7 +113,7 @@ public class ThreadedMds implements ModsDirScanner { } @Override - public void runOnce(ScanStage targetStage, BiConsumer discovered) { + public void runOnce(ScanStage targetStage, ThrowingBiConsumer discovered) { try { if (!Files.isDirectory(instance.modsDir())) { return;