aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/docs/content/zh/development/implementation.mdx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/docs/content/zh/development/implementation.mdx')
-rw-r--r--packages/docs/content/zh/development/implementation.mdx351
1 files changed, 351 insertions, 0 deletions
diff --git a/packages/docs/content/zh/development/implementation.mdx b/packages/docs/content/zh/development/implementation.mdx
new file mode 100644
index 0000000..d7586aa
--- /dev/null
+++ b/packages/docs/content/zh/development/implementation.mdx
@@ -0,0 +1,351 @@
+---
+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)。