信创 2026/11/01 20:57

[肥鱼大战信创] ARM64 国产机成功运行 Minecraft 26.X

其实这台信创机刚发下来的时候,最先折腾的其实就是 Minecraft。因为麒麟 OS 自带的库太老了,Linux 内核版本也非常低,然后还有华为这颗 9000X 的 ARM 负加成,导致能正常玩个能依靠【最能跨平台】的 Java 的游戏都难跑起来,当时自己折腾的时候,只能是找了一个老版本的 HMCL 来安游戏,最高还只能安 1.21.11 这个版本,再高的话 JDK25 就跑不起来了(因为系统少东西)。但即使是这样,游戏跑起来也不破 10 帧,安了 1.9.4 之后,因为版本老的缘故,帧率能稍微高一些,但也不算是能玩,神仙来了这帧率都得晕 3D,emmmm....

前一阵折腾完罪恶都市之后,大肥鱼提到了【软件不会默认跑在硬解】的问题。于是便让大肥鱼按照这个出发点来想办法,再加上让他试图自己补齐.so库,甚至都让他想,装在 Docker 里是不是能行得通。

结果大肥鱼一顿找,居然意外的找到了华子的 Vulcan 驱动,于是就让大肥鱼猛猛干,结果就是不仅把 Vulcan 调明白了(虽说 Vulcan 层华子也是少东西的,大肥鱼魔改之后加了点东西才能跑),高版本 JDK 也能跑了(也是大肥鱼神力,加了些东西),帧数还一度接近 200 帧,非常不错,已经是能玩的水平了,然后顺便让大肥鱼把启动器切换成了 Prism。

顺便又让大肥鱼解决了下老版本,但很可惜,老版本打死都跑不了硬解,即使是用华子自己的zink驱动(OpenGL 转译 Vulcan)都不行,一顿花屏小连招,玩都玩不了,只能跑软解,软解帧率还没法玩,所以说只能让大肥鱼阉割掉了启动器的老版本游戏下载,只能下载 26.X 或更新的游戏。

大肥鱼正在深度思考DeepSeek
已完成思考以下内容由 AI 生成

一句话先剧透:这台机器上跑 MC 的所有麻烦,根源都不是「哪个功能坏了」,而是「官方默认的那套东西,在这台非 x86、非主流 GPU 的机器上根本不成立」。我们做的每一件事,都是把「官方默认」换成「这台机器上能用的等价物」。

一、先聊三条「保命」原则

正式开始之前,有三条原则,它们决定了这次调试是「越调越乱」还是「越调越清楚」。这三条比任何一条具体命令都值钱。

第一条:分清「沙箱假象」和「机器真坏」。

如果你是在一个受限环境里操作(比如 WorkBuddy 的 KYSEC 沙箱、容器)(题主补充:大肥鱼说的 KYSEC 是麒麟 OS 的安全策略套件),你会看到一堆诡异的现象:系统目录是只读的、文件属主显示成 nobody、跑脚本报「权限不够」、sudo 好像失效了。这些大概率是沙箱给你的假视图,不是机器真的坏了。 别急着下「系统坏了」的结论,先想办法绕过去;绕不过就一句话说明现状,继续往下走。

第二条:图形程序要从桌面图标启动,别从 shell 里起。

沙箱会破坏图形栈。你在 shell 里起游戏,可能会看到 MIT-SHM ShmPutImage BadValue、Mesa shader cache 失败、vkCreateInstance -9 这些报错——它们全是沙箱产物,不是驱动坏了、也不是游戏坏了。 想判断「这游戏到底能不能硬解」,必须回到真机桌面,双击图标启动再下结论。

第三条:崩溃日志的调用栈,是唯一不会骗你的证据。

MC 崩溃后会在实例目录留下 hs_err_pid*.log。里面有两个信息最有用:elapsed time(告诉你活了多久,是启动期崩还是运行中崩),以及 Current thread + Native frames(告诉你崩在哪条调用链上)。先读栈,再动手,别靠猜。


二、全景:这台机器上有五个坑等着你

按「从底层到上层」的顺序,一共五层坑。每一层不解决,游戏就起不来或者跑不对:

坑一句话说清楚问题一句话说清楚解法
① 图形后端OpenGL 只能软渲染(llvmpipe),卡成幻灯片钉死 Vulkan 后端
② Vulkan 扩展驱动缺 VK_KHR_push_descriptor,MC 硬性要求写个 loader shim 伪造这个扩展
③ 运行库版本系统 glibc 2.31,Prism/Qt6 要 2.38自备新 glibc,只给需要的进程用
④ 下载源官方源批量下 jar 不可靠全部改道 BMCLAPI,并解决改道带来的校验错配
⑤ 退出崩溃点窗口 X 时 SDL_Quit 清理崩溃把 SDL_Quit 的跳板 patch 成 no-op

下面一个一个展开,每个都讲「为什么坏」「怎么修」「怎么让它以后不用再修」。


三、五个坑,逐个拆

坑 ①:OpenGL 软解,Vulkan 才是出路

现象:游戏只有个位数帧,glxinfo 一看,渲染器是 llvmpipe(软件光栅化,也就是用 CPU 硬算)。

为什么坏:Maleoon 920 是移动端 GPU。系统自带的 Mesa(glvnd 20.x)加上 zink(22.3.x)在这套组合下错配,OpenGL 这条路径走不通硬解,只能掉回软件渲染。但好消息是——Maleoon 提供了原生的 Vulkan ICD(/etc/vulkan/icd.d/maleoon_vulkan.json),Vulkan 这条路是通的、是硬解。

怎么修:

  • 环境变量钉死 MOJANG_GL_BACKEND=vulkan。
  • 更隐蔽的一个坑:MC 26.x 在「上次异常退出」时,会偷偷把 options.txt 里的 preferredGraphicsBackend 改回 opengl。所以光设一次环境变量不够,得在每次启动前检查 options.txt,发现不是 vulkan 就改回来。
  • 顺手清掉会干扰图形栈的继承变量(QT_QPA_PLATFORM、GDK_BACKEND、MESA_LOADER_DRIVER_OVERRIDE、LIBGL_ALWAYS_SOFTWARE 这些)。

让它以后不用再修:这一步做在「每次启动都会跑的 Java 包装器」里,而不是改某个实例的配置。这样你以后新建任何实例、换任何版本,都会自动生效。

坑 ②:驱动缺扩展,就「伪造」一个给它

现象:程序一启动就报「缺少 Vulkan 扩展」然后拒绝运行。

为什么坏:MC 26.x 用的 LWJGL 3.4.x 依赖 VK_KHR_push_descriptor 这个扩展,但 Maleoon 驱动没导出它。

怎么修:写一个 Vulkan loader shim——本质是一个 libvulkan.so.1,用 LD_LIBRARY_PATH 让程序优先加载它。它的职责是「两头骗」:

  • 对上(对游戏):谎称自己支持 VK_KHR_push_descriptor,让游戏以为驱动有这个扩展;
  • 对下(对真驱动):把 push descriptor 的 API 调用,翻译成等价的普通 descriptor API 去执行。

让它以后不用再修:shim 是按「拦截哪几个函数」来设计的,跟具体游戏、具体版本无关。这套思路是通用的——任何「应用硬性要求、驱动不提供」的扩展,都能用同一个办法解决。

坑 ③:glibc 版本对不上,自备一套「局部使用」

现象:Prism 启动器自己需要 Mesa24/Qt6(依赖 glibc 2.38),但系统只有 glibc 2.31,起不来。

怎么修:

  • 自备一套新 glibc 和新的动态链接器 ld-linux-aarch64.so.1,放在自包含目录里。
  • 用 ld.so --library-path <新glibc路径> <程序> 的方式,只让 Prism 本体用新运行库。

这里有个特别容易踩的坑,务必记住:千万、千万不要把新 glibc 路径写进 LD_LIBRARY_PATH 再 export 出去。因为 Prism 派生的 Java 子进程会继承这个变量,于是出现「系统 2.31 的 ld.so + 2.38 的 libc」混用的局面,报 _dl_audit_symbind_alt 未定义、exit 127,表现就是「找不到正确版本的 Java」。正确姿势是:--library-path 只作用于 Prism 本体,在启动 Java 之前把 LD_LIBRARY_PATH 清掉。

坑 ④:下载源改道 BMCLAPI,顺带根治校验错配

这是五个坑里最耗时间、也最容易反复的一个,值得多花点篇幅。

现象:下几十上百个 jar 的时候,官方源(libraries.minecraft.net、maven.fabricmc.net 这些)时快时慢、纯看运气,批量下载基本靠不住。

第一步:改道。 架一个本地 meta 中转(一个本地 HTTP 服务,比如 127.0.0.1:8099),让 Prism 通过 MetaURLOverride 指向它。这个中转把 meta 数据里所有官方下载域名改写成 BMCLAPI 镜像再返回,于是 Prism 实际下载时走的就是 BMCLAPI 这个国内稳定镜像。

为什么一定要走中转层、而不是去改 Prism 的二进制?因为 Prism 的下载 URL 全来自 meta 数据(版本 JSON),二进制里只硬编码了 meta 索引源,而 BMCLAPI 不提供 Prism 的 /v1/ 索引协议。所以「中转层」是唯一能统一改道的地方——它天然就是「焊死下载源」的正确位置。

第二步:解决改道带来的新问题——sha256 错配。 改道之后,版本 JSON 的字节变了(URL 变了),导致文件 sha256 和 index 里记录的对不上,Prism 校验失败。

这里我们踩过坑,结论一定要记牢,不然会来回折腾:

  • ❌ 别为了 sha 自洽去「关掉改道、退回官方源」——官方源批量下载不可靠,这是更严重的问题,不能拆东墙补西墙。
  • ❌ 别「把 index 里的 sha 校正成改道后的实际值」——这样仍有首次下载的时序错配(Prism 已经缓存了 index、重试又不重拉)。
  • ✅ 唯一根治:直接删掉 index.json 里每个版本条目的 sha256 字段,让 Prism 跳过版本文件的 sha256 校验。Prism 源码 BaseEntity.cpp 里写得很清楚:if (!m_sha256.isEmpty()) addValidator(...)——字段为空,它就不加校验器。这个方案零编译、零预拉、对所有版本和加载器都通用。

第三条铁律:珍惜下载源。 别为了「提前自洽」去预拉、预热、批量抓版本文件——那是对 BMCLAPI 这种无私镜像的变相轰炸。正确的取舍是:允许「首次下某个全新版本可能失败一次、第二次重试即成功」(第一次失败时文件已被缓存、index 已回填)。宁愿让用户多点一次,也别去轰炸下载源。缓存,就是对它最大的尊重。

坑 ⑤:退出崩溃,把 SDL_Quit 的跳板 patch 掉

现象:游戏能正常玩,但点窗口 X 退出时崩溃(SIGSEGV);而从主界面点【退出游戏】按钮,有时却不崩。

为什么坏:读崩溃日志的调用栈,真相一目了然:

Main.main → exitWorldAndClose() → Minecraft.close() → SDL_Quit()
  → libSDL3 内部清理 → 跳到非法地址 → SIGSEGV

关键在于——「优雅退出」该做的事(保存世界、断开连接)在崩溃前其实已经全部做完了,真正崩的只是最后一步 SDL_Quit() 释放 SDL 资源。这是 libSDL3 在 Maleoon 920 + xwayland 这个特定组合下的清理 bug。而点【退出游戏】按钮不崩,是因为那条路径会先把窗口、上下文卸载干净,再调 SDL_Quit,SDL 内部状态是干净的。

怎么修(极简,只改 4 个字节):我们发现 libSDL3 用的是 DYNAPI 机制——每个导出函数都是一个「跳板」,先从一张表里读出真实函数指针,再 br x16 跳过去。于是思路变得非常简单:把 SDL_Quit 跳板末尾的 br x16(机器码 d61f0200)直接 patch 成 ret(d65f03c0),SDL_Quit 就变成了 no-op。游戏退出时跳过会崩的清理,进程正常收尾,不崩、不弹错误、不生成崩溃日志。反正世界早就保存好了,跳过这一步不丢任何数据。

这里有个「负向经验」也很有价值:一开始我们想做一个「转发 shim」,让它转发真库所有符号、只拦截 SDL_Quit。结果 --wrap、--whole-archive、version script、--export-dynamic 各种招数全试了一遍——全失败。因为 GNU ld 根本不会把「链接进来的 shared library 符号」re-export 成 shim 自己的动态符号,LWJGL 用 dlsym 按名查找时一个都找不到。最后才悟到:当库函数是「跳板」结构时,直接 patch 跳板,比写 shim 转发干净一百倍。

让它以后不用再修:把 patch 逻辑做成幂等脚本,集成进 Java 包装器,每次启动自动自愈。以后换新版本、新 natives 目录,只要还走这套包装器,就自动对新库打补丁。


四、贯穿始终的几条工程原则(比命令更值钱)

回头看,这次能成,靠的是这几条原则。它们不限于 MC,任何「国产化环境适配」都能用:

  1. 做通用解法,不做单点补丁。 每次修完都问自己一句:「下一个新实例、新版本,还会不会踩这个坑?」要改就改在「对所有东西都生效」的地方(比如中转的改道规则、Java 包装器),而不是只改某个实例的配置文件。
  2. 自包含、可整体搬迁。 目标是整个目录拷到任意一台同架构机器、放任意路径都能直接跑。所以目录里不允许有指向外部的绝对路径软链,所有路径都从自身位置推导。
  3. 自愈,胜过一次性修复。 环境变量钉死、options.txt 改回 Vulkan、SDL 补丁——这些统统放进「每次启动都会跑」的包装器里,而不是修一次拉倒。因为 MC 会在异常退出后自己改配置,你不自愈,它就会复发。
  4. 对下载源克制。 不预拉、不高并发,缓存即尊重。
  5. 诊断靠证据,不靠猜。 崩溃读 hs_err 调用栈,识别进程看真实 cmdline,下载失败看中转日志。每个结论都要有日志或源码撑着。

五、给复现者的一份「顺序检查清单」

如果你要在类似的国产 ARM 机器上复现,建议严格按这个顺序来,每步验证通过再进下一步:

  1. 确认 GPU 和驱动:ls /etc/vulkan/icd.d/ 看有没有 Maleoon 的 ICD;确认 Vulkan 可用、OpenGL 是不是 llvmpipe。
  2. 先分清软解还是硬解:只有 Vulkan 是硬解路径。钉死 MOJANG_GL_BACKEND=vulkan。
  3. 解决 Vulkan 扩展缺失:程序报缺扩展,就写 loader shim 伪造(思路见坑 ②)。
  4. 解决运行库版本:自备新 glibc,ld.so --library-path 局部使用,别污染到子进程。
  5. 解决下载源:架本地中转,meta 改道 BMCLAPI,删 index 的 sha256 字段根治校验错配;全程珍惜下载源。
  6. 解决退出崩溃:读 hs_err 确认崩在 SDL_Quit,用 DYNAPI 跳板 patch 把它变 no-op。
  7. 全程贯彻通用化 + 自愈 + 自包含,把每一步都做成「下次启动自动生效」。

六、收个尾

国产 ARM 跑 MC,说穿了就是一句话:把官方默认的那些「x86、主流 GPU」假设,一个一个换成本机可用的等价物——用 Vulkan 代替 OpenGL、用 loader shim 代替「指望驱动补齐扩展」、用自备新 glibc 代替「等系统升级」、用 BMCLAPI 代替官方源、用 patch 跳板代替「等 SDL 修 bug」。

真正的难点从来不在某一个技术点,而在于:把每一次修复,都沉淀成通用、自愈、可搬迁的机制,并且从头到尾靠日志和源码证据定位,而不是靠拍脑袋。

已有 0 条评论 (旧评论在前)
  1. 暂无评论 / 0 Comment
添加新评论 (Markdown Supported)
(ノ°ο°)ノ