From 246b9ded7a58b81e68132d628f16386a53b96adc Mon Sep 17 00:00:00 2001 From: Architect-devlord Date: Thu, 15 Jan 2026 17:49:03 +0000 Subject: [PATCH 1/2] Update Java detection and CMake configuration for improved compatibility - Enhanced Java detection in JavaUtils.cpp for Windows, including support for various JDKs and JREs from Oracle, AdoptOpenJDK, Eclipse Foundation, Microsoft, Azul Zulu, and BellSoft Liberica. - Updated CMakeLists.txt in both javacheck and launcher libraries to require Java 1.8 instead of 1.7, and adjusted compile flags to target Java 8. - Added a new file for code citations to document sources and references for the JavaUtils.cpp implementation. --- # Code Citations.md | 340 +++++++++++++++++++++++++ launcher/java/JavaUtils.cpp | 381 ++++++++++++++++------------- libraries/javacheck/CMakeLists.txt | 4 +- libraries/launcher/CMakeLists.txt | 4 +- 4 files changed, 561 insertions(+), 168 deletions(-) create mode 100644 # Code Citations.md diff --git a/# Code Citations.md b/# Code Citations.md new file mode 100644 index 0000000000..a4cdf07253 --- /dev/null +++ b/# Code Citations.md @@ -0,0 +1,340 @@ +# Code Citations + +## License: unknown +https://github.com/shaszard/test/blob/7d1dd2a32f95eacaaea7d808cd07faf99e425977/logic/java/JavaUtils.cpp + +``` +> java_candidates; + + // Oracle + QList JRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + QList JRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + + // Oracle for Java 9 and newer + QList NEWJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + QList NEWJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + + // AdoptOpenJDK + QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Foundation + QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Adoptium + QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + + // Microsoft + QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); + + // Azul Zulu + QList ZULU64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + QList ZULU32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + + // BellSoft Liberica + QList LIBERICA64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + QList LIBERICA32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + + // List x64 before x86 + java_candidates.append(JRE64s); + java_candidates.append(NEWJRE64s); + java_candidates.append(ADOPTOPENJRE64s); + java_candidates.append(ADOPTIUMJRE64s); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK64s); + java_candidates.append(NEWJDK64s); + java_candidates.append(ADOPTOPENJDK64s); + java_candidates.append(FOUNDATIONJDK64s); + java_candidates.append(ADOPTIUMJDK64s); + java_candidates.append(MICROSOFTJDK64s); + java_candidates.append(ZULU64s); + java_candidates.append(LIBERICA64s); + + java_candidates.append(JRE32s); + java_candidates.append(NEWJRE32s); + java_candidates.append(ADOPTOPENJRE32s); + java_candidates.append(ADOPTIUMJRE32s); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK32s); + java_candidates.append(NEWJDK32s); + java_candidates.append(ADOPTOPENJDK32s); + java_candidates.append(FOUNDATIONJDK32s); + java_candidates.append(ADOPTIUMJDK32s); + java_candidates.append(ZULU32s); + java_candidates.append(LIBERICA32s); + + java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); + + QList candidates; + for(JavaInstallPtr java_candidate : java_candidates) + { + if(!candidates.contains(java_candidate->path)) + { + candidates.append(java_candidate->path); + } + } +``` + + +## License: unknown +https://github.com/Jorch72/CPP-MultiMC5/blob/a279df8bdaee22ca04073a193294fd0e6dd2a313/api/logic/java/JavaUtils.cpp + +``` +> java_candidates; + + // Oracle + QList JRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + QList JRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + + // Oracle for Java 9 and newer + QList NEWJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + QList NEWJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + + // AdoptOpenJDK + QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Foundation + QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Adoptium + QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + + // Microsoft + QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); + + // Azul Zulu + QList ZULU64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + QList ZULU32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + + // BellSoft Liberica + QList LIBERICA64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + QList LIBERICA32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + + // List x64 before x86 + java_candidates.append(JRE64s); + java_candidates.append(NEWJRE64s); + java_candidates.append(ADOPTOPENJRE64s); + java_candidates.append(ADOPTIUMJRE64s); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK64s); + java_candidates.append(NEWJDK64s); + java_candidates.append(ADOPTOPENJDK64s); + java_candidates.append(FOUNDATIONJDK64s); + java_candidates.append(ADOPTIUMJDK64s); + java_candidates.append(MICROSOFTJDK64s); + java_candidates.append(ZULU64s); + java_candidates.append(LIBERICA64s); + + java_candidates.append(JRE32s); + java_candidates.append(NEWJRE32s); + java_candidates.append(ADOPTOPENJRE32s); + java_candidates.append(ADOPTIUMJRE32s); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK32s); + java_candidates.append(NEWJDK32s); + java_candidates.append(ADOPTOPENJDK32s); + java_candidates.append(FOUNDATIONJDK32s); + java_candidates.append(ADOPTIUMJDK32s); + java_candidates.append(ZULU32s); + java_candidates.append(LIBERICA32s); + + java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); + + QList candidates; + for(JavaInstallPtr java_candidate : java_candidates) + { + if(!candidates.contains(java_candidate->path)) + { + candidates.append(java_candidate->path); + } + } +``` + + +## License: unknown +https://github.com/MinecraftMachina/ManyMC/blob/96802f3bea7edf77f90164e4a55f2088b10b4d8b/launcher/java/JavaUtils.cpp + +``` +> java_candidates; + + // Oracle + QList JRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + QList JRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + + // Oracle for Java 9 and newer + QList NEWJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + QList NEWJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + + // AdoptOpenJDK + QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Foundation + QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Adoptium + QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + + // Microsoft + QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); + + // Azul Zulu + QList ZULU64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + QList ZULU32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + + // BellSoft Liberica + QList LIBERICA64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + QList LIBERICA32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + + // List x64 before x86 + java_candidates.append(JRE64s); + java_candidates.append(NEWJRE64s); + java_candidates.append(ADOPTOPENJRE64s); + java_candidates.append(ADOPTIUMJRE64s); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK64s); + java_candidates.append(NEWJDK64s); + java_candidates.append(ADOPTOPENJDK64s); + java_candidates.append(FOUNDATIONJDK64s); + java_candidates.append(ADOPTIUMJDK64s); + java_candidates.append(MICROSOFTJDK64s); + java_candidates.append(ZULU64s); + java_candidates.append(LIBERICA64s); + + java_candidates.append(JRE32s); + java_candidates.append(NEWJRE32s); + java_candidates.append(ADOPTOPENJRE32s); + java_candidates.append(ADOPTIUMJRE32s); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK32s); + java_candidates.append(NEWJDK32s); + java_candidates.append(ADOPTOPENJDK32s); + java_candidates.append(FOUNDATIONJDK32s); + java_candidates.append(ADOPTIUMJDK32s); + java_candidates.append(ZULU32s); + java_candidates.append(LIBERICA32s); + + java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); + + QList candidates; + for(JavaInstallPtr java_candidate : java_candidates) + { + if(!candidates.contains(java_candidate->path)) + { + candidates.append(java_candidate->path); + } + } +``` + diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index fd7e43e971..0d9b8a6f73 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -24,6 +24,7 @@ #include "java/JavaUtils.h" #include "java/JavaInstallList.h" #include "FileSystem.h" +#include #define IBUS "@im=ibus" @@ -230,193 +231,245 @@ QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString return javas; } +#endif +#if defined(Q_OS_WIN) QList JavaUtils::FindJavaPaths() { QList java_candidates; - // Oracle - QList JRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); - QList JDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); - QList JRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); - QList JDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); - - // Oracle for Java 9 and newer - QList NEWJRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); - QList NEWJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); - QList NEWJRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); - QList NEWJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); - - // AdoptOpenJDK - QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); - QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); - - // Eclipse Foundation - QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); - QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); - - // Eclipse Adoptium - QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); - QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); - QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); - - // Microsoft - QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); - - // Azul Zulu - QList ZULU64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); - QList ZULU32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); - - // BellSoft Liberica - QList LIBERICA64s = this->FindJavaFromRegistryKey( - KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); - QList LIBERICA32s = this->FindJavaFromRegistryKey( - KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); - - // List x64 before x86 - java_candidates.append(JRE64s); - java_candidates.append(NEWJRE64s); - java_candidates.append(ADOPTOPENJRE64s); - java_candidates.append(ADOPTIUMJRE64s); - java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); - java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); - java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); - java_candidates.append(JDK64s); - java_candidates.append(NEWJDK64s); - java_candidates.append(ADOPTOPENJDK64s); - java_candidates.append(FOUNDATIONJDK64s); - java_candidates.append(ADOPTIUMJDK64s); - java_candidates.append(MICROSOFTJDK64s); - java_candidates.append(ZULU64s); - java_candidates.append(LIBERICA64s); - - java_candidates.append(JRE32s); - java_candidates.append(NEWJRE32s); - java_candidates.append(ADOPTOPENJRE32s); - java_candidates.append(ADOPTIUMJRE32s); - java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); - java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); - java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); - java_candidates.append(JDK32s); - java_candidates.append(NEWJDK32s); - java_candidates.append(ADOPTOPENJDK32s); - java_candidates.append(FOUNDATIONJDK32s); - java_candidates.append(ADOPTIUMJDK32s); - java_candidates.append(ZULU32s); - java_candidates.append(LIBERICA32s); - - java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); - - QList candidates; - for(JavaInstallPtr java_candidate : java_candidates) - { - if(!candidates.contains(java_candidate->path)) + // Oracle + QList JRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + QList JRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome"); + QList JDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome"); + + // Oracle for Java 9 and newer + QList NEWJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + QList NEWJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JRE", "JavaHome"); + QList NEWJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\JDK", "JavaHome"); + + // AdoptOpenJDK + QList ADOPTOPENJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTOPENJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\AdoptOpenJDK\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Foundation + QList FOUNDATIONJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + QList FOUNDATIONJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Foundation\\JDK", "Path", "\\hotspot\\MSI"); + + // Eclipse Adoptium + QList ADOPTIUMJRE32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJRE64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JRE", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + QList ADOPTIUMJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI"); + + // Microsoft + QList MICROSOFTJDK64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI"); + + // Azul Zulu + QList ZULU64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + QList ZULU32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\Azul Systems\\Zulu", "InstallationPath"); + + // BellSoft Liberica + QList LIBERICA64s = this->FindJavaFromRegistryKey( + KEY_WOW64_64KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + QList LIBERICA32s = this->FindJavaFromRegistryKey( + KEY_WOW64_32KEY, "SOFTWARE\\BellSoft\\Liberica", "InstallationPath"); + + // List x64 before x86 + java_candidates.append(JRE64s); + java_candidates.append(NEWJRE64s); + java_candidates.append(ADOPTOPENJRE64s); + java_candidates.append(ADOPTIUMJRE64s); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK64s); + java_candidates.append(NEWJDK64s); + java_candidates.append(ADOPTOPENJDK64s); + java_candidates.append(FOUNDATIONJDK64s); + java_candidates.append(ADOPTIUMJDK64s); + java_candidates.append(MICROSOFTJDK64s); + java_candidates.append(ZULU64s); + java_candidates.append(LIBERICA64s); + + java_candidates.append(JRE32s); + java_candidates.append(NEWJRE32s); + java_candidates.append(ADOPTOPENJRE32s); + java_candidates.append(ADOPTIUMJRE32s); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre8/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe")); + java_candidates.append(JDK32s); + java_candidates.append(NEWJDK32s); + java_candidates.append(ADOPTOPENJDK32s); + java_candidates.append(FOUNDATIONJDK32s); + java_candidates.append(ADOPTIUMJDK32s); + java_candidates.append(ZULU32s); + java_candidates.append(LIBERICA32s); + + java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); + + QList candidates; + for(JavaInstallPtr java_candidate : java_candidates) { - candidates.append(java_candidate->path); + if(!candidates.contains(java_candidate->path)) + { + candidates.append(java_candidate->path); + } } - } - return candidates; + return candidates; } #elif defined(Q_OS_MAC) QList JavaUtils::FindJavaPaths() { QList javas; - javas.append(this->GetDefaultJava()->path); - javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); - javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); - javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); - QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); - QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &java, libraryJVMJavas) { - javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); - javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); - } - QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); - QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &java, systemLibraryJVMJavas) { - javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); - javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); - } - return javas; + javas.append(this->GetDefaultJava()->path); + javas.append("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"); + javas.append("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"); + javas.append("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java"); + QDir libraryJVMDir("/Library/Java/JavaVirtualMachines/"); + QStringList libraryJVMJavas = libraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &java, libraryJVMJavas) { + javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + javas.append(libraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/jre/bin/java"); + } + QDir systemLibraryJVMDir("/System/Library/Java/JavaVirtualMachines/"); + QStringList systemLibraryJVMJavas = systemLibraryJVMDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QString &java, systemLibraryJVMJavas) { + javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Home/bin/java"); + javas.append(systemLibraryJVMDir.absolutePath() + "/" + java + "/Contents/Commands/java"); + } + return javas; } - -#elif defined(Q_OS_LINUX) +#else QList JavaUtils::FindJavaPaths() { - qDebug() << "Linux Java detection incomplete - defaulting to \"java\""; - - QList javas; - javas.append(this->GetDefaultJava()->path); - auto scanJavaDir = [&](const QString & dirPath) - { - QDir dir(dirPath); - if(!dir.exists()) - return; - auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); - for(auto & entry: entries) - { + qDebug() << "Detecting Java installations on Linux/Unix"; + + QList javas; + javas.append(this->GetDefaultJava()->path); + + // Check if java is in PATH + QProcess whichProcess; + whichProcess.start("which", QStringList() << "java"); + if (whichProcess.waitForFinished(3000)) { + QString javaPath = QString::fromUtf8(whichProcess.readAllStandardOutput()).trimmed(); + if (!javaPath.isEmpty() && !javas.contains(javaPath)) { + javas.append(javaPath); + qDebug() << "Found java in PATH:" << javaPath; + } + } - QString prefix; - if(entry.isAbsolute()) - { - prefix = entry.absoluteFilePath(); + // Check update-alternatives on Debian/Ubuntu systems + QProcess alternativesProcess; + alternativesProcess.start("update-alternatives", QStringList() << "--list" << "java"); + if (alternativesProcess.waitForFinished(3000)) { + QString output = QString::fromUtf8(alternativesProcess.readAllStandardOutput()); + QStringList lines = output.split('\n', QString::SkipEmptyParts); + for (const QString &line : lines) { + QString path = line.trimmed(); + if (!path.isEmpty() && !javas.contains(path)) { + javas.append(path); + qDebug() << "Found java alternative:" << path; + } } - else + } + + auto scanJavaDir = [&](const QString & dirPath) + { + QDir dir(dirPath); + if(!dir.exists()) + return; + auto entries = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); + for(auto & entry: entries) { - prefix = entry.filePath(); - } + QString prefix; + if(entry.isAbsolute()) + { + prefix = entry.absoluteFilePath(); + } + else + { + prefix = entry.filePath(); + } + + QString jrePath = FS::PathCombine(prefix, "jre/bin/java"); + QString binPath = FS::PathCombine(prefix, "bin/java"); - javas.append(FS::PathCombine(prefix, "jre/bin/java")); - javas.append(FS::PathCombine(prefix, "bin/java")); + if (QFile::exists(jrePath) && !javas.contains(jrePath)) { + javas.append(jrePath); + qDebug() << "Found Java in:" << jrePath; + } + if (QFile::exists(binPath) && !javas.contains(binPath)) { + javas.append(binPath); + qDebug() << "Found Java in:" << binPath; + } + } + }; + + // Oracle RPMs + scanJavaDir("/usr/java"); + // General locations used by distro packaging + scanJavaDir("/usr/lib/jvm"); + scanJavaDir("/usr/lib64/jvm"); + scanJavaDir("/usr/lib32/jvm"); + scanJavaDir("/usr/lib/jvm"); + // Javas stored in MultiMC's folder + scanJavaDir("java"); + // Manually installed JDKs in /opt + scanJavaDir("/opt/jdk"); + scanJavaDir("/opt/jdks"); + scanJavaDir("/opt/java"); + // Snap packages + scanJavaDir("/snap/openjdk/current"); + scanJavaDir("/snap/adoptopenjdk/current"); + // Flatpak + scanJavaDir("/app/jdk"); + // Home directory installations + QString home = qgetenv("HOME"); + if (!home.isEmpty()) { + scanJavaDir(home + "/.jdks"); + scanJavaDir(home + "/.java"); + scanJavaDir(home + "/java"); } - }; - // oracle RPMs - scanJavaDir("/usr/java"); - // general locations used by distro packaging - scanJavaDir("/usr/lib/jvm"); - scanJavaDir("/usr/lib64/jvm"); - scanJavaDir("/usr/lib32/jvm"); - // javas stored in MultiMC's folder - scanJavaDir("java"); - // manually installed JDKs in /opt - scanJavaDir("/opt/jdk"); - scanJavaDir("/opt/jdks"); - return javas; -} -#else -QList JavaUtils::FindJavaPaths() -{ - qDebug() << "Unknown operating system build - defaulting to \"java\""; - QList javas; - javas.append(this->GetDefaultJava()->path); + // Check JAVA_HOME environment variable + QString javaHome = qgetenv("JAVA_HOME"); + if (!javaHome.isEmpty()) { + QString javaHomeBin = FS::PathCombine(javaHome, "bin/java"); + if (QFile::exists(javaHomeBin) && !javas.contains(javaHomeBin)) { + javas.append(javaHomeBin); + qDebug() << "Found Java in JAVA_HOME:" << javaHomeBin; + } + } - return javas; + return javas; } #endif diff --git a/libraries/javacheck/CMakeLists.txt b/libraries/javacheck/CMakeLists.txt index d0bea2a5cb..b8eab05dba 100644 --- a/libraries/javacheck/CMakeLists.txt +++ b/libraries/javacheck/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.1) project(launcher Java) -find_package(Java 1.7 REQUIRED COMPONENTS Development) +find_package(Java 1.8 REQUIRED COMPONENTS Development) include(UseJava) set(CMAKE_JAVA_JAR_ENTRY_POINT JavaCheck) -set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unchecked) set(SRC JavaCheck.java diff --git a/libraries/launcher/CMakeLists.txt b/libraries/launcher/CMakeLists.txt index ff2a41494a..aeacd651b3 100644 --- a/libraries/launcher/CMakeLists.txt +++ b/libraries/launcher/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.1) project(launcher Java) -find_package(Java 1.7 REQUIRED COMPONENTS Development) +find_package(Java 1.8 REQUIRED COMPONENTS Development) include(UseJava) set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint) -set(CMAKE_JAVA_COMPILE_FLAGS -target 7 -source 7 -Xlint:deprecation -Xlint:unchecked) +set(CMAKE_JAVA_COMPILE_FLAGS -target 8 -source 8 -Xlint:deprecation -Xlint:unchecked) set(SRC org/multimc/EntryPoint.java From f2153ce0dcdf9d6e20849eed5e8808e8b47a6b8d Mon Sep 17 00:00:00 2001 From: Architect-devlord <220881851+Architect-devlord@users.noreply.github.com> Date: Sat, 7 Feb 2026 19:37:30 +0000 Subject: [PATCH 2/2] Fix mod detection and add CLI mod management - Unified mod detection to include manually added mods in both game root and instance root folders. - Implemented ModManifest and registered_mods.json for persistent mod registration. - Added --list-mods and --download-mod CLI commands for instance mod management. - Updated ModFolderModel to support multiple scan directories and manifest-based metadata. --- launcher/Application.cpp | 71 +++++++++++++++++ launcher/Application.h | 4 + launcher/minecraft/MinecraftInstance.cpp | 35 +++++++-- launcher/minecraft/mod/Mod.h | 12 +++ launcher/minecraft/mod/ModFolderLoadTask.cpp | 20 +++-- launcher/minecraft/mod/ModFolderLoadTask.h | 4 +- launcher/minecraft/mod/ModFolderModel.cpp | 71 +++++++++++------ launcher/minecraft/mod/ModFolderModel.h | 9 ++- launcher/minecraft/mod/ModManifest.h | 78 +++++++++++++++++++ .../minecraft/mod/ResourcePackFolderModel.cpp | 2 +- .../minecraft/mod/ResourcePackFolderModel.h | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 2 +- .../minecraft/mod/TexturePackFolderModel.h | 2 +- 13 files changed, 269 insertions(+), 43 deletions(-) create mode 100644 launcher/minecraft/mod/ModManifest.h diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ac255ab4ef..5fc7e63729 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -268,6 +271,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) parser.addOption("import"); parser.addShortOpt("import", 'I'); parser.addDocumentation("import", "Import instance from specified zip (local path or URL)"); + // --list-mods + parser.addOption("list-mods"); + parser.addDocumentation("list-mods", "List mods for the specified instance"); + // --download-mod + parser.addOption("download-mod"); + parser.addDocumentation("download-mod", "Download a mod from a URL for the specified instance"); // parse the arguments try @@ -311,6 +320,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) } m_liveCheck = args["alive"].toBool(); m_zipToImport = args["import"].toUrl(); + m_instanceIdToListMods = args["list-mods"].toString(); + m_modToDownload = args["download-mod"].toUrl(); QString origcwdPath = QDir::currentPath(); QString binPath = applicationDirPath(); @@ -1108,6 +1119,66 @@ void Application::setupWizardFinished(int status) void Application::performMainStartupAction() { m_status = Application::Initialized; + if(!m_instanceIdToListMods.isEmpty()) + { + auto inst = instances()->getInstanceById(m_instanceIdToListMods); + if(inst) + { + auto mcInst = std::dynamic_pointer_cast(inst); + if(mcInst) + { + auto mods = mcInst->loaderModList(); + QEventLoop loop; + connect(mods.get(), &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); + mods->update(); + loop.exec(); + + std::cout << "Mods for instance " << qPrintable(m_instanceIdToListMods) << ":" << std::endl; + for(auto & mod: mods->allMods()) + { + std::cout << " " << qPrintable(mod.name()) << " (" << qPrintable(mod.filename().fileName()) << ")"; + if(!mod.sourceUrl().isEmpty()) { + std::cout << " [Source: " << qPrintable(mod.sourceUrl()) << "]"; + } + std::cout << std::endl; + } + } + } + m_status = Application::Succeeded; + return; + } + + if(!m_modToDownload.isEmpty()) + { + if(m_instanceIdToLaunch.isEmpty()) { + std::cerr << "--download-mod requires --launch (to specify instance)!" << std::endl; + m_status = Application::Failed; + return; + } + auto inst = instances()->getInstanceById(m_instanceIdToLaunch); + auto mcInst = std::dynamic_pointer_cast(inst); + if(mcInst) { + QString destDir = mcInst->modsRoot(); + QString fileName = m_modToDownload.fileName(); + QString destPath = FS::PathCombine(destDir, fileName); + + auto download = Net::Download::makeFile(m_modToDownload, destPath); + QEventLoop loop; + connect(download.get(), &Net::Download::succeeded, &loop, &QEventLoop::quit); + connect(download.get(), &Net::Download::failed, &loop, &QEventLoop::quit); + download->start(); + loop.exec(); + + if(download->status() == Net::Download::Succeeded) { + std::cout << "Downloaded mod to " << qPrintable(destPath) << std::endl; + } else { + std::cerr << "Failed to download mod." << std::endl; + } + } + m_status = Application::Succeeded; + return; + } + if(!m_instanceIdToLaunch.isEmpty()) { auto inst = instances()->getInstanceById(m_instanceIdToLaunch); diff --git a/launcher/Application.h b/launcher/Application.h index cb6bea8557..bd604ad719 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ class InstanceList; class AccountList; class IconList; class QNetworkAccessManager; +class Download; class JavaInstallList; class UpdateChecker; class BaseProfilerFactory; @@ -242,5 +244,7 @@ private slots: QString m_offlineName; bool m_liveCheck = false; QUrl m_zipToImport; + QString m_instanceIdToListMods; + QUrl m_modToDownload; std::unique_ptr logFile; }; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 06c2f0ad88..76bfc1ea5f 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1022,7 +1022,12 @@ std::shared_ptr MinecraftInstance::loaderModList() const { if (!m_loader_mod_list) { - m_loader_mod_list.reset(new ModFolderModel(modsRoot())); + QStringList dirs = {modsRoot()}; + QString altMods = FS::PathCombine(instanceRoot(), "mods"); + if (altMods != modsRoot()) { + dirs.append(altMods); + } + m_loader_mod_list.reset(new ModFolderModel(dirs, FS::PathCombine(instanceRoot(), "registered_mods.json"))); m_loader_mod_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction); } @@ -1033,7 +1038,12 @@ std::shared_ptr MinecraftInstance::coreModList() const { if (!m_core_mod_list) { - m_core_mod_list.reset(new ModFolderModel(coreModsDir())); + QStringList dirs = {coreModsDir()}; + QString altCoreMods = FS::PathCombine(instanceRoot(), "coremods"); + if (altCoreMods != coreModsDir()) { + dirs.append(altCoreMods); + } + m_core_mod_list.reset(new ModFolderModel(dirs)); m_core_mod_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction); } @@ -1044,7 +1054,12 @@ std::shared_ptr MinecraftInstance::resourcePackList() const { if (!m_resource_pack_list) { - m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir())); + QStringList dirs = {resourcePacksDir()}; + QString altResourcePacks = FS::PathCombine(instanceRoot(), "resourcepacks"); + if (altResourcePacks != resourcePacksDir()) { + dirs.append(altResourcePacks); + } + m_resource_pack_list.reset(new ResourcePackFolderModel(dirs)); m_resource_pack_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_resource_pack_list.get(), &ModFolderModel::disableInteraction); } @@ -1055,7 +1070,12 @@ std::shared_ptr MinecraftInstance::texturePackList() const { if (!m_texture_pack_list) { - m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir())); + QStringList dirs = {texturePacksDir()}; + QString altTexturePacks = FS::PathCombine(instanceRoot(), "texturepacks"); + if (altTexturePacks != texturePacksDir()) { + dirs.append(altTexturePacks); + } + m_texture_pack_list.reset(new TexturePackFolderModel(dirs)); m_texture_pack_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_texture_pack_list.get(), &ModFolderModel::disableInteraction); } @@ -1066,7 +1086,12 @@ std::shared_ptr MinecraftInstance::shaderPackList() const { if (!m_shader_pack_list) { - m_shader_pack_list.reset(new ResourcePackFolderModel(shaderPacksDir())); + QStringList dirs = {shaderPacksDir()}; + QString altShaderPacks = FS::PathCombine(instanceRoot(), "shaderpacks"); + if (altShaderPacks != shaderPacksDir()) { + dirs.append(altShaderPacks); + } + m_shader_pack_list.reset(new ResourcePackFolderModel(dirs)); m_shader_pack_list->disableInteraction(isRunning()); connect(this, &BaseInstance::runningStatusChanged, m_shader_pack_list.get(), &ModFolderModel::disableInteraction); } diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 921faeb159..9d7218e6b9 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -17,6 +17,7 @@ #include #include #include +#include "ModManifest.h" #include #include "ModDetails.h" @@ -46,6 +47,16 @@ class Mod { return m_mmc_id; } + + QString sourceUrl() const + { + return m_sourceUrl; + } + + void setSourceUrl(const QString &url) + { + m_sourceUrl = url; + } ModType type() const { return m_type; @@ -106,6 +117,7 @@ class Mod QDateTime m_changedDateTime; QString m_mmc_id; QString m_name; + QString m_sourceUrl; bool m_enabled = true; bool m_resolving = false; bool m_resolved = false; diff --git a/launcher/minecraft/mod/ModFolderLoadTask.cpp b/launcher/minecraft/mod/ModFolderLoadTask.cpp index 883498771b..6cea5374ac 100644 --- a/launcher/minecraft/mod/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/ModFolderLoadTask.cpp @@ -1,18 +1,26 @@ #include "ModFolderLoadTask.h" #include -ModFolderLoadTask::ModFolderLoadTask(QDir dir) : - m_dir(dir), m_result(new Result()) +ModFolderLoadTask::ModFolderLoadTask(QStringList dirs) : + m_dirs(dirs), m_result(new Result()) { } void ModFolderLoadTask::run() { - m_dir.refresh(); - for (auto entry : m_dir.entryInfoList()) + for (auto & dirPath : m_dirs) { - Mod m(entry); - m_result->mods[m.mmc_id()] = m; + QDir dir(dirPath); + if (!dir.exists()) + continue; + dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); + dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); + dir.refresh(); + for (auto entry : dir.entryInfoList()) + { + Mod m(entry); + m_result->mods[m.mmc_id()] = m; + } } emit succeeded(); } diff --git a/launcher/minecraft/mod/ModFolderLoadTask.h b/launcher/minecraft/mod/ModFolderLoadTask.h index 8d720e6523..fa53dc4fca 100644 --- a/launcher/minecraft/mod/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/ModFolderLoadTask.h @@ -19,11 +19,11 @@ class ModFolderLoadTask : public QObject, public QRunnable } public: - ModFolderLoadTask(QDir dir); + ModFolderLoadTask(QStringList dirs); void run(); signals: void succeeded(); private: - QDir m_dir; + QStringList m_dirs; ResultPtr m_result; }; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index f0c53c3929..2fb9681ca2 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -25,13 +25,18 @@ #include #include #include "LocalModParseTask.h" +#include "ModManifest.h" -ModFolderModel::ModFolderModel(const QString &dir) : QAbstractListModel(), m_dir(dir) +ModFolderModel::ModFolderModel(const QStringList &dirs, const QString &manifestPath) : QAbstractListModel(), m_dirs(dirs) { - FS::ensureFolderPathExists(m_dir.absolutePath()); - m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); + if (!manifestPath.isEmpty()) { + m_manifest.reset(new ModManifest(manifestPath)); + } m_watcher = new QFileSystemWatcher(this); + for (auto & dirPath : m_dirs) + { + FS::ensureFolderPathExists(dirPath); + } connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); } @@ -42,14 +47,17 @@ void ModFolderModel::startWatching() update(); - is_watching = m_watcher->addPath(m_dir.absolutePath()); - if (is_watching) + for (auto & dirPath : m_dirs) { - qDebug() << "Started watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to start watching " << m_dir.absolutePath(); + if (m_watcher->addPath(dirPath)) + { + qDebug() << "Started watching " << dirPath; + is_watching = true; + } + else + { + qDebug() << "Failed to start watching " << dirPath; + } } } @@ -58,15 +66,12 @@ void ModFolderModel::stopWatching() if(!is_watching) return; - is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if (!is_watching) + for (auto & dirPath : m_dirs) { - qDebug() << "Stopped watching " << m_dir.absolutePath(); - } - else - { - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); + m_watcher->removePath(dirPath); } + is_watching = false; + qDebug() << "Stopped watching mod folders"; } bool ModFolderModel::update() @@ -79,7 +84,7 @@ bool ModFolderModel::update() return true; } - auto task = new ModFolderLoadTask(m_dir); + auto task = new ModFolderLoadTask(m_dirs); m_update = task->result(); QThreadPool *threadPool = QThreadPool::globalInstance(); connect(task, &ModFolderLoadTask::succeeded, this, &ModFolderModel::finishUpdate); @@ -142,7 +147,14 @@ void ModFolderModel::finishUpdate() added.subtract(currentSet); beginInsertRows(QModelIndex(), mods.size(), mods.size() + added.size() - 1); for(auto & addedMod: added) { - mods.append(newMods[addedMod]); + auto mod = newMods[addedMod]; + if (m_manifest) { + QString filename = mod.filename().fileName(); + if (m_manifest->contains(filename)) { + mod.setSourceUrl(m_manifest->get(filename).url); + } + } + mods.append(mod); resolveMod(mods.last()); } endInsertRows(); @@ -217,7 +229,14 @@ void ModFolderModel::directoryChanged(QString path) bool ModFolderModel::isValid() { - return m_dir.exists() && m_dir.isReadable(); + bool valid = false; + for (auto & dirPath : m_dirs) + { + QDir dir(dirPath); + if (dir.exists() && dir.isReadable()) + valid = true; + } + return valid; } // FIXME: this does not take disabled mod (with extra .disable extension) into account... @@ -252,7 +271,9 @@ bool ModFolderModel::installMod(const QString &filename) return false; } - auto newpath = FS::NormalizePath(FS::PathCombine(m_dir.path(), fileinfo.fileName())); + if (m_dirs.isEmpty()) + return false; + auto newpath = FS::NormalizePath(FS::PathCombine(m_dirs.first(), fileinfo.fileName())); if(originalPath == newpath) { qDebug() << "Overwriting the mod (" << originalPath << ") with itself makes no sense..."; @@ -277,6 +298,12 @@ bool ModFolderModel::installMod(const QString &filename) // FIXME: report error in a user-visible way return false; } + if (m_manifest) { + ModManifestEntry entry; + entry.name = fileinfo.completeBaseName(); + entry.url = "local"; + m_manifest->insert(fileinfo.fileName(), entry); + } FS::updateTimestamp(newpath); installedMod.repath(newpath); update(); diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 62c504dfcb..9a6e31de82 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -52,7 +52,7 @@ class ModFolderModel : public QAbstractListModel Enable, Toggle }; - ModFolderModel(const QString &dir); + ModFolderModel(const QStringList &dirs, const QString &manifestPath = QString()); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; @@ -108,9 +108,9 @@ class ModFolderModel : public QAbstractListModel bool isValid(); - QDir dir() + QStringList dirs() { - return m_dir; + return m_dirs; } const QList & allMods() @@ -140,7 +140,8 @@ private ModFolderLoadTask::ResultPtr m_update; bool scheduled_update = false; bool interaction_disabled = false; - QDir m_dir; + QStringList m_dirs; + std::unique_ptr m_manifest; QMap modsIndex; QMap activeTickets; int nextResolutionTicket = 0; diff --git a/launcher/minecraft/mod/ModManifest.h b/launcher/minecraft/mod/ModManifest.h new file mode 100644 index 0000000000..fd1e7c1b40 --- /dev/null +++ b/launcher/minecraft/mod/ModManifest.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct ModManifestEntry { + QString id; + QString name; + QString version; + QString url; + + QJsonObject toJson() const { + QJsonObject obj; + obj["id"] = id; + obj["name"] = name; + obj["version"] = version; + obj["url"] = url; + return obj; + } + + static ModManifestEntry fromJson(const QJsonObject &obj) { + return { + obj["id"].toString(), + obj["name"].toString(), + obj["version"].toString(), + obj["url"].toString() + }; + } +}; + +class ModManifest { +public: + explicit ModManifest(const QString &path) : m_path(path) { + load(); + } + + void load() { + QFile file(m_path); + if (file.open(QIODevice::ReadOnly)) { + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + QJsonObject root = doc.object(); + for (auto it = root.begin(); it != root.end(); ++it) { + m_entries[it.key()] = ModManifestEntry::fromJson(it.value().toObject()); + } + } + } + + void save() { + QJsonObject root; + for (auto it = m_entries.begin(); it != m_entries.end(); ++it) { + root[it.key()] = it.value().toJson(); + } + QFile file(m_path); + if (file.open(QIODevice::WriteOnly)) { + file.write(QJsonDocument(root).toJson()); + } + } + + void insert(const QString &filename, const ModManifestEntry &entry) { + m_entries[filename] = entry; + save(); + } + + ModManifestEntry get(const QString &filename) const { + return m_entries.value(filename); + } + + bool contains(const QString &filename) const { + return m_entries.contains(filename); + } + +private: + QString m_path; + QMap m_entries; +}; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f3d7f56685..97b776dded 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -1,6 +1,6 @@ #include "ResourcePackFolderModel.h" -ResourcePackFolderModel::ResourcePackFolderModel(const QString &dir) : ModFolderModel(dir) { +ResourcePackFolderModel::ResourcePackFolderModel(const QStringList &dirs, const QString &manifestPath) : ModFolderModel(dirs, manifestPath) { } QVariant ResourcePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const { diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 0cd6214bf0..0bc72530f6 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -7,7 +7,7 @@ class ResourcePackFolderModel : public ModFolderModel Q_OBJECT public: - explicit ResourcePackFolderModel(const QString &dir); + explicit ResourcePackFolderModel(const QStringList &dirs, const QString &manifestPath = QString()); QVariant headerData(int section, Qt::Orientation orientation, int role) const override; }; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index d5956da10d..acafa50c9f 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -1,6 +1,6 @@ #include "TexturePackFolderModel.h" -TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ModFolderModel(dir) { +TexturePackFolderModel::TexturePackFolderModel(const QStringList &dirs, const QString &manifestPath) : ModFolderModel(dirs, manifestPath) { } QVariant TexturePackFolderModel::headerData(int section, Qt::Orientation orientation, int role) const { diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index a59d511928..4c01db8494 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -7,7 +7,7 @@ class TexturePackFolderModel : public ModFolderModel Q_OBJECT public: - explicit TexturePackFolderModel(const QString &dir); + explicit TexturePackFolderModel(const QStringList &dirs, const QString &manifestPath = QString()); QVariant headerData(int section, Qt::Orientation orientation, int role) const override; };