summaryrefslogtreecommitdiff
path: root/docs/handbook/meshmc/java-detection.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/meshmc/java-detection.md')
-rw-r--r--docs/handbook/meshmc/java-detection.md411
1 files changed, 411 insertions, 0 deletions
diff --git a/docs/handbook/meshmc/java-detection.md b/docs/handbook/meshmc/java-detection.md
new file mode 100644
index 0000000000..00f6ed6805
--- /dev/null
+++ b/docs/handbook/meshmc/java-detection.md
@@ -0,0 +1,411 @@
+# Java Detection
+
+## Overview
+
+MeshMC requires a compatible Java installation to launch Minecraft. The Java detection system automatically discovers Java installations across all supported platforms, validates their architecture and version, and manages bundled Java downloads.
+
+## Architecture
+
+### Key Classes
+
+| Class | File | Purpose |
+|---|---|---|
+| `JavaUtils` | `java/JavaUtils.{h,cpp}` | Platform-specific Java discovery |
+| `JavaChecker` | `java/JavaChecker.{h,cpp}` | Java installation validator |
+| `JavaCheckerJob` | `java/JavaCheckerJob.{h,cpp}` | Batch validation task |
+| `JavaInstall` | `java/JavaInstall.{h,cpp}` | Java installation descriptor |
+| `JavaInstallList` | `java/JavaInstallList.{h,cpp}` | Discovered Java list model |
+| `JavaVersion` | `java/JavaVersion.{h,cpp}` | Version string parser |
+
+## JavaUtils
+
+Platform-specific Java discovery:
+
+```cpp
+class JavaUtils : public QObject
+{
+ Q_OBJECT
+public:
+ JavaUtils();
+
+ // Discovery
+ QList<QString> FindJavaPaths();
+ static QString GetDefaultJava();
+ QList<JavaInstallPtr> FindJavaFromRegistryKey(
+ DWORD keyType, QString keyName, QString keyJavaDir, QString subkeySuffix = ""
+ ); // Windows only
+
+private:
+ // Platform-specific search paths
+ QStringList platformSearchPaths();
+};
+```
+
+### Platform Search Paths
+
+#### Linux
+
+```cpp
+QStringList JavaUtils::platformSearchPaths()
+{
+ return {
+ "/usr/lib/jvm", // Distro packages
+ "/usr/lib64/jvm", // 64-bit distros
+ "/usr/lib32/jvm", // 32-bit compat
+ "/opt/java", // Manual installs
+ "/opt/jdk", // Manual JDK installs
+ QDir::homePath() + "/.sdkman/candidates/java", // SDKMAN
+ QDir::homePath() + "/.jdks", // IntelliJ downloads
+ "/snap/openjdk", // Snap packages
+ "/usr/lib/jvm/java-*/jre", // JRE subdirs
+ };
+}
+```
+
+Scans each directory recursively for `bin/java` executables.
+
+#### macOS
+
+```cpp
+QStringList JavaUtils::platformSearchPaths()
+{
+ return {
+ "/Library/Java/JavaVirtualMachines", // System JDKs
+ "/System/Library/Java/JavaVirtualMachines", // Apple JDKs
+ "/usr/local/opt/openjdk", // Homebrew
+ "/opt/homebrew/opt/openjdk", // Homebrew (Apple Silicon)
+ QDir::homePath() + "/Library/Java/JavaVirtualMachines", // User JDKs
+ QDir::homePath() + "/.sdkman/candidates/java",
+ QDir::homePath() + "/.jdks",
+ };
+}
+```
+
+Also runs `/usr/libexec/java_home -V` to discover system-registered JDKs.
+
+#### Windows
+
+```cpp
+QStringList JavaUtils::platformSearchPaths()
+{
+ QStringList paths;
+ // Registry-based discovery
+ FindJavaFromRegistryKey(KEY_WOW64_64KEY,
+ "SOFTWARE\\JavaSoft\\Java Runtime Environment", "JavaHome");
+ FindJavaFromRegistryKey(KEY_WOW64_64KEY,
+ "SOFTWARE\\JavaSoft\\Java Development Kit", "JavaHome");
+ FindJavaFromRegistryKey(KEY_WOW64_64KEY,
+ "SOFTWARE\\JavaSoft\\JDK", "JavaHome");
+ FindJavaFromRegistryKey(KEY_WOW64_64KEY,
+ "SOFTWARE\\Eclipse Adoptium\\JDK", "Path", "\\hotspot\\MSI");
+ FindJavaFromRegistryKey(KEY_WOW64_64KEY,
+ "SOFTWARE\\Microsoft\\JDK", "Path", "\\hotspot\\MSI");
+
+ // Filesystem paths
+ paths << "C:/Program Files/Java"
+ << "C:/Program Files (x86)/Java"
+ << "C:/Program Files/Eclipse Adoptium"
+ << "C:/Program Files/Microsoft";
+
+ return paths;
+}
+```
+
+## JavaChecker
+
+Validates a Java installation by spawning a subprocess:
+
+```cpp
+class JavaChecker : public QObject
+{
+ Q_OBJECT
+public:
+ void performCheck();
+
+ // Input
+ QString m_path; // Path to java binary
+ int m_minMem = 0; // Minimum memory to test
+ int m_maxMem = 0; // Maximum memory to test
+ int m_permGen = 0; // PermGen size to test
+
+ // Results
+ struct Result {
+ QString path;
+ QString javaVersion;
+ QString realArch; // "amd64", "aarch64", etc.
+ bool valid = false;
+ bool is_64bit = false;
+ int id = 0;
+ QString errorLog;
+ QString outLog;
+ };
+
+signals:
+ void checkFinished(JavaChecker::Result result);
+
+private:
+ QProcess* m_process = nullptr;
+};
+```
+
+### Check Process
+
+`JavaChecker` spawns the Java binary with a small Java program (`javacheck.jar`):
+
+```
+java -jar javacheck.jar
+```
+
+The `javacheck` program (in `libraries/javacheck/`) prints system properties:
+
+```java
+// javacheck/src/main/java/org/projecttick/meshmc/JavaCheck.java
+public class JavaCheck {
+ public static void main(String[] args) {
+ System.out.println("os.arch=" + System.getProperty("os.arch"));
+ System.out.println("java.version=" + System.getProperty("java.version"));
+ System.out.println("java.vendor=" + System.getProperty("java.vendor"));
+ System.out.println("sun.arch.data.model=" + System.getProperty("sun.arch.data.model"));
+ System.out.println("java.runtime.name=" + System.getProperty("java.runtime.name"));
+ }
+}
+```
+
+Output is parsed to populate the `Result` struct.
+
+### Timeout
+
+The check process has a timeout (typically 30 seconds). If Java hangs or takes too long, the check is marked as failed.
+
+## JavaCheckerJob
+
+Batch job for checking multiple Java installations:
+
+```cpp
+class JavaCheckerJob : public Task
+{
+ Q_OBJECT
+public:
+ explicit JavaCheckerJob(QString job_name);
+
+ void addJavaCheckerAction(JavaCheckerPtr base);
+
+signals:
+ void checkFinished(JavaChecker::Result result);
+
+protected:
+ void executeTask() override;
+
+private slots:
+ void partFinished(JavaChecker::Result result);
+
+private:
+ QList<JavaCheckerPtr> m_checks;
+ int m_done = 0;
+};
+```
+
+Used by `JavaInstallList` to validate all discovered Java paths in parallel.
+
+## JavaInstall
+
+Descriptor for a single Java installation:
+
+```cpp
+class JavaInstall
+{
+public:
+ using Ptr = std::shared_ptr<JavaInstall>;
+
+ QString id; // Unique identifier
+ QString path; // Path to java binary
+ JavaVersion version; // Parsed version
+ QString arch; // Architecture (amd64, aarch64)
+ bool is_64bit; // 64-bit flag
+ bool recommended; // Whether this is the recommended choice
+};
+```
+
+## JavaInstallList
+
+Model for the discovered Java installations:
+
+```cpp
+class JavaInstallList : public BaseVersionList
+{
+ Q_OBJECT
+public:
+ void load(); // Triggers discovery + validation
+ void updateListData(QList<BaseVersion::Ptr> versions) override;
+
+ // Filtering
+ BaseVersion::Ptr getRecommended();
+
+protected:
+ void loadList();
+ void sortVersionList();
+
+ QList<BaseVersion::Ptr> m_vlist;
+ bool loaded = false;
+};
+```
+
+### Discovery Flow
+
+```
+JavaInstallList::load()
+ │
+ ├── JavaUtils::FindJavaPaths()
+ │ └── Returns list of java binary paths
+ │
+ ├── Create JavaCheckerJob
+ │ └── Add JavaChecker for each path
+ │
+ ├── Run JavaCheckerJob
+ │ ├── Spawn each java with javacheck.jar (parallel)
+ │ └── Parse output → JavaChecker::Result
+ │
+ ├── Filter valid results
+ │ └── Discard paths where valid == false
+ │
+ └── Create JavaInstall entries
+ └── Store in m_vlist, emit signal
+```
+
+## JavaVersion
+
+Version string parsing and comparison:
+
+```cpp
+class JavaVersion
+{
+public:
+ JavaVersion() {}
+ JavaVersion(const QString& rhs);
+
+ bool operator<(const JavaVersion& rhs) const;
+ bool operator>(const JavaVersion& rhs) const;
+ bool operator==(const JavaVersion& rhs) const;
+ bool requiresPermGen() const;
+
+ int major() const { return m_major; }
+ int minor() const { return m_minor; }
+ int security() const { return m_security; }
+ QString toString() const;
+
+private:
+ int m_major = 0;
+ int m_minor = 0;
+ int m_security = 0;
+ QString m_prerelease;
+ bool m_parseable = false;
+};
+```
+
+### Version String Formats
+
+Handles both old and new Java version schemes:
+
+| Format | Example | Major |
+|---|---|---|
+| Old (1.x.y) | `1.8.0_312` | 8 |
+| New (x.y.z) | `17.0.2` | 17 |
+| New (x.y.z+b) | `21.0.1+12` | 21 |
+| EA builds | `22-ea` | 22 |
+
+### PermGen Detection
+
+```cpp
+bool JavaVersion::requiresPermGen() const
+{
+ return m_major < 8; // PermGen removed in Java 8
+}
+```
+
+Used to conditionally add `-XX:PermSize` and `-XX:MaxPermSize` JVM arguments for Java 7 and below.
+
+## Java Compatibility
+
+### Version Requirements
+
+| Minecraft Version | Minimum Java | Recommended |
+|---|---|---|
+| 1.16.5 and below | Java 8 | Java 8 |
+| 1.17 - 1.17.1 | Java 16 | Java 16 |
+| 1.18 - 1.20.4 | Java 17 | Java 17 |
+| 1.20.5+ | Java 21 | Java 21 |
+
+`ComponentUpdateTask` determines the required Java version from the Minecraft version metadata and validates the configured Java installation.
+
+### Compatibility Warnings
+
+When launching with an incompatible Java version:
+
+```cpp
+// LaunchController checks Java compatibility
+if (settings->get("IgnoreJavaCompatibility").toBool()) {
+ // Skip check, launch anyway
+} else {
+ // Show warning dialog
+ // "Minecraft X.Y requires Java Z, but Java W is configured"
+ // Options: Continue anyway / Change Java / Cancel
+}
+```
+
+## Managed Java Downloads
+
+The `java/download/` subdirectory handles automatic Java downloads:
+
+### ArchiveDownloadTask
+
+Downloads and extracts Java archives:
+- Platform-appropriate archives (tar.gz for Linux/macOS, zip for Windows)
+- Progress tracking during download and extraction
+- Installs to `<data_dir>/java/<version>/`
+
+### ManifestDownloadTask
+
+Fetches Java availability manifest:
+- Queries Mojang's runtime manifest for available Java versions
+- Selects appropriate version for the operating system and architecture
+
+### Java Auto-Setup
+
+In the setup wizard, if no compatible Java is found:
+
+```
+JavaPage (wizard)
+ │
+ ├── Display "No compatible Java found"
+ ├── Offer to download recommended version
+ │
+ └── On accept:
+ ├── Fetch runtime manifest
+ ├── Select appropriate Java version
+ ├── Download archive
+ ├── Extract to data_dir/java/
+ └── Set JavaPath setting to extracted binary
+```
+
+## JavaPage Settings UI
+
+The Java settings page (`ui/pages/global/JavaPage.h`) provides:
+
+| Control | Description |
+|---|---|
+| Java Path | Text field + Browse button for java binary |
+| Auto-detect | Scans system and lists all found Java installations in a dialog |
+| Test | Validates the current Java path using `JavaChecker` |
+| Min Memory | Spinbox for minimum heap allocation (MB) |
+| Max Memory | Spinbox for maximum heap allocation (MB) |
+| PermGen | Spinbox for PermGen (only shown for Java < 8) |
+| JVM Arguments | Text field for additional JVM flags |
+| Ignore Compatibility | Checkbox to skip version compatibility checks |
+
+### Auto-Detect Dialog
+
+When "Auto-detect" is clicked:
+1. `JavaInstallList::load()` runs full discovery
+2. Results shown in a table: Path, Version, Architecture, 64-bit
+3. User selects one → fills Java Path field
+4. Recommended installation is highlighted