From 5ea74c4deaa54187f16927bb2080eb2215c9bd40 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 18 Apr 2023 18:16:20 +0800 Subject: [PATCH] auto dep install + fixed launcher check --- src/ml/northwestwind/Config.java | 6 +- src/ml/northwestwind/Constants.java | 2 +- src/ml/northwestwind/Profile.java | 102 +++++++++++++++++----------- src/ml/northwestwind/Utils.java | 9 ++- update.json | 3 + 5 files changed, 78 insertions(+), 44 deletions(-) diff --git a/src/ml/northwestwind/Config.java b/src/ml/northwestwind/Config.java index 9f6e8c2..e38d431 100644 --- a/src/ml/northwestwind/Config.java +++ b/src/ml/northwestwind/Config.java @@ -12,7 +12,7 @@ public class Config { public static File directory, modpackDir, profileDir, exportDir, tempDir; - public static boolean acceptParentVersionMod, suppressUpdates, silentExceptions; + public static boolean acceptParentVersionMod, suppressUpdates, silentExceptions, disableOptional, alwaysInstallOptional; public static long retries; public static void run(String[] args) { @@ -57,6 +57,8 @@ public static void load() { acceptParentVersionMod = (boolean) json.getOrDefault("acceptParent", true); suppressUpdates = (boolean) json.getOrDefault("suppressUpdates", false); silentExceptions = (boolean) json.getOrDefault("silentExceptions", false); + disableOptional = (boolean) json.getOrDefault("disableOptional", false); + alwaysInstallOptional = (boolean) json.getOrDefault("alwaysInstallOptional", false); retries = (long) json.getOrDefault("retries", 3L); } catch (Exception e) { if (!silentExceptions) e.printStackTrace(); @@ -105,6 +107,8 @@ public static void save() { jo.put("suppressUpdates", suppressUpdates); jo.put("retries", retries); jo.put("silentExceptions", silentExceptions); + jo.put("disableOptional", disableOptional); + jo.put("alwaysInstallOptional", alwaysInstallOptional); PrintWriter pw = new PrintWriter("cf.json"); pw.write(jo.toJSONString()); diff --git a/src/ml/northwestwind/Constants.java b/src/ml/northwestwind/Constants.java index 9dd3607..51c9723 100644 --- a/src/ml/northwestwind/Constants.java +++ b/src/ml/northwestwind/Constants.java @@ -6,7 +6,7 @@ public class Constants { public static final String CURSEFORGE_API = "https://northwestwind.ml/api/curseforge/mods/"; - public static final String VERSION = "1.4.1"; + public static final String VERSION = "1.5.0"; private static final String OS = System.getProperty("os.name").toLowerCase(); public static final boolean IS_WINDOWS = (OS.contains("win")); public static final boolean IS_MAC = (OS.contains("mac")); diff --git a/src/ml/northwestwind/Profile.java b/src/ml/northwestwind/Profile.java index 1a4e520..27b1fa8 100644 --- a/src/ml/northwestwind/Profile.java +++ b/src/ml/northwestwind/Profile.java @@ -194,6 +194,67 @@ private static void delete(String[] names) { System.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a(String.format("%d success, %d failed", folders.size() - failed.get(), failed.get())).reset()); } + private static void downloadMod(String arg, JSONObject config, File modsFolder, Map mods, Map modNames) { + try { + String[] modIds = arg.split("_"); + String id = modIds[0], fileId = null; + if (modIds.length > 1) { + fileId = modIds[1]; + if (!Utils.isInteger(fileId)) throw new NoSuchObjectException("Mod file ID is invalid: " + fileId); + } + if (!Utils.isInteger(id)) throw new NoSuchObjectException("Mod ID is invalid: " + id); + JSONObject json = Utils.runRetry(() -> (JSONObject) Utils.readJsonFromUrl(Constants.CURSEFORGE_API + id)); + if (((long) json.get("classId")) != 6) + throw new NoSuchObjectException("The ID " + id + " does not represent a mod."); + JSONObject bestFile; + if (fileId == null) { + JSONArray files = Utils.runRetry(() -> (JSONArray) Utils.readJsonFromUrl(Constants.CURSEFORGE_API + id + "/files")); + List f = (List) files.stream().filter(o -> Utils.checkVersion((JSONArray) ((JSONObject) o).get("gameVersions"), config)).collect(Collectors.toList()); + f.sort((a, b) -> (int) ((long) ((JSONObject) b).get("id") - (long) ((JSONObject) a).get("id"))); + if (f.size() < 1) + throw new InputMismatchException("No available file of " + id + " found for this profile."); + bestFile = (JSONObject) f.get(0); + } else { + String finalFileId = fileId; + bestFile = Utils.runRetry(() -> (JSONObject) Utils.readJsonFromUrl(Constants.CURSEFORGE_API + id + "/files/" + finalFileId)); + } + String fileName = (String) bestFile.get("fileName"); + JSONArray dependencies = (JSONArray) bestFile.get("dependencies"); + if (dependencies.size() > 0) { + for (Object dep : dependencies) { + JSONObject dependency = (JSONObject) dep; + long modId = (long) dependency.get("modId"); + long relation = (long) dependency.get("relationType"); + if (relation == 2) { + if (Config.disableOptional) continue; + if (!Config.alwaysInstallOptional) { + System.out.print(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Mod ").a(fileName).a(" has optional dependency ").a(Utils.getModSlug(Long.toString(modId))).a(". Do you want to install it? [y/n] ")); + if (!Utils.readYesNo()) continue; + } + downloadMod(Long.toString(modId), config, modsFolder, mods, modNames); + } else if (relation == 3) { + System.out.print(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Mod ").a(fileName).a(" has optional dependency ").a(Utils.getModSlug(Long.toString(modId))).a(". Installing...")); + downloadMod(Long.toString(modId), config, modsFolder, mods, modNames); + } + } + } + String downloadUrl = (String) bestFile.get("downloadUrl"); + if (downloadUrl == null) { + long parsed = (long) bestFile.get("id"); + long first = parsed / 1000; + downloadUrl = String.format("https://edge.forgecdn.net/files/%d/%d/%s", first, parsed - first * 1000, fileName); + } + if (mods.containsKey(id)) { + File oldFile = new File(modsFolder.getAbsolutePath() + File.separator + modNames.get(id)); + if (oldFile.exists() && oldFile.isFile()) oldFile.delete(); + } + String loc = Utils.downloadFile(downloadUrl, modsFolder.getAbsolutePath(), fileName.replace(".jar", "_" + id + "_" + bestFile.get("id") + ".jar")); + System.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Downloaded " + loc)); + } catch (Exception e) { + if (!Config.silentExceptions) e.printStackTrace(); + } + } + private static void add(String[] ids) { String profile = ids[0]; List profiles = Config.loadProfiles(); @@ -217,46 +278,7 @@ private static void add(String[] ids) { if (!modsFolder.exists() || !modsFolder.isDirectory()) modsFolder.mkdir(); Map mods = Utils.getAllMods(modsFolder.getAbsolutePath()); Map modNames = Utils.getAllModNames(modsFolder.getAbsolutePath()); - for (String arg : Arrays.stream(ids).skip(1).toArray(String[]::new)) { - try { - String[] modIds = arg.split("_"); - String id = modIds[0], fileId = null; - if (modIds.length > 1) { - fileId = modIds[1]; - if (!Utils.isInteger(fileId)) throw new NoSuchObjectException("Mod file ID is invalid: " + fileId); - } - if (!Utils.isInteger(id)) throw new NoSuchObjectException("Mod ID is invalid: " + id); - JSONObject json = Utils.runRetry(() -> (JSONObject) Utils.readJsonFromUrl(Constants.CURSEFORGE_API + id)); - if (((long) json.get("classId")) != 6) - throw new NoSuchObjectException("The ID " + id + " does not represent a mod."); - JSONObject bestFile; - if (fileId == null) { - JSONArray files = Utils.runRetry(() -> (JSONArray) Utils.readJsonFromUrl(Constants.CURSEFORGE_API + id + "/files")); - List f = (List) files.stream().filter(o -> Utils.checkVersion((JSONArray) ((JSONObject) o).get("gameVersions"), config)).collect(Collectors.toList()); - f.sort((a, b) -> (int) ((long) ((JSONObject) b).get("id") - (long) ((JSONObject) a).get("id"))); - if (f.size() < 1) - throw new InputMismatchException("No available file of " + id + " found for this profile."); - bestFile = (JSONObject) f.get(0); - } else { - String finalFileId = fileId; - bestFile = Utils.runRetry(() -> (JSONObject) Utils.readJsonFromUrl(Constants.CURSEFORGE_API + id + "/files/" + finalFileId)); - } - String downloadUrl = (String) bestFile.get("downloadUrl"); - if (downloadUrl == null) { - long parsed = (long) bestFile.get("id"); - long first = parsed / 1000; - downloadUrl = String.format("https://edge.forgecdn.net/files/%d/%d/%s", first, parsed - first * 1000, bestFile.get("fileName")); - } - if (mods.containsKey(id)) { - File oldFile = new File(modsFolder.getAbsolutePath() + File.separator + modNames.get(id)); - if (oldFile.exists() && oldFile.isFile()) oldFile.delete(); - } - String loc = Utils.downloadFile(downloadUrl, modsFolder.getAbsolutePath(), ((String) bestFile.get("fileName")).replace(".jar", "_" + id + "_" + bestFile.get("id") + ".jar")); - System.out.println(Ansi.ansi().fg(Ansi.Color.GREEN).a("Downloaded " + loc)); - } catch (Exception e) { - if (!Config.silentExceptions) e.printStackTrace(); - } - } + for (String arg : Arrays.stream(ids).skip(1).toArray(String[]::new)) downloadMod(arg, config, modsFolder, mods, modNames); System.out.println(Ansi.ansi().fg(Ansi.Color.YELLOW).a("Finished all mod downloads.")); } diff --git a/src/ml/northwestwind/Utils.java b/src/ml/northwestwind/Utils.java index 2d9830b..9e88c67 100644 --- a/src/ml/northwestwind/Utils.java +++ b/src/ml/northwestwind/Utils.java @@ -222,12 +222,12 @@ public static boolean checkVersion(JSONArray versions, JSONObject config) { boolean launcherMatch = false, versionMatch = false; int trueCount = 0; for (String s : LAUNCHERS) - if (versions.contains(s)) { + if (versions.stream().filter(str -> ((String) str).equalsIgnoreCase(s)).findAny().isPresent()) { + trueCount++; if (s.equalsIgnoreCase(launcher)) { launcherMatch = true; break; } - trueCount++; } // Assume it is ok if no launcher is specified by the mod file if (trueCount == 0) launcherMatch = true; @@ -323,6 +323,11 @@ public static boolean readYesNo() { return res.equalsIgnoreCase("y"); } + public static String getModSlug(String id) { + JSONObject json = (JSONObject) readJsonFromUrl(Constants.CURSEFORGE_API + id); + return (String) json.get("slug"); + } + @FunctionalInterface public interface SupplierWithException { T getWithException() throws Exception; diff --git a/update.json b/update.json index 3bfd5bd..1479c2c 100644 --- a/update.json +++ b/update.json @@ -1,4 +1,7 @@ { + "1.5.0": { + "title": "Automated mod dependencies installation & Fixed launcher checking for profile add" + }, "1.4.1": { "title": "Prevent crashing when no versions directory is found" },