Skip to content

refactor: LauncherHelper 迁移至 Kotlin,启动任务链改为协程驱动 - #1877

Open
ShirosakiMio wants to merge 4 commits into
mainfrom
refactor/launcher-helper-kotlin-coroutine
Open

ShirosakiMio wants to merge 4 commits into
mainfrom
refactor/launcher-helper-kotlin-coroutine

Conversation

@ShirosakiMio

Copy link
Copy Markdown
Collaborator

改动概述

将启动编排 LauncherHelper 从 776 行的 Java 任务链(15 层 thenComposeAsync + AtomicReference 传值 + withStage/withStagesHint)重写为单个线性协程流程,同时保留启动弹窗的任务列表阶段展示与下载进度展示。外部调用方 Versions.launch 只使用构造器 + launch(),公开签名未变。

方案要点

  • 线性协程:launch0() 按顺序执行版本解析 → mod 扫描/lwjgl3ify 补丁 → Java 检查 → 依赖补全 → 解包补丁 jar → 登录 → 构建启动器 → 启动前检查 → 主线程拉起 JVMActivity;弹窗标题固定为「启动游戏」,阶段列表随协程推进打勾
  • 弹窗协程化:路径/渲染器/原生库插件/modloader/mod 检查与 Java 不匹配、登录异常弹窗改为 suspendCancellableCoroutine 挂起等按钮回调,选「取消」即中止启动,语义与原 CompletableFuture + completeExceptionally 一致
  • fclcore 子任务桥接 runTracked():游戏文件补全 + 整合包补全(Task.allOf 并行)与 GameVerificationFixTask 仍经独立 TaskExecutor 运行,执行器接入弹窗既有阶段列表复用下载进度展示;协程取消级联 executor.cancel(),任务被外部取消(exception 为 null)则按协程取消处理
  • TaskListPane 纯展示模式:新构造器按传入阶段列表预生成阶段行(复用 StageNode 本地化逻辑),attachExecutor 将任务行事件转发进同一列表;原先内嵌在 TaskListener 的回调抽为共享方法,任务名映射链抽成 applyTaskName,执行器驱动与外部驱动共用一套代码
  • 取消语义:TaskDialog 取消按钮直接 cancel() 协程 Job;从检查弹窗取消则抛 CancellationException 由 launch() 捕获后幂等关闭弹窗,两条路径弹窗都正确关闭且不弹错误框

顺带修复

  • menu_setting.json 解析结果为 null 时 scaleFactor 保持初始值 0 的问题,统一回退 1.0
  • 检查弹窗(模组不兼容等)点「取消」后启动弹窗卡在屏幕不关闭的问题
  • TaskDialog 多次 setExecutor 时释放上一个 TaskListPane,避免监听与视图树残留(安装器等原有流程行为不变)

测试

  • Windows(Zulu 17)::FCL:compileDebugKotlin、compileDebugJavaWithJavac、assembleDebug 全部通过
  • x86_64 模拟器(Android 15)端到端实测:
    • 1.12.2-Legacy Fabric、1.20.1-Forge(47.4.23)、Fabulously Optimized 1.21.1(44 模组)均完整启动到 MC 标题画面
    • 阶段列表随协程推进逐行打勾,取消路径弹窗全部关闭、无 Start JVMActivity、可再次启动
    • 模组不兼容弹窗:点「取消」启动中止且弹窗全关;点「无视风险」正常启动
    • 全程 logcat FATAL EXCEPTION 计数为 0,无协程相关异常

- 15 层 thenComposeAsync 任务链替换为单个线性协程(Dispatchers.IO),
  阶段进度经弹窗标题实时展示;取消按钮直接 cancel 协程 Job,
  用户取消静默、失败切主线程弹错误框,语义与原 executor 一致
- 路径/渲染器/插件/modloader/mod 检查与 Java 不匹配、登录异常弹窗
  改为 suspendCancellableCoroutine 挂起等按钮回调
- fclcore 子任务(游戏补全、整合包补全、GameVerificationFixTask)经
  runTracked 桥接:独立 TaskExecutor 绑定弹窗复用下载进度,取消双向级联
- TaskDialog 多次 setExecutor 时先 release 上一个 TaskListPane 防残留
- 顺带修复 menu_setting.json 解析为 null 时 scaleFactor 落回 0 的问题
- 已在 x86_64 模拟器实测:1.12.2-Fabric 与 1.20.1-Forge 均正常启动,
  取消路径无错误弹窗且可再次启动,全程无 FATAL 异常
- TaskListPane 新增纯展示模式:按传入阶段列表预生成阶段行,
  attachExecutor 转发桥接执行器事件;任务名映射链抽为 applyTaskName 共享
- TaskDialog 新增 setStages/beginStage/succeedStage/failStage;
  setExecutor 检测到展示模式列表时转发而非重建,安装器流程行为不变
- LauncherHelper 以与原 withStagesHint 一致的六个阶段 key 驱动阶段行,
  进入下一阶段时前一阶段打勾,成功收尾与失败分别标记
- launch() 的 CancellationException 分支幂等关闭启动弹窗:经检查弹窗
  取消时 Job 未真正 cancel,原实现无人关闭弹窗导致其卡在屏幕上
- 设备实测:阶段列表随协程推进打勾;模组不兼容弹窗点取消后全部弹窗
  关闭且启动中止,点无视风险则正常启动
awaitChoice 在主线程执行,但返回后调用方恢复在 IO 上下文,跳转版本管理页的界面操作
(选中 manage 页、切到“游戏”标签页)在后台线程执行会抛 CalledFromWrongThreadException,
该异常随即被同一函数的兜底 catch 吞掉——用户选择“安装”却照常启动游戏。改为显式切回主
线程执行跳转,跳转失败同样取消本次启动,不再退化为继续启动
- launch0 吸收 10b27b1:cursorOffset 随 MenuSetting 读取,启动前 FCLBridge.initForceResolution,getLaunchOptions 改 5 参
- launch() 异常兜底放宽到 Throwable,收尾弹窗/提示改 NonCancellable,防取消瞬间 withContext 抛出致 Error 路径失控
- 启动协程结束时取消 mainScope,消除常驻 SupervisorJob 泄漏
- awaitChoice/awaitLoginChoice 增加 invokeOnCancellation 关闭停留屏幕的弹窗
- TaskListPane.attachExecutor 接入新执行器前解除上一个的监听
- lwjgl3ify 注释对齐内存合并语义
@ShirosakiMio
ShirosakiMio force-pushed the refactor/launcher-helper-kotlin-coroutine branch from 45f903d to 9ed8341 Compare October 27, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant