feat(launcher): rewrite mds-flow runOnce to wait on future of internal scan if scan thread is running
This commit is contained in:
parent
9b55a3f08d
commit
a67a0b4b4f
@ -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<Path, Mod> discovered);
|
||||
void runOnce(ScanStage targetStage, ThrowingBiConsumer<Path, Mod, IOException> discovered);
|
||||
}
|
||||
|
@ -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<SavedTask> savedTasks = new HashSet<>();
|
||||
record SavedTask(ScanStage at, ThrowingBiConsumer<Path, Mod, IOException> discovered, CompletableFuture<Void> future) implements ThrowingBiConsumer<Path, Mod, IOException> {
|
||||
@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<Path, Mod, IOException> discovered) {
|
||||
CompletableFuture<Void> 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<ScanStage, SavedTask> 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<ScanStage, ThrowingBiConsumer<Path, Mod, IOException>> 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<Void> 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<Path> getToScan() throws IOException {
|
||||
if (descriptions.isEmpty()) return Set.copyOf(JFiles.list(instance.modsDir()));
|
||||
Set<Path> toScan = new HashSet<>();
|
||||
@ -141,34 +217,12 @@ public class FlowMds implements ModsDirScanner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runOnce(ScanStage targetStage, BiConsumer<Path, Mod> discovered) {
|
||||
public void runOnce(ScanStage targetStage, ThrowingBiConsumer<Path, Mod, IOException> 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<Void> 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);
|
||||
|
@ -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<Tuple<ScanStage, ThrowingConsumer<Tuple<Path, Mod>, IOException>>> functions = new ArrayList<>();
|
||||
|
||||
public void addTask(ScanStage stage, ThrowingConsumer<MdsMod, IOException> task) {
|
||||
public void addTask(ScanStage stage, @Nullable ThrowingConsumer<MdsMod, IOException> 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<Path, MdsMod, IOException> task) {
|
||||
public void addTask(ScanStage stage, @Nullable ThrowingBiConsumer<Path, ? super MdsMod, IOException> task) {
|
||||
if (task == null) return;
|
||||
functions.add(Tuple.of(stage, tuple -> {
|
||||
if (tuple.right() instanceof MdsMod mmod) task.accept(tuple.left(), mmod);
|
||||
}));
|
||||
|
@ -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<Path, Mod> discovered) {
|
||||
public void runOnce(ScanStage targetStage, ThrowingBiConsumer<Path, Mod, IOException> discovered) {
|
||||
}
|
||||
}
|
||||
|
@ -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<Path, Mod> discovered, String gameVersion) implements Runnable {
|
||||
public record FileScanTask(ProtoInstance instance, Path file, ThrowingBiConsumer<Path, Mod, IOException> discovered, String gameVersion) implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Files.exists(file)) return;
|
||||
|
@ -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<Path, Mod> discovered) {
|
||||
public void runOnce(ScanStage targetStage, ThrowingBiConsumer<Path, Mod, IOException> discovered) {
|
||||
try {
|
||||
if (!Files.isDirectory(instance.modsDir())) {
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user