Skip to content

Commit b7ade35

Browse files
committed
fix(mods): don't prompt to install dependencies that are already installed
The 'install dependencies' dialog listed every dependency the selected Modrinth version declared, with no check against what's already in the mods folder — so reinstalling/updating a mod with common deps (Fabric API, Cloth Config, etc.) nagged you to 'install' something already there, matched only against the exact same version's own hash, not any installed version of the dependency itself. Added getInstalledModrinthProjectIds(): bulk-hashes every jar in the mods folder and resolves them against Modrinth's version_files endpoint in one request to get each installed jar's project id. handleInstallation() now filters the dependency list against that set before prompting — already-installed deps are silently dropped, and if that empties the list entirely, the dialog is skipped altogether and the mod downloads directly, same as the no-dependencies case. (CurseForge-sourced mods don't populate versionDependencyIds at all today, so this only affects the Modrinth dependency-resolution path — consistent with the existing 'no deps' short-circuit already only applying there.)
1 parent 367c03e commit b7ade35

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ModsSearchFragment.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,36 @@ public void handleInstallation(Context context, ModDetail modDetail, int selecte
450450
return;
451451
}
452452

453+
// Drop any dependency that's already sitting in the mods folder (as any
454+
// version, not just the one this mod happens to ask for) — otherwise every
455+
// reinstall/update of a mod with common dependencies (Fabric API, Cloth
456+
// Config, etc.) nags you to "install" something you already have.
457+
PojavApplication.sExecutorService.execute(() -> {
458+
java.util.Set<String> installedProjectIds = getInstalledModrinthProjectIds();
459+
List<String> remainingIds = new ArrayList<>();
460+
List<String> remainingTypes = new ArrayList<>();
461+
for (int i = 0; i < depIds.length; i++) {
462+
if (depIds[i] != null && installedProjectIds.contains(depIds[i])) continue;
463+
remainingIds.add(depIds[i]);
464+
remainingTypes.add((depTypes != null && i < depTypes.length) ? depTypes[i] : "required");
465+
}
466+
467+
if (remainingIds.isEmpty()) {
468+
// Every dependency is already installed — no need to prompt at all
469+
mMainHandler.post(() ->
470+
downloadMod(context, url, fileName, new String[0], new String[0], oldFilePath));
471+
return;
472+
}
473+
474+
mMainHandler.post(() -> promptForDependencies(context, url, fileName,
475+
remainingIds.toArray(new String[0]), remainingTypes.toArray(new String[0]), oldFilePath));
476+
});
477+
}
478+
479+
/** Fetches display names for the (already filtered to not-yet-installed) dependency
480+
* list, then shows the install dialog once every name has resolved. */
481+
private void promptForDependencies(Context context, String url, String fileName,
482+
String[] depIds, String[] depTypes, String oldFilePath) {
453483
// Fetch project names for all deps, then show dialog
454484
String[] labels = new String[depIds.length];
455485
final boolean[] checkedDefaults = new boolean[depIds.length];
@@ -572,6 +602,55 @@ private void downloadDependency(String projectId, File modsDir) {
572602
}
573603
}
574604

605+
/**
606+
* Hashes every jar currently in the mods folder and bulk-resolves them against
607+
* Modrinth's version_files endpoint to find which Modrinth project each one
608+
* belongs to — used to filter "install dependencies" prompts down to ones that
609+
* aren't already installed under some other version/file name.
610+
*/
611+
private java.util.Set<String> getInstalledModrinthProjectIds() {
612+
java.util.Set<String> projectIds = new java.util.HashSet<>();
613+
File modsDir = getModsDir();
614+
File[] files = modsDir.listFiles(f -> f.isFile() &&
615+
(f.getName().endsWith(".jar") || f.getName().endsWith(".jar.disabled")));
616+
if (files == null || files.length == 0) return projectIds;
617+
618+
List<String> hashes = new ArrayList<>();
619+
for (File f : files) {
620+
String hash = sha1Hex(f);
621+
if (hash != null) hashes.add(hash);
622+
}
623+
if (hashes.isEmpty()) return projectIds;
624+
625+
try {
626+
com.google.gson.JsonObject body = new com.google.gson.JsonObject();
627+
com.google.gson.JsonArray hashArray = new com.google.gson.JsonArray();
628+
for (String hash : hashes) hashArray.add(hash);
629+
body.add("hashes", hashArray);
630+
body.addProperty("algorithm", "sha1");
631+
632+
java.util.Map<String, String> headers = new java.util.HashMap<>();
633+
headers.put("Content-Type", "application/json");
634+
headers.put("Accept", "application/json");
635+
636+
String responseRaw = net.kdt.pojavlaunch.modloaders.modpacks.api.ApiHandler.postRaw(
637+
headers, "https://api.modrinth.com/v2/version_files", body.toString());
638+
if (responseRaw == null) return projectIds;
639+
640+
com.google.gson.JsonObject response = com.google.gson.JsonParser.parseString(responseRaw).getAsJsonObject();
641+
for (java.util.Map.Entry<String, com.google.gson.JsonElement> entry : response.entrySet()) {
642+
if (!entry.getValue().isJsonObject()) continue;
643+
com.google.gson.JsonObject version = entry.getValue().getAsJsonObject();
644+
if (version.has("project_id") && !version.get("project_id").isJsonNull()) {
645+
projectIds.add(version.get("project_id").getAsString());
646+
}
647+
}
648+
} catch (Exception e) {
649+
Log.w(TAG, "Failed to bulk-resolve installed mod hashes: " + e.getMessage());
650+
}
651+
return projectIds;
652+
}
653+
575654
private String fetchProjectName(String projectId) {
576655
try {
577656
net.kdt.pojavlaunch.modloaders.modpacks.api.ApiHandler handler =

0 commit comments

Comments
 (0)