package io.gitlab.jfronny.inceptum.frontend.gui.window; import imgui.ImGui; import imgui.flag.ImGuiTableFlags; import imgui.type.ImString; import io.gitlab.jfronny.inceptum.Inceptum; import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeMod; import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta; import io.gitlab.jfronny.inceptum.model.inceptum.ModDescription; import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthProjectType; import io.gitlab.jfronny.inceptum.util.PathUtil; import io.gitlab.jfronny.inceptum.util.mds.IWModDescription; import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner; import io.gitlab.jfronny.inceptum.util.source.CurseforgeModSource; import io.gitlab.jfronny.inceptum.util.source.ModDownload; import io.gitlab.jfronny.inceptum.util.source.ModSource; import io.gitlab.jfronny.inceptum.util.source.ModrinthModSource; import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthSearchResult; import io.gitlab.jfronny.inceptum.model.modrinth.ModrinthVersion; import io.gitlab.jfronny.inceptum.util.Utils; import io.gitlab.jfronny.inceptum.util.api.CurseforgeApi; import io.gitlab.jfronny.inceptum.util.api.ModrinthApi; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.util.List; public class AddModWindow extends Window { private final ImString query = new ImString("", GuiUtil.INPUT_FIELD_LENGTH); private final Path modsDir; private final InstanceMeta instance; private final ModsDirScanner mds; private int cfPage = 0; private int mrPage = 0; private ModrinthSearchResult mr = null; private List cf = null; public AddModWindow(Path modsDir, InstanceMeta instance, ModsDirScanner mds) { super(modsDir.getParent().getFileName().toString() + " - Add Mods"); this.modsDir = modsDir; this.instance = instance; this.mds = mds; } private void reSearch() throws IOException { String query = this.query.get(); new Thread(() -> { try { ModrinthSearchResult ms = ModrinthApi.search(query, mrPage, instance.getMinecraftVersion(), ModrinthProjectType.mod); if (!this.query.get().equals(query)) return; mr = ms; List cs = CurseforgeApi.search(instance.getMinecraftVersion(), query, cfPage, "Popularity"); if (!this.query.get().equals(query)) return; cf = cs; } catch (IOException e) { Utils.LOGGER.error("Could not run search", e); } }, "search" + query).start(); } @Override public void draw() { try { if (ImGui.inputTextWithHint("Search", "Your search query", query)) { reSearch(); } if (ImGui.beginTabBar("ModsSelect")) { if (ImGui.beginTabItem("Modrinth")) { if (mr != null) { boolean hasNext = (mr.offset + mr.hits.size() < mr.total_hits); if (mrPage > 0) { if (ImGui.button("Previous Page")) { mrPage--; reSearch(); } if (hasNext) ImGui.sameLine(); } if (hasNext && ImGui.button("Next Page")) { mrPage++; reSearch(); } } if (mr != null && ImGui.beginTable("mods" + modsDir, 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Borders)) { for (ModrinthSearchResult.ModResult mod : mr.hits) { String modId = (mod.slug != null ? mod.slug : mod.project_id); final String idPrefix = "local-"; if (mod.project_id.startsWith(idPrefix)) mod.project_id = mod.project_id.substring(idPrefix.length()); //TODO detail view ImGui.tableNextColumn(); ImGui.text(mod.title); ImGui.tableNextColumn(); ImGui.text(mod.description); ImGui.tableNextColumn(); boolean alreadyPresent = false; for (IWModDescription mdsMod : mds.getMods()) { alreadyPresent = mdsMod.mod().isPresent() && mdsMod.mod().get().sources.keySet().stream() .anyMatch(s -> s instanceof ModrinthModSource ms && ms.getModId().equals(mod.project_id)); if (alreadyPresent) break; } if (alreadyPresent) { ImGui.text("Installed"); } else { if (ImGui.button("Add##" + mod.project_id)) { ModrinthVersion stable = null; ModrinthVersion beta = null; ModrinthVersion latest = null; for (ModrinthVersion version : ModrinthApi.getVersions(mod.project_id)) { if (version.game_versions.contains(instance.getMinecraftVersion()) && version.loaders.contains("fabric")) { latest = version; if (version.version_type == ModrinthVersion.VersionType.beta || version.version_type == ModrinthVersion.VersionType.release) { beta = version; } if (version.version_type == ModrinthVersion.VersionType.release) { stable = version; } } } if (stable != null) beta = stable; if (beta != null) latest = beta; if (latest == null) { Inceptum.showError("No valid version could be identified for this mod", "No version found"); } else { ModrinthVersion finalLatest = latest; new Thread(() -> { try { download(new ModrinthModSource(finalLatest.id), modsDir.resolve((mod.slug == null ? mod.project_id : mod.slug) + PathUtil.EXT_IMOD), mds).write(); } catch (IOException e) { Inceptum.showError("Could not download mod", e); } }).start(); } } } ImGui.sameLine(); if (ImGui.button("Web##" + mod.project_id)) { Utils.openWebBrowser(new URI("https://modrinth.com/mod/" + modId)); } } ImGui.endTable(); } ImGui.endTabItem(); } if (ImGui.beginTabItem("Curseforge")) { if (cf != null) { boolean hasNext = cf.size() == 20; if (cfPage > 0) { if (ImGui.button("Previous Page")) { cfPage--; reSearch(); } if (hasNext) ImGui.sameLine(); } if (hasNext && ImGui.button("Next Page")) { cfPage++; reSearch(); } } if (cf != null && ImGui.beginTable("curseforge" + modsDir, 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Borders)) { for (CurseforgeMod mod : cf) { //TODO detail view ImGui.tableNextColumn(); ImGui.text(mod.name); ImGui.tableNextColumn(); ImGui.text(mod.summary); ImGui.tableNextColumn(); boolean alreadyPresent = false; for (IWModDescription mdsMod : mds.getMods()) { alreadyPresent = mdsMod.mod().isPresent() && mdsMod.mod().get().sources.keySet().stream() .anyMatch(s -> s instanceof CurseforgeModSource ms && ms.getProjectId() == mod.id); if (alreadyPresent) break; } if (alreadyPresent) { ImGui.text("Installed"); } else { if (ImGui.button("Add##" + mod.id)) { CurseforgeMod.LatestFileIndex latest = null; for (CurseforgeMod.LatestFileIndex file : mod.latestFilesIndexes) { if (file.gameVersion.equals(instance.getMinecraftVersion())) { if (latest == null) latest = file; } } if (latest == null) { Inceptum.showError("No valid version could be identified for this mod", "No version found"); } else { CurseforgeMod.LatestFileIndex finalLatest = latest; new Thread(() -> { try { download(new CurseforgeModSource(mod.id, finalLatest.fileId), modsDir.resolve((mod.slug == null ? mod.id : mod.slug) + PathUtil.EXT_IMOD), mds).write(); } catch (IOException e) { Inceptum.showError("Could not download mod", e); } }).start(); } } } ImGui.sameLine(); if (ImGui.button("Web##" + mod.id)) { Utils.openWebBrowser(new URI(mod.links.websiteUrl)); } } ImGui.endTable(); } ImGui.endTabItem(); } ImGui.endTabBar(); } } catch (IOException | URISyntaxException e) { Utils.LOGGER.error("Something went wrong while rendering an AddModWindow", e); } } public static DownloadMeta download(ModSource ms, Path metaFile, ModsDirScanner mds) throws IOException { for (IWModDescription value : mds.getMods()) { if (value.mod().isEmpty()) continue; for (ModSource source : value.mod().get().sources.keySet()) { if (ms.equals(source)) { return new DownloadMeta(new ModDownload(value.mod().get().sha1, value.mod().get().murmur2, value.path()), value.mod().get(), source, metaFile); } } } ModDownload md = ms.download(); ModDescription manifest = ModDescription.of(md.sha1(), md.murmur2(), ms, mds.getGameVersion()); for (ModSource dependency : ms.getDependencies(mds.getGameVersion())) { DownloadMeta depMan = download(dependency, metaFile.getParent().resolve(dependency.getShortName() + PathUtil.EXT_IMOD), mds); depMan.description.dependents.add(md.file().getFileName().toString()); manifest.dependencies.add(depMan.download.file().getFileName().toString()); depMan.write(); } return new DownloadMeta(md, manifest, ms, metaFile); } public record DownloadMeta(ModDownload download, ModDescription description, ModSource source, Path metaFile) { public void write() throws IOException { Utils.writeObject(metaFile, description); } } }