Skip to content

Releases: EasyColorful666/VibrantbitLauncher.Avalonia

VibrantbitLauncher.Avalonia 1.0.10.9

Choose a tag to compare

@github-actions github-actions released this 23 Oct 12:26

更新日志(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 + 260ms DispatcherTimer 防抖,边打边筛。
    版本清单有 900+ 条,打字过程中列表会被反复重建,既抖又看不清结果;
    而版本号本身是短词(1.21、25w45a),一次输完再搜几乎没有成本。

    • 新增「搜索」按钮(与输入框同为 36 高,上下边严格对齐),点它或按回车才发起搜索。
    • 按 Esc 清空关键词并恢复当前筛选范围。
    • 新增 _appliedKeyword 字段:输入框里的文字与正在生效的关键词彻底分开。
      否则「打了字但没提交」时切换版本类型筛选,会把半截关键词一起带进查询。
  • 搜索匹配精度提高(Helpers/VersionSearchMatcher.cs)

    1. 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
      搜 21 时 1.21.4(638)现在稳定排在 1.210(558)之前;改之前两者同档。
    2. 段对齐优先:搜 1.2 时 1.2.5(848)排在 1.21.4(717)之前 ——
      前缀命中但数字段未对齐的降一档,避免 1.2 被 1.21.x 淹没。
    3. 多关键词 AND:空格分隔,全部命中才算命中,分数取平均。
      1.21 pre 现在只出 1.21.4-pre2 这类结果,不再因为整串比对而一个都搜不到。
    4. 连接符归一扩展到段边界识别: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.ps1
    linux-x64 / linux-arm64 ubuntu-latest publish-all.sh
    osx-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。

变更

  • 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」显示
    `匹...
Read more

VibrantbitLauncher.Avalonia 1.0.10.8

Choose a tag to compare

@github-actions github-actions released this 23 Oct 11:45

更新日志(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 + 260ms DispatcherTimer 防抖,边打边筛。
    版本清单有 900+ 条,打字过程中列表会被反复重建,既抖又看不清结果;
    而版本号本身是短词(1.21、25w45a),一次输完再搜几乎没有成本。

    • 新增「搜索」按钮(与输入框同为 36 高,上下边严格对齐),点它或按回车才发起搜索。
    • 按 Esc 清空关键词并恢复当前筛选范围。
    • 新增 _appliedKeyword 字段:输入框里的文字与正在生效的关键词彻底分开。
      否则「打了字但没提交」时切换版本类型筛选,会把半截关键词一起带进查询。
  • 搜索匹配精度提高(Helpers/VersionSearchMatcher.cs)

    1. 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
      搜 21 时 1.21.4(638)现在稳定排在 1.210(558)之前;改之前两者同档。
    2. 段对齐优先:搜 1.2 时 1.2.5(848)排在 1.21.4(717)之前 ——
      前缀命中但数字段未对齐的降一档,避免 1.2 被 1.21.x 淹没。
    3. 多关键词 AND:空格分隔,全部命中才算命中,分数取平均。
      1.21 pre 现在只出 1.21.4-pre2 这类结果,不再因为整串比对而一个都搜不到。
    4. 连接符归一扩展到段边界识别: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.ps1
    linux-x64 / linux-arm64 ubuntu-latest publish-all.sh
    osx-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。

变更

  • 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.Xml 7.4MB、System.Linq.Expressions 3.5MB、
    System.Data.Common 2.6MB、System.Private.DataContractSerialization 2.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 索引。

    排查过程(留档):

    形态 体积 结果
    目录 + 剪裁...
Read more

VibrantbitLauncher.Avalonia 1.0.10.6

Choose a tag to compare

@github-actions github-actions released this 23 Oct 11:34

更新日志(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 + 260ms DispatcherTimer 防抖,边打边筛。
    版本清单有 900+ 条,打字过程中列表会被反复重建,既抖又看不清结果;
    而版本号本身是短词(1.21、25w45a),一次输完再搜几乎没有成本。

    • 新增「搜索」按钮(与输入框同为 36 高,上下边严格对齐),点它或按回车才发起搜索。
    • 按 Esc 清空关键词并恢复当前筛选范围。
    • 新增 _appliedKeyword 字段:输入框里的文字与正在生效的关键词彻底分开。
      否则「打了字但没提交」时切换版本类型筛选,会把半截关键词一起带进查询。
  • 搜索匹配精度提高(Helpers/VersionSearchMatcher.cs)

    1. 数字段边界判定:命中结束处若紧跟数字,说明数字段被拦腰截断,只给降级分。
      搜 21 时 1.21.4(638)现在稳定排在 1.210(558)之前;改之前两者同档。
    2. 段对齐优先:搜 1.2 时 1.2.5(848)排在 1.21.4(717)之前 ——
      前缀命中但数字段未对齐的降一档,避免 1.2 被 1.21.x 淹没。
    3. 多关键词 AND:空格分隔,全部命中才算命中,分数取平均。
      1.21 pre 现在只出 1.21.4-pre2 这类结果,不再因为整串比对而一个都搜不到。
    4. 连接符归一扩展到段边界识别: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.ps1
    linux-x64 / linux-arm64 ubuntu-latest publish-all.sh
    osx-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。

变更

  • 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.Xml 7.4MB、System.Linq.Expressions 3.5MB、
    System.Data.Common 2.6MB、System.Private.DataContractSerialization 2.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,不用重启)。

界面设计

右侧面板刻意没有做成三张等宽卡片(模板化界面的典型特征,...

Read more

VibrantbitLauncher.Avalonia 1.0.10.5

Choose a tag to compare

@github-actions github-actions released this 23 Oct 04:55

更新日志(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.ps1
    linux-x64 / linux-arm64 ubuntu-latest publish-all.sh
    osx-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。

变更

  • 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.Xml 7.4MB、System.Linq.Expressions 3.5MB、
    System.Data.Common 2.6MB、System.Private.DataContractSerialization 2.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。

    三个叠在一起的根因,全部用真实数据实测确认:

    1. 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 字节,
      SHA1 be81e6d2… 与 install_profile 声明的 PATCHED_SHA 逐字节一致。

    2. 处理器产物被当成可下载依赖。
      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),只由处理器本地生成。

    3. 处理器没产出也不报错。
      EnsurePatchedLibrary 发现产物不存在时只打一条 warning 就当安装成功,
      留下一个「装好了但起不来」的实例。现在:client 侧处理器明确声明了
      {PATCHED} 输出却没产出 → 安装直接失败并说明原因(附磁盘空间提示);
      没有该声明(服务端专有布局)才降级为 warning。

  • 「补全资源」对上述文件只会失败,不会修。
    旧流程把缺失库交给库的下载器,而这类文件的 url 是空字符串,必然 404。
    现在补全时先识别「没有 url、只能由处理器生成」的库:

    • 若能解析出 Forge / NeoForge 实例(id 形如 26.2_forge_65.1.5),
      就自动重跑一次加载器安装来重建补丁文件(库已在本地,实际只重跑处理器);
    • 重跑成功并复核通过 → 返回「已补全」;
    • 重跑失败 → 明确提示「需要重新安装加载器」,不再让用户对着「连接超时」猜。
  • 顺带修掉每个 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),随后该字段清零,不会重复迁移。

修复

  • 安装页:勾选一个加载器后,其余加载器的列表框仍可操作。

    只禁用勾选框是不够的 —— 另一个加载器的列表还能点,选中版本后...

Read more

1.0.7.14

Choose a tag to compare

@EasyColorful666 EasyColorful666 released this 18 Oct 00:43

更新日志(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 主色圆点,避免同一行出现两个互相竞争的动作。
  • 强调色预算收紧到 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 个账户」计数徽标,账户为空时自动隐藏。
  • 账户卡片增加「当前使用」状态。以前只靠一个 RadioButton 表达选中,而它和
    ViewModel 里的 selectedAccount 是两份状态,自动恢复上次账户时两边会对不上。
    现在由 User.IsSelected 单一数据源驱动三处视觉:主色边框、顶部渐变条、头像外环,
    外加右上角勾选徽章;RadioButton 换成普通按钮,文字在「使用该账户 / 当前使用」之间切换。
  • 卡片上的账户类型改为中文(「微软账户 / 外置账户 / 离线账户」),「皮肤 / 删除」按钮改为小号描边样式,
    删除按钮保留危险色。

新增

  • 账户页动效:
    • 卡片入场:从下方 10px、97% 缩放、全透明浮到终态,多个卡片按 45ms 错落出现;
      已入场过的卡片不会在增删账户时重复播放。
    • 卡片悬停:整卡 1.8% 抬升 + 阴影 + 边框高亮,头像同时放大 5%。
    • 选中态:顶部渐变条淡入、头像外环浮现、勾选徽章从 60% 弹性放大。
    • 添加账户按钮:悬停时图标块放大 8%、背景加深,右侧箭头右移 3px、透明度提高。
    • 全部动效走 Transitions 声明式表达,并补充 .performanceMode 覆盖,
      性能模式下一次性关掉(缩放复位、过渡清空)。
  • 新增两个语义化画刷键 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)
    • 进度窗口:标题「配装码安装」,格式标签「配装分享码」,步骤「下载配装模组」
    • 提示文案:开始 / 完成 / 取消 / 失败全部按「配装」措辞
  • 取消后任务显示「失败」:新增 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 规则与安装逻辑口径不一致。
  • 勾选状态与已选版本号强绑定:未勾选的加载器,其 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 堆栈:
    这类异常是「程序退出时仍在飞的...
Read more

1.0.6.12

Choose a tag to compare

@EasyColorful666 EasyColorful666 released this 15 Oct 13:13

更新日志(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 与文件级事件),原来只有百分比和一句文案。
    • 关闭窗口不会中断安装;「下载任务」列表里整合包任务的「查看详情」按钮现在直接打开这个窗口
      (安装进行中可随时把详情页调出来)。
  • **下...
Read more

1.0.5.20

Choose a tag to compare

@EasyColorful666 EasyColorful666 released this 07 Oct 04:49

更新日志(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 / Metal
    • linux-* → 去掉 Win32 / Win32.Automation / Native / Metal
    • osx-* → 去掉 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 个平台做了结构校验(.app bundle 布局、install.sh 存在、载荷剥离清单),
    但没有在对应系统上真正启动过。万一某个平台起不来,先用 -NoTrim 重发以排除平台剥离这个因素。

  • 写 .ps1 时踩到的两个环境坑(都已在脚本里处理):

    1. Windows PowerShell 5.1 会按系统 ANSI 代码页解析「无 BOM 的 .ps1」,中文注释会整体乱码并诱发语法错误
      → 脚本必须存成 UTF-8 with BOM;
    2. 某些执行环境把 Remove-Item 改写成「移到回收站」,可能删成功了却仍然抛错
      → 删除后复查目录是否真的还在,只有还在才算失败。

[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 同样的步...

Read more