---
title: 内部实现
description: DropOut 核心功能的详细实现和技术规范
---

# 内部实现

本页详细介绍了启动器各个核心模块的技术实现细节、数据结构和处理流程。

## 1. 身份验证系统 (Authentication)

身份验证链包含多个异步步骤，任何一步失败都会中断整个流程。DropOut 采用 Microsoft Device Code Flow 实现免重定向的安全登录。

### 1.1 详细流程
1.  **设备代码请求**: 
    - 调用 `https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode`
    - 获取用户验证码及验证 URL。
2.  **令牌交换**: 轮询 `/oauth2/v2.0/token` 获取主访问令牌 (Access Token) 和刷新令牌 (Refresh Token)。
3.  **Xbox Live 认证**: 获取 `Token` 和 `uhs` (User Hash)。
4.  **XSTS 授权**: 重定向至 `rp://api.minecraftservices.com/`。
5.  **Minecraft 登录**: 使用 `XBL3.0 x=<uhs>;<xsts_token>` 格式令牌换取最终的游戏 Access Token。

### 1.2 账户存储与安全
账户数据持久化在 `accounts.json` 中，包含账户类型及加密后的令牌信息：
```json
{
  "active_account_uuid": "...",
  "accounts": [
    {
      "type": "Microsoft",
      "username": "...",
      "uuid": "...",
      "access_token": "...",
      "refresh_token": "...",
      "expires_at": 1234567890
    },
    { "type": "Offline", "username": "Player", "uuid": "..." }
  ]
}
```
**离线 UUID**: 使用基于用户名的确定性 UUID v3，确保本地存档和配置的一致性。

```rust
// core/auth.rs 实现详情
pub fn generate_offline_uuid(username: &str) -> String {
    let namespace = Uuid::NAMESPACE_OID;
    Uuid::new_v3(&namespace, username.as_bytes()).to_string()
}
```

### 1.3 身份验证相关接口
| 命令 / 事件 | 类型 | 说明 |
| :--- | :--- | :--- |
| `start_microsoft_login` | Invoke | 启动设备流并返回验证码 |
| `complete_microsoft_login` | Invoke | 轮询并完成全套身份验证链 |
| `login_offline` | Invoke | 创建并切换至离线账户 |
| `auth-progress` | Event | 实时上报认证进度（如 "Xbox Live Auth..."） |

---

## 2. Java 运行环境管理 (Java Runtime)

### 2.1 目录与元数据
Java 目录 (Catalog) 缓存了来自 Adoptium 的可用版本及其平台特定链接。
-   **存储**: `java_catalog.json` 记录了各个版本的 SHA256 校验和及文件大小。

```json
// java_catalog.json 结构示例
{
  "releases": [
    {
      "major_version": 17,
      "image_type": "jdk",
      "version": "17.0.9+9",
      "download_url": "...",
      "checksum": "...",
      "file_size": 123456789
    }
  ],
  "cached_at": 1700000000
}
```

-   **检测**: 通过对候选路径执行 `java -version` 命令，解析其 `stderr` 输出中的版本字符串和 64-Bit 标识。

```rust
// core/java.rs 检测逻辑
fn check_java_installation(path: &PathBuf) -> Option<JavaInstallation> {
    let mut cmd = Command::new(path);
    cmd.arg("-version");
    // 解析 stderr 输出中的 "version" 关键字信息
}
```

### 2.2 自动安装逻辑
1.  **下载**: 支持断点续传，大型文件通过分片并行下载。
2.  **解压**: 针对不同操作系统处理压缩包（macOS 处理 .tar.gz 内部的 .app 结构，Windows 处理 .zip）。
3.  **验证**: 下载后强制进行哈希校验，确保运行时环境的完整性。

### 2.3 Java 管理相关接口
| 命令 / 事件 | 类型 | 说明 |
| :--- | :--- | :--- |
| `detect_java` | Invoke | 扫描系统已安装的 Java 环境 |
| `download_adoptium_java` | Invoke | 启动异步下载并自动解压配置 |
| `cancel_java_download` | Invoke | 通过原子标志位取消当前的下载任务 |
| `java-download-progress` | Event | 报告文件大小、速度、百分比和 ETA |

---

## 3. 游戏启动逻辑与 JVM 优化

### 3.1 内存分配方案
-   **Xmx & Xms**: 建议将初始内存与最大内存设为一致。
-   **策略**: 启动器会自动根据系统总内存和 Java 架构提示用户最优分配，且启动器生成的参数具有最高优先级。

### 3.2 G1GC 优化策略
针对 1.17+ 版本及高负载模组环境，DropOut 默认注入精简的 G1GC 参数链：
```bash
-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 
-XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC 
```

---

## 4. 模组加载机制 (Mod Loaders)

### 4.1 版本合并 (Inheritance)
模组加载器通过 `inheritsFrom` 字段链接原版 Minecraft 版本 JSON。

```json
// 典型的 Fabric 版本配置 (部分)
{
  "id": "fabric-loader-0.15.0-1.20.4",
  "inheritsFrom": "1.20.4",
  "mainClass": "net.fabricmc.loader.impl.launch.knot.KnotClient",
  "libraries": [
    {
      "name": "net.fabricmc:fabric-loader:0.15.0",
      "url": "https://maven.fabricmc.net/"
    }
  ]
}
```

启动阶段执行以下合并：
1.  **库优先级**: 加载器自身的库（如 Fabric Loader）排在原版库之前。
2.  **主类覆盖**: `mainClass` 始终取自子级定义（即模组加载器的入口，如 Fabric 的 `KnotClient`）。
3.  **参数链**: 合并 `jvm` 和 `game` 参数数组，自动剔除不适用的系统规则。

```rust
// core/version_merge.rs 合并核心实现
pub fn merge_versions(child: GameVersion, parent: GameVersion) -> GameVersion {
    let mut merged_libraries = child.libraries;
    merged_libraries.extend(parent.libraries);
    // 合并参数逻辑并返回新的 GameVersion 实例
}
```

### 4.2 库路径解析 (Maven)
启动器实现了完整的 Maven 路径转换逻辑：
`group:artifact:version` → `group_path/artifact/version/artifact-version.jar`

```rust
// core/maven.rs 坐标转换路径逻辑
pub fn to_path(&self) -> String {
    let group_path = self.group.replace('.', "/");
    format!("{}/{}/{}/{}-{}.{}", 
        group_path, self.artifact, self.version, 
        self.artifact, self.version, self.extension
    )
}
```

### 4.3 模组安装接口
启动器根据不同加载器的特性采用差异化安装策略。

#### Forge 安装逻辑
对于 Forge，DropOut 会下载官方 Installer 并通过子进程调用 Java 运行标准安装程序，确保这一过程与官方流程完全一致（包括字节码补丁生成）。

```rust
// core/forge.rs 调用官方安装器 (简化示例)
Command::new(java_path)
    .args(&[
        "-jar", 
        installer_path.to_str().unwrap(), 
        "--installClient", 
        game_dir.to_str().unwrap()
    ])
    .output()?;
```

| 命令 / 事件 | 类型 | 说明 |
| :--- | :--- | :--- |
| `install_fabric` | Invoke | 下载 Fabric 库并生成继承 JSON |
| `install_forge` | Invoke | 运行 Forge Installer 并执行字节码打桩 |
| `mod-loader-progress` | Event | 报告安装阶段（如 "processing", "complete"） |

---

## 5. 通讯与监控系统

### 5.1 事件总线 (Event Bus)
后端通过 Tauri 的 `Window` 实例向上层发送异步脉冲。

```rust
// 后端 emit_log! 宏简化日志外发
macro_rules! emit_log {
    ($window:expr, $msg:expr) => {
        let _ = $window.emit("launcher-log", $msg);
        println!("[Launcher] {}", $msg);
    };
}
```

-   **`launcher-log`**: 通用的控制台输出流，用于全局操作轨迹记录。
-   **`game-stdout` / `game-stderr`**: 捕获游戏子进程的标准输出与错误，通过独立线程实时回传前端。

```typescript
// 前端监听示例 (Svelte 5)
import { listen } from "@tauri-apps/api/event";

const unlisten = await listen("launcher-log", (event) => {
    logStore.addLine(event.payload);
});
```

-   **`version-installed`**: 异步任务完成后通知 UI 更新版本列表状态。

### 5.2 启动日志脱敏
为防止敏感信息泄露至第三方日志平台，启动器在记录启动指令前会对令牌进行遮蔽。

```rust
// 基于 main.rs 的参数遮蔽逻辑 (部分)
let masked_args: Vec<String> = args.iter().enumerate().map(|(i, arg)| {
    if i > 0 && (args[i-1] == "--accessToken" || args[i-1] == "--uuid") {
        "***".to_string()
    } else {
        arg.clone()
    }
}).collect();
```

---

## 6. 架构与开发规范

### 6.1 核心架构模式

#### 后端命令模式 (Tauri)
所有后端功能均封装为异步 Command，并通过 `State` 注入全局状态。

1. **定义命令**:
```rust
#[tauri::command]
async fn start_game(
    window: Window,
    auth_state: State<'_, AccountState>,
    config_state: State<'_, ConfigState>
) -> Result<String, String> {
    emit_log!(window, "Starting game launch...");
    // 业务逻辑...
    Ok("Success".into())
}
```

2. **注册命令 (main.rs)**:
```rust
tauri::Builder::default()
    .invoke_handler(tauri::generate_handler![start_game, ...])
```

3. **前端调用**:
```typescript
import { invoke } from "@tauri-apps/api/core";
// 参数名需与 Rust 函数参数保持蛇形/驼峰对应
const result = await invoke("start_game", { versionId: "1.20.4" });
```

#### 错误处理模式
所有 Tauri 命令统一返回 `Result<T, String>`，其中 Err 类型固定为 String，以便前端直接展示错误信息。

```rust
// 统一使用 map_err 将错误转换为 String
.await
.map_err(|e| e.to_string())?;

// 前端捕获
try {
    await invoke("...")
} catch (e) {
    // e 即为 Rust 返回的错误字符串
    console.error(e); 
}
```

#### 前端状态管理 (Svelte 5 Runes)
前端全线采用 Svelte 5 的 `Runes` 系统 (`$state`, `$effect`) 替代旧版的 `writable` stores。

```typescript
// ui/src/stores/auth.svelte.ts
export class AuthState {
  // 使用 $state 定义响应式数据
  currentAccount = $state<Account | null>(null);
  isLoginModalOpen = $state(false);

  constructor() {
    // 初始化逻辑
  }
}

// 导出单例用于全局访问
export const authState = new AuthState();
```

#### 前端视图路由 (Manual Routing)
DropOut 不使用常规的 URL 路由，而是通过全局状态管理当前激活的视图组件。

```typescript
// ui/src/stores/ui.svelte.ts
export class UIState {
    activeView = $state("home"); // "home" | "versions" | "settings"
    
    setView(view: string) {
        this.activeView = view;
    }
}
```

```svelte
<!-- App.svelte -->
<script>
    import { uiState } from "./stores/ui.svelte";
</script>

{#if uiState.activeView === "home"}
    <HomeView />
{:else if uiState.activeView === "settings"}
    <SettingsView />
{/if}
```

### 6.2 开发最佳实践
1.  **静默刷新**: 在调用 `start_game` 前检查 `expires_at`，若过期则调用 `refresh_full_auth`。
2.  **UA 伪装**: 微软认证 WAF 会拦截空 UA 请求，务必在 `reqwest` 客户端中模拟真实 UA。
3.  **日志脱敏**: 启动日志中需屏蔽 `--accessToken` 和 `--uuid` 等敏感参数值。

### 6.3 未来路线图 (Roadmap)
-   **账户多端同步**: 探索 OS 级加密存储方案以替代现有明文 `accounts.json`。
-   **自动依赖解析**: 安装模组时自动解析并下载 Modrinth/CurseForge 上的前置库。
-   **智能冲突检测**: 启动前扫描 `mods/` 文件夹，识别潜在的类冲突或版本不匹配。
-   **配置文件共享**: 支持一键导出/导入完整的实例配置包（Modpack）。
