# Arguments

The argument system builds the final argv (\`\[JVM args] + \[main class]

* \[game args] + \[raw args]`) from a resolved` Version\` plus per-launch overrides. This page is the canonical reference for placeholders, JVM options and game options — other launch docs cross-ref here.

## The `Arguments` trait

```rust
pub trait Arguments {
    fn build_arguments(
        &self,
        builder: &Version,
        profile: Option<&UserProfile>,
        arg_overrides:  &HashMap<String, String>,
        arg_removals:   &HashSet<String>,
        jvm_overrides:  &HashMap<String, String>,
        jvm_removals:   &HashSet<String>,
        raw_args:       &[String],
    ) -> Vec<String>;
}
```

Blanket-implemented for every `VersionInfo` — you almost never call it directly; `LaunchBuilder::run` does. `profile = None` keeps the hardcoded defaults (`access_token = "0"`, `user_type = "legacy"`), useful for dry-run inspection.

## Standard placeholders

The loader metadata uses `${name}` placeholders that the builder substitutes from a variable map. The full set:

### Authentication

| Placeholder            | Description          | Example                                  |
| ---------------------- | -------------------- | ---------------------------------------- |
| `${auth_player_name}`  | Player username      | `"Player123"`                            |
| `${auth_uuid}`         | Player UUID          | `"550e8400-e29b-41d4-a716-446655440000"` |
| `${auth_access_token}` | Access token         | `"eyJhbGc…"` or `"0"` offline            |
| `${auth_xuid}`         | Xbox User ID         | `"2535405290…"` or `"0"`                 |
| `${clientid}`          | Client ID            | `"{client-id}"`                          |
| `${user_type}`         | User type            | `"msa"`, `"mojang"` or `"legacy"`        |
| `${user_properties}`   | User properties JSON | `"{}"`                                   |

`${user_type}` is derived from `profile.provider`: `Microsoft → "msa"`, `Azuriom → "mojang"`, `Offline / Custom → "legacy"`.

### Directories

| Placeholder              | Description                                                      |
| ------------------------ | ---------------------------------------------------------------- |
| `${game_directory}`      | Per-instance runtime dir (honours `KEY_GAME_DIRECTORY` override) |
| `${assets_root}`         | Shared assets dir                                                |
| `${natives_directory}`   | Extracted natives, temp dir per launch                           |
| `${library_directory}`   | Shared libraries dir                                             |
| `${classpath}`           | Java classpath                                                   |
| `${classpath_separator}` | `:` (Linux / macOS) or `;` (Windows)                             |

### Version / launcher

| Placeholder            | Description                                    |
| ---------------------- | ---------------------------------------------- |
| `${version_name}`      | Minecraft version                              |
| `${version_type}`      | `"release"`, `"snapshot"`, …                   |
| `${assets_index_name}` | Asset index ID                                 |
| `${launcher_name}`     | Launcher name (defaults to `AppState::name()`) |
| `${launcher_version}`  | Launcher version                               |

## Placeholder key constants

Every placeholder has a typed constant in `lighty_launch::arguments` to avoid stringly-typed code:

```rust
use lighty_launch::arguments::{
    KEY_AUTH_PLAYER_NAME, KEY_AUTH_UUID, KEY_AUTH_ACCESS_TOKEN, KEY_AUTH_XUID,
    KEY_CLIENT_ID, KEY_USER_TYPE, KEY_USER_PROPERTIES,
    KEY_VERSION_NAME, KEY_VERSION_TYPE, KEY_ASSETS_INDEX_NAME,
    KEY_GAME_DIRECTORY, KEY_ASSETS_ROOT, KEY_NATIVES_DIRECTORY, KEY_LIBRARY_DIRECTORY,
    KEY_LAUNCHER_NAME, KEY_LAUNCHER_VERSION,
    KEY_CLASSPATH, KEY_CLASSPATH_SEPARATOR,
};
```

`.set(KEY_LAUNCHER_NAME, "MyApp")` overrides the placeholder value. Anything else is appended as a `--key value` (or `--flag` for empty value) game argument.

## JVM options

`with_jvm_options()` returns a builder where `.set(key, value)` writes to a `HashMap<String, String>`. The `-` prefix is added automatically based on the key shape:

| Input                             | Becomes                  |
| --------------------------------- | ------------------------ |
| `set("Xmx", "4G")`                | `-Xmx4G`                 |
| `set("XX:+UseG1GC", "")`          | `-XX:+UseG1GC`           |
| `set("Djava.library.path", "/p")` | `-Djava.library.path=/p` |

The defaults (used when the loader metadata supplies no JVM section):

```
-Djava.library.path=${natives_directory}
-Dminecraft.launcher.brand=${launcher_name}
-Dminecraft.launcher.version=${launcher_version}
-Xmx2G
-XX:+UnlockExperimentalVMOptions
-XX:+UseG1GC
-XX:G1NewSizePercent=20
-XX:G1ReservePercent=20
-XX:MaxGCPauseMillis=50
-XX:G1HeapRegionSize=32M
-cp ${classpath}
```

A handful of JVM args are **always present** even if the loader metadata omits them — the builder injects them at the top of the list:

* `-Djava.library.path=${natives_directory}` (LWJGL natives)
* `-Dminecraft.launcher.brand=${launcher_name}`
* `-Dminecraft.launcher.version=${launcher_version}`
* `-XstartOnFirstThread` on macOS (LWJGL / GLFW requirement)
* `-cp ${classpath}` — always the last JVM arg before the main class

Common knobs:

| Option                         | Purpose              | Example        |
| ------------------------------ | -------------------- | -------------- |
| `-Xmx`                         | Max heap             | `"4G"`, `"8G"` |
| `-Xms`                         | Initial heap         | `"2G"`         |
| `-XX:+UseG1GC` / `-XX:+UseZGC` | GC choice            | `""`           |
| `-XX:MaxGCPauseMillis`         | GC pause target (ms) | `"50"`         |
| `-XX:G1HeapRegionSize`         | G1 region size       | `"32M"`        |
| `-Dfile.encoding`              | File encoding        | `"UTF-8"`      |
| `-Djava.net.preferIPv4Stack`   | Prefer IPv4          | `"true"`       |

## Game options

`with_arguments()` exposes the same `.set` / `.remove` shape but for game args. Two cases:

1. **Known launch placeholder constant** (`KEY_LAUNCHER_NAME`, etc.) — substitutes the variable in the existing argv.
2. **Anything else** — appended as a fresh argument (`--key value`, or `--key` for empty value).

Common knobs:

| Option                    | Purpose                    |
| ------------------------- | -------------------------- |
| `--width`, `--height`     | Window size                |
| `--fullscreen`            | Fullscreen mode            |
| `--quickPlayPath`         | Quick play server file     |
| `--quickPlaySingleplayer` | Quick play world           |
| `--quickPlayMultiplayer`  | Quick play server          |
| `--quickPlayRealms`       | Quick play realm           |
| `--demo`                  | Demo mode                  |
| `--server` / `--port`     | Auto-connect server / port |

## Access-token routing

The `--accessToken` value is the most sensitive piece of data the launch pipeline touches, so the resolution is narrow. At `crates/launch/src/arguments/arguments.rs` the variable-map builder does:

1. If `profile.token_handle` is `Some` **and** the `keyring` feature is active on `lighty-launch`, read the token from the OS keychain via `TokenHandle::read()` — the token never sat in `UserProfile` heap memory between authentication and launch.
2. Otherwise, clone the in-memory `SecretString` stored in `profile.access_token`.
3. `ExposeSecret::expose_secret(&secret)` is called **exactly once**, at the moment the value is inserted into the placeholder map. No copy of the plaintext is kept past that call.
4. When `profile = None` (offline / dry-run callers), the hardcoded default `"0"` is used.

```toml
[dependencies]
lighty-launch = { version = "...", features = ["events", "keyring"] }
```

See `AUTH_SECRETS.md` at the workspace root for the full threat model.

## Final assembly example

```
java
  -Djava.library.path=/tmp/natives-xxxxx
  -Dminecraft.launcher.brand=MyLauncher
  -Dminecraft.launcher.version=26.5.12
  -Xmx4G -Xms2G -XX:+UseG1GC
  -cp /path/lib1.jar:/path/lib2.jar:...
  net.minecraft.client.main.Main
  --username Player123
  --version 1.21.1
  --gameDir   /home/user/.local/share/MyLauncher/instance
  --assetsDir /home/user/.local/share/MyLauncher/assets
  --assetIndex 16
  --uuid 550e8400-…
  --accessToken 0
  --width 1920 --height 1080
```

## Putting it together

```rust
# use lighty_auth::UserProfile;
# use lighty_core::AppState;
# use lighty_java::JavaDistribution;
# use lighty_launch::errors::InstallerResult;
# use lighty_launch::launch::Launch;
# use lighty_loaders::types::Loader;
# use lighty_version::VersionBuilder;
use lighty_launch::arguments::KEY_LAUNCHER_NAME;
# async fn run() -> InstallerResult<()> {
# AppState::init("MyLauncher").ok();
# let profile = UserProfile::offline("Player", "");
# let mut instance = VersionBuilder::new("inst", Loader::Fabric, "0.16.9", "1.21.1");

instance.launch(&profile, JavaDistribution::Temurin)
    .with_jvm_options()
        .set("Xmx", "6G")
        .set("XX:+UseG1GC", "")
        .set("XX:+AlwaysPreTouch", "")
        .set("Dfile.encoding", "UTF-8")
        .done()
    .with_arguments()
        .set(KEY_LAUNCHER_NAME, "MyCustomLauncher")
        .set("width",  "1920")
        .set("height", "1080")
        .done()
    .run()
    .await?;
# Ok(()) }
```

## Related

* [How to use](/lightylauncher/crates/launch/docs/how-to-use.md) — short usage snippets
* [Launch](/lightylauncher/crates/launch/docs/launch.md) — pipeline detail, builder API
* [Installation](/lightylauncher/crates/launch/docs/installation.md) — what gets built into the classpath
* [Exports](/lightylauncher/crates/launch/docs/exports.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hamadi.gitbook.io/lightylauncher/crates/launch/docs/arguments.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
