Repository navigation
Releases: EasyColorful666/VibrantbitLauncher.Avalonia
Release list
VibrantbitLauncher.Avalonia 1.0.10.9
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
[1.0.10.9] - 2026-10-23
修复
-
「设置 → 主页」的配置不重启程序不会跟着变
SettingsPageViewModel是 DI 单例,InitializeViewModel()只在第一次进入设置页时跑一次。
之后每次进入只刷新日志 + 重新检测 Java,概览里的其余项全部停在首次读到的旧值上:概览项 会在哪里被改 游戏目录 启动页「快捷设置 → 更改游戏目录」、更换玩家 / 实例 Java 路径 启动页「快捷设置 → 选择 Java」 内存(-Xmx/-Xms) 启动页快捷设置、手工编辑 settings.json 下载源摘要 下载中心的源切换 当前主题 启动页快捷设置的明暗切换 于是用户改完这些再切回「设置 → 主页」,看到的还是旧值,必须重启程序才「突然变对」。
现在每次导航进入设置页都会先调
SyncOverviewFromSettings(),从当前配置重新回填概览:SyncOverviewFromSettings(); // 游戏目录 / Java / 内存 / 下载源 / 主题 RefreshLogs(); await LoadJavaAsync();
回填只写字段 +
OnPropertyChanged,不走 setter —— 这些 setter 会把值回写并落盘,
只是「把配置里的现状显示出来」不该产生写入,也不该触发 ApplyTheme / ApplyDownloadSettings。 -
概览里的「Java 路径」可能显示成另一个 JDK
原来直接绑定
SelectedJava.JavaPath,而下拉框在「配置里的路径不在本次检测结果中」时
会退回到列表第一项(手动填的路径、目录被移走、升级后旧目录被删)。
结果概览显示的是别的 JDK,看起来像配置被人改了。新增
JavaPathText:优先取配置值,只有配置为空时才退回选中项。
[1.0.10.8] - 2026-10-23
优化
-
整合包本体改用多线程并发分块下载
「安装整合包」这条路上的下载是手写的一条 HTTP 流复制:单连接、
FileMode.Create
覆盖写、8 KB 缓冲循环读。而同一个整合包里的几十个模组走的却是统一下载内核
(FileDownloader→ 多连接分块 + 续传 + 换源)。结果就是包里的小文件先下完,几百 MB 的本体反而成了整条链路最慢的一环 ——
在 Modrinth 官方 CDN 上单连接常年只有几百 KB/s。现在本体也进内核:
var item = new DownloadItem(info.DownloadUrl, tempFile, DisplayName: info.Name); await FileDownloader.DownloadAsync(item, onProgress: (received, total) => { /* 200ms 节流上报 */ });
拿到的能力:
能力 之前 现在 连接数 1 默认 4 块并行(设置页「单文件分块数」,可到 16) 断点续传 无,断了从头再来 块级续传 换源 无,URL 挂了就是失败 按源健康度降级重试 卡死检测 无,实际靠 300s 总超时 字节级看门狗(首字节 20s / 空闲 25s) 落盘 直接写目标文件 先 .vblpart再改名,中途断网不留半截包速度文案改取全局速度表,与任务中心显示的总速度一致(原来算的是「这一个文件」
的局部速度,和并行下载时的实际表现对不上)。 -
.mrpack纳入 zip 完整性校验整合包是 Modrinth 直链,链接里既没有
size也没有sha1,被截断的包原本
会一路「下载成功」,直到解包阶段才报「整合包格式不正确」—— 用户完全指不到网络上去。
现在和.jar/.zip一样,下载完先读一次 zip 中央目录,几毫秒,残包当场失败。
修复
-
临时目录残留的同名整合包会让新包被跳过
内核的「已存在」判定是:目标文件非空、且调用方没给期望大小 → 认为已经下好了。
整合包正好符合这个条件,于是上一次异常退出留下的旧包会被当成新包直接拿去建实例。
现在下载前先删掉同名的残留文件。
排查记录:单文件版「双击完全没反应」
不是程序坏了,是系统盘写满。
单文件 exe 启动时,.NET host 必须先把内嵌的运行时解压到
%TEMP%\.net\VibrantbitLauncher.Avalonia\<随机串>\,然后才是托管代码的第一行。
这一步失败时进程直接退出:
- WinExe 子系统没有控制台,错误信息没人看得见;
- 托管代码一行都没跑,
startup-trace.log里连BEGIN都不会写。
判断方法:%LOCALAPPDATA%\VibrantbitLauncher\logs\startup-trace.log 中,
非单文件版有 singlefile=False … SHOWN,单文件版连记录都没有 —— 那就是卡在 host 解压阶段。
清掉 %TEMP%\.net 或腾出磁盘空间后立刻恢复正常。
[1.0.10.7] - 2026-10-23
修复
-
模组文件页(
ModPage)补上返回按钮模组 / 资源安装这条链路里,它是唯一的下钻页:
下载中心(DownloadHubPage,左侧分类) └─ 社区资源 → 模组 / 光影 / 整合包(DownloadResourcesPage,右侧页签内容) └─ 点某个项目 → ModPage(选版本、加载器、下装)之前只有「左侧导航跳走」这一条退路,等于丢掉来路。现在标题行改为
Auto,*两列:左侧「返回」按钮,右侧保留原来的标题 + 说明两行。返回优先走
GoBack()(栈里压着的是下载中心宿主,回去时左侧分类与右侧页签
都保持原样);栈为空时退回DownloadHubPage。
说明(为什么另外两个页面没加)
DownloadResourcesPage(模组 / 光影 / 整合包列表):它是下载中心
DownloadHubPage右半边的内容之一,由左侧分类切换,本身不是下钻页。
给它加返回按钮语义上是错的 —— 点了会离开整个下载中心,而不是回到「上一层」。InstallPage(安装版本):1.0.10.6 已经加过返回按钮。
[1.0.10.6] - 2026-10-23
更改
-
GitHub Actions 改为「每次推送到仓库就自动部署」
on.push增加了branches: [master, main],不再只认v*tag。
现在推到默认分支就会跑满 7 个平台并自动创建 / 更新 Release。tag 规则沿用已有逻辑:推分支时 tag 取 csproj 的
<Version>(形如v1.0.10.6),
所以版本号 bump 过才新建 Release,版本号没变则更新同一个 Release
(覆盖同名附件,正文随 CHANGELOG 刷新),不会堆出一串同版号 Release。concurrency同步调整:分支推送 / PR 上的新推送会取消旧运行,避免两次部署
抢同一个 Release;tag 发布仍然绝不取消(掐断会留下半个 Release)。⚠️ 代价是每次推送都跑 7 个平台。纯文档改动想跳过,提交信息里加[skip ci]。 -
下载页搜索改为「点按钮 / 按回车」触发,去掉输入即搜
原来输入框挂了
TextChanged+ 260msDispatcherTimer防抖,边打边筛。
版本清单有 900+ 条,打字过程中列表会被反复重建,既抖又看不清结果;
而版本号本身是短词(1.21、25w45a),一次输完再搜几乎没有成本。- 新增「搜索」按钮(与输入框同为 36 高,上下边严格对齐),点它或按回车才发起搜索。
- 按
Esc清空关键词并恢复当前筛选范围。 - 新增
_appliedKeyword字段:输入框里的文字与正在生效的关键词彻底分开。
否则「打了字但没提交」时切换版本类型筛选,会把半截关键词一起带进查询。
-
搜索匹配精度提高(
Helpers/VersionSearchMatcher.cs)- 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
搜21时1.21.4(638)现在稳定排在1.210(558)之前;改之前两者同档。 - 段对齐优先:搜
1.2时1.2.5(848)排在1.21.4(717)之前 ——
前缀命中但数字段未对齐的降一档,避免1.2被1.21.x淹没。 - 多关键词 AND:空格分隔,全部命中才算命中,分数取平均。
1.21 pre现在只出1.21.4-pre2这类结果,不再因为整串比对而一个都搜不到。 - 连接符归一扩展到段边界识别:
pre-2/pre2、rc-1/rc1、snapshot-10/snapshot10互通。
18 条用例(精确命中、段对齐排序、连接符互通、多词 AND、快照 / 远古版、负例)
全部通过。 - 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
修复
-
首次使用向导:标题栏只显示「首次使用向导」
原先把「Vibrantbit Launcher」放在
Title、「首次使用向导」放在Subtitle,
模板会把两者上下叠印。现只保留Title="首次使用向导"。 -
首次使用向导:登录账户板块居中
外层容器原本是
MaxWidth="660" HorizontalAlignment="Left",宽度被最宽的
按钮行(约 330)撑出来,于是图标和文案在那 330 里居中、按钮却从最左边起排,
整块贴在窗口左侧、右边留出一大片空白。
改为外层Stretch(列表仍MaxWidth=660靠左),空状态与登录按钮各自居中。
有账户后按钮自动拉回左对齐,与列表共用左边界。 -
登录账户三个按钮的高度与下拉框不统一
Padding="20,10"把按钮顶到 40 高,而 ComboBox / TextBox 是 32。
改为MinHeight="32" Padding="18,0",与「快速设置」页贴在下拉框右侧的按钮一致。 -
模组下载页全部报「无法访问 Modrinth」(剪裁版专有,1.0.10.2 起潜伏)
真实异常不是网络问题,而是:
InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.PublishTrimmed=true时 SDK 会把功能开关
System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault置为 false,
于是任何没有TypeInfoResolver的JsonSerializer.Serialize/Deserialize
在运行期直接抛。本项目所有 JSON 都走反射(SettingsService、SkinService、
MojangNewsHelper、ModrinthSearchService……),所以剪裁版一开局域网请求就炸;
同一个 exe 的非剪裁版完全正常 —— 这也是当初「标准档一切正常」的原因。已在 csproj 的剪裁段显式写
<JsonSerializerIsReflectionEnabledByDefault>true</…>。
实测发布的runtimeconfig.json里已出现该开关,代价约 1MB。
类型元数据能否存活仍由TrimmerRootAssembly负责,本项目程序集已整份 root。⚠️ 顺带给所有使用剪裁档的人一条判据:日志里出现
Reflection-based serialization has been disabled就是这个问题,与代理无关。 -
通知重影(两条提示文字叠在一起)
SnackbarHost原来是Panel,而 Panel 会把所有子元素摆在同一位置 ——
两条通知同时在场时必然重影(用户截图里「提示」和正文都被画了两遍)。
已改为StackPanel,真正纵向堆叠。 -
通知没有入场 / 出场动画
- 入场:淡入 + 从右侧 28px 滑入,190ms。
- 出场:淡出 + 向右 28px 滑走,150ms,动画结束才从宿主移除。
- 同一条提示重复到达时不再叠一条,只把已有那条的倒计时重新拉满
(否则连续两次启动失败会排四条一模一样的通知)。 - 点 × 立即移除,不播出场动画 —— 用户点关闭就是要它马上消失。
- 性能模式(= 减动效)下全部跳过动画,直接出现 / 消失。
-
安装页缺少返回按钮
标题行改为
Auto,*,Auto三列,左侧新增「返回」按钮:优先走导航栈
GoBack(),栈为空时退回「原版游戏」下载页,保证任何进入路径下都有反应。
[1.0.10.5] - 2026-10-23
修复
-
GitHub Actions 上 Windows 三个架构全部发布失败(
Cannot overwrite variable IsLinux because it is read-only or constant.)publish-all.ps1里原本写了:$isWin = $rid.StartsWith('win-') $isOsx = $rid.StartsWith('osx-') $isLinux = $rid.StartsWith('linux-') # ← 这里炸
$IsLinux/$IsWindows/$IsMacOS是 PowerShell 的只读自动变量,而且
PowerShell 变量名不区分大小写,所以$isLinux与$IsLinux是同一个变量,
赋值直接抛PSSecurityException级别的错误并终止脚本。为什么本地一直没复现:本机是 Windows PowerShell 5.1,那三个变量是 PowerShell
6.0(Core) 才引入的,5.1 里根本不存在,赋值只是新建普通变量;
而 GitHub Actions 的shell: pwsh是 7.x,变量存在且只读 —— 必炸。三个 Windows RID 报同一个错,是因为报错发生在
foreach ($rawRid in $Rids)的
第一轮循环里,直接终止整个脚本;Linux / macOS 走的是.sh,没有这段代码。已把三个变量改名为
$ridWin/$ridOsx/$ridLinux,并加注释说明原因。
同时对全脚本做了「所有被赋值变量 × 只读自动变量」的全量比对(56 个赋值变量,
含带类型赋值与参数名),确认没有第二处同类冲突。⚠️ 与代码无关但同样关键:仓库里那份publish-all.ps1仍是 642 行的旧版,
本次修复在本地工作区(831 行),必须提交并推送后 CI 才会生效。 -
workflow 文件名统一为
build.yml早期本地新建了
release.yml并打算删除build.yml;同时远端也在build.yml
上做了一次重写(内容与release.yml逐字节相同,只是文件名不同)。
两边一旦合并就会出现「删一个、加一个」的假冲突。现统一沿用远端已在跑的
build.yml,本地不再保留release.yml,避免同一份配置存在两个路径。 -
workflow 的 PR 触发分支修正为
[master, main]仓库默认分支是
master,原配置只写main,PR 上的构建校验永远不会触发。
两个都列上,将来改名也不会静默失效。
[1.0.10.4] - 2026-10-22
新增
-
GitHub Actions 自动发布
.github/workflows/build.yml:7 个平台并行构建并汇总到 GitHub Release。RID Runner 脚本 win-x64 / win-x86 / win-arm64 windows-latest publish-all.ps1linux-x64 / linux-arm64 ubuntu-latest publish-all.shosx-x64 / osx-arm64 macos-latest publish-all.sh- Release 正文 =
CHANGELOG.md原文(body_path),不做二次加工。 - 触发:推送
v*tag 或手动运行(手动时 tag 取 csproj 的<Version>);
PR 到 main 只构建不发布,保留 CI 校验又不污染 Release。 - 校验和由汇总 job 重新算一份合并的
SHA256SUMS.txt:
每个矩阵 job 各自只算得出「自己那一个包」,直接合并会互相覆盖。 fail-fast: false:单个平台失败不影响其余 6 个继续构建,汇总 job 整体失败,
不发残缺的 Release。
- Release 正文 =
变更
-
publish-all.sh补上程序集剪裁开关,与publish-all.ps1对齐(默认开启)。此前两条路径是漂移的:ps1 自 1.0.10.2 起默认剪裁(29MB),sh 脚本没有对应开关,
CI 一旦走 sh 就会产出 52MB 的标准档。现在 sh 也默认-p:EnableAppTrimming=true,
并提供--no-app-trim逃生阀(等价 ps1 的-NoAppTrim)。
实测 sh 路径 win-x64 产出 29.0MB,与本机 ps1 结果一致。
移除
- 旧版
.github/workflows/build.yml的发布逻辑整体重写。它原来用--generate-notes,
且 SELF_CONTAINED 固定为 false(1.0.9.6 起已改默认自包含),
与新的发布需求冲突。
现沿用build.yml这个文件名(远端已在使用),内容替换为重写后的版本,
保留其 PR 构建校验能力。
[1.0.10.3] - 2026-10-21
新增
-
下载页版本类型筛选(多选框):正式版 / 测试版 / 远古版 / 愚人节版四个多选框,
可任意叠加。勾选才显示,不勾选整类隐藏;默认只勾「正式版」。
每个多选框上带该类版本的总数(实测清单:正式版 103 / 测试版 748 / 远古版 61 / 愚人节版 8,合计 920)。 -
版本分类器
Models/McVersionKindClassifier:清单type→ 四大类的唯一判定点。必需按 id 认愚人节版的原因:Mojang 清单里没有「愚人节」这个 type,
15w14a、3D Shareware v1.34、24w14potato等 8 个版本一律标成snapshot。
不单独认的话它们会混进「测试版」。名单忽略大小写(清单里实际 id 是
22w13oneblockatatime,与官方公告的大小写不同)。
界面
- 四个多选框做成胶囊(高 30 / 圆角 99 / 12px 文字),沿用启动页主题切换的视觉语言,
但保留多选语义(分段控件是四选一,这里需要叠加)。 - 状态齐全:hover / pressed / checked / checked:hover / checked:pressed / focus-visible / disabled;
过渡 150ms +CubicEaseOut(反馈档,比页面转场 250ms 快一档)。 - 每个胶囊带 6px 类型色圆点(主色 / 橙 / 灰 / 紫),选中态一律转白,不与主色抢注意力。
- 列表行内给非正式版补类型角标(测试版 / 远古版 / 愚人节),正式版不标(常态是噪音)。
修正
- 搜索计数分母改为「当前勾选范围内」的数量:只勾正式版再搜「1.20」显示
`匹...
VibrantbitLauncher.Avalonia 1.0.10.8
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
[1.0.10.8] - 2026-10-23
优化
-
整合包本体改用多线程并发分块下载
「安装整合包」这条路上的下载是手写的一条 HTTP 流复制:单连接、
FileMode.Create
覆盖写、8 KB 缓冲循环读。而同一个整合包里的几十个模组走的却是统一下载内核
(FileDownloader→ 多连接分块 + 续传 + 换源)。结果就是包里的小文件先下完,几百 MB 的本体反而成了整条链路最慢的一环 ——
在 Modrinth 官方 CDN 上单连接常年只有几百 KB/s。现在本体也进内核:
var item = new DownloadItem(info.DownloadUrl, tempFile, DisplayName: info.Name); await FileDownloader.DownloadAsync(item, onProgress: (received, total) => { /* 200ms 节流上报 */ });
拿到的能力:
能力 之前 现在 连接数 1 默认 4 块并行(设置页「单文件分块数」,可到 16) 断点续传 无,断了从头再来 块级续传 换源 无,URL 挂了就是失败 按源健康度降级重试 卡死检测 无,实际靠 300s 总超时 字节级看门狗(首字节 20s / 空闲 25s) 落盘 直接写目标文件 先 .vblpart再改名,中途断网不留半截包速度文案改取全局速度表,与任务中心显示的总速度一致(原来算的是「这一个文件」
的局部速度,和并行下载时的实际表现对不上)。 -
.mrpack纳入 zip 完整性校验整合包是 Modrinth 直链,链接里既没有
size也没有sha1,被截断的包原本
会一路「下载成功」,直到解包阶段才报「整合包格式不正确」—— 用户完全指不到网络上去。
现在和.jar/.zip一样,下载完先读一次 zip 中央目录,几毫秒,残包当场失败。
修复
-
临时目录残留的同名整合包会让新包被跳过
内核的「已存在」判定是:目标文件非空、且调用方没给期望大小 → 认为已经下好了。
整合包正好符合这个条件,于是上一次异常退出留下的旧包会被当成新包直接拿去建实例。
现在下载前先删掉同名的残留文件。
[1.0.10.7] - 2026-10-23
修复
-
模组文件页(
ModPage)补上返回按钮模组 / 资源安装这条链路里,它是唯一的下钻页:
下载中心(DownloadHubPage,左侧分类) └─ 社区资源 → 模组 / 光影 / 整合包(DownloadResourcesPage,右侧页签内容) └─ 点某个项目 → ModPage(选版本、加载器、下装)之前只有「左侧导航跳走」这一条退路,等于丢掉来路。现在标题行改为
Auto,*两列:左侧「返回」按钮,右侧保留原来的标题 + 说明两行。返回优先走
GoBack()(栈里压着的是下载中心宿主,回去时左侧分类与右侧页签
都保持原样);栈为空时退回DownloadHubPage。
说明(为什么另外两个页面没加)
DownloadResourcesPage(模组 / 光影 / 整合包列表):它是下载中心
DownloadHubPage右半边的内容之一,由左侧分类切换,本身不是下钻页。
给它加返回按钮语义上是错的 —— 点了会离开整个下载中心,而不是回到「上一层」。InstallPage(安装版本):1.0.10.6 已经加过返回按钮。
[1.0.10.6] - 2026-10-23
更改
-
GitHub Actions 改为「每次推送到仓库就自动部署」
on.push增加了branches: [master, main],不再只认v*tag。
现在推到默认分支就会跑满 7 个平台并自动创建 / 更新 Release。tag 规则沿用已有逻辑:推分支时 tag 取 csproj 的
<Version>(形如v1.0.10.6),
所以版本号 bump 过才新建 Release,版本号没变则更新同一个 Release
(覆盖同名附件,正文随 CHANGELOG 刷新),不会堆出一串同版号 Release。concurrency同步调整:分支推送 / PR 上的新推送会取消旧运行,避免两次部署
抢同一个 Release;tag 发布仍然绝不取消(掐断会留下半个 Release)。⚠️ 代价是每次推送都跑 7 个平台。纯文档改动想跳过,提交信息里加[skip ci]。 -
下载页搜索改为「点按钮 / 按回车」触发,去掉输入即搜
原来输入框挂了
TextChanged+ 260msDispatcherTimer防抖,边打边筛。
版本清单有 900+ 条,打字过程中列表会被反复重建,既抖又看不清结果;
而版本号本身是短词(1.21、25w45a),一次输完再搜几乎没有成本。- 新增「搜索」按钮(与输入框同为 36 高,上下边严格对齐),点它或按回车才发起搜索。
- 按
Esc清空关键词并恢复当前筛选范围。 - 新增
_appliedKeyword字段:输入框里的文字与正在生效的关键词彻底分开。
否则「打了字但没提交」时切换版本类型筛选,会把半截关键词一起带进查询。
-
搜索匹配精度提高(
Helpers/VersionSearchMatcher.cs)- 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
搜21时1.21.4(638)现在稳定排在1.210(558)之前;改之前两者同档。 - 段对齐优先:搜
1.2时1.2.5(848)排在1.21.4(717)之前 ——
前缀命中但数字段未对齐的降一档,避免1.2被1.21.x淹没。 - 多关键词 AND:空格分隔,全部命中才算命中,分数取平均。
1.21 pre现在只出1.21.4-pre2这类结果,不再因为整串比对而一个都搜不到。 - 连接符归一扩展到段边界识别:
pre-2/pre2、rc-1/rc1、snapshot-10/snapshot10互通。
18 条用例(精确命中、段对齐排序、连接符互通、多词 AND、快照 / 远古版、负例)
全部通过。 - 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
修复
-
首次使用向导:标题栏只显示「首次使用向导」
原先把「Vibrantbit Launcher」放在
Title、「首次使用向导」放在Subtitle,
模板会把两者上下叠印。现只保留Title="首次使用向导"。 -
首次使用向导:登录账户板块居中
外层容器原本是
MaxWidth="660" HorizontalAlignment="Left",宽度被最宽的
按钮行(约 330)撑出来,于是图标和文案在那 330 里居中、按钮却从最左边起排,
整块贴在窗口左侧、右边留出一大片空白。
改为外层Stretch(列表仍MaxWidth=660靠左),空状态与登录按钮各自居中。
有账户后按钮自动拉回左对齐,与列表共用左边界。 -
登录账户三个按钮的高度与下拉框不统一
Padding="20,10"把按钮顶到 40 高,而 ComboBox / TextBox 是 32。
改为MinHeight="32" Padding="18,0",与「快速设置」页贴在下拉框右侧的按钮一致。 -
模组下载页全部报「无法访问 Modrinth」(剪裁版专有,1.0.10.2 起潜伏)
真实异常不是网络问题,而是:
InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.PublishTrimmed=true时 SDK 会把功能开关
System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault置为 false,
于是任何没有TypeInfoResolver的JsonSerializer.Serialize/Deserialize
在运行期直接抛。本项目所有 JSON 都走反射(SettingsService、SkinService、
MojangNewsHelper、ModrinthSearchService……),所以剪裁版一开局域网请求就炸;
同一个 exe 的非剪裁版完全正常 —— 这也是当初「标准档一切正常」的原因。已在 csproj 的剪裁段显式写
<JsonSerializerIsReflectionEnabledByDefault>true</…>。
实测发布的runtimeconfig.json里已出现该开关,代价约 1MB。
类型元数据能否存活仍由TrimmerRootAssembly负责,本项目程序集已整份 root。⚠️ 顺带给所有使用剪裁档的人一条判据:日志里出现
Reflection-based serialization has been disabled就是这个问题,与代理无关。 -
通知重影(两条提示文字叠在一起)
SnackbarHost原来是Panel,而 Panel 会把所有子元素摆在同一位置 ——
两条通知同时在场时必然重影(用户截图里「提示」和正文都被画了两遍)。
已改为StackPanel,真正纵向堆叠。 -
通知没有入场 / 出场动画
- 入场:淡入 + 从右侧 28px 滑入,190ms。
- 出场:淡出 + 向右 28px 滑走,150ms,动画结束才从宿主移除。
- 同一条提示重复到达时不再叠一条,只把已有那条的倒计时重新拉满
(否则连续两次启动失败会排四条一模一样的通知)。 - 点 × 立即移除,不播出场动画 —— 用户点关闭就是要它马上消失。
- 性能模式(= 减动效)下全部跳过动画,直接出现 / 消失。
-
安装页缺少返回按钮
标题行改为
Auto,*,Auto三列,左侧新增「返回」按钮:优先走导航栈
GoBack(),栈为空时退回「原版游戏」下载页,保证任何进入路径下都有反应。
[1.0.10.5] - 2026-10-23
修复
-
GitHub Actions 上 Windows 三个架构全部发布失败(
Cannot overwrite variable IsLinux because it is read-only or constant.)publish-all.ps1里原本写了:$isWin = $rid.StartsWith('win-') $isOsx = $rid.StartsWith('osx-') $isLinux = $rid.StartsWith('linux-') # ← 这里炸
$IsLinux/$IsWindows/$IsMacOS是 PowerShell 的只读自动变量,而且
PowerShell 变量名不区分大小写,所以$isLinux与$IsLinux是同一个变量,
赋值直接抛PSSecurityException级别的错误并终止脚本。为什么本地一直没复现:本机是 Windows PowerShell 5.1,那三个变量是 PowerShell
6.0(Core) 才引入的,5.1 里根本不存在,赋值只是新建普通变量;
而 GitHub Actions 的shell: pwsh是 7.x,变量存在且只读 —— 必炸。三个 Windows RID 报同一个错,是因为报错发生在
foreach ($rawRid in $Rids)的
第一轮循环里,直接终止整个脚本;Linux / macOS 走的是.sh,没有这段代码。已把三个变量改名为
$ridWin/$ridOsx/$ridLinux,并加注释说明原因。
同时对全脚本做了「所有被赋值变量 × 只读自动变量」的全量比对(56 个赋值变量,
含带类型赋值与参数名),确认没有第二处同类冲突。⚠️ 与代码无关但同样关键:仓库里那份publish-all.ps1仍是 642 行的旧版,
本次修复在本地工作区(831 行),必须提交并推送后 CI 才会生效。 -
workflow 文件名统一为
build.yml早期本地新建了
release.yml并打算删除build.yml;同时远端也在build.yml
上做了一次重写(内容与release.yml逐字节相同,只是文件名不同)。
两边一旦合并就会出现「删一个、加一个」的假冲突。现统一沿用远端已在跑的
build.yml,本地不再保留release.yml,避免同一份配置存在两个路径。 -
workflow 的 PR 触发分支修正为
[master, main]仓库默认分支是
master,原配置只写main,PR 上的构建校验永远不会触发。
两个都列上,将来改名也不会静默失效。
[1.0.10.4] - 2026-10-22
新增
-
GitHub Actions 自动发布
.github/workflows/build.yml:7 个平台并行构建并汇总到 GitHub Release。RID Runner 脚本 win-x64 / win-x86 / win-arm64 windows-latest publish-all.ps1linux-x64 / linux-arm64 ubuntu-latest publish-all.shosx-x64 / osx-arm64 macos-latest publish-all.sh- Release 正文 =
CHANGELOG.md原文(body_path),不做二次加工。 - 触发:推送
v*tag 或手动运行(手动时 tag 取 csproj 的<Version>);
PR 到 main 只构建不发布,保留 CI 校验又不污染 Release。 - 校验和由汇总 job 重新算一份合并的
SHA256SUMS.txt:
每个矩阵 job 各自只算得出「自己那一个包」,直接合并会互相覆盖。 fail-fast: false:单个平台失败不影响其余 6 个继续构建,汇总 job 整体失败,
不发残缺的 Release。
- Release 正文 =
变更
-
publish-all.sh补上程序集剪裁开关,与publish-all.ps1对齐(默认开启)。此前两条路径是漂移的:ps1 自 1.0.10.2 起默认剪裁(29MB),sh 脚本没有对应开关,
CI 一旦走 sh 就会产出 52MB 的标准档。现在 sh 也默认-p:EnableAppTrimming=true,
并提供--no-app-trim逃生阀(等价 ps1 的-NoAppTrim)。
实测 sh 路径 win-x64 产出 29.0MB,与本机 ps1 结果一致。
移除
- 旧版
.github/workflows/build.yml的发布逻辑整体重写。它原来用--generate-notes,
且 SELF_CONTAINED 固定为 false(1.0.9.6 起已改默认自包含),
与新的发布需求冲突。
现沿用build.yml这个文件名(远端已在使用),内容替换为重写后的版本,
保留其 PR 构建校验能力。
[1.0.10.3] - 2026-10-21
新增
-
下载页版本类型筛选(多选框):正式版 / 测试版 / 远古版 / 愚人节版四个多选框,
可任意叠加。勾选才显示,不勾选整类隐藏;默认只勾「正式版」。
每个多选框上带该类版本的总数(实测清单:正式版 103 / 测试版 748 / 远古版 61 / 愚人节版 8,合计 920)。 -
版本分类器
Models/McVersionKindClassifier:清单type→ 四大类的唯一判定点。必需按 id 认愚人节版的原因:Mojang 清单里没有「愚人节」这个 type,
15w14a、3D Shareware v1.34、24w14potato等 8 个版本一律标成snapshot。
不单独认的话它们会混进「测试版」。名单忽略大小写(清单里实际 id 是
22w13oneblockatatime,与官方公告的大小写不同)。
界面
- 四个多选框做成胶囊(高 30 / 圆角 99 / 12px 文字),沿用启动页主题切换的视觉语言,
但保留多选语义(分段控件是四选一,这里需要叠加)。 - 状态齐全:hover / pressed / checked / checked:hover / checked:pressed / focus-visible / disabled;
过渡 150ms +CubicEaseOut(反馈档,比页面转场 250ms 快一档)。 - 每个胶囊带 6px 类型色圆点(主色 / 橙 / 灰 / 紫),选中态一律转白,不与主色抢注意力。
- 列表行内给非正式版补类型角标(测试版 / 远古版 / 愚人节),正式版不标(常态是噪音)。
修正
- 搜索计数分母改为「当前勾选范围内」的数量:只勾正式版再搜「1.20」显示
匹配 3 / 103 个版本,不再显示容易误读的匹配 3 / 共 920。 - 列表尚未加载完成时不再短暂显示「当前筛选条件下没有版本」的空状态。
[1.0.10.2] - 2026-10-21
变更
-
单文件体积 52.29 MB → 29.04 MB(-44%),win-x64 自包含单文件。
自包含发布默认把整个
Microsoft.NETCore.App运行时包原样复制进来
(System.Private.Xml7.4MB、System.Linq.Expressions3.5MB、
System.Data.Common2.6MB、System.Private.DataContractSerialization2.0MB ……),
其中大半代码本应用一行都不走。改用PublishTrimmed+TrimMode=partial
按调用图裁剪,只保留真正走到的部分。 -
发布目录剔除调试器 / 转储分析组件(约 6.3MB 未压缩):
mscordaccore.dll、mscordaccore_amd64_*.dll、mscordbi.dll、
Microsoft.DiaSymReader.Native.amd64.dll。它们只在 VS「附加到进程」或
dotnet-dump/ 蓝屏转储分析时加载,应用运行不碰。
实测<DebuggerSupport>false</DebuggerSupport>对自包含载荷无效
(同名 dll 一个不少、体积零变化),只能在TrimPublishPayload里删。
副作用:发布出的 exe 不能再被附加调试、不能抓托管转储;
dotnet build的产物不受影响,日常调试照旧。 -
新增
Properties/PublishProfiles/WindowsTrimmed.pubxml(剪裁档,29MB)。
与Windows.pubxml(标准档,52MB)并存,出问题可一键回退。 -
publish-all.ps1/publish-all.sh默认走剪裁档,新增-NoAppTrim
回退到标准档。注意与既有的-NoTrim(关平台后端剥离)不是一回事。
修复
-
剪裁档一启动就
XamlLoadException: No precompiled XAML found for VibrantbitLauncher.App(28.03MB 那版)。根因不是「Avalonia 不兼容剪裁」,而是同一份剪裁配置被写了两处:
pubxml 与 csproj 各写一遍PublishTrimmed/TrimMode/TrimmerRootAssembly,
叠加后 ILLink 重写程序集时丢掉了 Avalonia 的!AvaloniaResources索引。排查过程(留档):
形态 体积 结果 目录 + 剪裁...
VibrantbitLauncher.Avalonia 1.0.10.6
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
[1.0.10.6] - 2026-10-23
更改
-
GitHub Actions 改为「每次推送到仓库就自动部署」
on.push增加了branches: [master, main],不再只认v*tag。
现在推到默认分支就会跑满 7 个平台并自动创建 / 更新 Release。tag 规则沿用已有逻辑:推分支时 tag 取 csproj 的
<Version>(形如v1.0.10.6),
所以版本号 bump 过才新建 Release,版本号没变则更新同一个 Release
(覆盖同名附件,正文随 CHANGELOG 刷新),不会堆出一串同版号 Release。concurrency同步调整:分支推送 / PR 上的新推送会取消旧运行,避免两次部署
抢同一个 Release;tag 发布仍然绝不取消(掐断会留下半个 Release)。⚠️ 代价是每次推送都跑 7 个平台。纯文档改动想跳过,提交信息里加[skip ci]。 -
下载页搜索改为「点按钮 / 按回车」触发,去掉输入即搜
原来输入框挂了
TextChanged+ 260msDispatcherTimer防抖,边打边筛。
版本清单有 900+ 条,打字过程中列表会被反复重建,既抖又看不清结果;
而版本号本身是短词(1.21、25w45a),一次输完再搜几乎没有成本。- 新增「搜索」按钮(与输入框同为 36 高,上下边严格对齐),点它或按回车才发起搜索。
- 按
Esc清空关键词并恢复当前筛选范围。 - 新增
_appliedKeyword字段:输入框里的文字与正在生效的关键词彻底分开。
否则「打了字但没提交」时切换版本类型筛选,会把半截关键词一起带进查询。
-
搜索匹配精度提高(
Helpers/VersionSearchMatcher.cs)- 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
搜21时1.21.4(638)现在稳定排在1.210(558)之前;改之前两者同档。 - 段对齐优先:搜
1.2时1.2.5(848)排在1.21.4(717)之前 ——
前缀命中但数字段未对齐的降一档,避免1.2被1.21.x淹没。 - 多关键词 AND:空格分隔,全部命中才算命中,分数取平均。
1.21 pre现在只出1.21.4-pre2这类结果,不再因为整串比对而一个都搜不到。 - 连接符归一扩展到段边界识别:
pre-2/pre2、rc-1/rc1、snapshot-10/snapshot10互通。
18 条用例(精确命中、段对齐排序、连接符互通、多词 AND、快照 / 远古版、负例)
全部通过。 - 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
修复
-
首次使用向导:标题栏只显示「首次使用向导」
原先把「Vibrantbit Launcher」放在
Title、「首次使用向导」放在Subtitle,
模板会把两者上下叠印。现只保留Title="首次使用向导"。 -
首次使用向导:登录账户板块居中
外层容器原本是
MaxWidth="660" HorizontalAlignment="Left",宽度被最宽的
按钮行(约 330)撑出来,于是图标和文案在那 330 里居中、按钮却从最左边起排,
整块贴在窗口左侧、右边留出一大片空白。
改为外层Stretch(列表仍MaxWidth=660靠左),空状态与登录按钮各自居中。
有账户后按钮自动拉回左对齐,与列表共用左边界。 -
登录账户三个按钮的高度与下拉框不统一
Padding="20,10"把按钮顶到 40 高,而 ComboBox / TextBox 是 32。
改为MinHeight="32" Padding="18,0",与「快速设置」页贴在下拉框右侧的按钮一致。 -
模组下载页全部报「无法访问 Modrinth」(剪裁版专有,1.0.10.2 起潜伏)
真实异常不是网络问题,而是:
InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.PublishTrimmed=true时 SDK 会把功能开关
System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault置为 false,
于是任何没有TypeInfoResolver的JsonSerializer.Serialize/Deserialize
在运行期直接抛。本项目所有 JSON 都走反射(SettingsService、SkinService、
MojangNewsHelper、ModrinthSearchService……),所以剪裁版一开局域网请求就炸;
同一个 exe 的非剪裁版完全正常 —— 这也是当初「标准档一切正常」的原因。已在 csproj 的剪裁段显式写
<JsonSerializerIsReflectionEnabledByDefault>true</…>。
实测发布的runtimeconfig.json里已出现该开关,代价约 1MB。
类型元数据能否存活仍由TrimmerRootAssembly负责,本项目程序集已整份 root。⚠️ 顺带给所有使用剪裁档的人一条判据:日志里出现
Reflection-based serialization has been disabled就是这个问题,与代理无关。 -
通知重影(两条提示文字叠在一起)
SnackbarHost原来是Panel,而 Panel 会把所有子元素摆在同一位置 ——
两条通知同时在场时必然重影(用户截图里「提示」和正文都被画了两遍)。
已改为StackPanel,真正纵向堆叠。 -
通知没有入场 / 出场动画
- 入场:淡入 + 从右侧 28px 滑入,190ms。
- 出场:淡出 + 向右 28px 滑走,150ms,动画结束才从宿主移除。
- 同一条提示重复到达时不再叠一条,只把已有那条的倒计时重新拉满
(否则连续两次启动失败会排四条一模一样的通知)。 - 点 × 立即移除,不播出场动画 —— 用户点关闭就是要它马上消失。
- 性能模式(= 减动效)下全部跳过动画,直接出现 / 消失。
-
安装页缺少返回按钮
标题行改为
Auto,*,Auto三列,左侧新增「返回」按钮:优先走导航栈
GoBack(),栈为空时退回「原版游戏」下载页,保证任何进入路径下都有反应。
[1.0.10.5] - 2026-10-23
修复
-
GitHub Actions 上 Windows 三个架构全部发布失败(
Cannot overwrite variable IsLinux because it is read-only or constant.)publish-all.ps1里原本写了:$isWin = $rid.StartsWith('win-') $isOsx = $rid.StartsWith('osx-') $isLinux = $rid.StartsWith('linux-') # ← 这里炸
$IsLinux/$IsWindows/$IsMacOS是 PowerShell 的只读自动变量,而且
PowerShell 变量名不区分大小写,所以$isLinux与$IsLinux是同一个变量,
赋值直接抛PSSecurityException级别的错误并终止脚本。为什么本地一直没复现:本机是 Windows PowerShell 5.1,那三个变量是 PowerShell
6.0(Core) 才引入的,5.1 里根本不存在,赋值只是新建普通变量;
而 GitHub Actions 的shell: pwsh是 7.x,变量存在且只读 —— 必炸。三个 Windows RID 报同一个错,是因为报错发生在
foreach ($rawRid in $Rids)的
第一轮循环里,直接终止整个脚本;Linux / macOS 走的是.sh,没有这段代码。已把三个变量改名为
$ridWin/$ridOsx/$ridLinux,并加注释说明原因。
同时对全脚本做了「所有被赋值变量 × 只读自动变量」的全量比对(56 个赋值变量,
含带类型赋值与参数名),确认没有第二处同类冲突。⚠️ 与代码无关但同样关键:仓库里那份publish-all.ps1仍是 642 行的旧版,
本次修复在本地工作区(831 行),必须提交并推送后 CI 才会生效。 -
workflow 文件名统一为
build.yml早期本地新建了
release.yml并打算删除build.yml;同时远端也在build.yml
上做了一次重写(内容与release.yml逐字节相同,只是文件名不同)。
两边一旦合并就会出现「删一个、加一个」的假冲突。现统一沿用远端已在跑的
build.yml,本地不再保留release.yml,避免同一份配置存在两个路径。 -
workflow 的 PR 触发分支修正为
[master, main]仓库默认分支是
master,原配置只写main,PR 上的构建校验永远不会触发。
两个都列上,将来改名也不会静默失效。
[1.0.10.4] - 2026-10-22
新增
-
GitHub Actions 自动发布
.github/workflows/build.yml:7 个平台并行构建并汇总到 GitHub Release。RID Runner 脚本 win-x64 / win-x86 / win-arm64 windows-latest publish-all.ps1linux-x64 / linux-arm64 ubuntu-latest publish-all.shosx-x64 / osx-arm64 macos-latest publish-all.sh- Release 正文 =
CHANGELOG.md原文(body_path),不做二次加工。 - 触发:推送
v*tag 或手动运行(手动时 tag 取 csproj 的<Version>);
PR 到 main 只构建不发布,保留 CI 校验又不污染 Release。 - 校验和由汇总 job 重新算一份合并的
SHA256SUMS.txt:
每个矩阵 job 各自只算得出「自己那一个包」,直接合并会互相覆盖。 fail-fast: false:单个平台失败不影响其余 6 个继续构建,汇总 job 整体失败,
不发残缺的 Release。
- Release 正文 =
变更
-
publish-all.sh补上程序集剪裁开关,与publish-all.ps1对齐(默认开启)。此前两条路径是漂移的:ps1 自 1.0.10.2 起默认剪裁(29MB),sh 脚本没有对应开关,
CI 一旦走 sh 就会产出 52MB 的标准档。现在 sh 也默认-p:EnableAppTrimming=true,
并提供--no-app-trim逃生阀(等价 ps1 的-NoAppTrim)。
实测 sh 路径 win-x64 产出 29.0MB,与本机 ps1 结果一致。
移除
- 旧版
.github/workflows/build.yml的发布逻辑整体重写。它原来用--generate-notes,
且 SELF_CONTAINED 固定为 false(1.0.9.6 起已改默认自包含),
与新的发布需求冲突。
现沿用build.yml这个文件名(远端已在使用),内容替换为重写后的版本,
保留其 PR 构建校验能力。
[1.0.10.3] - 2026-10-21
新增
-
下载页版本类型筛选(多选框):正式版 / 测试版 / 远古版 / 愚人节版四个多选框,
可任意叠加。勾选才显示,不勾选整类隐藏;默认只勾「正式版」。
每个多选框上带该类版本的总数(实测清单:正式版 103 / 测试版 748 / 远古版 61 / 愚人节版 8,合计 920)。 -
版本分类器
Models/McVersionKindClassifier:清单type→ 四大类的唯一判定点。必需按 id 认愚人节版的原因:Mojang 清单里没有「愚人节」这个 type,
15w14a、3D Shareware v1.34、24w14potato等 8 个版本一律标成snapshot。
不单独认的话它们会混进「测试版」。名单忽略大小写(清单里实际 id 是
22w13oneblockatatime,与官方公告的大小写不同)。
界面
- 四个多选框做成胶囊(高 30 / 圆角 99 / 12px 文字),沿用启动页主题切换的视觉语言,
但保留多选语义(分段控件是四选一,这里需要叠加)。 - 状态齐全:hover / pressed / checked / checked:hover / checked:pressed / focus-visible / disabled;
过渡 150ms +CubicEaseOut(反馈档,比页面转场 250ms 快一档)。 - 每个胶囊带 6px 类型色圆点(主色 / 橙 / 灰 / 紫),选中态一律转白,不与主色抢注意力。
- 列表行内给非正式版补类型角标(测试版 / 远古版 / 愚人节),正式版不标(常态是噪音)。
修正
- 搜索计数分母改为「当前勾选范围内」的数量:只勾正式版再搜「1.20」显示
匹配 3 / 103 个版本,不再显示容易误读的匹配 3 / 共 920。 - 列表尚未加载完成时不再短暂显示「当前筛选条件下没有版本」的空状态。
[1.0.10.2] - 2026-10-21
变更
-
单文件体积 52.29 MB → 29.04 MB(-44%),win-x64 自包含单文件。
自包含发布默认把整个
Microsoft.NETCore.App运行时包原样复制进来
(System.Private.Xml7.4MB、System.Linq.Expressions3.5MB、
System.Data.Common2.6MB、System.Private.DataContractSerialization2.0MB ……),
其中大半代码本应用一行都不走。改用PublishTrimmed+TrimMode=partial
按调用图裁剪,只保留真正走到的部分。 -
发布目录剔除调试器 / 转储分析组件(约 6.3MB 未压缩):
mscordaccore.dll、mscordaccore_amd64_*.dll、mscordbi.dll、
Microsoft.DiaSymReader.Native.amd64.dll。它们只在 VS「附加到进程」或
dotnet-dump/ 蓝屏转储分析时加载,应用运行不碰。
实测<DebuggerSupport>false</DebuggerSupport>对自包含载荷无效
(同名 dll 一个不少、体积零变化),只能在TrimPublishPayload里删。
副作用:发布出的 exe 不能再被附加调试、不能抓托管转储;
dotnet build的产物不受影响,日常调试照旧。 -
新增
Properties/PublishProfiles/WindowsTrimmed.pubxml(剪裁档,29MB)。
与Windows.pubxml(标准档,52MB)并存,出问题可一键回退。 -
publish-all.ps1/publish-all.sh默认走剪裁档,新增-NoAppTrim
回退到标准档。注意与既有的-NoTrim(关平台后端剥离)不是一回事。
修复
-
剪裁档一启动就
XamlLoadException: No precompiled XAML found for VibrantbitLauncher.App(28.03MB 那版)。根因不是「Avalonia 不兼容剪裁」,而是同一份剪裁配置被写了两处:
pubxml 与 csproj 各写一遍PublishTrimmed/TrimMode/TrimmerRootAssembly,
叠加后 ILLink 重写程序集时丢掉了 Avalonia 的!AvaloniaResources索引。排查过程(留档):
形态 体积 结果 目录 + 剪裁 55.80 MB ✅ 剪裁本身没问题 单文件 + 剪裁 + 不压缩 55.10 MB ✅ 单文件打包也没问题 单文件 + 剪裁 + 压缩(配置只留一处) 29.04 MB ✅ 单文件 + 剪裁 + 压缩(配置写两处) 28.03 MB ❌ 现在
PublishTrimmed/TrimMode/TrimmerRootAssembly只在 csproj 一处维护,
pubxml 与发布脚本都只传-p:EnableAppTrimming=true。 -
TrimmerRootAssembly去掉Condition:pubxml 是在 csproj 全部求值
之后才 Import 的,条件块永远看不到 pubxml 里的开关,导致「开了剪裁但 root 全丢」。
改为无条件声明(不剪裁时 ILLink 不跑,该 Item 零影响,实测标准档仍是 52.29MB)。 -
剪裁保留根 8 个程序集:
VibrantbitLauncher.Avalonia、Avalonia.Base、
Avalonia.Controls、Avalonia.Markup.Xaml、FluentAvalonia、
MinecraftLaunch、MinecraftLaunch.Base、CommunityToolkit.Mvvm。本项目
AvaloniaUseCompiledBindingsByDefault=false,XAML 绑定与 JSON 序列化
全靠反射,这些「运行期才用到的元数据」正好是剪裁器最爱删的东西。
已知(需要人工验收)
剪裁档的隐性失效表现是「某个页面空白 / 点了没反应」,且只在发布产物里出现。
发布前请逐项点一遍:冷启动、快捷设置、列表飞入、设置读写后重启、账号登录、
皮肤换肤、下载、新建 Forge 实例并补全资源、拖放文件 / 文件选择对话框
(走 COM,剪裁对 BuiltInComInteropSupport 有 IL2026 警告,最可能出问题的一处)。
任何一项异常就 -NoAppTrim 回退。
[1.0.10.0] - 2026-10-20
新增
-
所有列表框:逐项从左飞入(
Helpers/ListMotion.cs+Styles/Animations.axaml一条全局样式)。每次列表挂到可视树上(进入页面 / 换版本后重建),项容器按视觉顺序从上到下,
每项错开 50ms 依次从左滑入 34px 并淡入(单项 300ms,CubicEaseOut)。- 只对前 16 项错开:几百项的列表如果按 50ms 累加,最后一项要等十几秒才出现;
- 容器尚未生成(数据异步填充 / 空列表)时退化为整个列表飞入,不会「什么都不发生」;
- 动画结束会清掉
RenderTransform/Opacity的局部值 ——
否则会压掉ListBoxItem自己的「按下回缩」样式(局部值优先级高于样式); - 性能模式下直接返回,一帧都不多画。
-
启动页右侧「官方新闻」改为「快捷设置」:Minecraft 文件夹 / Java 运行环境 / 外观(亮色·暗色)。
- Minecraft 文件夹:显示当前绝对路径,点「更改」选目录,写完立刻重扫版本列表;
- Java:下拉列出已检测到的 Java(版本 + 路径),也可「浏览」手动指定
java.exe,
选择前会过JavaHelper.IsUsableJava静态体检;不设置时回退「启动时自动选择」; - 外观:亮色 / 暗色分段胶囊,切换后立即生效(走
SettingsService.ApplyTheme,不用重启)。
界面设计
右侧面板刻意没有做成三张等宽卡片(模板化界面的典型特征,...
VibrantbitLauncher.Avalonia 1.0.10.5
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
[1.0.10.5] - 2026-10-23
修复
-
GitHub Actions 上 Windows 三个架构全部发布失败(
Cannot overwrite variable IsLinux because it is read-only or constant.)publish-all.ps1里原本写了:$isWin = $rid.StartsWith('win-') $isOsx = $rid.StartsWith('osx-') $isLinux = $rid.StartsWith('linux-') # ← 这里炸
$IsLinux/$IsWindows/$IsMacOS是 PowerShell 的只读自动变量,而且
PowerShell 变量名不区分大小写,所以$isLinux与$IsLinux是同一个变量,
赋值直接抛PSSecurityException级别的错误并终止脚本。为什么本地一直没复现:本机是 Windows PowerShell 5.1,那三个变量是 PowerShell
6.0(Core) 才引入的,5.1 里根本不存在,赋值只是新建普通变量;
而 GitHub Actions 的shell: pwsh是 7.x,变量存在且只读 —— 必炸。三个 Windows RID 报同一个错,是因为报错发生在
foreach ($rawRid in $Rids)的
第一轮循环里,直接终止整个脚本;Linux / macOS 走的是.sh,没有这段代码。已把三个变量改名为
$ridWin/$ridOsx/$ridLinux,并加注释说明原因。
同时对全脚本做了「所有被赋值变量 × 只读自动变量」的全量比对(56 个赋值变量,
含带类型赋值与参数名),确认没有第二处同类冲突。⚠️ 与代码无关但同样关键:仓库里那份publish-all.ps1仍是 642 行的旧版,
本次修复在本地工作区(831 行),必须提交并推送后 CI 才会生效。 -
workflow 文件名统一为
build.yml早期本地新建了
release.yml并打算删除build.yml;同时远端也在build.yml
上做了一次重写(内容与release.yml逐字节相同,只是文件名不同)。
两边一旦合并就会出现「删一个、加一个」的假冲突。现统一沿用远端已在跑的
build.yml,本地不再保留release.yml,避免同一份配置存在两个路径。
[1.0.10.4] - 2026-10-22
新增
-
GitHub Actions 自动发布
.github/workflows/build.yml:7 个平台并行构建并汇总到 GitHub Release。RID Runner 脚本 win-x64 / win-x86 / win-arm64 windows-latest publish-all.ps1linux-x64 / linux-arm64 ubuntu-latest publish-all.shosx-x64 / osx-arm64 macos-latest publish-all.sh- Release 正文 =
CHANGELOG.md原文(body_path),不做二次加工。 - 触发:推送
v*tag 或手动运行(手动时 tag 取 csproj 的<Version>);
PR 到 main 只构建不发布,保留 CI 校验又不污染 Release。 - 校验和由汇总 job 重新算一份合并的
SHA256SUMS.txt:
每个矩阵 job 各自只算得出「自己那一个包」,直接合并会互相覆盖。 fail-fast: false:单个平台失败不影响其余 6 个继续构建,汇总 job 整体失败,
不发残缺的 Release。
- Release 正文 =
变更
-
publish-all.sh补上程序集剪裁开关,与publish-all.ps1对齐(默认开启)。此前两条路径是漂移的:ps1 自 1.0.10.2 起默认剪裁(29MB),sh 脚本没有对应开关,
CI 一旦走 sh 就会产出 52MB 的标准档。现在 sh 也默认-p:EnableAppTrimming=true,
并提供--no-app-trim逃生阀(等价 ps1 的-NoAppTrim)。
实测 sh 路径 win-x64 产出 29.0MB,与本机 ps1 结果一致。
移除
- 旧版
.github/workflows/build.yml的发布逻辑整体重写。它原来用--generate-notes,
且 SELF_CONTAINED 固定为 false(1.0.9.6 起已改默认自包含),
与新的发布需求冲突。
现沿用build.yml这个文件名(远端已在使用),内容替换为重写后的版本,
保留其 PR 构建校验能力。
[1.0.10.3] - 2026-10-21
新增
-
下载页版本类型筛选(多选框):正式版 / 测试版 / 远古版 / 愚人节版四个多选框,
可任意叠加。勾选才显示,不勾选整类隐藏;默认只勾「正式版」。
每个多选框上带该类版本的总数(实测清单:正式版 103 / 测试版 748 / 远古版 61 / 愚人节版 8,合计 920)。 -
版本分类器
Models/McVersionKindClassifier:清单type→ 四大类的唯一判定点。必需按 id 认愚人节版的原因:Mojang 清单里没有「愚人节」这个 type,
15w14a、3D Shareware v1.34、24w14potato等 8 个版本一律标成snapshot。
不单独认的话它们会混进「测试版」。名单忽略大小写(清单里实际 id 是
22w13oneblockatatime,与官方公告的大小写不同)。
界面
- 四个多选框做成胶囊(高 30 / 圆角 99 / 12px 文字),沿用启动页主题切换的视觉语言,
但保留多选语义(分段控件是四选一,这里需要叠加)。 - 状态齐全:hover / pressed / checked / checked:hover / checked:pressed / focus-visible / disabled;
过渡 150ms +CubicEaseOut(反馈档,比页面转场 250ms 快一档)。 - 每个胶囊带 6px 类型色圆点(主色 / 橙 / 灰 / 紫),选中态一律转白,不与主色抢注意力。
- 列表行内给非正式版补类型角标(测试版 / 远古版 / 愚人节),正式版不标(常态是噪音)。
修正
- 搜索计数分母改为「当前勾选范围内」的数量:只勾正式版再搜「1.20」显示
匹配 3 / 103 个版本,不再显示容易误读的匹配 3 / 共 920。 - 列表尚未加载完成时不再短暂显示「当前筛选条件下没有版本」的空状态。
[1.0.10.2] - 2026-10-21
变更
-
单文件体积 52.29 MB → 29.04 MB(-44%),win-x64 自包含单文件。
自包含发布默认把整个
Microsoft.NETCore.App运行时包原样复制进来
(System.Private.Xml7.4MB、System.Linq.Expressions3.5MB、
System.Data.Common2.6MB、System.Private.DataContractSerialization2.0MB ……),
其中大半代码本应用一行都不走。改用PublishTrimmed+TrimMode=partial
按调用图裁剪,只保留真正走到的部分。 -
发布目录剔除调试器 / 转储分析组件(约 6.3MB 未压缩):
mscordaccore.dll、mscordaccore_amd64_*.dll、mscordbi.dll、
Microsoft.DiaSymReader.Native.amd64.dll。它们只在 VS「附加到进程」或
dotnet-dump/ 蓝屏转储分析时加载,应用运行不碰。
实测<DebuggerSupport>false</DebuggerSupport>对自包含载荷无效
(同名 dll 一个不少、体积零变化),只能在TrimPublishPayload里删。
副作用:发布出的 exe 不能再被附加调试、不能抓托管转储;
dotnet build的产物不受影响,日常调试照旧。 -
新增
Properties/PublishProfiles/WindowsTrimmed.pubxml(剪裁档,29MB)。
与Windows.pubxml(标准档,52MB)并存,出问题可一键回退。 -
publish-all.ps1/publish-all.sh默认走剪裁档,新增-NoAppTrim
回退到标准档。注意与既有的-NoTrim(关平台后端剥离)不是一回事。
修复
-
剪裁档一启动就
XamlLoadException: No precompiled XAML found for VibrantbitLauncher.App(28.03MB 那版)。根因不是「Avalonia 不兼容剪裁」,而是同一份剪裁配置被写了两处:
pubxml 与 csproj 各写一遍PublishTrimmed/TrimMode/TrimmerRootAssembly,
叠加后 ILLink 重写程序集时丢掉了 Avalonia 的!AvaloniaResources索引。排查过程(留档):
形态 体积 结果 目录 + 剪裁 55.80 MB ✅ 剪裁本身没问题 单文件 + 剪裁 + 不压缩 55.10 MB ✅ 单文件打包也没问题 单文件 + 剪裁 + 压缩(配置只留一处) 29.04 MB ✅ 单文件 + 剪裁 + 压缩(配置写两处) 28.03 MB ❌ 现在
PublishTrimmed/TrimMode/TrimmerRootAssembly只在 csproj 一处维护,
pubxml 与发布脚本都只传-p:EnableAppTrimming=true。 -
TrimmerRootAssembly去掉Condition:pubxml 是在 csproj 全部求值
之后才 Import 的,条件块永远看不到 pubxml 里的开关,导致「开了剪裁但 root 全丢」。
改为无条件声明(不剪裁时 ILLink 不跑,该 Item 零影响,实测标准档仍是 52.29MB)。 -
剪裁保留根 8 个程序集:
VibrantbitLauncher.Avalonia、Avalonia.Base、
Avalonia.Controls、Avalonia.Markup.Xaml、FluentAvalonia、
MinecraftLaunch、MinecraftLaunch.Base、CommunityToolkit.Mvvm。本项目
AvaloniaUseCompiledBindingsByDefault=false,XAML 绑定与 JSON 序列化
全靠反射,这些「运行期才用到的元数据」正好是剪裁器最爱删的东西。
已知(需要人工验收)
剪裁档的隐性失效表现是「某个页面空白 / 点了没反应」,且只在发布产物里出现。
发布前请逐项点一遍:冷启动、快捷设置、列表飞入、设置读写后重启、账号登录、
皮肤换肤、下载、新建 Forge 实例并补全资源、拖放文件 / 文件选择对话框
(走 COM,剪裁对 BuiltInComInteropSupport 有 IL2026 警告,最可能出问题的一处)。
任何一项异常就 -NoAppTrim 回退。
[1.0.10.0] - 2026-10-20
新增
-
所有列表框:逐项从左飞入(
Helpers/ListMotion.cs+Styles/Animations.axaml一条全局样式)。每次列表挂到可视树上(进入页面 / 换版本后重建),项容器按视觉顺序从上到下,
每项错开 50ms 依次从左滑入 34px 并淡入(单项 300ms,CubicEaseOut)。- 只对前 16 项错开:几百项的列表如果按 50ms 累加,最后一项要等十几秒才出现;
- 容器尚未生成(数据异步填充 / 空列表)时退化为整个列表飞入,不会「什么都不发生」;
- 动画结束会清掉
RenderTransform/Opacity的局部值 ——
否则会压掉ListBoxItem自己的「按下回缩」样式(局部值优先级高于样式); - 性能模式下直接返回,一帧都不多画。
-
启动页右侧「官方新闻」改为「快捷设置」:Minecraft 文件夹 / Java 运行环境 / 外观(亮色·暗色)。
- Minecraft 文件夹:显示当前绝对路径,点「更改」选目录,写完立刻重扫版本列表;
- Java:下拉列出已检测到的 Java(版本 + 路径),也可「浏览」手动指定
java.exe,
选择前会过JavaHelper.IsUsableJava静态体检;不设置时回退「启动时自动选择」; - 外观:亮色 / 暗色分段胶囊,切换后立即生效(走
SettingsService.ApplyTheme,不用重启)。
界面设计
右侧面板刻意没有做成三张等宽卡片(模板化界面的典型特征,三块等大的框会把视线摊平):
改成「一整块面板 + 两条 1px 分割线」的设置行结构,视觉噪音更少,
也让右侧始终是配角 —— 主角仍是左边的「启动游戏」按钮。
- 字号只用两档:正文 14 / 说明 12(沿用全局 token,不新增档位);
- 圆角只加一种:胶囊 99;
- 主色只出现在「当前选中的那个胶囊」上,面积 < 3%;
- 胶囊补齐 hover / pressed / checked / focus-visible / disabled 五态,
键盘焦点用描边而非加粗边框(避免布局跳动); - 新增
Helpers/MotionSettings.cs作为减动效的统一入口(性能模式即减动效),
窗口动效 / 列表动效 / XAML 过渡三处统一读它。
设计验收(desktop-ui-designer 技能):
| 项 | 结果 |
|---|---|
--ui 人审美验收 |
100 / 100(字号 2 档 · 圆角 1 种 · 网格齐 · 四态齐 · 文本兜底齐) |
--motion 动效验收 |
100 / 100(时长 150ms 落在 50ms 网格 · 非线性缓动 · 有减动效 · 状态覆盖 4 类) |
--taste AI 味打分 |
100 / 100 |
调整
- 窗口飞出时长 280ms → 300ms,对齐 50ms 时长网格。
[1.0.9.9] - 2026-10-20
修复
-
Forge 26.x 安装「补丁文件生成不出来」→ 实例装完启动报 patched jar missing。
三个叠在一起的根因,全部用真实数据实测确认:
-
Java 处理器丢了 classpath(决定性根因)。
新版 Forge 的 install_profile 里,二进制打补丁处理器不再声明mainClass,
而它的 jar 不是 fat jar(jopt-simple / lzma-java / javaxdelta / trove 都在
processor.classpath里)。旧代码在没有 mainClass 时走java -jar,
等于把 classpath 整个丢弃,第一行就报
NoClassDefFoundError: joptsimple/OptionException,
forge-<mc>-<ver>-client.jar永远生成不出来。现在与官方 installer 一致:从 jar 的
META-INF/MANIFEST.MF读Main-Class,
用java -cp <jar + classpath> <Main-Class>启动。实测:Minecraft 26.2 + Forge 65.1.5,正确参数下生成
forge-26.2-65.1.5-client.jar共 79,191,305 字节,
SHA1be81e6d2…与 install_profile 声明的PATCHED_SHA逐字节一致。 -
处理器产物被当成可下载依赖。
install_profile.data里的PATCHED/MC_UNPACKED是
[group:artifact:version:classifier]形式的 maven 坐标,但它们是本地生成的;
maven 上根本没有(实测forge-26.2-65.1.5-client.jar在官方 maven 与
BMCLAPI 镜像都返回 404)。旧代码把它们塞进下载队列,连撞 4 轮 404 / 超时,
最后报「加载器依赖库下载失败:连接超时」,用户完全看不出真正原因。现在下载前先排除这些坐标(
CollectProcessorOutputs),只由处理器本地生成。 -
处理器没产出也不报错。
EnsurePatchedLibrary发现产物不存在时只打一条 warning 就当安装成功,
留下一个「装好了但起不来」的实例。现在:client 侧处理器明确声明了
{PATCHED}输出却没产出 → 安装直接失败并说明原因(附磁盘空间提示);
没有该声明(服务端专有布局)才降级为 warning。
-
-
「补全资源」对上述文件只会失败,不会修。
旧流程把缺失库交给库的下载器,而这类文件的 url 是空字符串,必然 404。
现在补全时先识别「没有 url、只能由处理器生成」的库:- 若能解析出 Forge / NeoForge 实例(id 形如
26.2_forge_65.1.5),
就自动重跑一次加载器安装来重建补丁文件(库已在本地,实际只重跑处理器); - 重跑成功并复核通过 → 返回「已补全」;
- 重跑失败 → 明确提示「需要重新安装加载器」,不再让用户对着「连接超时」猜。
- 若能解析出 Forge / NeoForge 实例(id 形如
-
顺带修掉每个 Forge 安装后都会出现的假错误日志
未观察到的任务异常 AggregateException: No process is associated with this object。
心跳与僵死看门狗是无人 await 的后台任务,进程 Dispose 后再读HasExited
会抛InvalidOperationException,异常最终在 finalizer 线程冒出来。
现在统一走HasExitedSafe()。
[1.0.9.8] - 2026-10-20
新增
-
下载器:并发数与分块数拆成两个独立设置(「设置 → 网络 → 下载」)。
原来只有一个「下载线程数」,同一个值既当「同时下几个文件」又当「单个文件切几块」,
想给单个大文件提速时会把并发文件数一起拉高 —— 镜像端直接 403。现在分成:设置 含义 默认 范围 同时下载文件数 文件级并发(批量下载时同时下几个) 6 1~16 单文件分块数 连接级并发(单个文件切几块并行) 4 1~16 总连接数 ≈ 并发数 × 分块数。分块数填 1 表示不分块、走单连接,
这是镜像限流时的逃生阀(原来的下限被硬编码成 2,想关都关不掉)。底层仍走
Downloader多连接分块内核(FileDownloader),本次改动是:- 分块数上限 8 → 16,下限 2 → 1,并允许关闭
ParallelDownload(单块时开并行没意义); - 每个文件的连接池大小跟随分块数(
MaxConnectionsPerServer = max(16, 分块数 × 2)),
避免块数调高后多出来的块排队等连接、分块并行退化成串行; - 各批量下载路径按自身容忍度取并发上限:原版 / 补资源 16,整合包 / 加载器 8,
Fabric 4(实测并发 10 路打 BMCLAPI 会被瞬时限流)。
旧配置自动迁移:旧的「下载线程数」会拆成「并发 = 旧值、分块 = 旧值」
(都夹到 1~16),随后该字段清零,不会重复迁移。 - 分块数上限 8 → 16,下限 2 → 1,并允许关闭
修复
-
安装页:勾选一个加载器后,其余加载器的列表框仍可操作。
只禁用勾选框是不够的 —— 另一个加载器的列表还能点,选中版本后...
1.0.7.14
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
无人测试,可能会有bug
[1.0.7.14] - 2026-10-17
修复
-
明暗主题在普通窗口里不生效(浅色底 + 浅色字,整窗发白)。
根因是两套主题来源打架:App.axaml里FluentAvaloniaTheme设了PreferSystemTheme="True",它会自己把
Application.RequestedThemeVariant改成操作系统的主题;- 而本工程的窗口底色、背景遮罩、拖放面板都是按用户设置算好手写进资源的。
于是出现「变体 = 系统(暗色),底色 = 应用设置(亮色)」:文字用暗色主题的浅色字,
衬在 #F3F3F3 的亮底上,正文几乎看不见。主窗口因为有 Mica 材质垫底、且会自己按
ActualThemeVariant重算底色,看起来是正常的 —— 这就是「只有 FAAppWindow 管用」的假象。修法:
PreferSystemTheme/PreferUserAccentColor改为False(应用自带主题开关,由自己说了算)。ApplyTheme()顺序调整为:先关掉 FA 的两个跟随开关 → 再写主题色与手写色 → 最后才切主题变体。
以前第一步就切变体,而那一刻PreferSystemTheme还是 True,会被它改回系统值。- 启动流程里把
ApplyTheme()提前到第一个窗口出现之前,避免窗口先按系统主题建出来。 - 新增运行时守卫:
ActualThemeVariant一旦和用户设置不一致,记一条 Warning 并按设置纠正回来。
修复
- 应用级
Style Selector="Window"的底色兜底原先引用SolidBackgroundFillColorBase——
那是 Color 资源(同名 Brush 是*Brush版本),赋给IBrush类型的Background
会因类型不匹配静默失效,等于没有兜底。改用本工程自己的ApplicationBackgroundBrush。
说明
- 排查手段:把截图当数据读(解 PNG 采样像素)。窗口底色实测
#F3F3F3(亮色手写值)、
正文几乎没有深色像素、只有主按钮#1599FF饱和 —— 一眼判定「底色按设置、文字按变体」,
而不是「整窗被降低透明度」或「字体颜色写错」。 - 日志新增:
主题已应用:{Variant}(强调色 …, 渐变 …);变体不一致时打 Warning。
[1.0.7.13] - 2026-10-17
改进
- 账户页二次重构:从「卡片墙」改为「焦点卡 + 列表组」。上一版是 232×268 的等高卡片整齐排布,
每张卡的视觉音量相同 —— 没有主次,眼睛不知道先看哪,这正是「看着整齐但不高级」的根因。现在:- 整屏只有一个焦点:当前启动身份单独做成 Hero 卡(92px 头像 + 主色渐变环 + 26 号名字 +
7% 渐变洗底)。其余账户全部降一档,收成 64px 的行。 - 不再是一排等宽卡片:账户改为同一张卡里的一组行,靠"组"表达归属,
而不是靠重复的卡片边框。行与行只差背景与一条选中环。 - 行内操作分三档重量:「换肤」文字按钮(次色)→ 删除只留图标(悬停才变危险色)→
「切换」描边按钮(唯一的行内主操作)。使用中的行把「切换」换成主色「使用中」文字 +
名字前的 6px 主色圆点,避免同一行出现两个互相竞争的动作。
- 整屏只有一个焦点:当前启动身份单独做成 Hero 卡(92px 头像 + 主色渐变环 + 26 号名字 +
- 强调色预算收紧到 3% 以内:删掉三张卡片各一条的主色渐变条与常驻主色图标块,
彩色只剩 Hero 的渐变环 / 渐变洗底 / 选中圆点。右栏三个「添加身份」图标块改成中性浅底,
悬停才借 12% 主色。页面里实心主色按钮 0 个。 - 页头的「N 个账户」徽标移到「全部账户」区块标题右侧,与它修饰的对象放在一起。
- 空状态文案改为一步到位的引导("从右侧选一种登录方式,添加后即可启动游戏")。
- 有账户但一个都没选时,Hero 位置不再空着,而是给出「尚未选择启动身份」的引导态。
新增
FontSizeHero(26)字号档:页标题 34、区块标题 22 之间原本缺一级,Hero 名字只能硬写尺寸。AccountPageViewModel.CurrentUser/HasCurrentUser/HasUsers/AccountCountText:
空状态、计数、Hero 内容全部改由绑定驱动,页面 code-behind 不再逐个x:Name开关IsVisible。
修复
- 键盘焦点在账户页不可见:行内按钮的模板底色被自定义样式覆盖后,Fluent 默认焦点框也跟着丢了。
现在每个可交互元素都有显式的:focus-visible(1px 主色环 + 12% 浅底,不改BorderThickness,
避免文字跳动),列表行由ListBoxItem:focus-visible点亮行边框。 Users.CollectionChanged原先在Loaded里订阅,每次导航回账户页都会重复挂一次处理器;
改为构造函数订阅一次。
说明
- 三级深度:L0 页面 / L1 Hero 卡与列表组(1px 内描边,无阴影)/ 仅悬停时一层浅影,
取代了上一版卡片常驻阴影的"贴纸感"。 - 入场动效分三层:Hero(0ms)→ 右栏(80ms)→ 列表逐行(100ms 起,每行 +40ms),
只在这三处动;.performanceMode下全部关闭。
[1.0.7.12] - 2026-10-17
改进
- 账户页重构为「左卡片墙 + 右操作栏」。原来三个添加账户的大按钮横排在页面底部,
既吃掉一整行高度,窗口变窄时还会换行错位。现在:- 三个入口收进右侧固定宽度的操作栏(300px),尺寸统一为紧凑的「图标 + 主副标题 + 箭头」行,
纵向排列、始终可见,不再随卡片数量上下漂移。 - 左侧卡片区拿到全部剩余宽度,卡片从 248×284 收到 232×268,环绕排布更紧凑,
窄窗口下能多放一列。 - 页头右侧增加「N 个账户」计数徽标,账户为空时自动隐藏。
- 三个入口收进右侧固定宽度的操作栏(300px),尺寸统一为紧凑的「图标 + 主副标题 + 箭头」行,
- 账户卡片增加「当前使用」状态。以前只靠一个
RadioButton表达选中,而它和
ViewModel 里的selectedAccount是两份状态,自动恢复上次账户时两边会对不上。
现在由User.IsSelected单一数据源驱动三处视觉:主色边框、顶部渐变条、头像外环,
外加右上角勾选徽章;RadioButton换成普通按钮,文字在「使用该账户 / 当前使用」之间切换。 - 卡片上的账户类型改为中文(「微软账户 / 外置账户 / 离线账户」),「皮肤 / 删除」按钮改为小号描边样式,
删除按钮保留危险色。
新增
- 账户页动效:
- 卡片入场:从下方 10px、97% 缩放、全透明浮到终态,多个卡片按 45ms 错落出现;
已入场过的卡片不会在增删账户时重复播放。 - 卡片悬停:整卡 1.8% 抬升 + 阴影 + 边框高亮,头像同时放大 5%。
- 选中态:顶部渐变条淡入、头像外环浮现、勾选徽章从 60% 弹性放大。
- 添加账户按钮:悬停时图标块放大 8%、背景加深,右侧箭头右移 3px、透明度提高。
- 全部动效走
Transitions声明式表达,并补充.performanceMode覆盖,
性能模式下一次性关掉(缩放复位、过渡清空)。
- 卡片入场:从下方 10px、97% 缩放、全透明浮到终态,多个卡片按 45ms 错落出现;
- 新增两个语义化画刷键
AppAccentSurfaceBrush(主色 12%)、AppAccentSurfaceStrongBrush(20%),
由SettingsService.ApplyAccentCore随主题色实时覆写,避免页面里硬编码#1F0078D4
这类换色后会失配的值。
[1.0.7.11] - 2026-10-17
修复
- 配装码「准备阶段」点取消无效。1.0.7.10 把取消令牌打通到了下载层,但漏了一段:
从点「开始添加」到安装会话建立之间,还有「生成 .mrpack」「解析清单」两步,
这段时间还没有会话令牌可取消。此时点「取消添加」,RequestCancel打到
ModpackInstallSession.Current上——首次安装时它是 null,第二次安装时它是上一次的已终态会话,
IsInProgress判 false 直接 return。表现是按钮变成「正在取消…」,安装却照常开始。
现在补上第二条通道_prepareCts:LoadoutImportWindow每次安装新建一个令牌,
一路传到LoadoutShareService.ImportAsNewInstanceAsync(…, ct)→
ModpackInstallCoordinator.InstallAsync(…, ct);- 生成安装包阶段取消 →
Task.Run(…, ct)立即中断,安静返回,界面显示「已取消这次添加」 - 清单解析完、会话刚建好时发现已请求取消 → 立刻
session.RequestCancel(),
让安装器第一行ThrowIfCancellationRequested直接收兵,不会「取消了却开始下载」 - 已进入下载阶段 → 仍走会话令牌(1.0.7.10 已打通)
- 生成安装包阶段取消 →
ModpackInstallCoordinator的清单解析增加OperationCanceledException分支:
以前取消异常会落到通用catch (Exception)里被报成「解析整合包失败」。现在按取消处理,
只给一句「已取消这次安装」,不弹错误。
说明
- 取消现在覆盖「生成安装包 → 解析清单 → 装原版 → 装加载器 → 下模组」全链路,
任一阶段点取消都能停下;已下载的文件保留,重试不会从头再来。
[1.0.7.10] - 2026-10-17
修复
- 配装码安装时取消不了。根因:原版 Minecraft 那一大段下载(几百 MB)完全没有接取消令牌 ——
ModpackInstaller.EnsureVanillaAsync调VanillaDownloader.InstallAsync时没传ct,
而后者内部也没接受令牌,MinecraftResourceDownloader.VerifyAndDownloadDependenciesAsync
本来就有(parallelism, ct)重载却只传了一半。结果点「取消安装」后界面显示「正在取消…」,
实际照样把整个原版下载完才停。
现在令牌一路打通:版本 JSON 请求(CreateLinkedTokenSource+ 单次超时)、写盘、
库下载器全部可中断;「外层取消」与「内部超时」用when (ct.IsCancellationRequested)区分,
取消时立即上抛、不再换源重试。 - 配装码安装被显示成「整合包」。配装码内部是打成临时
.mrpack交给整合包安装器的,
以前没告诉协调器来源,于是任务名写「整合包:xxx」、类型显示「游戏版本」、
进度窗口标题「整合包安装」。
新增Models/InstallKind.cs(Modpack/Loadout)贯穿
ModpackInstallCoordinator→ModpackInstallSession→ 界面:- 下载任务页:任务名「配装码:xxx」,类型列「配装码」(新增
DownloadTaskType.LoadoutInstall) - 进度窗口:标题「配装码安装」,格式标签「配装分享码」,步骤「下载配装模组」
- 提示文案:开始 / 完成 / 取消 / 失败全部按「配装」措辞
- 下载任务页:任务名「配装码:xxx」,类型列「配装码」(新增
- 取消后任务显示「失败」:新增
DownloadTaskService.CancelTask,取消的任务在任务中心
标为「已取消」,不再染成失败色。 - 配装码窗口安装期间无法取消:安装中「开始添加」按钮变为「取消添加」,
点击即向安装会话发取消请求并给出「正在取消…」反馈;关闭按钮不再被禁用
(关窗只是收起界面,安装会在后台继续,可在「下载任务」里看)。
改进
- 纯原版配装码也能装了:原来
ShareableCount == 0一律拒绝,导致「想让它帮我装个原版本体」
的码用不了。现在只有「既没有模组、也没有游戏版本」才拒绝;有版本无模组时照常装出本体 + 加载器。
[1.0.7.9] - 2026-10-17
修复
- 启动页有版本时仍显示「还没有可用的游戏版本」空状态。
根因:Refresh()是 fire-and-forget(Task.Run后台扫盘),而空状态只在页面
Loaded那一刻判了一次MinecraftVersions.Count == 0—— 那时集合肯定是空的,
之后填进来的版本不会再把空状态关掉。
改为订阅集合变化 + 扫描状态变化,每次都重算:有版本 → 整块隐藏;没版本 → 才显示。 - 首次进入会闪一下「没有版本」:新增
RunPageViewModel.IsRefreshing,
扫描期间空状态显示「正在扫描本地版本…」(不显示图标、提示与按钮),
扫完仍为空才切换成真正的空态文案。
说明
- 底部工具栏
启动游戏 | 版本设置 | 配装码 | 刷新里的**「配装码」按钮始终可见**,
与空状态是否存在无关;空状态里的「用配装码添加游戏」按钮只属于空态。
[1.0.7.8] - 2026-10-17
新增
- 启动页新增「配装码」入口:在「版本设置」右侧加了「配装码」按钮,点开粘贴一串分享码,
即可从零装出一个完整实例(Minecraft 本体 + 对应加载器 + 对方的所有模组)。
面向「刚拿到新电脑、一个版本都没装」的玩家 —— 这条路径不依赖任何已存在的实例。 - 版本列表空状态也带入口:没有可用版本时,空状态面板里直接给「用配装码添加游戏」按钮,
不用在空页面上找按钮。
改进
- 新增
Views/Windows/LoadoutImportWindow.axaml(.cs):粘贴即识别(VBL1-分享码与裸 JSON 都吃),
识别后展示配装名、游戏版本 + 加载器、可下载模组数、未收录模组数、分享者的内存设置;
点「开始添加」后禁用输入,避免「界面显示的和实际在装的不是同一份清单」。 - 安装复用
LoadoutShareService.ImportAsNewInstanceAsync(内部走整合包协调器),
因此与拖入.mrpack是同一条路径:进度详情页、任务中心、完成提示、重名加后缀全都一致。 - 只有真正装出实例才刷新启动页版本列表;打开看一眼就关掉不会白扫一遍磁盘。
说明
- 识别失败(粘贴到一半)只在窗口内提示,不弹通知 —— 边打边解析必然会短暂失败,弹窗会很吵。
- 分享码里没有记录 Minecraft 版本时不予安装(会明确说明原因),避免装出一个不知道装什么的空实例。
[1.0.7.7] - 2026-10-17
新增
- 启动前「补全资源」窗口(
Views/Windows/AssetRepairWindow.axaml):
点「启动」时先做一次完整性体检,缺东西就弹出本窗口,列出缺什么、点一下只补缺的那部分,
带进度条与速度显示。补完自动重新体检,确认干净后提示「可以重新启动游戏」。- 取代原来的行为:启动流程里直接抛
FileNotFoundException,
弹一句「缺少资源索引文件:…assets\indexes\5.json。说明该版本的资源没下载完整,
请在「下载中心」重新安装该版本」—— 无路可走,且重装整个版本要几百 MB。
- 取代原来的行为:启动流程里直接抛
Services/InstanceIntegrityChecker.cs(体检,纯本地 IO):
沿inheritsFrom链检查四类资源 ——
客户端 jar(缺失 / 小于 512 KB / 不是 ZIP 头都算坏)、
资源索引(assets/indexes/<id>.json)、
依赖库(按downloads.artifact.path,退化按 maven 坐标拼;跳过被rules排除的非 Windows 库)、
资源对象(按索引里的objects[].hash逐个查assets/objects/xx/<hash>)。
同类问题聚合为一条,避免几千个资源对象把列表刷爆。Services/AssetRepairService.cs(补全,复用既有链路):
阶段 ① 原版层走VanillaDownloader.InstallAsync—— 库的MinecraftResourceDownloader
本身就是「校验 + 只下缺的」,超时 / 双源 / 中文报错都在里面;
阶段 ② 加载器层对实例本身再跑一次VerifyAndDownloadDependenciesAsync,
失败只记日志(不拖垮原版补全)。进度条按 9:1 分配。
改进
- 体检结果不再「一缺就拦死」:加载器实例的
client.jar/ 资源索引在父版本目录里,
体检与补全都按inheritsFrom链定位到原版根版本(ResolveRootVersion),
不会把 Forge / Fabric 实例误判成「客户端文件缺失」。
说明
- 体检失败(例如版本 JSON 坏到读不出来)只记 Debug 并放行,不阻断启动 ——
宁可让游戏自己报错,也不要因为体检器自己的问题挡路。 - 补全期间「关闭」按钮禁用:底层下载没有外部取消令牌,中途关窗会让下载在后台空跑。
[1.0.7.6] - 2026-10-17
改进
- 安装页的模组加载器勾选框改为严格单选:勾上 Fabric 就立刻取消勾选并禁用 Forge / NeoForge / Quilt,
勾上 Forge 同理,其余依此类推。取消勾选后四个框恢复为「有货就能选」,可以换装别的加载器。- 之前沿用 WPF 版的部分共存规则(Forge 只挡 Fabric/Quilt,NeoForge 只挡 Fabric/Quilt),
会出现「Forge 与 NeoForge 同时勾着」的组合。 - 但安装逻辑本身是按 Forge → Fabric → Quilt → NeoForge 的顺序取第一个非空版本号,
同时勾两个只会静默只装其中一个,玩家却以为两个都装了 —— UI 规则与安装逻辑口径不一致。
- 之前沿用 WPF 版的部分共存规则(Forge 只挡 Fabric/Quilt,NeoForge 只挡 Fabric/Quilt),
- 勾选状态与已选版本号强绑定:未勾选的加载器,其
Select*Version一律清空,
避免「框没勾却还带着版本号」被安装流程当成要装。 - 安装前的兜底守卫:
InstallPageViewModel里若检测到同时有多个加载器版本号非空,
直接提示「同一个实例只能安装一个模组加载器,请只勾选其中一个」并中止,
不再静默只装第一个(防止将来新增入口没守住单选规则)。
说明
- 卡片描边(
loaderCard.selected)与勾选框状态保持同步,点哪个亮哪个。 - 版本列表加载中时四个框仍统一禁用。
[1.0.7.5] - 2026-10-17
改进
- 版本清单改为「磁盘缓存优先」:上次成功拉到的清单存到
%LOCALAPPDATA%\VibrantbitLauncher\cache\version_manifest_v2.json,
下次启动先秒出列表,网络刷新改到后台做(成功则同时更新磁盘与进程内缓存)。
首次运行 / 缓存缺失时才必须等网络。缓存损坏、目录不可写都只记 Debug,一律降级为「去网络拿」。 - 清单请求改为双源并行竞速:官方
launchermeta.mojang.com与镜像bmclapi2.bangbang93.com
同时发,谁先返回完整清单就用谁,另一个立刻取消。原来是串行「一个源 15 秒 × 3 次,全败才换源」,
最坏要挂 90 秒 —— 用户随手关窗口就会撞出SocketException 995/TaskCanceledException的中断异常。 - 单次超时 15s → 8s,每源重试 3 → 2 次:清单只有几百 KB,正常 1 秒内到;卡住的主机应尽快让位。
- 接入源健康度:清单拉取成功回报
Good、失败回报Failed,
下次会自动把上次超时的主机排到后面(与下载层共用同一套降级记录)。
修复
- 关窗口时日志里出现
TaskCanceledException/SocketException 995堆栈:
这类异常是「程序退出时仍在飞的...
1.0.6.12
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
[1.0.6.12] - 2026-10-15
找到「单文件发布后窗口全空白/透明」的真正根因:TrimPlatformPayload 把
Avalonia.Metal.dll 当「macOS 后端」删掉了,但 Avalonia 12 的 Windows 渲染管线实际依赖它。
修复
Avalonia.Metal不再被裁剪(VibrantbitLauncher.Avalonia.csproj的
TrimPlatformPayload,win 与 linux 两个分支都去掉了对它的删除)。
名字带「Metal」很容易被当成 macOS 专有,实测删掉它之后窗口建得出来、但内容一片空白,
整窗透出桌面(在无 DWM 合成的机器上则是白屏)。
说明
-
为什么「调试正常、一发布就透明」:
TrimPlatformPayload/TrimPublishPayload
只在 publish 阶段生效,dotnet build与 Visual Studio 调试都不经过它们。 -
定位过程(同机对照,只差一个 dll):
产物 窗口内容 Debug(VS 调试) 正常 Release build(不裁剪)正常 Release publish单文件(裁剪)空白/透明 Release publish非单文件(裁剪)空白/透明 Release publish -p:SkipPlatformTrim=true正常 未裁剪目录里只删 Avalonia.Metal.dll空白/透明 未裁剪目录里只删 Avalonia.Native/ X11 / FreeDesktop / DBus / Designer 组正常 -
判据用的是
PrintWindow抓窗口自身内容:正常 ≈ 510 KB,空白 ≈ 3 KB
(窗口样式位WS_EX_LAYERED两版一致,说明不是窗口样式问题,是内容没渲染)。 -
⚠️ 教训:这轮之前把「白屏/透明」归因到主题资源(ApplicationBackgroundBrush)与 Mica 回退,
那两处确实是真问题、修得没错,但不是「整窗空白」的主因。遇到「窗口完全没内容」,
先比较build与publish的产物差异,别只在 XAML / 主题里找。 -
版本产物体积:裁剪后仍能省掉 X11 / FreeDesktop / DBus / Native / Designer 等约 2.5 MB。
[1.0.6.11] - 2026-10-15
继续收敛「换台电脑窗口透明 / 白屏」一族问题。
修复
- 主窗口 Mica 回退时整窗穿透:
TryEnableMicaEffect之前无条件把窗口背景设成透明 +
请求 Mica 材质,假设 Mica 一定生效。但 Mica 能否渲染由 DWM 决定——系统关闭「透明效果」、
远程桌面、部分虚拟机 / 显卡驱动下,请求 Mica 会回退到 Transparent 甚至 None,
而主窗口内容区没有任何不透明背景(设计上「未设壁纸时保持纯 Mica 底」)→ 整窗连同标题栏
一起穿透到桌面。修法:hint 照发,但背景是否透明以实际达成的
ActualTransparencyLevel为准(订阅其属性变化 + 开启时各校一次);只有 Mica / AcrylicBlur
真的生效才透明,否则清掉局部值让AppWindowBackgroundBrush(不透明)回来,
并把TransparencyBackgroundFallback设成不透明兜底。 - 9 个二级窗口加窗口级不透明背景兜底:欢迎向导 / 登录 / 皮肤管理 / 崩溃分析等窗口
之前只靠根Grid的ApplicationBackgroundBrush(1.0.6.10 补的定义)着色,窗口自身
Background为空——一旦 Grid 层异常(资源解析失败、内容页未加载),窗口就是「无背景」,
在 DWM 合成开启的机器上表现为整窗透明透出桌面(同一根因在无合成机器上表现为白屏)。
现在给这 9 个窗口的Window元素直接挂Background="{DynamicResource ApplicationBackgroundBrush}",
与根 Grid 双保险;DynamicResource保证切主题时同步换色。 - 应用级窗口底色兜底:
App.axaml加一条Style Selector="Window",把
Background兜到 FluentAvalonia 官方键SolidBackgroundFillColorBase(必然存在、随主题换色)。
局部值优先级高于应用样式,所以主窗口的 Mica 透明背景与二级窗口自写的背景都不会被压掉,
这条只兜「XAML 里什么都没写」的窗口——属于防漏网,不是本次症状的直接原因。
说明
- 「白屏」与「透明」是同一根因的两种表现:窗口无不透明背景时,DWM 合成开 → alpha 通道
真透明(看到桌面);合成关 / 老显卡 → 透出白底。1.0.6.10 修的
ApplicationBackgroundBrush、本版的窗口级兜底与 Mica 级别校验,三层加起来才算闭环。 - 排查时请先确认对方机器跑的 exe 版本(关于页 / 文件属性 → 详细信息),
1.0.6.10 之前的产物必然有这族问题。
[1.0.6.10] - 2026-10-13
修掉一个换台电脑就整窗白屏的必现问题:ApplicationBackgroundBrush 是从 WPF 版
一路抄过来的键名,但 Avalonia / FluentAvalonia 根本没有这个资源键。
修复
- 所有二级窗口在别的电脑上全白(用户报:窗口出来了,内容一片空白)。
根因:登录 / 皮肤管理 / 欢迎向导 / 崩溃分析 / 整合包进度 等 9 个窗口的根
<Grid>都写着Background="{DynamicResource ApplicationBackgroundBrush}",
而这个键在 Avalonia 侧任何地方都没有定义(连 Avalonia Flet / FluentAvalonia 主题里也没有)。
DynamicResource解析不到就落到null→ 根面板没有背景 → 直接透出窗口默认底色 ——
在别的机器上就是整窗全白;主窗口用的是AppWindowBackgroundBrush,所以本地看着正常,
一直没暴露。这解释了「本机好、别的电脑全白、且日志一切都正常」的现场。
修法:① 在Styles/AppTheme.axaml里补上该键的不透明兜底定义;
② 在SettingsService.ApplyTheme()里跟AppWindowBackgroundBrush一起按明暗主题覆写,
保证任何主题下都是不透明色(Light#F3F3F3/ Dark#202020)。
⚠️ 不要图省事把它放进ThemeDictionaries:对「被ResourceInclude合并进来的字典」
跨变体查询不生效,实测 Dark 主题仍会取到浅色,必须走运行时覆写。
说明
- 反向对照已验证:反射探针直接查编译好的产品程序集资源字典 ——
移除定义时ApplicationBackgroundBrush在 Light/Dark/Default 三个变体下全部返回 null(3 FAIL),
补上定义后 全部可解析;再走SettingsService.Load()+ApplyTheme()验证运行时覆写,
Light →#FFF3F3F3、Dark →#FF202020,且 Alpha 恒为 255(不会透出白底)—— 8 项断言全 PASS。 - 同一轮还修了
FabricDownloader的空闲超时(见上一节),大文件在慢速链路上不再反复重下。
[1.0.6.9] - 2026-10-11
给整合包安装补齐 Fabric / Forge / NeoForge / Quilt 四家加载器的自动安装 ——
其中 Forge / NeoForge / Quilt 三家改为自实现安装器,不再走 MinecraftLaunch 的库安装器
(那三条路实测全都装不上,下面逐条说明根因)。
新增
Services/LoaderInstaller.cs:自实现的加载器安装器,负责
①选版本(含版本号归一化)②下载 installer jar(官方源 ↔ BMCLAPI 镜像互为兜底)
③解install_profile.json/version.json④铺实例版本 JSON ⑤下载全部依赖库
⑥按processors跑 java 安装处理器。
网络层复用FabricDownloader那套已经打磨过的短超时 + 双源兜底 + 重试 + 中文报错,
不再受库的FlurlClient(15 秒硬超时、无镜像)拖累。- Quilt 也走自实现(
LoaderInstaller.InstallQuiltAsync):Quilt 的 profile 就是一份
现成版本 JSON(无 processor),拉下来改 id → 下库即可。
修复
- Forge / NeoForge 装不上(
NullReferenceException)。库的
ForgeInstaller.DownloadForgePackageAsync硬编码maven.neoforged.net,
既不参与 BMCLAPI 改写也无兜底;实测该主机直接连接失败(curl 退出码 7),
而 BMCLAPI 上其实有完整镜像。异常又被库裹成一个没有上下文的NullReferenceException。
现在自己拼下载地址并走双源兜底,并且补了DownloadSources的
maven.neoforged.net/releases/ → bmclapi/maven/与
maven.quiltmc.org/repository/release/ → bmclapi/maven/两条映射。 - Quilt 装不上(
Nullable object must have a value)。库的
QuiltInstaller→MinecraftEntry.GetRequiredLibraries()→
MinecraftLibrary.ParseJsonNode里有一句Sha1 = libNode.Sha1.Value,
而 Quilt profile 的 14 个库声明(url = https://maven.fabricmc.net/)
都没有sha1/size/downloads字段 → 对 null 的Nullable<string>取.Value,
实测 MC 1.20.1 + Quilt 0.20.0 100% 复现。这是上游库的 bug,自实现下库即绕开。 - BMCLAPI 的 NeoForge 版本号带 MC 前缀导致拼出 404。列表前 55 条是
version = "47.1.5",最后 5 条却是version = "1.20.1-47.1.105"
(条目里另有rawVersion = "1.20.1-forge-47.1.105"与现成的installerPath)。
库原样把带前缀的版本号塞进下载 URL。新增
LoaderInstaller.NormalizeLoaderVersion,循环剥离前导的加载器名与 MC 版本号,
无论forge-47.4.10/1.20.1-47.4.10/1.20.1-forge-47.1.105哪种形态都归一化到纯版本号。 - 整合包安装会创建「空壳实例目录」,进而毒化之后的每次版本解析。
ResolveGameRoot原本在安装一开始就Directory.CreateDirectory,
于是versions/<实例名>/在没有版本 JSON 的情况下就存在了 ——
而库的MinecraftParser.GetMinecrafts()会遍历versions/下每一个子目录,
撞上没有<目录名>.json的目录直接抛FileNotFoundException。
现在ResolveGameRoot只算路径不建目录(目录推迟到真正要写文件时再建),
并新增PurgeBrokenInstanceDirs:装加载器前清掉「没有版本 JSON 且没有其它文件」的空壳目录,
让历史失败残留不再拖垮后续安装。 - 安装器抛出的异常会掩盖真实原因。Quilt 安装器内部把失败咽进
Completed事件后,
还会在return entry ?? throw new ArgumentNullException("entry")处抛一个
与真实原因无关的ArgumentNullException;Forge 安装器则是throw;原样再抛。
现在两边都收口:Completed里记一份、InstallAsync抛出的也接住,
优先报Completed里的原始原因。 - Installer 版本号比较的两个坑(内联进上面的实现,一并记着防回归):
Quilt 的DisplayVersion是"1.20.1-0.27.1"(带 MC 前缀),
纯加载器版本在BuildVersion;ForgeInstallEntry.DisplayVersion == ForgeVersion才是纯版本。 - 慢速链路下大文件(Forge/NeoForge 安装器 8~9 MB)会反复从 0 重下、永远装不上。
FabricDownloader.DownloadFileAsync原来的timeout是整次尝试的墙钟上限(CancelAfter),
对几十 KB 的小清单合适;但安装器有几 MB,在限速镜像 / 代理下整体耗时可能远超上限,
而数据其实一直在流 —— 到点被判失败后重试又从 0 开始,于是永远下不完(实测复现)。
新增可选参数idleTimeout(空闲超时):只要还在持续收到字节就把 deadline 往后顺延,
只有「彻底卡死」才超时。LoaderInstaller下安装器时传 90 秒空闲超时。
说明
- Fabric 分支不动:它本来就走 1.0.6.1 起的自实现
FabricDownloader,是四家里唯一没问题的。 - 验证方式:① 反射验证台直接调编译好的产品程序集里的
NormalizeLoaderVersion/BuildInstallerUrl与DownloadSources.ToMirror,
17 项断言全 PASS(含1.20.1-forge-47.1.105这类双前缀形态、以及三条镜像改写);
② 端到端用最小.mrpack真实装 Quilt / Forge / NeoForge 三个实例。 - 排查过程确认了一个上层事实:库的
HttpUtil.FlurlClient必须在启动时由
InitializeHelper.Initialize()注入(应用里由App.axaml.cs的
SettingsService.ApplyDownloadSettings()完成)—— 库内部多处直接
HttpUtil.FlurlClient.Request(...),绕过了它自己的 null 检查,未初始化时就是 NRE。
[1.0.6.8] - 2026-10-11
修掉整合包安装里两个互相叠加的「必现」问题:本地没有该基础版本时安装直接崩掉,
以及失败一次就永久占掉一个实例名。
修复
- 拖入 / 下载整合包报「目标目录不存在或已被删除,请检查游戏目录设置」
(拖入 Modrinth 上的「Zombie Invade 100 Days」,1.20.1 / Forge 47.4.10,稳定复现)。
根因在ModpackInstaller.EnsureVanillaAsync里判断「这个基础版本装没装」的那一句:
new MinecraftParser(mcFolder).GetMinecraft(version)在实例目录不存在时会抛
DirectoryNotFoundException(实测…\versions\1.20.1 not found),而不是返回 null。
1.0.6.5 起一直如此,只是之前测的都是本地已装好的版本,这条分支从未被走到。
于是「装一个本地还没有的版本」这一步会把整个安装带崩,异常再被
Helpers/DownloadErrors统一翻译成了跟真实原因无关的「目标目录不存在」。
现在把解析器调用包在 try/catch 里:「解析不出来」和「解析抛异常」一律视作「还没装」,
继续往下走下载流程 —— 该装的照样装,只是不再崩。 - 一次失败的安装会永久占掉一个实例名。
BuildInstanceId判断重名用的是
「versions/<id>目录在不在」,而ResolveGameRoot在安装一开始就先把该目录建出来了,
所以失败一次就留下一个空目录,下次重试被当成重名 → 名字一路(1) (2) (3)…加下去。
现场实测留下了三个空目录(Zombie Invade 100 Days_forge_47.4.10及其(1)、(2))。
现在判据改成**「有没有版本 JSON」**(versions/<id>/<id>.json),与
GameInstanceHelper.GetInstanceIds()完全一致 —— 只有真正可用的实例才占名字。
说明
- 这两个问题互相放大:崩 → 留下空目录 → 下次重试名字变了 → 再崩,用户看到的就是
「怎么试都装不上,而且实例列表越来越乱」。修好之后,缺基础版本的整合包会正常进入
「下载 Minecraft 版本 → 安装加载器 → 下载整合包文件 → 铺开覆盖层」的完整流程。 - 同时确认了**「基础版本已装好」的快路径没有被破坏**:仍然直接复用、不会重新下载。
- 验证方式:新增反射验证台,直接调用编译好的产品程序集里的私有方法(测的就是会发布出去的那份 IL),
16 项断言全部通过(另 1 项因需要已初始化的SettingsService而跳过),
其中包含「旧判据在同一现场会给出(3)」的反向对照,确认用例能区分修复前后。 - 已清理测试目录里那三个空残留目录。
[1.0.6.7] - 2026-10-11
给整合包安装补上进度详情页,并让下载页的数据自动保持最新。
新增
- 整合包安装进度详情页(
Views/Windows/ModpackProgressWindow)。
以前开始装整合包后只有一个「45%」的进度条,用户看不到「在装哪个文件、卡在哪一步、哪些文件失败了」。
现在会自动弹出一个独立窗口(可随时关掉、安装照常继续),展示:- 包信息:整合包名、格式(Modrinth
.mrpack/ CurseForge.zip)、所需 Minecraft 版本、
加载器与版本、即将创建的实例 id; - 阶段条:准备安装 → 安装游戏版本 → 安装加载器 → 下载整合包文件 → 写入随包文件 → 校验实例,
每步有「等待 / 进行中 / 完成 / 失败」四种状态(原版包会自动省掉加载器那一步); - 文件清单:清单里每个待下载文件逐行显示状态与失败原因,支持滚动(虚拟化,几百个文件也不卡);
- 实时读数:总进度、当前步骤、下载速度、已用时间、按平均速度估算的剩余时间;
- 取消安装按钮(二次确认 —— 取消后已下载的文件会保留,但实例可能不完整);
- 完成后显示结果摘要(新建的实例、下载数、随包文件数、未成功的文件),并提供
打开实例目录按钮。 - 数据源是新增的
Services/ModpackInstallSession:把安装器的进度回调累积成
「阶段走到哪、每个文件什么状态、一共花了多久」。安装器那边为此扩展了进度载荷
(ModpackProgress增加Phase与文件级事件),原来只有百分比和一句文案。 - 关闭窗口不会中断安装;「下载任务」列表里整合包任务的「查看详情」按钮现在直接打开这个窗口
(安装进行中可随时把详情页调出来)。
- 包信息:整合包名、格式(Modrinth
- **下...
1.0.5.20
更新日志(Avalonia 版)
本文件记录 VibrantbitLauncher.Avalonia 的所有重要变更。
本工程是 WPF 版 VibrantbitLauncher 的 Avalonia 重构分支,与之并存、互不影响。
版本号唯一来源是VibrantbitLauncher.Avalonia.csproj的<Version>(四段式主.次.修订.补丁)。
[1.0.5.20] - 2026-10-07
修复发布包在 macOS / Linux 上无法直接使用的问题,并给 macOS 包补上开箱即用的安装脚本。
修复
- 打包丢了可执行位:Windows 文件系统没有「可执行位」,在 Windows 上打出来的
.tar.gz
里所有文件权限都是 0666、目录 0777 → 解压到 macOS 后.app的主可执行文件不可执行
(双击毫无反应),Linux 上./VibrantbitLauncher.Avalonia、./install.sh也都跑不起来。
修法:打包统一走make_tar/New-TarGz,检测到 GNU tar 就加--mode=u+rwx,go+rx强制 0755;
只有 bsdtar(Windows 自带)时退化为原行为并明确警告。.sh侧直接用tar(Git Bash / Linux / macOS 上都是 GNU tar);.ps1侧优先用 Git 自带的C:\Program Files\Git\usr\bin\tar.exe(GNU tar 1.35),
系统自带的System32\tar.exe是 bsdtar 3.8、不支持--mode,仅在没有 Git 时兜底。- 实测归档内权限由
-rw-rw-rw-变为-rwxr-xr-x(目录 0755)。
Info.plist带 UTF-8 BOM:PS 5.1 的Set-Content -Encoding UTF8会写 BOM,plist 以EF BB BF
开头,macOS 的plutil会判为非法格式。改为 UTF-8 无 BOM 落盘。SHA256SUMS.txt带 UTF-8 BOM:同样来自 PS 5.1 的-Encoding UTF8。带 BOM 时
sha256sum -c SHA256SUMS.txt在 Linux / macOS 上会直接失败(首行文件名被拼上了 BOM)。改为无 BOM。install.sh的中文被写成问号:PS 5.1 用-Encoding ASCII落盘,中文注释全成?。改为 UTF-8 无 BOM。SHA256SUMS.txt用了 CRLF 换行:写文件时取了[Environment]::NewLine,在 Windows 上是\r\n,
于是 Linux / macOS 上sha256sum -c读到的文件名尾部多一个\r,报
No such file or directory(十个包全报 FAILED)。改为\n。- Windows 的 zip 覆盖被环境的删除钩子打断:
Compress-Archive覆盖同名文件时内部会先删一次,
而某些环境把「删除」改写成回收站操作并抛错(SAFE_DELETE_FAIL_CLOSED),
会让整个平台被标记为失败。改为先用Remove-FileSafe清掉旧 zip(删完复查是否真的没了),
并把 zip 生成失败降级为警告(tar.gz 已经出好,不受影响)。
新增
- macOS 包内附
install.command(双击即执行),一次做完三件事:
①chmod +x补可执行位;②xattr -dr com.apple.quarantine去掉下载隔离属性(绕过 Gatekeeper);
③codesign --force --deep --sign -做 ad-hoc 自签名。第 ③ 步是硬性的:Apple Silicon 内核拒绝执行未签名的 arm64 二进制,
而在 Windows 上交叉发布的 osx-arm64 可执行文件没有签名,直接双击会闪退或提示「已损坏」。
ad-hoc 签名属本地自签,不需要开发者证书、不需要联网。 - macOS 包内附
README-macOS.txt:架构选择(M 系列 → osx-arm64 / Intel → osx-x64)、
装 .NET 10 Runtime(macOS 上没有 "Desktop Runtime",就是 Runtime;Homebrew 装的可能需
export DOTNET_ROOT=$(brew --prefix)/opt/dotnet/libexec)、安装三步、四类常见故障的排查命令、卸载方式。 - 两个脚本的头部注释与
-h帮助补上 macOS 首次运行说明。
说明
publish-all.ps1侧改用 Git 自带的 GNU tar,并把打包拆成两步:tar --mode=... --force-local -cf
打成裸 tar,再用 .NET 的GZipStream压成.tar.gz。绕开两个坑:
GNU tar 会把C:\…里的冒号当成「远程主机」语法(Cannot connect to C: resolve failed,用--force-local关掉);
而-z会 fork 外部gzip.exe,PowerShell 这边的 PATH 里没有 Git 的usr\bin(gzip: command not found)。
publish-all.sh侧不需要这层,GNU tar +-z直接可用。- 本机仍只能实跑 win-x64;osx / linux 六个平台只做了归档结构校验(权限位、BOM、
.app布局、
包内脚本存在)。macOS 上的实际启动需要真机验证。 - 验证方式:
tar -tvzf看归档内权限位、xxd看首字节(BOM / CR)、sha256sum -c验全套哈希,
本次三个平台 7 个包全部通过;发布全量 7 平台一次跑通(退出码 0)。
[1.0.5.19] - 2026-10-07
新增多平台一键发布能力:一条命令产出 7 个平台的可分发包,并把 CI 也统一到同一套脚本上。
新增
publish-all.ps1(Windows)与publish-all.sh(Linux / macOS / Git Bash),两者功能等价:
一次产出win-x64/win-x86/win-arm64/linux-x64/linux-arm64/osx-x64/osx-arm64。
逐 RID 发布 → 按平台打包 → 算 SHA256 → 打一张汇总表;单个平台失败不影响其它平台,退出码反映最终结果。- 原始发布目录落在
publish/<rid>/,可分发包落在dist/; win-*出.tar.gz+.zip(Windows 上双击解压更顺手);linux-*出.tar.gz,包内附install.sh(解压后sh install.sh赋可执行权限);osx-*额外组装VibrantbitLauncher.Avalonia.app(含Info.plist,版本号从 csproj 读)再打包;dist/SHA256SUMS.txt汇总全部包的校验和。- 可选参数:
-Rids(只发部分平台)、-SelfContained(自带运行时)、-Clean、-NoPackage、-NoTrim。
- 原始发布目录落在
改进
-
csproj 的
<RuntimeIdentifier>改为条件默认:Condition="'$(RuntimeIdentifier)' == ''"时才回落win-x64。
这样dotnet publish -r <rid>可以自由指定平台,而本地dotnet build/ F5 的输出目录仍固定为
bin/<cfg>/net10.0/win-x64/(避免出现「编译成功但跑到的是旧 exe」这种假象)。 -
TrimPlatformPayload从「仅 win-x64」泛化为「按 RID 剥掉其它平台的后端程序集」:win-*→ 去掉 X11 / FreeDesktop / DBus / Native / Metallinux-*→ 去掉 Win32 / Win32.Automation / Native / Metalosx-*→ 去掉 Win32 / Win32.Automation / X11 / FreeDesktop / DBus- 设计器相关(
DesignerSupport/Remote.Protocol)所有平台都去掉
实测每个平台再省 1.6 ~ 2.8 MB。逃生阀:
-p:SkipPlatformTrim=true。 -
csproj 显式写出
PublishReadyToRun=false/EnableCompressionInSingleFile=false,
与 IDE 的发布配置文件(pubxml)行为对齐(此前只有 pubxml 里有,命令行靠默认值)。 -
CI(
.github/workflows/build.yml)改为直接调用publish-all.sh,不再自己维护第二套打包逻辑,
避免两套逻辑长期漂移;同时补上此前缺失的win-x86,矩阵从 6 个 RID 扩到 7 个。 -
.gitignore增加/publish/与/dist/(都是几十 MB 的二进制产物)。
说明
-
发布模式仍是框架依赖(不含 .NET 运行时)。目标机器需要自备:
Windows → .NET 10 Desktop Runtime;Linux / macOS → .NET 10 Runtime。
想免装运行时,用-SelfContained重发(体积约 +20 MB)。 -
实测 7 个平台(单文件 / 压缩包 / 平台剥离省下的体积):
RID 单文件 压缩包 剥离省下 win-x64 35.8 MB 15.4 MB —(早已剥离) win-x86 33.5 MB 14.7 MB 2.8 MB win-arm64 34.5 MB 14.8 MB 2.8 MB linux-x64 31.2 MB 13.5 MB 1.7 MB linux-arm64 30.9 MB 13.2 MB 1.6 MB osx-x64 35.9 MB 16.2 MB 2.7 MB osx-arm64 36.0 MB 16.2 MB 2.8 MB 全 7 个平台串行发布总耗时约 72 秒(本机增量实测)。
-
只有 win-x64 能在本机实机验证(实跑结果:日志
VibrantbitLauncher 1.0.5.19 启动,异常数 0,配置正常加载)。
其余 6 个平台做了结构校验(.appbundle 布局、install.sh存在、载荷剥离清单),
但没有在对应系统上真正启动过。万一某个平台起不来,先用-NoTrim重发以排除平台剥离这个因素。 -
写
.ps1时踩到的两个环境坑(都已在脚本里处理):- Windows PowerShell 5.1 会按系统 ANSI 代码页解析「无 BOM 的 .ps1」,中文注释会整体乱码并诱发语法错误
→ 脚本必须存成 UTF-8 with BOM; - 某些执行环境把
Remove-Item改写成「移到回收站」,可能删成功了却仍然抛错
→ 删除后复查目录是否真的还在,只有还在才算失败。
- Windows PowerShell 5.1 会按系统 ANSI 代码页解析「无 BOM 的 .ps1」,中文注释会整体乱码并诱发语法错误
[1.0.5.18] - 2026-10-07
继续精简发布体积:37.9 MB → 35.8 MB(单文件,−2.1 MB)。这次是重写 SkinService、把
SixLabors.ImageSharp 整块摘掉,功能与像素结果完全不变。
改进
- 新增
Helpers/RgbaBitmap.cs:基于 SkiaSharp(Avalonia 渲染后端本来就依赖它)的最小 RGBA 位图,
提供 PNG 解码 / 编码、像素读写、最近邻缩放。 SkinService改用RgbaBitmap自己实现头像裁剪与正面立绘拼合,不再依赖MinecraftLaunch.Skin的
CropSkinHeadBitmap();新增公开入口SkinService.WriteHeadBitmap(path, skinBytes)与
SkinService.TryReadImageSize(...)(只读尺寸、不解码像素)。SkinService.ValidateSkinFile改为只读图片头拿尺寸,不再为校验而整图解码。AccountPageViewModel的三处头像裁剪(微软登录 / 外置登录 / 离线默认皮肤)统一改走SkinService.WriteHeadBitmap。
移除
SixLabors.ImageSharp(2.0 MB):全项目仅皮肤裁剪用到,已由 SkiaSharp 顶替。MinecraftLaunch.Skin(0.02 MB):唯一被用到的是CropSkinHeadBitmap(),其实现正是 ImageSharp。- 新增显式
SkiaSharp引用(版本与 Avalonia 12.1.0 解析出的 3.119.4 一致,不改变发布载荷,
只是把原先的隐式传递引用写明白,避免将来 Avalonia 换版本时编译静默失效)。
说明
- 踩到的坑:
SKBitmap.Decode返回的是预乘(Premul)位图。 半透明像素在预乘 / 反预乘往返时会有 ±1 取整误差,
A=1的像素 RGB 甚至会被抹成 0。改用SKCodec并显式要求SKAlphaType.Unpremul后,
实测可与 PNG 原值逐字节一致。 - 头像裁剪的算法与参数(底图 (8,8)–(16,16)、叠加层 (40,8)–(48,16)、叠加层仅
A=255时覆盖、
最近邻放大到 60×60)逐字照搬原库行为,刻意连它「叠加层必须完全不透明才生效」这个略显粗糙的规则一起保留,
就是为了让换库前后生成的头像逐像素一致。 - 对照验证 18 项全过:新实现 vs 旧库
CropSkinHeadBitmap、新实现 vs 旧SaveFrontView(ImageSharp 复现),
以及新实现 vs 改动前旧代码真实产出的存量头像 / 立绘 PNG,逐像素一致;
另覆盖 64×32 旧皮肤的镜像分支、半透明 /A=1合成皮肤、以及文件校验的 6 种输入。 - 发布产物确认:
SixLabors.ImageSharp与MinecraftLaunch.Skin已不在载荷中;实跑 0 异常。
[1.0.5.17] - 2026-10-07
继续精简发布体积:42.6 MB → 37.9 MB(单文件,−4.7 MB / −11%)。
改进
- 剥掉打进发布目录、但 Windows 上永远不会加载的平台程序集(新增
TrimPlatformPayload目标,
带RuntimeIdentifier == win-x64条件,其它平台发布不受影响):
Avalonia.X11、Avalonia.FreeDesktop、Avalonia.FreeDesktop.AtSpi、Tmds.DBus.Protocol(Linux)、
Avalonia.Native、Avalonia.Metal(macOS)、Avalonia.DesignerSupport、Avalonia.Remote.Protocol(设计器)、
System.Diagnostics.EventLog.Messages(事件日志资源)。
根因是Avalonia.Desktop会把所有平台后端一起带进发布载荷,而 win-x64 上UsePlatformDetect()
只会走 Win32 分支。
说明
- 实测排除的几条路:框架依赖 + 单文件 + 压缩被 SDK 直接拒绝(
NETSDK1176:压缩仅支持独立部署);
自包含 + 单文件 + 压缩反而更大(55.3 MB);剪裁(PublishTrimmed)因绑定走反射不能开。 Avalonia.Controls.DataGrid/Avalonia.Controls.ColorPicker(共 0.8 MB)虽然本工程没直接用,
但 FluentAvalonia 有引用,删除有风险,保持不动。
大幅完善设置页面:左栏由「主页 / 个性化 / 其他」三个分类扩成
主页 / 启动 / 个性化 / 网络 / 日志 / 关于 六个分类,并把新增的启动与下载设置真正接进游戏启动链路。
新增
- 启动 · Java 虚拟机与内存:Java 版本选择(带「重新检测」)、最大内存(-Xmx)/ 初始内存(-Xms)
下拉(默认 2G/1G,可「自动推荐」按本机内存一半挑档)、附加 JVM 参数多行输入(一行一条,#开头忽略)。 - 启动 · 游戏目录:Minecraft 文件夹可选可打开;版本隔离开关(每个版本用独立 saves/mods/config)。
- 启动 · 高级:禁用 IPv6(
-Djava.net.preferIPv4Stack=true)、GC 模式(自动/G1GC/ZGC/Parallel/Serial)、
全屏启动、Minecraft 窗体大小(宽×高,改后同时用于${resolution_width/height}与--width/--height)。 - 个性化 · 主界面:启动游戏后对启动器自身的行为(不处理 / 最小化 / 关闭)——只在游戏进程确实起来后才执行。
- 网络 · 下载:BMCLAPI 镜像开关(改动立即影响后续所有下载的 URL 改写)、下载线程数、失败重试次数。
- 日志分类独立成页:记录等级、保留天数、是否输出到调试器、日志目录与打开/导出/清空、最近日志列表。
- 关于分类:版本号、简介与致谢。
改进
- 启动链路全部接入用户设置:内存、GC、IPv6、自定义 JVM 参数、窗体大小 / 全屏、版本隔离
原先都写死在代码里,现在一律读配置(启动日志会打印实际生效的参数)。 - 设置项改动即落盘(
settings.json),初始化回填控件时用抑制标志挡住无意义的重复保存。 - 新增
Models/SettingOption.cs(通用「键-显示名」选项)与内存档位模型,供各下拉复用。 - 下载并发数改为读配置(原版安装器的并发也从 10 固定值改为跟随设置)。
修复
- 设置页「关于」图标用了不存在的
FASymbol成员Info,导致 XAML 编译报
AVLN3000: Unable to find suitable setter ... for property Symbol。已改为合法成员Help。
注:增量构建会跳过 XAML 校验、看不到该错误,需完整重建才会暴露。
修复安装 1.13 以前的老版本(1.12.2 等)时立刻报
「Operation is not valid due to the current state of the object.」的问题。
修复
-
老版本(1.13 以前)装不上、也列不出来。根因在 MinecraftLaunch 4.0.7 的解析器:
MinecraftParser.IsVanilla()拿版本 JSON 里的arguments直接调
JsonElement.TryGetProperty("game"),而 1.13 以前的版本用的是老式的
minecraftArguments字符串、根本没有arguments字段;此时该 JsonElement 的
ValueKind 是 Undefined,System.Text.Json 会抛InvalidOperationException,
文案正是上面那句。同一个 bug 还存在于GameArgumentParser/JvmArgumentParser
(它们也无条件对Arguments调 TryGetProperty),所以老版本连启动都会炸。 -
修法:给缺
arguments的版本 JSON 补一个"arguments": {"game": []}
(见新增的Helpers/VersionJsonNormalizer.cs)。库没有留出「JSON 落盘后、解析前」的钩子,
所以新写了Services/VanillaDownloader.cs——按VanillaInstaller同样的步...