关于 The Land of StarLight (TLSL)
The Land Of StarLight(中文全称星光领域,英文简称 TLSL)成立于 2014 年,是一个主营 Minecraft 相关内容的综合性平台。平台主要负责人为 Disy(因名称常被抢注,在多数平台以 Disy920 出现)。
TLSL 的前身可追溯至 2012 年初建立的凋灵服务器(TWS)。经过多次重组与模式调整,于 2014 年 10 月定名为星光服务器(StarLight-Server,简称 SLS)。2020 年初,随着运营模式改变与业务扩展,TLSL 正式成立,SLS 成为其主营子项目。
如今 TLSL 旗下拥有 Minecraft 服务器 StarLight-Server(SLS)、面向 Fabric 端的领地模组 Enclosure 等项目,致力于打造一个完善的 Minecraft 交流与开发平台。
Starlight Launcher 即是面向 SLS(星光服务器) 打造的 Minecraft 桌面启动器。
Starlight Launcher(星光启动器) 是一款免费、开源、跨平台的 Minecraft Java 版第三方启动器,支持在一个客户端中搜索、安装和更新来自 Modrinth 与 CurseForge 的模组、整合包、资源包和光影,并提供实例管理、多种账户认证与个性化外观。
本项目基于 Modrinth App 构建,并在其基础上由 Axolotl 启动器 修改而来,移除了不适用于本项目的商业化模块,专注于提供纯净、无广告的桌面启动体验。
(注:本项目是调用 Modrinth 公开 API 的独立客户端,与 Rinth, Inc. 无任何关联。)
核心优势
- 真跨平台体验:告别繁琐的环境配置,原生支持 Windows、macOS(完美兼容 Intel 与 Apple Silicon)及各类主流 Linux 发行版。
- 现代化内容生态:集成 Modrinth 和 CurseForge,可在启动器中一键浏览。游戏实例、整合包、模组、资源包及光影均可一键安装与升级,彻底告别手动管理依赖的痛苦。
- 高度定制化:无论是主题色调、背景图片,还是离线皮肤,核心功能与视觉展现均由你自由支配。
- All in one 全新体验:启动器内置 “实验室” 功能,囊括种子地图、投影工坊等海量使用工具,带来全新原生轮椅体验。
下载与安装
请前往 Releases 下载适合你操作系统的最新安装包。 已安装的用户每次均可通过内置的 Tauri 签名校验机制,自动在后台完成更新,无需手动下载安装更新。
| 系统平台 | 推荐下载文件 |
|---|---|
| Windows (10/11 x64) | 下载 .exe (NSIS) 安装程序 |
| macOS | 下载 通用 .dmg 镜像文件 |
| Linux (x64) | 提供 .AppImage,.deb,.rpm 多种格式 |
参与项目开发
Starlight Launcher 的进步离不开社区的反馈与贡献。 如果遇到 Bug 或有新的功能点子,欢迎提交 Issue。如需搭建本地开发环境或查阅打包发布规范,请阅读详细的 贡献指南 (CONTRIBUTING.md)。 参与社区和贡献代码前,也请先阅读行为准则 (CODE_OF_CONDUCT.md)。
🌟 启动过渡动画(本次改动)
本节记录「游戏启动时窗口平滑过渡」功能的完整实现,供后续维护、移植与继续开发使用。 生效平台:仅 Windows(其他平台为空实现,行为与改动前一致)。
一、功能目标
消除「点击启动 → MC 游戏窗口突然弹出 → 把启动器挤走」的割裂感,用一套完整的窗口动画把这段过渡变得顺滑。
启动一个实例后,会经历以下动画序列:
点击「启动实例」
↓
① 纯色遮罩出现(启动器主题色,无图)—— 立刻盖住后续所有抖动
↓
② 启动器放大到全屏(0.4s 动画,全程置顶 + 聚焦)
↓
③ 全屏后,遮罩上渐显 Mojang SVG logo(0.5s)
↓
④ 等待 MC 游戏窗口出现(最多 60 秒,期间遮罩一直盖着)
↓
⑤ 缩小前:遮罩背景色 → Mojang 红 (#db1f29) + 渐隐 logo(0.5s)
↓
⑥ 启动器缩放到 MC 窗口的位置和大小(0.4s)
↓
⑦ 等待 1 秒(让游戏稳定,遮罩仍在)
↓
⑧ 整体淡出透明(0.5s)→ 聚焦游戏窗口 → 启动器最小化
↓
进入轻量模式(隐藏到系统托盘)
设计要点:
- 遮罩提前:纯色遮罩在放大之前就出现,所以放大和缩小时的窗口抖动都被盖住,用户看不到。
- 缩放与淡出串行:先缩放完成,再淡出,不再同步进行(早期版本两者同步,观感差)。
- 动画放慢:缩放 0.4s(早期 0.2s 太快),淡出 0.5s。
- 修复重开透明:最小化后重置窗口透明度,并且恢复时用轻量模式重建全新窗口,彻底杜绝「重新打开启动器一片透明、需要点击/拖动才恢复」的问题。
- 轻量模式接管:过渡动画结束后,统一进入轻量模式(隐藏到托盘),不再分别判断用户的「轻量模式 / 启动后隐藏」设置,避免冲突。
二、改动文件总览
| 文件 | 改动类型 | 说明 |
|---|---|---|
apps/app/src/mc_transition.rs |
新建 | 核心模块(约 470 行),负责窗口查找、四段动画、遮罩/淡出协调 |
apps/app/src/main.rs |
修改 | 声明模块 + 注册 4 个 Tauri 命令 |
apps/app/src/lightweight_mode.rs |
修改 | launched 分支接入过渡,并统一进轻量模式 |
apps/app/Cargo.toml |
修改 | windows crate 增加 Win32_Graphics_Gdi feature |
apps/app-frontend/src/App.vue |
修改 | 遮罩三阶段、淡出、透明度重置等前端逻辑 |
apps/app-frontend/public/mojang-logo.svg |
新建 | 过渡遮罩上显示的 Mojang logo |
packages/app-lib/.env |
新建 | 从 .env.prod 复制,提供编译期环境变量(非功能改动) |
三、各文件详细说明
1. apps/app/src/mc_transition.rs(新建 · 核心)
搜索定位关键词:
mc_transition
关键常量:
| 常量 | 值 | 含义 |
|---|---|---|
FIND_RETRIES |
120 | 等待 MC 窗口的最大轮询次数 |
FIND_INTERVAL_MS |
500 | 每次轮询间隔(120 × 500ms = 60 秒) |
ANIM_DURATION_MS |
400 | 放大/缩小动画时长(0.4s) |
ANIM_STEPS |
50 | 动画帧数(400ms / 50 = 8ms 一帧) |
FADE_TIMEOUT_MS |
700 | 等待前端淡出的超时兜底 |
静态变量(跨 await 传信号):
FADE_DONE— 等前端淡出完成COVER_DONE— 等纯色遮罩出现LOGO_IN_DONE— 等 logo 渐显完成LOGO_OUT_DONE— 等 logo 渐隐 + 变红完成
关键函数:
| 函数 | 关键词 | 作用 |
|---|---|---|
pub async fn run(app, pid, maximize) |
run() invoked |
主入口,串联全部 8 步 |
async fn animate_resize(...) |
animate_resize |
分帧移动 + 缩放窗口 |
fn ease_in_out(t) |
ease_in_out |
缓入缓出曲线 |
win_impl::find_window(pid) |
find_cb / FOUND_HWND |
按 pid 枚举顶层窗口 |
win_impl::covers_monitor(raw) |
covers_monitor |
判断窗口是否铺满显示器 |
win_impl::get_rect(raw) |
get_rect |
取窗口矩形 |
win_impl::focus(raw) |
SetForegroundWindow |
把焦点交给游戏 |
win_impl::dump_pid_windows(pid) |
dump_pid_windows |
调试用:列出 pid 下所有窗口 |
#[tauri::command] mc_transition_fade_done |
— | 前端淡出完成回调 |
#[tauri::command] mc_transition_cover_ready |
— | 遮罩出现完成回调 |
#[tauri::command] mc_transition_logo_in_done |
— | logo 渐显完成回调 |
#[tauri::command] mc_transition_logo_out_done |
— | logo 渐隐完成回调 |
⚠️ 三个关键设计约束(改代码前必读):
-
HWND 不能跨 await 持有。
HWND是裸指针,非Send,一旦跨await持有,async 块就不是Send,无法被tauri::async_runtime::spawn。 → 因此全程用usize保存窗口句柄(变量mc_raw),只在同步的 win32 调用里通过to_hwnd()临时转回。 搜索关键词:to_hwnd、FOUND_HWND -
BOOL的导入路径。windowscrate 0.61 中,BOOL位于windows::core::BOOL,不在Win32::Foundation。 搜索关键词:use windows::core::BOOL -
窗口尺寸过滤阈值。
find_cb里只认宽高都 > 50 的可见窗口,用来排除工具窗口 / 0 尺寸窗口。
⚠️ 调试代码仍在(上线前需清理):
fn dbg(msg)—— 把日志直接写入%USERPROFILE%\mc_transition_debug.log,绕过 tracing 配置- 全文件多处
dbg(...)调用 - 等待窗口期间每 2 秒调用一次
dump_pid_windows - 搜索关键词:
fn dbg(、mc_transition_debug.log、dump_pid_windows
2. apps/app/src/main.rs
- 第 25 行附近:
mod mc_transition; invoke_handler的generate_handler!宏内注册 4 个命令:
mc_transition::mc_transition_fade_done,
mc_transition::mc_transition_cover_ready,
mc_transition::mc_transition_logo_in_done,
mc_transition::mc_transition_logo_out_done,
- 搜索关键词:
mod mc_transition、mc_transition_fade_done
3. apps/app/src/lightweight_mode.rs
process_event 中 None if payload.event == "launched" 分支(约 250 行起):
- 新增调用:
crate::mc_transition::run(&app, payload.pid, payload.maximize_window).await; - 过渡完成后统一进入轻量模式:
state.enter(&app) - 删除了原来分别判断
settings.enter_lightweight_mode_on_game_launch与settings.hide_on_process_start的逻辑 - 搜索关键词:
crate::mc_transition::run、launched、enter lightweight mode after transition
执行顺序:
maximize_minecraft_window(若用户勾选最大化)
→ mc_transition::run(完整过渡动画)
→ state.enter()(隐藏到托盘)
4. apps/app/Cargo.toml
[target."cfg(windows)".dependencies.windows] 的 features 中新增:
"Win32_Graphics_Gdi",
原因:
GetMonitorInfoW/MonitorFromWindow/MONITORINFO属于 GDI 模块,不加会编译失败。
- 搜索关键词:
Win32_Graphics_Gdi
5. apps/app-frontend/src/App.vue
变量声明(约 376~381 行):
let unlistenMcTransitionFadeout: (() => void) | undefined
let unlistenMcTransitionFocus: (() => void) | undefined
let unlistenMcTransitionCover: (() => void) | undefined
let unlistenMcTransitionLogoIn: (() => void) | undefined
let unlistenMcTransitionLogoOut: (() => void) | undefined
let unlistenMcTransitionReset: (() => void) | undefined
onMounted 内注册的监听(约 500 行起):
| 监听事件 | 作用 |
|---|---|
mc-transition-fadeout |
#app 设 opacity: 0 + 500ms 过渡,510ms 后回调 mc_transition_fade_done |
mc-transition-cover-show |
动态创建遮罩 div(主题色背景 + 隐藏的 logo),渐显后回调 mc_transition_cover_ready |
mc-transition-logo-in |
logo 渐显(opacity 0→1),520ms 后回调 mc_transition_logo_in_done |
mc-transition-logo-out |
遮罩背景 → #db1f29 + logo 渐隐,520ms 后回调 mc_transition_logo_out_done |
mc-transition-reset-opacity |
重置 #app 透明度 + 移除遮罩 |
透明度重置(修复重开透明)——三重兜底:
window的focus事件document的visibilitychange事件- Tauri 的
getCurrentWindow().onFocusChanged
任一触发即调用 resetLauncherOpacity(),把 #app 的 opacity 清空。
onUnmounted 内清理(约 674 行起):6 个 unlistenMcTransition*.?.()
- 搜索关键词:
unlistenMcTransitionFadeout、mc-transition-fadeout、mc_transition_fade_done、mcCoverEl、resetLauncherOpacity
遮罩实现细节:
遮罩是运行时动态创建的 DOM(不是 Vue template),避免改动庞大的 template 结构:
mcCoverEl = document.createElement('div') // 固定定位、铺满、主题色背景、最高 z-index
mcCoverImg = document.createElement('img') // /mojang-logo.svg,初始 opacity 0
- 遮罩 id:
mc-transition-cover - 背景色:
var(--color-brand, #db1f29)(跟随启动器主题色) - logo 宽度:42%,最大 640px
6. apps/app-frontend/public/mojang-logo.svg(新建)
过渡遮罩上显示的 Mojang 官方 logo(矢量图)。
四、实测记录
调试日志 %USERPROFILE%\mc_transition_debug.log 中的一次完整流程实录:
run() invoked pid=3088 maximize=false
try#0 ... try#38 candidates: [] <- MC 窗口约 20 秒后才出现
FOUND hwnd=0x3b03da try#40 covers=false
...
关键发现:
- MC 窗口需要约 20 秒才出现(带 mod 的实例更慢),所以等待轮询必须足够长(当前 60s)。
- 早期版本只等 5 秒,导致「启动器毫无变化」的假象——实际是超时静默放弃了。
五、编译与运行(Windows)
前置准备
:: 1. 子模块(本项目不是 git 仓库,需手动 clone)
git clone https://github.com/Cubitect/cubiomes.git apps/app/vendor/cubiomes
git clone https://github.com/Axolotl-Launcher/blockbench-skin-standalone.git third-party/blockbench
:: 2. 环境变量(或把 .env.prod 复制为 packages/app-lib/.env)
set "PATH=%USERPROFILE%\.cargo\bin;%PATH%"
set "MODRINTH_URL=https://modrinth.com/"
set "MODRINTH_API_BASE_URL=https://api.modrinth.com/"
set "MODRINTH_ARCHON_BASE_URL=https://archon.modrinth.com/"
set "MODRINTH_API_URL=https://api.modrinth.com/v2/"
set "MODRINTH_API_URL_V3=https://api.modrinth.com/v3/"
set "MODRINTH_SOCKET_URL=wss://api.modrinth.com/"
set "MODRINTH_LAUNCHER_META_URL=https://launcher-meta.modrinth.com/"
构建
:: 前端
pnpm install
pnpm --filter @modrinth/app-frontend run build
:: 后端 debug(产物 target/debug/theseus_gui.exe,约 155 MB)
cargo build -p theseus_gui
:: 后端 release(产物 target/release/theseus_gui.exe,约 75 MB)
cargo build -p theseus_gui --release
已知坑
| 坑 | 现象 | 解决 |
|---|---|---|
| 子模块缺失 | build.rs panic:Blockbench skin editor submodule is missing |
手动 clone 两个子模块 |
| 资源路径校验 | resource path resources\blockbench-skin doesn't exist |
构建会自动生成;若报错手动建空目录 |
| 环境变量缺失 | environment variable MODRINTH_API_URL not defined |
见上方环境变量,或复制 .env |
| 数据库迁移冲突 | migration ... was previously applied but is missing |
删除 %APPDATA%\cool.starlight.launcher\release\app.db 后重启 |
| exe 被占用 | failed to remove file ...theseus_gui.exe(拒绝访问) |
taskkill /F /IM theseus_gui.exe 后重编 |
pnpm app:build 失败 |
找不到 cargo | 那会走完整 tauri build 打包;只构建前端请用 --filter @modrinth/app-frontend |
六、待办 / TODO
| # | 事项 | 优先级 | 关键词 |
|---|---|---|---|
| 1 | 清理调试代码:删 fn dbg + 所有 dbg(...) + dump_pid_windows 调用 |
高 | fn dbg(、mc_transition_debug.log |
| 2 | 等待窗口超时(60s)可做成可配置 / 事件驱动 | 中 | FIND_RETRIES |
| 3 | 多显示器场景未充分测试(已按窗口所在屏取显示器) | 中 | monitor_rect |
| 4 | 前端淡出若卡顿靠 700ms 兜底 | 低 | FADE_TIMEOUT_MS |
| 5 | SetForegroundWindow 可能被系统拒绝,未做失败重试 |
低 | focus |
| 6 | 未来若做 macOS / Linux 支持:需 CGWindowListCopyWindowInfo / X11(Wayland 基本不可行) |
低 | #[cfg(target_os] |
七、快速搜索索引
| 想找什么 | 搜这个关键词 |
|---|---|
| 核心模块 | mc_transition |
| 过渡主流程 | pub async fn run(app |
| 八步流程标记 | step 1 ~ step 8 |
| 找 MC 窗口 | find_window、find_cb |
| 全屏判定 | covers_monitor |
| 缩放动画 | animate_resize、ANIM_DURATION_MS |
| 缓动曲线 | ease_in_out |
| 纯色遮罩事件 | mc-transition-cover-show |
| logo 渐显事件 | mc-transition-logo-in |
| logo 渐隐/变红事件 | mc-transition-logo-out |
| 淡出事件 | mc-transition-fadeout |
| 透明度重置事件 | mc-transition-reset-opacity |
| 前端回调命令 | mc_transition_cover_ready、mc_transition_logo_in_done、mc_transition_logo_out_done、mc_transition_fade_done |
| 接入点 | crate::mc_transition::run |
| 透明度重置函数 | resetLauncherOpacity |
| 遮罩 DOM | mc-transition-cover、mcCoverEl |
| 调试日志(待删) | fn dbg(、mc_transition_debug.log |
| 调试日志文件 | %USERPROFILE%\mc_transition_debug.log |
| windows feature | Win32_Graphics_Gdi |
| 数据库冲突 | previously applied but is missing |
文档结束。功能已跑通,上线前记得清理调试代码喵。