Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CCBlueX/LiquidLauncher/llms.txt

Use this file to discover all available pages before exploring further.

LiquidLauncher automatically downloads and manages Java runtimes, ensuring the correct version is always available.

Supported Distributions

The launcher supports three major Java distributions:

Eclipse Temurin

Default distribution
  • Supports all LTS versions (8, 11, 17, 21)
  • AdoptOpenJDK successor
  • Excellent compatibility

GraalVM

Performance focused
  • Java 17+ only
  • Advanced optimizations
  • Native image support

Azul Zulu

Enterprise ready
  • All LTS versions
  • Commercial support available
  • Wide platform support

Distribution Selection

src-tauri/src/minecraft/java/distribution.rs
#[derive(Deserialize, Serialize, Clone)]
pub enum JavaDistribution {
    #[serde(rename = "temurin")]
    Temurin,
    #[serde(rename = "graalvm")]
    GraalVM,
    #[serde(rename = "zulu")]
    Zulu,
}

impl Default for JavaDistribution {
    fn default() -> Self {
        // Temurin supports any version of java
        JavaDistribution::Temurin
    }
}
The default distribution (Temurin) is automatically selected and supports all Java versions.

Automatic Download

Java runtimes are downloaded automatically when launching a build that requires them.

Download Process

1

Determine Required Version

Each build specifies its required Java version:
src-tauri/src/app/client_api.rs
pub struct Build {
    pub jre_distribution: JavaDistribution,
    pub jre_version: u32,
    // ...
}
2

Check If Installed

The launcher checks if the required runtime is already installed
src-tauri/src/minecraft/java/jre_downloader.rs
let runtime_path = runtimes_folder.join(
    format!("{}_{}", jre_distribution.get_name(), jre_version)
);
3

Download Runtime

If not present, downloads from the distribution’s CDN:
src-tauri/src/minecraft/java/jre_downloader.rs
pub async fn jre_download<F>(
    runtimes_folder: &Path,
    jre_distribution: &JavaDistribution,
    jre_version: &u32,
    on_progress: F,
) -> Result<PathBuf>
where
    F: Fn(u64, u64),
{
    let url = jre_distribution.get_url(jre_version).await?;
    let retrieved_bytes = download_file(&url, on_progress).await?;
    // ...
}
4

Extract Archive

Extracts the downloaded archive (ZIP on Windows, TAR.GZ on Linux/macOS):
src-tauri/src/minecraft/java/jre_downloader.rs
match OS {
    OperatingSystem::WINDOWS => zip_extract(cursor, runtime_path.as_path()).await?,
    OperatingSystem::LINUX | OperatingSystem::OSX => {
        tar_gz_extract(cursor, runtime_path.as_path()).await?
    }
    _ => bail!("Unsupported OS"),
}
5

Locate Binary

Finds and verifies the Java executable

Download URLs

Each distribution has its own download URL format:
JavaDistribution::Temurin => {
    let os_name = OS.get_adoptium_name()?;
    format!(
        "https://api.adoptium.net/v3/binary/latest/{}/ga/{}/{}/jre/hotspot/normal/eclipse?project=jdk",
        jre_version, os_name, os_arch
    )
}

Java Binary Location

The launcher automatically locates the Java binary based on the operating system:
src-tauri/src/minecraft/java/jre_downloader.rs
pub async fn find_java_binary(
    runtimes_folder: &Path,
    jre_distribution: &JavaDistribution,
    jre_version: &u32,
) -> Result<PathBuf> {
    let runtime_path = runtimes_folder.join(
        format!("{}_{}", jre_distribution.get_name(), jre_version)
    );

    let mut files = fs::read_dir(&runtime_path).await?;

    if let Some(jre_folder) = files.next_entry().await? {
        let folder_path = jre_folder.path();

        let java_binary = match OS {
            OperatingSystem::WINDOWS => folder_path.join("bin").join("javaw.exe"),
            OperatingSystem::OSX => folder_path
                .join("Contents")
                .join("Home")
                .join("bin")
                .join("java"),
            _ => folder_path.join("bin").join("java"),
        };

        if java_binary.exists() {
            return Ok(java_binary.absolutize()?.to_path_buf());
        }
    }

    Err(anyhow::anyhow!("Failed to find JRE"))
}
  • Windows: bin/javaw.exe (windowed executable, no console)
  • macOS: Contents/Home/bin/java (app bundle structure)
  • Linux: bin/java (standard Unix layout)

Permissions Management

On Unix systems, the launcher ensures the Java binary has execution permissions:
src-tauri/src/minecraft/java/jre_downloader.rs
#[cfg(unix)]
{
    use std::os::unix::fs::PermissionsExt;

    let metadata = fs::metadata(&java_binary).await?;

    if !metadata.permissions().mode() & 0o111 != 0 {
        // try to change permissions
        let mut permissions = metadata.permissions();
        permissions.set_mode(0o111);
        fs::set_permissions(&java_binary, permissions).await?;
    }
}
This ensures downloaded Java runtimes are immediately executable without manual permission changes.

Runtime Execution

The JavaRuntime wrapper handles process execution:
src-tauri/src/minecraft/java/runtime.rs
pub struct JavaRuntime(PathBuf);

impl JavaRuntime {
    pub async fn execute(&self, arguments: Vec<String>, game_dir: &Path) -> Result<Child> {
        if !self.0.exists() {
            bail!("Java runtime not found at: {}", self.0.display());
        }

        debug!("Executing Java runtime: {}", self.0.display());

        let mut command = Command::new(&self.0);
        command.current_dir(game_dir);
        command.args(arguments);

        command.stderr(Stdio::piped()).stdout(Stdio::piped());

        let child = command.spawn()?;
        Ok(child)
    }
}

IO Handling

The launcher captures and processes stdout/stderr:
src-tauri/src/minecraft/java/runtime.rs
pub async fn handle_io<D: Send + Sync>(
    &self,
    running_task: &mut Child,
    on_stdout: fn(&D, &[u8]) -> Result<()>,
    on_stderr: fn(&D, &[u8]) -> Result<()>,
    terminator: Receiver<()>,
    data: &D,
) -> Result<()> {
    let mut stdout = running_task.stdout.take().unwrap();
    let mut stderr = running_task.stderr.take().unwrap();

    loop {
        tokio::select! {
            read_len = stdout.read(&mut stdout_buf) => {
                let _ = on_stdout(&data, &stdout_buf[..read_len?]);
            },
            read_len = stderr.read(&mut stderr_buf) => {
                let _ = on_stderr(&data, &stderr_buf[..read_len?]);
            },
            _ = &mut terminator => {
                running_task.kill().await?;
                break;
            },
            exit_status = running_task.wait() => {
                let code = exit_status?.code().unwrap_or(7900);
                if code != 0 && code != -1073740791 {
                    bail!("Process exited with non-zero exit code: {}.", code);
                }
                break;
            },
        }
    }
    Ok(())
}

Version Support

Each distribution has different version support:
src-tauri/src/minecraft/java/distribution.rs
pub fn supports_version(&self, version: u32) -> bool {
    match self {
        JavaDistribution::Temurin => true, // Supports 8, 11, 17, 21
        JavaDistribution::GraalVM => version >= 17, // Only supports 17+
        JavaDistribution::Zulu => true, // Community builds for all LTS versions
    }
}
If a build requires Java 8, GraalVM will automatically fall back to Temurin.

Custom Java Path

Advanced users can specify a custom Java installation:
src-tauri/src/app/options.rs
pub struct StartOptions {
    pub java_distribution: DistributionSelection,
    // ...
}

#[derive(Deserialize, Serialize, Clone)]
#[serde(tag = "type", content = "value")]
pub enum DistributionSelection {
    #[serde(rename = "automatic")]
    Automatic(String),
    #[serde(rename = "custom")]
    Custom(String),
    #[serde(rename = "manual")]
    Manual(JavaDistribution),
}
  • Automatic: Launcher automatically selects the best distribution
  • Manual: User chooses a specific distribution (Temurin, GraalVM, Zulu)
  • Custom: User provides path to existing Java installation

Storage

Java runtimes are stored in the launcher’s data directory:
~/.liquidlauncher/runtimes/
├── temurin_8/
├── temurin_17/
├── temurin_21/
├── graalvm_17/
└── zulu_21/
Each runtime is isolated by distribution and version to prevent conflicts.

Version Selection

Each build specifies its required Java version

Auto-Updates

Java versions are updated with build updates

Build docs developers (and LLMs) love