Files
Unlucky-Launcher/README.zh-CN.md
T
AskaEth e827f7a0ef fix(docs): M118 r2 —— 发布页第二形态换成实测可用的 --renderer=cpu,NOTICE 停止声称 RF-2.0 已履行
复审 r2 的 2 条 P1 + 3 条 p2 小账,全部只动文档(5 份,0 个 .rs)。

P1-1(`SLINT_BACKEND` → `ICED_BACKEND` 换了个名字的死开关):
`packaging/release-notes.md` 的「No GPU」小节原来把 `ICED_BACKEND=tiny-skia` 当作
`--software-renderer` 的等价替代命令给用户。实测该值会被本进程无条件覆盖:
- `ICED_BACKEND=tiny-skia ./unlucky-launcher-iced.exe --screenshot …`
  → `[probe] … request=auto -> ICED_BACKEND=wgpu` / `[shot] … renderer=wgpu` / `[backend] final=wgpu`
- `--software-renderer --screenshot …` → `request=cpu -> ICED_BACKEND=tiny-skia` / `renderer=tiny-skia`
- `--renderer=cpu --screenshot …`      → `request=cpu -> ICED_BACKEND=tiny-skia` / `renderer=tiny-skia`
所以改成两条都实测可用的形态(`--software-renderer` / `--renderer=cpu`),不再把环境变量当用户开关。

P1-2(RF-2.0 履行方式的措辞与上游 §2 冲突):
`NOTICE` 第 1 条原写「由 v0.1.0 的 NOTICE、发布页说明与本文件的历史记录共同承担」——§2 只承认
(a) AboutSlint 部件 / (b) 公开下载页的官方徽章两种履行,NOTICE 文本不是履行。改为:义务仍挂在
v0.1.0 已发布资产上(条件挂在每次分发,不挂版本号);§2 只承认两种形式;**按本仓库记录,该项义务
在 v0.1.0 上尚未以任一形式落地**(部件一直只有排期,发布页那行是文字链接不是徽章图);NOTICE /
发布页说明 / 本节都只是记录。

p2 小账:
- `NOTICE` 第 3 条的依赖许可:`tiny-skia` 从「Apache-2.0 / MIT」改正为 BSD-3-Clause,并把
  winit(Apache-2.0)/ wgpu(MIT OR Apache-2.0)分别写明;README(中英)同款写法一并对齐。
- `packaging/README.md` §4:`libGL/libEGL` 行改成「Slint 版 femtovg / iced 版 wgpu(推断,未实测)」,
  并把限定语改成「除标注『推断,未实测』的那一行外,整表是 Slint 版实测」,消除表内与限定语打架。
- `README.md` / `README.zh-CN.md` 的 Slint 义务段补 `v0.1.0` 限定并按 §2 口径重写,避免读者以为
  「Made with Slint」说的是下一版。

证据:`git diff --name-only` = NOTICE, README.md, README.zh-CN.md, packaging/README.md,
packaging/release-notes.md;`packaging/README.md` 仍 399 行(守卫上限 400);release body 模板用
`{{VERSION}}=0.2.0` 渲染通过(5956 字符,全篇 Slint 命中 4 行,均在「本版不含 Slint」的解释段)。
2026-11-04 00:09:39 +08:00

15 KiB
Raw Blame History

Unlucky Launcher (ULL)

English · 简体中文

官方项目主页:https://github.com/AskaEth/Unlucky-Launcher | 下载、议题、发布说明都在这里: https://github.com/AskaEth/Unlucky-Launcher/releases

Unlucky Launcher(ULL)是一个用 Rust 写的免费开源 Minecraft: Java 版启动器(Apache-2.0)。 每个平台就一个原生二进制——没有安装包、没有捆绑运行时、没有 WebView。启动器内置 AI Agent 客户端(对话 / 改配置 / 崩溃诊断),也能作为 MCP server 接进别的 Agent 工具。

特性

  • 单文件原生二进制:启动器本体不需要 JVM、不需要 WebView、不带旁挂运行时。游戏要的 JDK 由 启动器按实例下载管理,不是启动器的依赖。
  • 默认构建里只有一套 UI:UI 是 iced 0.14 + Material 3 Expressive。早先的 Slint 实现 (crates/app)作为历史参考保留源码:已被 workspace exclude,默认构建不编、测试不跑、 发布不发。唯一已发布的 tag v0.1.0 发的仍是 Slint 版(见「当前状态」)。
  • CPU 软渲染兜底:--software-renderer 让没有可用 GPU 或驱动有问题的老机也能正常显示界面。
  • AI Agent + MCP:启动器内可对话、改设置、读崩溃诊断;ull agent serve 把同一个内核以 MCP server 形态(stdio 或 Streamable HTTP)开放给外部 Agent 工具。
  • 加载器:Fabric / Quilt / NeoForge / Forge;整合包与 CurseForge / Modrinth 客户端在 core 里。
  • Java 管理:Temurin → Oracle → Microsoft → Zulu 回退链,带地区判定。
  • 账户:用 Microsoft 账户登录,认证走 OAuth 2.0 device code flow → Xbox Live → XSTS → api.minecraftservices.com;账户令牌只保存在本机。
  • 引擎:多源下载(健康度评分、镜像、分块传输、边下边算)与 YAML 规则引擎的崩溃诊断。
  • 许可:Apache-2.0 © AskaEth (YeiJ)。

当前状态

两条轨道,还不是同一个东西——看小标题再决定下载什么。

轨道 A —— 历史:v0.1.0,Slint 版(目前唯一的 tag)

历史实现——已不在默认构建与发布矩阵内。 Slint 那棵树(crates/app)作为源码参考留在仓库里: 已被 cargo workspace exclude,所以 cargo build / cargo test / cargo clippy 都不会碰它, 也没有任何产物从它构建。想了解那一版 UI 怎么写的就直接读源码——不要再尝试构建它(它的 manifest 整份是 workspace 继承写法,离开 workspace 后连解析都过不了)。下面这段原文保留,因为它记录的是 v0.1.0 资产里到底装了什么;从 v0.1.0 之后,流水线构建的是 iced 版,对外文件名不变。

  • GitHub Releases v0.1.0(2026-11-01,标记为 pre-release):单文件产物覆盖 Windows x64(裸 .exe)、Linux x64、macOS(arm64 与 x64).tar.gz。每个 Release 另挂 SHA256SUMS.txt 与 LICENSE / NOTICE / README.md 三份文本——Windows 是「一个 exe 装不下文本」所以并列挂出, unix 归档里本来就装着。
  • UI:Slint + Metro。这是已交付的图形界面——实例、下载、账户、Java、日志、设置 各页都由 core 引擎驱动(应用内 AI 对话面板仍是脚本化的占位)。
  • 同一个二进制就是 CLI:unlucky-launcher instances list、unlucky-launcher launch <实例>、 unlucky-launcher logs <sid> 等命令在任何 UI 代码之前分流——不开窗、不探渲染后端、不扫字体。 命令集:instances、install、launch、accounts、sessions、logs、diag、config、 java、agent,外加 --help / --version。
  • 另有一个更轻的 ull(cargo build --release --bin ull,零 UI 依赖):同一套命令,但二进制不 链接任何图形栈——连 fontconfig 都不需要。服务器、CI 容器、Termux 拿 ull;有桌面的机器拿 unlucky-launcher,一个文件同时管 GUI 与命令行。两者都从下一个 tag 起进发布资产 (v0.1.0 的资产早于这个变化)。
  • Windows ARM64 与 Linux ARM64 两个槽位已接进发布流水线,随下一个 tag 发布;v0.1.0 的资产早于 这两个槽位。

轨道 B —— dev 上的下一代 UI(iced + M3 Expressive,施工中)

这条轨道还没发布——要看只能自己从源码编。

  • 方向已锁死:视觉 = Material 3 Expressive,框架 = iced 0.14,UI 层从头写;风格与框架都不会再换。
  • 已完成:令牌 crate crates/design(零 UI 框架依赖、零第三方 crate——真 HCT 色彩、排版档位、 形状刻度、间距、高程、动效、图标几何、组件度量)与骨架 crate crates/app-iced(能开窗、自绘标题栏 与窗口键、导航轨、颜色与字号只从令牌取、GPU→CPU 后端探测与自动降级、视觉验收用的截图与逐像素 对比通道)。
  • 没完成:组件库(按钮、对话框、列表、菜单、搜索栏,以及 M3 其余组件)、页面搬迁、i18n 文案。 iced 版里现在每一个页面都是占位。
  • Slint 版已退出构建,只留源码作参考:crates/app 不再是 workspace 成员(M118)——源码留作历史 参考,不编译、不测试、不发布,也不再有可用的构建方式。翻牌只剩改名这一步:在 iced 版通过等价 验收前,它的 bin 还叫 unlucky-launcher-iced;发布流水线已经把它按对外名 unlucky-launcher 打出去。

可能会遇到的 bin,先认清楚:

二进制 crate 状态
unlucky-launcher-iced crates/app-iced(iced 0.14) workspace 里唯一的 GUI bin;发布流水线把它按对外名 unlucky-launcher 落盘;子命令分发同款
unlucky-launcher crates/app(Slint) 历史:该 crate 已被 workspace exclude,这个 bin 不再被构建或分发;v0.1.0 的资产是最后一批 Slint 版
ull crates/cli 同一套命令的零 UI 构建,作为独立资产随发布分发

unlucky-launcher 与 ull 不是重复品:开窗时 GUI 二进制要用图形栈(X11 / Wayland / GL 走 dlopen,启动时直接链接 libfontconfig);ull 除了 glibc 什么都不需要,所以在没有窗口的服务器、 CI 容器、Termux 上跑得起来。没有桌面就用 ull,有桌面就用 unlucky-launcher <子命令>。两者打出来的 CLI 文案是同一套,自称的名字跟着你敲的那个二进制走:unlucky-launcher --help 首行是 unlucky-launcher ·,ull --help 首行是 ull ·。

子命令分发

unlucky-launcher <参数> 在跑任何 UI 代码之前先问一句「这是不是一次 CLI 调用」:

unlucky-launcher                       # GUI
unlucky-launcher --software-renderer   # GUI,CPU 渲染(行为不变)
unlucky-launcher instances list        # CLI,不开窗
unlucky-launcher --json launch 1.20.1  # CLI,全局选项排在子命令前面
unlucky-launcher --version             # CLI,输出版本 + 系统摘要

判据刻意收得很窄,所以原本能用的用法一条都没变:

  • 第一个参数是裸词(不以 - 开头)→ 它就在子命令的位置上,交给 CLI。GUI 的 flag 永远不会出现在 那个位置:--screenshot / --diff / --data-dir 的取值都排在 flag 之后。
  • 第一个参数是 flag → 只有当 crates/cli 的解析器也解析出一条真命令时才算 CLI 调用 (--help、--version、--json instances list)。单独 --data-dir X 解析成「没有子命令」, 仍然开 GUI;GUI 专属参数(--software-renderer、--screenshot*、--renderer、--theme、 --diff、--force-state 等)在 CLI 里一律解析失败,也仍然开 GUI。

未知子命令是错误,不是开窗:unlucky-launcher lst 打印「未知命令 lst」、提示 unlucky-launcher --help,以 1 退出。静默开窗会让脚本以为自己那个拼错的参数生效了。子命令表只有 一处定义(crates/cli/src/cli/parse.rs),所以两个 GUI 二进制和 ull 认的命令集天然一致。

模块地图

Unlucky-Launcher (cargo workspace)
├── crates/core       # 领域库:零 UI 依赖,八模块
├── crates/agent      # AI Agent:ToolSpec 注册表 + 内置客户端 + MCP server(双形态共用一个内核)
├── crates/design     # 设计令牌:真 HCT 色彩 / 排版 / 形状 / 间距 / 高程 / 动效 / 图标 / 组件度量
│                     #   —— 零 UI 依赖、零第三方 crate
├── crates/app        # 历史 Slint UI(bin `unlucky-launcher`):已被 workspace `exclude`,
│                     #   仅作源码参考——不再被构建 / 测试 / 发布
├── crates/app-iced   # iced UI(bin `unlucky-launcher-iced`):workspace 里唯一的 GUI bin
├── crates/cli        # CLI(bin `ull`):headless,零 UI
└── packaging         # 单文件产物矩阵与发布脚本

crates/core 的八个模块:

模块 职责
setting 三模式数据目录、schema 配置、原子写、迁移
download 多源健康度、镜像、分块下载、边下边算
game 版本/实例仓库、快照与继承解析
launch 10 阶段启动管线、进程、JSONL 事件流
auth Microsoft 账户登录(OAuth 2.0 device code → Xbox Live → XSTS → Minecraft Services)
addon 加载器安装、整合包、CurseForge / Modrinth
diagnose 五类信号采集、YAML 规则引擎、hs_err 解析
platform OS 抽象:路径、进程、文件锁、能力探测、运行期 OS 检测

构建

前置:

  • Rust stable(rust-toolchain.toml 会自动选工具链;门禁还需要 rustfmt / clippy 组件)
  • Windows:MSVC 工具链(CI 用这套)或 MinGW-w64(x86_64-pc-windows-gnu)
  • Linux:libxkbcommon-dev libfontconfig1-dev libgl1-mesa-dev libegl1-mesa-dev libwayland-dev libxcb1-dev——winit + wgpu/tiny-skia 编译期要用(Slint 版当年还要 femtovg)
cargo fmt --all --check                                # 格式
cargo clippy --workspace --all-targets -- -D warnings  # 静态检查(零 warning 门禁)
cargo test --workspace                                 # 测试
cargo build --release                                  # bin 落在 target/release/

--workspace 就是模块地图里那八个成员:crates/app 已在 workspace 层被排除(M118),上面这些命令 都不会编 Slint 那棵树——而且它没有构建方式:该 crate 只作源码参考,作为 workspace 成员已不可编。

编出来的 bin:

  • unlucky-launcher-iced——GUI(iced)。它同时也是 CLI:同一个二进制认上面列的全部子命令。名字是 临时的,翻牌时才改成 unlucky-launcher;发布产物已经用对外名:流水线编这个 bin, packaging/make_artifacts.py 落盘时改成 unlucky-launcher[.exe],所以下载链接不用变。
  • unlucky-launcher——历史:workspace 构建已不产出它(它属于被排除的 crates/app)。 v0.1.0 的资产是最后一批用这个名字发出去的构建。
  • ull——CLI 的独立无 UI 构建:同一套命令、不链接图形栈、体积更小,给完全没有显示设备的机器用。

渲染开关:

  • unlucky-launcher-iced --software-renderer 强制 tiny-skia(ICED_BACKEND=tiny-skia); --screenshot <png> 出一帧图后退出,这是 CI 无头冒烟 job 用的验收通道
  • 没有可用适配器时,iced 版靠显式后端探测自己退到 CPU
  • 历史(v0.1.0 的 Slint 版仍认这条):unlucky-launcher --software-renderer,即 SLINT_BACKEND=winit-software,让那一版走它自己的渲染器回退链

依赖许可检查(本地可选,CI 必跑):cargo deny check licenses bans sources。

CI 门禁

每次推送到 main 与每个 PR 都跑 fmt / clippy(warnings 视为错误)/ 全量测试,Linux 无头渲染冒烟 与它们同级、是硬门禁:红了就不许合。macOS 运行期探针还不算——它只在手动 workflow_dispatch 时跑, 结果留证据但不作为流水线结论(continue-on-error),直到该槽位在真实 runner 上首跑通过。

Workflow 内容
.github/workflows/ci.yml fmt + clippy + test + release build(Windows / Linux 矩阵);Linux runtime smoke(Xvfb 里真渲染一帧,再断言 PNG 非空白);macOS 运行期探针(仅手动 dispatch)
.github/workflows/guards.yml crates/core 零 UI 守门(依赖表 + cargo tree)、单文件 ≤400 行守门、cargo-deny 许可与来源守门
.github/workflows/release.yml tag v* → 分槽位构建 → 单文件产物 + SHA256SUMS.txt → GitHub Release

平台基线

最低基线 Windows 10;Windows 8.1 / 8 / 7 不在支持范围内。

  • Windows 10/11 的产物由标准工具链(MSVC / GNU)构建,构建方式与上游 cargo 一致
  • macOS 12+(Apple Silicon 与 Intel)与 Linux x64 / arm64 都是支持槽位;Linux 上 X11 是当前首选路径, Wayland 是大方向。macOS 产物未签名,首次启动要自己清掉 Gatekeeper 隔离标记 (xattr -d com.apple.quarantine)
  • Android Termux 试验级、不承诺:只保证 CLI,GUI 未验证
  • 每个平台跑各自的测试矩阵;能力差异用 feature gate + 运行期探测表达。

许可证与致谢

  • 本项目:Apache License 2.0,见 LICENSE;第三方声明见 NOTICE。
  • Slint(https://slint.dev),Royalty-Free 2.0:义务针对 v0.1.0 发布的那批构建。上游 §2 只承认 两种履行方式——应用「关于」页放 AboutSlint 部件,或在公开下载页显示官方 Slint 归属徽章。按本仓库 的记录,这项义务在 v0.1.0 上尚未以上述任一形式落地:关于页的部件一直只有排期、从未落地;v0.1.0 发布页上是文字链接,不是官方徽章图。v0.1.0 的 NOTICE、发布页说明与本节都只是记录,不构成履行。 义务跟着链接了 Slint 的产物走:已发布的 v0.1.0 资产(以及将来任何重新链接 Slint 的构建)适用; crates/app 已被 workspace exclude,流水线不再产出链接 Slint 的二进制,所以新的 iced 版产物不适用。
  • iced(https://github.com/iced-rs/iced),MIT:现役 UI 的框架;与它同栈的 winit(Apache-2.0)、 以及经它引入的 wgpu(MIT / Apache-2.0)/ tiny-skia(BSD-3-Clause)。
  • Google Material Color Utilities(Apache-2.0):crates/design 的色彩令牌是它 HCT / ColorSpec2021 算法的 Rust 重写移植,移植清单与逐函数对照写在 NOTICE。
  • 其余主要依赖:MCP server 的 rmcp 与 axum(Apache-2.0 / MIT)、tokio(MIT)、reqwest 与 rustls(MIT / Apache-2.0)、serde / serde_json(MIT / Apache-2.0)、zip / flate2 / tar (MIT / Apache-2.0)。cargo-deny 强制执行的白名单在 deny.toml;GPL 系依赖不得进主干。

感谢 HMCL 和 PCL2 (均 GPL-3.0)。这两款启动器陪着绝大多数 MC 玩家走了很多年, 本项目在设计取舍与实现细节上都从中受益良多。