Skip to content

fix: 老版本 TickEvent、headless 下浏览器/文件对话框、饰品修复;自动录制回放、配置备份回滚 - #8

Merged
gaoyu06 merged 17 commits into
masterfrom
fix/nova-review
Oct 16, 2026
Merged

gaoyu06 merged 17 commits into
masterfrom
fix/nova-review

Conversation

@gaoyu06

@gaoyu06 gaoyu06 commented Oct 16, 2026

Copy link
Copy Markdown
Member

修复全量审查中的 nova-1~22(nova-20/21 部分修复)。

  • 1.19.2、1.20.1、1.21.1 现在也派发 TickEvent,疾跑、潜行切换、饰品缓存等恢复。
  • headless 模式下:打开链接改用 MC 自带的 openUri,并提供复制链接兜底;文件选择改用 tinyfd。
  • 新功能:进服自动录制回放、保存最近 N 分钟片段;配置自动备份与回滚。
    验证:1.21.11 build+test 通过,26.2/1.19.2 compileKotlin 通过,两个版本 runClient 能进主菜单。因为没有辅助功能权限,没能进入世界验证疾跑。

1.19.2/1.20.1/1.21.1 上 Minecraft.tick 的 HEAD 注入被包在 >=1.21.5 里,
TickEvent 从不派发:疾跑、潜行切换、自由视角、平滑缩放、连击统计、
饰品 loadout 拉取都不工作,Optimization 的粒子计数从不清零,约 400 个
粒子后所有粒子被丢弃。tick()V 在六个版本签名一致,去掉门控。

Client.tick 仍是每帧(runTick HEAD,排空延迟切屏),TickEvent 是 20Hz
游戏 tick,两者语义不同,各只注入一次。

clickGuiKey 为 0(未绑定)时跳过 glfwGetKey,避免每帧 GLFW_INVALID_ENUM。
依赖:top.fpsmaster:prism 0.2.2、top.fpsmaster:music-api 0.1.2(暂从
mavenLocal 取)。CI 增加 Cadence 的 publishToMavenLocal,Prism/Cadence 的
checkout ref 先指向 review 分支并留 TODO,待发版后改成固定提交。

Prism 宿主适配:
- ToolkitScreen:Ctrl/Cmd+A/C/X/V 映射成 SELECT_ALL/COPY/CUT/PASTE,不再
  当字母输入;映射 HOME/END;每帧同步 SHIFT;FrameInput 接系统剪贴板。
- 滚轮:scrollY==0(纯横向滑动)忽略,触控板小数增量累积满一格再提交,
  修复横滑往下滚、竖滑过快(nova-17)。
- ConfigProfilesBridge 全部改为返回 Result。
- MusicBridge 上报 tracksState/playlistsState 四态与本地化原因;Cadence
  的 MusicApiException 按 kind 映射文案,NOT_LOGGED_IN/getLoginUid==null
  时清掉失效的网易云 cookie。Nova 没有音乐登录界面,网易云未登录时发现页
  仍回落到热歌,而不是停在无法操作的“登录后查看”。
- 音乐请求改用有界线程池;列表请求和取直链都带代际号,迟到的响应不再
  覆盖新结果,也不会播放已被替换的曲目。正在播放的曲目单独保存,搜索或
  打开歌单后标题、封面、歌词与下一首不再错位(nova-14)。
- Prism 屏幕自己绘制通知(HUD 上的通知在 Prism 屏幕下不再重复绘制)。
- 补齐 Prism 新增的 music.* 等 i18n key(中英)。

AWT(nova-2/3):MC 的 Main 强制 java.awt.headless=true,Desktop.browse
恒不可用,FileDialog 构造即抛 HeadlessException。
- 新增 NativeDialogs:链接和文件夹走 Util.getPlatform().openUri/openFile,
  文件选择走 MC 自带的 LWJGL tinyfd(六个版本都带 lwjgl-tinyfd),在工作
  线程弹出,结果回到渲染线程;取消、原生库缺失、同时只允许一个对话框都
  不抛进渲染栈。
- 微软登录:浏览器打不开时提示,登录码框改为“点击复制登录链接”。
- 背景图、配置导入、本地音乐导入、饰品文件夹、打开官网全部改用上面的
  实现;配置导入的结果以通知显示。
play/playFile 先 stop 再把共享的 stopped 置回 false:interrupt 打断不了
HttpURLConnection 的阻塞读和 SourceDataLine.write,旧线程 500ms 内没退出
就会读到 false 继续下载和播放,两路音频叠在一起、互相改进度;而这 500ms
的 join 跑在渲染线程上(nova-13)。

现在每次 play/stop 推进代际号,播放线程只认自己那一代,过期后自行关闭
line 和临时文件;stop 只 interrupt 不 join。
插入推荐服、置顶服之后,vanilla OnlineServerEntry 仍用 children().indexOf
得到的显示下标去调 ServerList.swap:点最后一行的上移会
IndexOutOfBoundsException 崩溃,其他移动会挪错服务器,点推荐服的下移会
交换玩家自己的两个服务器(nova-4)。

MixinServerSelectionList 记下本次排列的显示顺序,新增 MixinOnlineServerEntry
重定向 swap(II)V 里的 ServerList.swap:两个下标都按对象换算回 servers.dat
的位置,涉及推荐服(不在 servers.dat 里)的移动直接跳过。六个版本的
mixins.json 都已注册。
计分板(nova-5):listPlayerScores 来自 HashMap,原来不排序、也不套队伍
前后缀,Hypixel/起床战争这类把文字放在前后缀里的计分板顺序乱、内容空白;
旧版分支把最低分画在最上面。现在与 Gui.displayScoreboardSidebar 一致:
队伍颜色专属的 sidebar 槽优先,分数降序、同分按名字取前 15,名字过
PlayerTeam.formatNameForTeam,分数按 objective 的 numberFormat 显示
(blank 格式不再拼出数字)。<1.20.5 用 getPlayerScores 已排好的升序取最后
15 个倒序绘制。26.2 的队伍颜色改成 Optional<TeamColor>,单独分支。

性能(nova-7):每帧对全部组件(含未启用的)做两次 measure,TNTTimer
每次都全量扫实体。现在先过滤 shouldRender,只对要画的组件 adapt 一次
measure 一次再直接 renderAt;TNTTimer 的实体扫描按 HUD 帧缓存。
按 CONTRACTS 第 1、3 节:
- 所有 FPSMaster API 请求带 Accept-Language(游戏语言为中文发 zh-CN,否则
  en);ApiResult 新增 code,逻辑只看 code,界面直接显示后端本地化的
  message,网络失败/解析失败等本地错误用客户端自己的中英文案。登录错误、
  封禁提示、购买失败都改成这样,不再比对 "account is banned" 原文;
  ITEM_ALREADY_OWNED 时刷新已拥有列表。
- 他人 loadout 解析(nova-9):失败不写缓存导致下一 tick 立刻重发。现在
  失败按 5s 起步、翻倍、上限 5 分钟退避,404(后端未部署)直接等上限;
  成功路径对无饰品玩家写 5 分钟负缓存(原有);同一 tick 合并成批,
  inFlight 中的不算“未知”。
- 饰品贴图(nova-10):下载或校验失败后渲染路径每帧重下。现在记住失败:
  网络错误/5xx/429 从 30s 起退避,404 和校验失败等 30 分钟;重新加载自定义
  饰品时清掉本地饰品的失败记录。
- 账号绑定(nova-16):切换 MC 账号(applyUser)时重置 MinecraftLinkClient;
  Mojang joinServer 只有 401/403 判 OFFLINE,网络异常、429、5xx 判 FAILED
  并按 30s 起、上限 10 分钟退避重试,不再一次抖动就整局不绑定。
bootstrap 包(nova-6):LoginPacket 等会话包只在登录时经过一次,而原来只在
录制期间收集,录制又必须进服后才能开,录像里永远缺 LoginPacket,回放没有
ClientLevel。现在不录制时也收集(只编码 allowlist 里的少数包,其余直接跳过)。
另一个根因:LoginPacket 到达时 mc.player 还是 null,mc.connection 也是
null,1.20.5+ 没有 registries 就编码不了;改为从接收包的 Connection 的
listener 取 registryAccess。

新功能(Replay 模块设置项):
- 进服自动录制:加入服务器自动开始完整录制,离开时停止。离开世界时也会
  停止手动开始的录制,免得同一个文件接着录下一个服务器。
- 缓存最近片段 + 时长(1–10 分钟)+ 保存热键(默认 F7):ClipRing 按 60s
  分段,每段以关键帧(会话包 + 地形快照)开头,片段总从段边界开始,最旧的
  段要等下一段关键帧完整才淘汰,所以任何时刻都能存出可播放的录像。内存按
  时间窗和字节预算(堆的 1/8,32–256MB)双重限制。地形快照每 tick 12 个
  区块分摊,避免一次几十毫秒的卡顿;保存在独立线程从不可变副本写出,完成后
  通知。热键在模块关闭(未录制)时也有效,单独注册 ReplayHotkey。

写盘(与 nova-20 一并):停止录制不再在渲染线程/网络线程 join 写线程最多
5 秒,写线程自己排空并关闭文件;只有退出游戏时等待,防止守护线程丢掉尾巴。
打开录像的内存上限改为堆的 1/4(64–512MB),超过时截断并弹通知说明,
不再静默。

顺带:同一个键绑定多个模块时只保存一次配置(nova-12 的一部分)。
加载(nova-11):
- loadDefault 捕获任意异常(原来只捕获 CommandExecutionException),
  gson.fromJson 的字段类型错误也归为“配置无效”;单个值类型不对只跳过该值
  并记日志,不再让 onInitialize 崩溃、每次启动都崩。
- 损坏的配置移为 .invalid 后,按从新到旧尝试备份,第一个能加载的写回为
  当前配置;都不行才恢复默认。结果在加载画面结束后的第一个界面上以通知
  提示(语言此时才初始化)。

备份与回滚(新功能):
- ConfigBackups:configs/backups/<档案>/<时间>.json,内容变化且距上次
  10 分钟以上才备份,启动时成功加载的文件强制备份一份,保留 10 份。
- 配置档案界面面板下方新增“回滚到上一个备份”(两次点击确认),也可用
  .config rollback。回滚前先把当前状态备份,再回滚一次即撤销。

写盘(nova-12):saveActive 在渲染线程只做序列化,写文件和 fsync 交给单
线程 IO,同一路径只写最新一份;读取/替换档案文件(加载、导出、导入、重命名、
删除、回滚)前先等队列写完,退出游戏时也等待。写失败弹通知。

accounts.json(nova-15):改为原子写,另存一份 .bak,主文件损坏时从 .bak
恢复;微软 access/refresh token 移到用户目录(%APPDATA%/FPSMaster 或
~/.fpsmaster)的 nova-accounts.json(POSIX 下 0600),实例目录里不再有令牌;
旧文件里内联的令牌在首次加载后迁移并从实例文件中删除。
按 CONTRACTS 第 6 节:启动器改用环境变量传 token,旧的 -Dfpsmaster.auth.token
暂时并行保留给旧客户端。Nova 先读环境变量,读不到再读系统属性;无论哪条
路径都立即 clearProperty(token、refreshToken、tokenExpiresAt),之后转储
系统属性的崩溃报告、诊断或其他模组都读不到。附单测。
nova-8:tick 每帧调用,原来每帧都构造 presence 快照(最多 9 次 SHA-256 加
逐字节 String.format);失败不更新时间戳,下一帧就重发,后端故障时所有客户端
按帧率刷请求;退出时同步 send,最坏卡 30 秒。

现在先用廉价状态判断是否要发,只在发送时构造快照;服务器地址的哈希只在
地址变化时计算,hex 用 HexFormat。失败后 30s 起翻倍到 15 分钟退避,429
直接等上限。退出时的 offline 通知改为异步发送并最多等 1.5 秒。
nova-19:每帧对每一行重建字符串、去格式化再查 1 秒 TTL 的缓存,playerInfo
每行每帧线性遍历在线玩家。现在以 FormattedCharSequence 对象为键弱引用缓存
(聊天每帧交回的是同一个对象,行内容不变),未识别出的结果仍只缓存 1 秒,
以便发送者稍后出现在 Tab 列表;playerInfo 改查 500ms 刷新一次的名字索引。
nova-22:1.21.11+ 的 NovaBlurTexture.attach 每次重建都 createSampler 且从不
关闭,拖动窗口边缘时每帧一个;改用 RenderSystem.getSamplerCache()。离屏
RT 原来只在退出时释放(4K 下约 33MB 显存常驻),现在 ToolkitScreen 关闭后
若下一个界面不是 Prism 界面就释放,下次用到时再按需重建。
nova-21 的一部分:Client.openWebView 没有任何调用方,产品界面不再打开
WebView,这两个设置项对玩家没有任何作用。值本身保留(BasicBrowser 仍读取),
只是不在设置里列出。CEF 依赖整体去留与 mcef-nova 1.0.2 升级另行处理。
runClient 冒烟时可直接从日志确认旧版本也在派发游戏 tick;未开启 smoke 时
JIT 会把这两处判断折叠掉。
clear() 递增代号,link() 开始时记下代号;回调写 LINKED/OFFLINE/FAILED 前核对代号,
不一致就丢弃,避免新账号被标成已绑定旧 UUID、或被旧失败覆盖状态。
异步停止的写入队列记在 draining 里,stop(wait = true) 在当前录像之外也 join 它们,
自动录制在离开世界时停下后马上退出游戏不再丢掉录像结尾。
@gaoyu06
gaoyu06 merged commit bfce67b into master Oct 16, 2026
8 checks passed
@gaoyu06
gaoyu06 deleted the fix/nova-review branch October 24, 2026 13:05
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