- Cargo.toml [workspace.package] version = 0.2.0;Cargo.lock 由 cargo 重新解出一致
(只有 8 个 unlucky-* 成员条目变动,第三方版本一字未动)
- 各 crate 的 `unlucky-*` path 依赖 `version = "0.1"` 同步提到 "0.2",否则工作区
根本解析不出来(cargo metadata 会报 `failed to select a version for
unlucky-core = "^0.1"`);crates/app 虽被 exclude、cargo 不读,也一并跟上避免将来
重新纳入时立刻解析失败
- core::launch::LauncherConfig 兜底的 launcher_version 改成 env!("CARGO_PKG_VERSION"),
不再是把版本写死一份(CLI / presenter 本来就这么写)
- README.md / README.zh-CN.md:删掉「v0.1.0 是目前唯一的 tag / the only tagged release」
这类随 v0.2.0 发布即变假的陈述,改写成「v0.1.0 是最后一个 Slint 版 tag,v0.2.0 是
重写后的第一个 tag」;所有讲 v0.1.0 资产 / Slint Royalty-Free 义务的历史记录原样保留
- packaging/README.md:命名与校验示例跟当前 tag 走(v0.2.0);§7 实测记录里的 0.1.0
输出加一句「属实测记录、不回填」,并把过期的「四个 workspace member」改成「任何一个」
14 KiB
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)作为历史参考保留源码:已被 workspaceexclude,默认构建不编、测试不跑、 发布不发。v0.1.0那个 tag 发的仍是 Slint 版;iced 重写后的第一个 tag 是v0.2.0(见「当前状态」)。 - 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)。
当前状态
v0.1.0(2026-11-01)早于 UI 重写:那批资产是 Slint 版,它也是最后一个 Slint 版 tag。重写之后的
第一个 tag 是 v0.2.0——也就是 iced 这条线,下面说的这套东西都在线上,也正是发布流水线现在会打
出来的东西。
- iced 版界面功能已齐:十四页——实例、版本管理(三栏 + 搜索)、内容库(整合包 / Mod / 资源包 / 光影,带来源与版本筛选)、日志、设置(通用 / 外观 / Java / 下载)、账户(微软设备码、第三方社区服、 本地档案)、AI 右栏,以及安装流程——共用一套组件库与一套令牌。
- 文案跟随系统语言:自动探测、设置里可显式切换、重启后保持。界面文案统一走一份消息目录,视图层 没有硬编码字符串。
- 外观:窗口背景图、面板透明度、整窗透明度(Windows)、浅色 / 深色 / 跟随系统,以及在系统愿意给 的时候使用系统窗口圆角。CPU 渲染档会关掉半透明层,并在界面上说清楚。
- 整合包与安装链路都在构建里:CurseForge 清单 / Modrinth
.mrpack/ MultiMC·Prism 导入,mrpack 与 CurseForge 导出,安装选项(选加载器)与真取消。 - 还没做:安装器与自动更新通道;Windows 定型后的 macOS 真机复核;整合包导入的逐文件冲突策略 (当前只有一份全局策略,界面如实写明而不是假装支持);原生文件选择框(导入支持拖拽与粘贴路径)。
- Slint 版已退出构建,只留源码作参考:
crates/app不再是 workspace 成员(M118)——源码留作历史 参考,不编译、不测试、不发布,也不再有可用的构建方式。翻牌只剩改名这一步:它的 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/presenter # 业务宿主层:与框架无关的 state / host / dto / 转换
├── crates/design # 设计令牌:真 HCT 色彩 / 排版 / 形状 / 间距 / 高程 / 动效 / 图标 / 组件度量
│ # —— 零 UI 依赖、零第三方 crate
├── crates/i18n # 语言探测、消息目录、中文字体策略
├── crates/palette-extract # 唯一解码图片的 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 给出两种形式:(a) 在可从顶层菜单进入的「关于」页/ 对话框里显示官方AboutSlint部件(没有该页时改为启动画面);(b) 在可下载该二进制的公开网页上显示 官方归属徽章。v0.1.0的设置页「关于」分组里内置了AboutSlint部件(crates/app/ui/pages/ settings.slint,从侧栏进入);该部件是否满足 §2(a) 的全部条件、以及v0.1.0发布页上那是文字链接 而不是 §2(b) 点名的官方徽章图这两点,本节不作法律判断,具体以 Slint 官方许可原文为准。v0.1.0的 NOTICE、发布页说明与本节都只是记录,不构成履行声明。后续版本不再链接 Slint,该义务不随新产物 新增。义务跟着链接了 Slint 的产物走:已发布的v0.1.0资产(以及将来任何重新链接 Slint 的构建) 适用;crates/app已被 workspaceexclude,流水线不再产出链接 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 玩家走了很多年, 本项目在设计取舍与实现细节上都从中受益良多。