English summary — First real-hardware verification of Oh My Yes Steve Model (
ysm-3.0-dev+Oh my ysm lib 0.1.1, Minecraft 1.20.1 / Forge 47.4.3) on an Apple M5 Mac running macOS 26.6. Both the pure-JVM baseline and the self-built native accelerators (codec / render / physics / skinning, macOS arm64) work in game, with textures, animation and model switching verified. Two small native build fixes for macOS are included inpatches/. The run also found three platform-independent bugs that only show up with the production JARs: the dedicated server fails to start because common code uses LWJGLMemoryUtil, a resource reload (F3+T / language change) hangs forever on the loading screen, and built-in pack descriptors fail to load from the mod JAR. Full report below (Chinese, matching upstream docs).
| 项目 | 内容 |
|---|---|
| 被测项目 | AndreaFrederica/OhMyYesSteveModel;构建分支 AndreaFrederica/YesSteveModel feature/v3d-decoded-cache @ 25ae5414 |
| 测试与报告 | TianjiMeteor(由 Claude Code 协助执行构建、日志分析与整理) |
| 日期 | 2026-10-07 ~ 2026-10-08 |
| 状态 | 社区自发验证,非官方;上游未关联、未背书 |
左上:native 加速模式的 F3;右上:纯 JVM 模式的 F3;左下:⌥+Y 模型界面;右下:走路动画。
「通过」只表示在下述单一环境、单一场景下观察到预期行为,不代表 macOS 已完成验收,也不扩大上游支持状态中的承诺。
- 纯 JVM 基线在 macOS arm64 上可用:模组加载、F3 provider 显示、内置模型贴图、动画、服务器指令与 GUI 内切换模型均正常,没有出现官方 YSM 1.x 在 Mac 上的紫黑贴图问题。
- 自建 native 加速在 macOS arm64 上可用,需要两处小的构建修补(见 M1、M2)。修补后 native 自测 5/5、JNI 差分 codec 3/3、render 5/5、physics 57/57、skinning 5/5 全部通过。游戏内 F3 显示
Hash/Compression: native-ysmlib-codec-v1、Render: native-cpp-render-v1 (packed)、Physics: bullet-3.25-native-v6、Skinning: native-skinning-v1-parallel,与 build.md 的验收标准一致。 - 发现 3 个与平台无关、只在生产发行包里出现的问题(开发环境
runClient/runServer难以复现):- 专用服务器无法启动:通用代码依赖客户端专有的 LWJGL
MemoryUtil(X1)。 - 资源重载(F3+T / 切换语言)永久卡在 Mojang 加载界面:远程模型的贴图重载会读取已关闭的缓冲区(X2)。
- 内置模型包描述读取失败:jar 内 union 文件系统不支持
FileChannel.open(X3)。
- 专用服务器无法启动:通用代码依赖客户端专有的 LWJGL
- 另有 macOS 分发相关问题(libc++ 动态依赖、
minos、Gatekeeper 隔离)、构建文档缺口和 3 处测试的路径假设问题,详见下文。
| 项目 | 值 |
|---|---|
| 硬件 | Apple M5(10 核)、16 GB 内存 |
| 系统 | macOS 26.6.2(25G83);Command Line Tools 27.0(MacOSX.sdk → MacOSX27.0.sdk,另装有 26.5 SDK) |
| 源码 | AndreaFrederica/YesSteveModel 分支 feature/v3d-decoded-cache @ 25ae5414(2026-10-07) |
| JDK | Homebrew OpenJDK 17.0.20.1(构建与运行);21.0.12.1(仅作 Gradle toolchain,见 B1) |
| 构建工具 | Gradle Wrapper 8.12;Pixi 0.81.0;Meson 1.12.1;conda-forge clang 19.1.7、ld64 956.6、libcxx 23.1.2(headers 19.1.7) |
| 游戏 | Minecraft 1.20.1 + Forge 47.4.3;Prism Launcher 11.1.1;GL:Apple M5, 4.1 Metal - 90.5;LWJGL 3.3.1 |
| 安装的 Mod | 仅 ysm-3.0-dev-forge+mc1.20.1.jar 和 ysm-runtime-forge-0.1.1.jar(build.md 写的是 0.1.1,任务描述里的 0.1.0 已过时) |
| 服务器 | Forge 1.20.1-47.4.3 专用服务器,127.0.0.1:25580,离线模式,超平坦世界 |
| 步骤 | 命令 | 结果 |
|---|---|---|
| 前置 | ./gradlew -p runtime build |
✅ 5m26s,194 任务;runtime 测试 418 通过 / 0 失败 / 69 跳过(跳过的全部是需显式指定库路径的 Native* 测试) |
| 本体 | ./gradlew build -Pysm.fast_run=true |
❌ 按文档原样执行失败,原因依次见 B1、B2、B3、T1。补齐后 ✅:675 测试,0 失败,13 跳过 |
| native | pixi install --locked && pixi run test(runtime/native) |
❌ 编译失败(M1)。修补后 4/5,physics-smoke 失败(M2)。再修补后 ✅ 5/5 |
| JNI 差分 | nativeTest -PcodecLibrary=… -PrenderLibrary=… |
❌ 配置缓存报错(B3);加 --no-configuration-cache 后 ✅ codec 3/3、render 5/5 |
| JNI physics/skinning | :ysm-runtime-native-physics:test :ysm-runtime-native-scene:test -Dysm.native.physics.library=… -Dysm.native.skinning.library=… |
✅ physics 57/57(-Xcheck:jni)、scene 5/5 |
本机最终可用的本体构建命令(含本机网络代理,与上游无关):
./gradlew build -Pysm.fast_run=true \
-Porg.gradle.java.installations.paths=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home \
--no-configuration-cache
# 另需 JAVA_TOOL_OPTIONS=-Djava.io.tmpdir=$(cd "$TMPDIR" && pwd -P) 才能让 T1 的 6 个测试通过发行包 SHA-256:
5fb5ac0484c97ce69da0d8a5e767d761663365a9f66fe11ddba3f8645b43a093 ysm-3.0-dev-forge+mc1.20.1.jar
ff14c7d7512580fa32a70a75fc9eeabc3b9ae2646dc99f03ae4bfa727a0df9d0 ysm-runtime-forge-0.1.1.jar
3001fcae0b7f8054f8b2be6d5aa2ad34cde8d8fd635931ec8284abb9ba69a507 libysmlib_codec.dylib (自包含版本,见 M3)
bfc347c60a3e5a931227f39ce7b8d98399d21c8d1eb4eb40eaa435e8a2d12a09 libysmlib_render.dylib
b1084ca16790fa565d7eef25642f693ea4d78c90c6216a2fd04d2f33ebb39e11 libysmlib_physics.dylib
e027fff90355be7e47fe85ae0831107f0823b97a1458369f1e3e21a22ef1134f libysmlib_skinning.dylib
客户端通过 Prism 启动,连接上述专用服务器(服务器依赖 X1 的诊断性绕过才能启动)。native 库放在 <游戏目录>/ysmlib/natives/macos-arm64/;纯 JVM 通过 -Dysm.runtime.javaOnly=true 强制。
| 能力 | 纯 JVM | 自建 native | 证据 |
|---|---|---|---|
| 模组加载 / Forge 联机握手 | ✅ | ✅ | Connected to a modded server;日志 19/21/22-client-*.log |
| F3 provider 显示 | ✅ Native acceleration: disabled;Hash java-blake3、Compression java-zstd、Physics bullet-3.25-chicory-jvm-v1 … fallback=javaOnly、Skinning java-deformation-1、Render java-render |
✅ preferred;Hash/Compression native-ysmlib-codec-v1、Physics bullet-3.25-native-v6 abi=6、Skinning native-skinning-v1-parallel、Render native-cpp-render-v1 (packed)、State java-model-state |
screenshots/jvm/2-f3-jvm-full.png、screenshots/native/8-f3-native-full.png |
| native 库实际加载 | — | ✅ 4 个 dylib 均映射进客户端进程 | 17-native-client-loaded-dylibs.txt |
| 内置模型贴图 | ✅ | ✅ 无紫黑贴图 | screenshots/native/1~3,screenshots/jvm/1、3 |
| 动画 | ✅ 走路迈步摆臂 | ✅ HUD 小人头发、尾巴、耳朵逐帧变化 | screenshots/jvm/4-walk-03_astronaut.png,screenshots/native/3~5(HUD 小人姿势逐张不同) |
服务器指令切换模型 /ysm model set |
✅ → wine_fox/03_astronaut |
✅ → wine_fox/22_elf |
screenshots/jvm/3-switched-to-03_astronaut.png、screenshots/native/3-switched-to-22_elf.png |
| GUI 切换模型(⌥+Y) | ✅ 中文界面、预览、切换请求均正常 | 未直接测(GUI 中选择的模型在随后的 native 轮次中正常渲染) | screenshots/jvm/5-model-gui.png;日志 Sending model selection request |
| 模型选择跨重连保留 | ✅ | ✅ | screenshots/jvm/1-front-22_elf.png |
| 资源重载(F3+T / 切换语言) | ❌ 卡死(X2) | ❌ 卡死(X2) | 20-client-reload-hang-jstack-*.txt、21-client-jvm-run.log |
| 专用服务器(生产发行包) | ❌ 启动失败(X1) | ❌ 同左 | 14-server-native-FAILED.log |
内置包 misc/、wine_fox/ 描述 |
❌ 名称、图标缺失(X3) | ❌ 同左 | 服务器与客户端日志;screenshots/jvm/5-model-gui.png 中两个文件夹为通用图标 |
| 单人(集成服务器) | 未测 | 未测 | — |
冷启动的模型目录扫描:客户端 24.8 s、服务器 18.5 s;有缓存时 2.7~4.1 s(27 个模型,2 个错误均为 X3)。这不是性能测试,只作参考。
按影响从大到小排序。「平台」一栏标为「全部」的问题与 macOS 无关,只是由这次生产发行包实测发现的。
- 现象:只装两个发行 JAR 的 Forge 专用服务器在
CommonEvent的延迟任务里抛出ClassNotFoundException: org.lwjgl.system.MemoryUtil,随后Failed to start the minecraft server。 - 原因:LWJGL 只随客户端分发。以下非
client包的类直接 importorg.lwjgl.system.MemoryUtil(memAddress、memCopy、memByteBuffer):buffer/NativeNioBuffer、buffer/NativeBuffer、buffer/MappedFileBuffer、network/forge/ForgeUniBufferIO、natives/image/Image、natives/image/ImageEncoder、natives/render/BonePoseView、natives/render/NativeBakedModel。首个触发点:BuiltinModelCatalog.generateDefault → … → Zstd.output → NativeNioBuffer.allocate。开发环境的runServerclasspath 带有 LWJGL,所以发现不了。 - 佐证:把
lwjgl-3.3.1.jar和 macOS natives jar 追加到-DlegacyClassPath后,同一服务器 8.8 s 正常启动,并完成后续全部联机测试(15-server-diag-lwjgl.log)。这只是诊断手段,不建议作为修复。 - 建议:在通用代码里提供一个不依赖 LWJGL 的直接内存工具(例如由前置提供,或复用已有的
UnsafeUtil:通过Buffer.address字段偏移取地址,用Unsafe.copyMemory复制,用 JNINewDirectByteBuffer或现有 native 包装地址);客户端专用路径可以继续用 LWJGL。另外建议在发行验收里加一个「只用 Forge installer 装出的真实服务器 + 两个发行 JAR」的冒烟测试。
- 复现:联机后由服务器下发模型(远程模型),在游戏中按 F3+T,或在选项里切换语言。纯 JVM 与 native 模式各复现 1 次,共 2 次中 2 次(native 模式切换语言、JVM 模式按 F3+T)。
- 现象:日志出现
Model lazy resource failed … path=wine_fox/03_astronaut stage=TEXTURE resource=player/skin/uv,原因是IllegalStateException: Buffer is closed(RemoteModelContent$ReceivedChunkDataSource.readStoredVerified→ArrayBuffer.copy)。之后 Mojang 加载界面永远不消失;线程栈显示所有 Worker 空闲,渲染线程正常画帧。 - 原因链:原版
TextureManager.reload在渲染线程上对每个已注册贴图执行reset → register → loadTexture,全部完成后才执行cf.complete(null)。CustomPBRTextureSet.load(...)在catch (IOException | RuntimeException)中把RuntimeException原样重新抛出;原版loadTexture只把IOException降级为缺失贴图,其他Throwable都会构造 CrashReport(日志里紧跟着的 OSHIhw.cpufrequency警告就是构造它的痕迹)并抛出ReportedException。这个异常被thenAcceptAsync吞掉,cf永远不会完成。 - 建议:(1)
CustomPBRTextureSet.load把RuntimeException包装成IOException再抛出,最差也只会退化为缺失贴图,不会卡死;(2) 根因是远程模型接收到的 chunk 缓冲区在首次上传后就被释放,但贴图对象仍然持有指向它的数据源。重载时要么保留或重新取得数据,要么跳过重新上传、沿用已上传的 GPU 贴图。
- 现象:服务器和客户端启动时都报
Model catalog source failed root=BUILTIN source=wine_fox/(以及misc/),UnsupportedOperationException来自FileSystemProvider.newFileChannel。模型本身仍能加载(models=27 errors=2),但模型 GUI 里这两个包显示为通用文件夹图标和原始目录名,左上角出现错误红点。 - 原因:
ModelPackScanner.scan→ModelHashing.blake3(Path)对ysm-pack.png调用FileChannel.open。该方法已经为不支持map()的文件系统准备了退回路径,但 Forge union / jar 文件系统在FileChannel.open这一步就会抛出异常,而这一步在那个try之外。 - 建议:同样捕获
FileChannel.open抛出的UnsupportedOperationException,然后用Files.newInputStream走有界的流式哈希。
- 现象:Bullet 的
btMLCPSolver.cpp、btDantzigLCP.cpp、btMultiBodyMLCPConstraintSolver.cpp报use of undeclared identifier 'INFINITY'。 - 原因:macOS 26 及以上的 SDK 在
__has_feature(modules)为真时(严格的-std=c++20会触发)不再自己定义INFINITY,改用__need_infinity_nan向<float.h>索取。conda-forge clang 19 的float.h不支持这个约定(LLVM 20.1、21.1 也不支持,main 分支已支持),而且在严格 C++ 模式下不定义INFINITY。gnu++20、c++17都不受影响;26.5 SDK 与 27.0 SDK 表现相同。 - 修复(
patches/patch-macos-native.diff):在src/math/ysm_bullet_math.h(不在source.json的校验范围内,且已被 patchedbtScalar.h最先包含)的#include <math.h>之后加上#ifndef INFINITY / #define INFINITY (__builtin_inff()) / #endif。
- 现象:
physics_test.cpp:54断言失败。逐值对比发现,第 50 和第 60 步碰撞后的横向残差(约 1e-6~2e-5)数值大小与基准逐位相同,但符号相反,其中 4 个超出 1e-5 容差(05-physics-smoke-deviation.txt)。 - 原因:
btScalar.h在__APPLE__ && __ARM_NEON__ && __clang__时会定义BT_USE_NEON和BT_USE_SIMD_VECTOR3。meson.build用-D__BT_DISABLE_SSE__让 MSVC 保持标量路径(注释说明了「与 WASM 同一标量求解路径」),但这个宏关不掉 Apple 的 NEON 分支。Linux arm64 走的是非 Apple 分支,不受影响。 - 验证:用
-U__ARM_NEON__重新编译 Bullet 和 physics 后,所有值与 WASM 基准逐位一致(06-physics-smoke-noneon.txt)。 - 修复:在
meson.build中,host_machine.system() == 'darwin'时对physics_args追加-U__ARM_NEON__(同一个 diff)。
-
现象(默认
pixi run test产物,08-dylib-deps.txt):physics、render、skinning 动态链接@rpath/libc++.1.dylib,LC_RPATH是构建机上的绝对路径…/.pixi/envs/default/lib;4 个库的minos都是 16.0。在玩家的 Mac 上前者会导致dlopen失败、静默回退到 JVM;后者高于产品基线 macOS 14.5,违反BC.native-distribution-is-self-contained和BC.minimum-platform-baselines。 -
已验证配方(
09-native-static-variant.log、10-dylib-deps-static.txt):MACOSX_DEPLOYMENT_TARGET=14.5 LDFLAGS="-nostdlib++ $CONDA_PREFIX/lib/libc++.a -lc++abi" \ pixi run python tools/build.py configure --build-dir build-static pixi run meson compile -C build-static && pixi run meson test -C build-static
得到
minos 14.5,依赖只剩libSystem和系统自带的/usr/lib/libc++abi.dylib,5/5 测试通过;游戏内使用的就是这一版本(另外用install_name_tool -delete_rpath去掉了无用的构建机 rpath,并重新做了 ad-hoc 签名)。libc++abi是否算「libc 伴生系统库」请维护者裁定;如果不算,需要额外的静态 abi 库。
-
给 dylib 副本加上
com.apple.quarantine属性(模拟从浏览器下载)后,System.load失败:library load disallowed by system policy,系统同时弹出「Apple 无法验证 … 是否包含恶意软件」。去掉该属性后可以正常加载。 -
影响:如果 macOS native 以 zip 形式分发,玩家解压后加速会被拦下并回退 JVM(游戏不会崩溃)。建议:长期做法是 Developer ID 签名加公证;在此之前,可以在 build.md 的安装步骤里加一条玩家自行执行的命令,清除运行库目录的隔离属性:
xattr -cr "<游戏目录>/ysmlib/natives/macos-arm64"游戏目录位于用户目录下,通常不需要
sudo;如果目录属于其他用户或在系统位置,就改用sudo xattr -cr …。这和 Mac 上运行未公证 App 的常见做法(xattr -cr /Applications/<App>.app)是同一个原理。本次已确认:去掉com.apple.quarantine后,同一个 dylib 能正常System.load。官方启动器自带的 Java runtime(hardened runtime / library validation)本次未测。
createMinecraftArtifacts(NeoForged NFRT)需要 Java 21 toolchain。build.md 只写了 JDK 17。在 macOS 上,Gradle 自动探测找不到 Homebrew 装的 JDK,报Cannot find a Java installation … languageVersion=21。- 建议:在 build.md 里写明,并给出
-Porg.gradle.java.installations.paths=<JDK21>的示例,或配置 foojay toolchain resolver。
- 首次运行时缓存写入成功;之后
:runtime:ysm-runtime-forge:shadowJar的SelfDescribingSpec反序列化失败:readResolve.invoke(bean) must not be null。加--no-configuration-cache后正常。
runtime/native-codec/build.gradle和native-render/build.gradle的doFirst闭包引用了脚本对象providers,触发Cannot reference a Gradle script object from a Groovy closure。build.md 中的命令按原样执行必然失败。建议:在配置期把值读进局部变量,doFirst中只引用这个变量。
V3dCommandTest.restrictsInputsToOwnedRoots:V3dCommandPaths.rejectLinks一直检查到文件系统根目录的每一级父目录。macOS 的/var→/private/var,所以临时目录下的任何路径都会被拒绝。实际影响:只要游戏目录的任一上级目录是符号链接(例如把.minecraft链接到外置盘),/ysm v3d就会拒绝所有路径。建议只检查从 owned root 往下的各级路径段。ModelSourceDiscoveryTest(4 个)和ModelExporterTest(1 个):生产代码用toRealPath()规范化根目录,测试却用toAbsolutePath()查找,在 macOS 上对不上。这是测试本身的问题。- 佐证:把
java.io.tmpdir设为真实路径后,这 3 个类的 12 个测试全部通过(12-tmpdir-realpath-tests.log)。
- Mac 上的 GUI 快捷键:默认是
Alt+Y,在 Mac 上就是⌥+Y;只按 Y 没有任何反应,测试者第一次没能打开。建议在文档或按键提示里写出 Mac 按法。 - F3 太长:在 1708×960 窗口、自动界面缩放下,
Render行超出屏幕底部,看不到;把界面缩放调到 2 后才完整显示。build.md 的验收恰好依赖这一行。 cacheOnly预览路径在没有烘焙缓存时用 ERROR 级别记录Baked model cache is unavailable(misc/2_steve,材质tartaric_acid),看上去是预期分支,日志级别可以考虑降低。- 装了 YSM 的服务器接受了一个误启动的原版客户端(
Unknown custom packed identifier: ysm:protocol)。如果这是有意设计的可选客户端行为,可以忽略。 - 平台缓存位于
~/Library/Application Support/ysm/cache/remote,由所有实例共享,与AssetPaths的注释一致。
单人 / 集成服务器、LAN、多客户端;Iris / Embeddium 等联动;第一人称 FirstPerson 模组;自定义(非内置).ysm 模型和 v3 加密模型;音频播放;长时间运行和内存回收;官方启动器(Mojang Java runtime);Intel Mac;macOS 14 / 15 实机(只通过 minos 做了静态核对);modelManagementMockForge 等自动宿主验收(文档说明只在 Windows 上验证过)。
| 路径 | 内容 |
|---|---|
screenshots/native/ |
native 加速模式下的游戏内 F2 截图(8-f3-native-full.png 为完整 F3;6-f3-native-guiscale-auto.png 演示了自动缩放下 Render 行被截断) |
screenshots/jvm/ |
纯 JVM 模式下的游戏内 F2 截图 |
patches/patch-macos-native.diff |
M1 + M2 修补(2 个文件,10 行),基于 25ae5414 |
正文中引用的 NN-*.log / NN-*.txt(构建日志、线程栈、逐值对比、dylib 依赖等)是测试者本地保存的原始证据。由于包含本机路径等信息,没有公开;维护者如果需要,可以联系作者私下提供。
- 报告正文:CC BY 4.0,转载请署名 TianjiMeteor 并附本仓库链接。
patches/:派生自上游代码,沿用 Apache License 2.0。screenshots/:画面中的内置模型版权归原作者所有。酒狐系列按 CC BY-NC-SA 4.0 授权,作者见上游各模型ysm.json的metadata.authors,例如大正女仆(完美冻结、星屑海螺、哥斯拉(映素团队)、白帆小喵)、精灵(星海境、缇可拉)、宇航员(完全冻结、星屑海螺、哥斯拉(映素团队)、祸御神)。截图整体按 CC BY-NC-SA 4.0 提供,仅限非商业使用。Minecraft 画面元素归 Mojang Studios 所有。- 「Oh My Yes Steve Model」「Yes Steve Model」等名称归各自权利人所有;本仓库与上游及 Mojang 均无隶属关系。



