package io.gitlab.jfronny.inceptum.launcher.system.mds; import gsoncompile.extensions.io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson.GC_FabricModJson; import gsoncompile.extensions.io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta.GC_ModMeta; import io.gitlab.jfronny.gson.JsonParseException; import io.gitlab.jfronny.inceptum.common.Utils; import io.gitlab.jfronny.inceptum.launcher.model.fabric.FabricModJson; import io.gitlab.jfronny.inceptum.launcher.model.inceptum.ModMeta; import io.gitlab.jfronny.inceptum.launcher.system.instance.Mod; import io.gitlab.jfronny.inceptum.launcher.system.instance.ModPath; import io.gitlab.jfronny.inceptum.launcher.system.mds.noop.NoopMod; import io.gitlab.jfronny.inceptum.launcher.system.source.ModSource; import org.jetbrains.annotations.Nullable; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.*; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Function; public record FileScanTask(ProtoInstance instance, Path file, BiConsumer discovered, String gameVersion) implements Runnable { @Override public void run() { if (!Files.exists(file)) return; if (Files.isDirectory(file)) return; // Directories are not supported try { if (ModPath.isJar(file)) { final MetadataRef imod = new MetadataRef(file.parent.resolve(file.fileName + ModPath.EXT_IMOD), ModMeta::of); evaluateSources(file, imod); discovered.accept(imod.imodPath, new MdsMod(instance, imod.imodPath, file, false, imod.meta, getFmj(file, imod.meta))); } else if (ModPath.isImod(file)) { String fn = file.fileName.toString(); Path modFile = file.parent.resolve(fn.substring(0, fn.length() - ModPath.EXT_IMOD.length())); final MetadataRef imod = new MetadataRef(file, null); evaluateSources(modFile, imod); boolean managedJar = !Files.exists(modFile); discovered.accept(imod.imodPath, new MdsMod(instance, imod.imodPath, managedJar ? imod.jarPath : modFile, managedJar, imod.meta, getFmj(modFile, imod.meta))); } else discovered.accept(file, new NoopMod(file)); } catch (IOException | URISyntaxException | JsonParseException e) { Utils.LOGGER.error("Could not scan file for mod info", e); } } private void evaluateSources(Path modFile, MetadataRef ref) throws IOException, TEx { boolean modified = false; if (ref.meta.initialize(gameVersion)) { GC_ModMeta.write(ref.meta, ref.imodPath); modified = true; } ModSource selectedSource = null; for (ModSource source : ref.meta.sources.keySet()) { source.getUpdate(gameVersion); if (!Files.exists(source.jarPath)) source.download(); selectedSource = source; } if (selectedSource != null) { if (Files.exists(modFile)) { Files.delete(modFile); Path newImod = ref.imodPath.parent.resolve(selectedSource.shortName + ModPath.EXT_IMOD); Files.move(ref.imodPath, newImod); ref.imodPath = newImod; modified = true; } ref.jarPath = selectedSource.jarPath; } if (modified) ref.update(); } private @Nullable FabricModJson getFmj(Path modJarDefault, ModMeta md) throws IOException, URISyntaxException { if (!Files.exists(modJarDefault)) { if (md.sources.isEmpty()) { throw new FileNotFoundException("Mod " + modJarDefault.fileName.toString() + " doesn't specify a source and has no file"); } modJarDefault = List.copyOf(md.sources.keySet())[0].jarPath; } try (FileSystem fs = Utils.openZipFile(modJarDefault, false)) { Path fmjPath = fs.getPath("fabric.mod.json"); if (!Files.exists(fmjPath)) return null; return GC_FabricModJson.read(fmjPath); } } private static class MetadataRef { public MetadataRef(Path imodPath, @Nullable Function defaultMeta) throws IOException { this.imodPath = imodPath; if (!Files.exists(imodPath) && defaultMeta != null) GC_ModMeta.write(defaultMeta.apply(imodPath), imodPath); if (Files.exists(imodPath)) update(); } public Path imodPath; public Path jarPath; // filled in from evaluateSources public ModMeta meta; public void update() throws IOException { this.meta = GC_ModMeta.read(imodPath); } } }