跳到正文

OptiFabric Reforged

作者:kynarain

在 Fabric 上运行 OptiFine,并同时加载 Fabric API。

收藏

介绍

介绍由机器翻译自 CurseForge 原页面,仅供参考。

OptiFabric — 在 Fabric 上使用 OptiFine
一个将 OptiFine 引入 Fabric 的 Fabric 模组。把 OptiFabric 和你自己的 OptiFine jar 放进 mods/,剩下的交给它处理——无需手动修补 jar,无需运行 OptiFine 安装程序,也不需要 Forge。

ℹ️ 不会捆绑或再分发 OptiFine。请从官方网站获取与你的 Minecraft 版本匹配的版本——目前 1.20.6 和整个 26.x 系列(26.1.2 / 26.2)都只有预览版。

OptiFabric 按发布分支分别开发,每个分支都是独立项目;不同 jar 不能互换,并且每个文件名都标注了对应的 Minecraft 版本:

分支 Minecraft 版本 Jar 加载器 Java
1.20.6 1.20.6 OptiFabric-1.1.9+mc1.20.6.jar ≥ 0.19.3 21+
1.21.x 1.21 – 1.21.11(共十个版本) OptiFabric-2.2.16+mc<version>.jar ≥ 0.19.5 21+
26.x 26.1.2 / 26.2 OptiFabric-Reforged-2.2.11+mc26.1.2.jar / OptiFabric-Reforged-2.2.11+mc26.2.jar(每个版本各有一个 jar) ≥ 0.19.5 25

支持的 OptiFine 版本

点击此链接查看支持的 OptiFine 版本。 https://github.com/Kynarain/OptiFabric-Reforged/blob/main/docs/OPTIFINE_VERSIONS.md

模组兼容性列表

点击此链接查看模组兼容性列表。 https://github.com/Kynarain/OptiFabric-Reforged/blob/main/docs/COMPATIBILITY_ALL.md
**如果你想在 1.20.6 上使用锂,请使用 OptiLithium Reforged。不过,1.20.6 之后的版本不需要 OptiLithium Reforged。
https://www.curseforge.com/minecraft/mc-mods/optilithium-reforged

性能表现

以下数据来自第三方FPS Benchmark模组(https://www.curseforge.com/minecraft/mc-mods/fps-test-benchmark)(fpstest-1.0),每种配置都进行了一次 3 分钟的脚本化测试:包含 19 个电影化场景片段(森林、村庄、战斗、红石、洞穴、下界、末地),并使用确定性种子27182。所有配置都在同一台机器上运行,使用字节级完全相同的options.txt——唯一变量是安装的模组。

配置 已安装的模组 平均 FPS 1% 低帧 0.1% 低帧 最差帧 最低 FPS
原版 Fabric(基准) Fabric API 3631 774 442 17.2 ms 58
OptiFabric + OptiFine Fabric API + OptiFabric 2.2.10 + OptiFine 1.21.1 HD U J1 5040 1098 713 3.3 ms 302
OptiFabric + OptiFine + 锂 + FerriteCore + C2ME——我们通常的推荐配置 以上配置 + 锂 0.15.4 + FerriteCore 7.0.3 + C2ME 0.4.0-alpha.0.29(mc1.21.1) 4475 1077 721 3.1 ms 319
Sodium + 锂(替代渲染方案) Fabric API + Sodium 0.8.13 + 锂 0.15.4 5680 1017 349 12.5 ms 80

表格说明了什么

  • 在这里,OptiFabric + OptiFine 的平均表现比纯 Fabric 高 38.8%,最差帧也好 5 倍(3.3 毫秒 vs 17.2 毫秒)——OptiFine 自带的优化确实发挥了作用,因此使用这个模组并不是为了获得 OptiFine 的功能而必须付出的性能代价。
  • 与 Sodium 组合相比,平均表现落后约 13%,但帧时间尾部表现更好:0.1% Low 为 713 vs 349,最差帧为 3.1–3.3 毫秒 vs 12.5 毫秒,最低 FPS 为 302 vs 80。换句话说,Sodium 的平均速度略快,而 OptiFabric + OptiFine 更稳定。
  • 在此场景中,测得的三个“性能”模组表现中性至负面(5040 → 4475)。这是意料之中的:此基准测试刻意减少了世界生成和刻运行负载,而在真实的长期存档中,这恰恰是 C2ME / Lithium / FerriteCore 通常能发挥作用的地方。不要据此认为“这些模组毫无用处”。

测试环境(四次运行完全相同)

Minecraft 1.21.1 · Fabric Loader 0.19.5 · Java 22.0.2 · 最大堆内存 5836 MB · Intel i5-12600KF · AMD Radeon RX 7800 XT(驱动程序 25.12.1.251128)· Windows 10 · 渲染距离 8 · 关闭 VSync · maxFps:260(= 不限)· 关闭光影 · 相同的options.txt · 种子 27182。

原始报告(包含逐帧采样)已与源代码一同发布:https://github.com/Kynarain/OptiFabric-Reforged/tree/1.21.x/benchmarks/2026-10-05-fps-benchmark——一台机器、每种配置运行一次、一次基准测试;这些数字仅供参考,不是性能规格。

为什么需要它
OptiFine 是为原版(以及 Forge)打造的:它的补丁是针对官方混淆名称编译的,而 Fabric 使用中间命名空间。Fabric API 也会向许多相同的类中注入代码。两者结合后便会产生难以诊断的冲突——构造函数结构不同、合成字段名称不同、辅助方法被内联移除、对象创建被替换为 OptiFine 自己的子类,等等。

这些冲突并非表面问题。若在重映射器的类路径中没有游戏,重映射过程会静默地漏掉单个类中的 35 个方法,导致 281 个抽象契约损坏、254 个虚方法重写丢失。OptiFine 的重新编译器会把原版方法变成转发到其自有重载方法的薄封装——这会让 Fabric API 的注入点偏离原有位置,随后 Mixin 在加载时便会导致整个类加载失败。OptiFine 还会整体替换类(例如它的 ChunkOF 区块、包含父类在内的视频设置界面),并改写 Fabric 用于匹配注入点的 lambda 函数体。

在 26.x 系列中,问题的形式有所变化:Minecraft 26.1 及更新版本不再混淆——官方名称就是运行时名称,也没有 yarn 或可供重映射使用的真正 intermediary(26.1.2 仅发布了 0.0.0 占位版本)。因此,该系列完全不使用映射,在官方命名空间中运行,并使用自己的模组 ID(optifabric_reforged),因为一些模组会声明 `”breaks”: {“optifabric”: “*”}`,而 Fabric Loader 会根据 ID 匹配——仅更改显示名称并不足够。

它的作用
在启动的最早阶段(加载器的 preLaunch 阶段),OptiFabric 将:

运行 OptiFine 自带的安装程序,提取游戏类的补丁(从 1.21.6 起,补丁以 xdelta 差分文件形式提供;optifine.Patcher 的用法相同);
移除 volde-ification,并重建目标被 OptiFine 移动过的 lambda(LambdaRebuilder);
将补丁从 official 映射重映射为 intermediary——将游戏 jar 同时加入重映射器的类路径和输入,这样从超类型继承的成员才能保留其映射后的名称;
修复 OptiFine 与 Fabric API 之间已知的结构冲突,并将每个冲突都追溯到字节码层面;
将结果拆分为两部分:OptiFine 自身的类和资源放入游戏类路径,打过补丁的 net/minecraft/** 类放入 ClassCache;
将修复后的类交给 Fabric Loader 自带的 GameTransformer,并将所有内容缓存到 /.optifine// 下,这样后续启动就能复用缓存,无须重复处理(耗时 1–2 秒,而不是 5–7 秒)。
在 26.x 系列中会跳过步骤 2 和 3,这正是它作为独立项目存在的全部原因。

替换发生在 Mixin 之前:当 Minecraft 类即将加载时,Loader 会向游戏提供方请求 GameTransformer.transform(…) 返回现成的字节码。因此,OptiFabric 交出的是 Mixin 的输入,而不是输出——这就是其他模组针对这些相同类的 mixin 仍能正常工作的原因。由此也产生了一项硬性限制:交出这些类之前,绝不能对游戏类进行反射操作——只要调用一次 Class.getMethods(),就会加载该方法签名中涉及的所有类型,并使该类在本次会话剩余时间内固定为原版类。

缓存目录包含 cache-format.txt(当前为 26;格式不匹配时会重建所有内容)、Optifine-mapped.jar(不含 MC 类的 OptiFine,也就是放入类路径的内容)以及 Optifine.classes.gz(打过补丁的 MC 类)。

安装
使用 Fabric Loader 为你的 Minecraft 版本安装客户端(1.20.6 需要 ≥ 0.19.3,1.21.x 和 26.x 需要 ≥ 0.19.5),并使用 Java 21+——或者在整个 26.x 系列(26.1.2 / 26.2)中使用 Java 25,这是游戏本身的要求。
将 OptiFabric 和你为对应确切版本准备的 OptiFine jar 放入该实例的 mods/ 文件夹。直接放入 OptiFine 安装程序 jar 即可——无需先运行安装程序(已解压且包含 notch/ 类的 OptiFine 也能使用)。不要安装两个 OptiFine jar(游戏会报告 DUPLICATED),也不要混用来自两个系列的 jar。
使用 Fabric 配置启动游戏——不要使用启动器创建的 1.21.x-OptiFine_xxx 配置,因为它会注入 OptiFine 本身。如果运行正常,标题屏幕左上角会显示 OptiFine 版本,视频设置中也会出现 OptiFine 选项。
可以同时加载 Fabric API(此移植版专门针对它进行了适配)。启用版本隔离(PCL2 / HMCL)后,游戏目录和 mods/ 会位于 versions// 下,.optifine/ 缓存也会在那里创建。

要求

1.20.6 1.21.x 26.x
Minecraft 1.20.6 1.21 – 1.21.11(每个版本各需一个 jar) 26.1.2 / 26.2(每个版本各需一个 jar)
Fabric Loader 0.19.3 或更新版本 0.19.5 或更新版本 0.19.5 或更新版本
Java 21+(已在 25 上测试) 21+(已在 25 上测试) 25
端 客户端 客户端 客户端
OptiFine preview_OptiFine_1.20.6_HD_U_J1_pre18.jar(仅预览版) 你自己的构建版本,版本必须完全匹配 26.1.2:preview_OptiFine_26.1.2_HD_U_K1_pre2.jar,26.2:preview_OptiFine_26.2_HD_U_K2_pre4.jar(仅预览版,每个发行版本各需一个)
可选 Fabric API(支持) Fabric API(支持) Fabric API(已在 0.155.3+26.1.2、0.161.0+26.2 上测试)

验证了什么
每个版本都会通过一条命令、在单个加载器中进行检查,方式与游戏加载这些类时相同:使用 JVM 验证器和 ASM 数据流验证器检查每个已修补的类及每个 OptiFine 类,随后运行五种扫描器(Mixin 成员引用、@At注入点、抽象契约 / 丢失的虚方法重写 / 无法解析的引用、invokedynamic 句柄、本地变量捕获)。

Minecraft 已修补的游戏类(JVM) OptiFine 类(JVM) ASM 验证器 扫描器
1.20.6 425 / 425 — 0 个问题 干净
1.21.3 / 1.21.4 440 / 440 · 474 / 474 816 / 816 · 812 / 812 0 仅有 Indigo 已禁用时的 @At未命中
1.21.6 / 1.21.7 / 1.21.8 487 / 487 · 500 / 500 · 516 / 516 820 / 820 · 823 / 823 · 831 / 831 0 相同
1.21.9 / 1.21.10 / 1.21.11 519 / 519 · 553 / 553 · 570 / 570 832 / 832 · 836 / 836 · 874 / 874 0 相同
26.1.2 567 / 567 879 / 879 0 所有列均为 0(此版本未声明 contains_renderer,因此 Indigo 已启用)
26.2 571 / 571 879 / 879 0 全部为 0(比 1.21.x 版本更干净,后者仍有少量 Indigo 已禁用时的未命中)
游戏内验证:启动、标题画面、单人游戏、多人服务器、方块 / 区块 / 物品和实体渲染、光影(使用 ComplementaryReimagined 验证)、1.21.3–1.21.11 的抗锯齿、F3 调试界面;运行期间没有 [ERROR],也没有崩溃报告。在 26.1.2 中,Indigo 会注册自己的渲染器;按方块位置生成几何体的模组(LambdaBetterGrass 的草地美化、连接纹理)使用光影时也能正确渲染。        

已修复的兼容性问题

以下问题均由实际崩溃发现,并追溯到字节码层面:

Fabric API 的 ShaderProgramMixin 在 super() 之前注入 → OptiFine 的委托构造函数会被重写,而内联的标识符创建现在会复制游戏自身的 Identifier.ofVanilla(…) 调用(包装器精准定位该调用,而 new Identifier(…) 会绕过它);

OptiFine 留下了混淆状态且描述符不匹配的字段(例如粒子工厂表)→ 根据名称、类型和存储值重新对齐,并沿继承层次查找重写方法;

OptiFine 在重新编译时内联掉的私有辅助方法 → 恢复原版方法体,让注入重新有目标可用;

OptiFine 将方法改成转发至自身重载方法(区块构建、模型烘焙、方块轮廓、物品模型)→ 恢复原版方法体——这正是“所有模型都烘焙失败”和方块不可见问题的原因;

OptiFine 将对象创建重定向到自己的子类(ChunkOF 区块对象)→ 添加一个无操作标记,恢复 Fabric 的 NEW 注入点;

移植过来的修复器在替换构造函数时一并丢弃了字段初始化 → 不再替换该构造函数(这个问题会导致多人游戏会话在两秒后断开);

Lambda 方法体在重新编译时使用了不同的名称和签名(lambda$addMainPass$1 增加了一个参数)→ 恢复原版方法体;在 OptiFine 将方法引用替换为自己的 Lambda 时,LambdaMethodRefFix 会将它们重命名回方法名,因为该版本的 Fabric API 会根据引导句柄匹配自定义注入点;

一个区域构造函数从未存储区段位置(随后 ChunkCacheOF.renderStart() 会在空区域内触发 NPE)→ 修复器调用六参数构造函数,并从该方法已有的打包 long 值推导参数——1.21–1.21.4 的结构改为接收 ChunkSectionPos 对象,两种结构均已处理;
一个 Fabric hook 的上下文始终无法填入 OptiFine 的渲染阶段结构(BEFORE_BLOCK_OUTLINE)→ StubInjectionTargetFix 重命名目标并留下一个同名副本,导致 hook 注入到无人调用的代码中:崩溃消失了,方块轮廓仍由 OptiFine 绘制;

移动方块渲染器 hook 也遇到了同样的问题:其调用方位于另一个类中,原本会调用被注入的副本 → CallSiteRedirectFix 也重定向了该调用点(没有这项修复,多人游戏客户端约 30 秒后就会崩溃);

没有任何东西注册为渲染器时,按 F3 会崩溃——声明 contains_renderer 只能让 Indigo 退让;Fabric 自己的调试条目仍会调用 Renderer.get() → RendererApiFallback 注册了一个无操作的占位渲染器;

占位渲染器的第一个版本会导致整个会话崩溃——它使用 Class.getMethods() 读取 Fabric API 接口,在补丁后的类交接过来之前就加载了游戏类,并让它们永远固定为原版版本 → 现在占位渲染器仅根据接口的类文件本身通过 ASM 生成(不解析类型),通过 MethodHandles.findStatic 注册,并且只在类就位后注册;在 26.1.2 中,渲染器 API 还迁移到了 api.client.renderer.v1,旧的查找方式会静默失败;

不需要任何修复器修改的类不再重新计算堆栈帧——全局 override 修复器总是报告有变更,因此重新计算每个经过补丁的类会使合并后的局部变量类型退化为 java.lang.Object,游戏随即因 VerifyError: Bad type on operand stack 拒绝该类;

26.x:OptiFine 的精简壳类会留下两个同名方法,而 Fabric API 的目标名称不包含描述符 → 需要逐个调用点消除歧义(删除无人调用的重载;对仍被调用的方法重命名,并通过 CallSiteRedirectFix 跟随其调用方)。操作有误时会出现 LVTGeneratorError: Could not locate method metadata … 或 Scanned 0 target(s);

26.x:自行生成四边形的模型会悄无声息地消失——Fabric 的地形 hook 注入到原版区块构建中 BlockPos.betweenClosed 循环的位置,而 OptiFine 自己的 compile 重载没有这个循环,因此 hook 根本不会运行。FrapiTesselateBridgeFix + OptifineFrapiBridge 会将这一次镶嵌调用转交给 Fabric 的渲染器,并把四边形交给 OptiFine 自己的 BlockQuadOutput,因此顶点格式、渲染层、光照和着色器属性仍由 OptiFine 处理;只有 emitQuads 声明在 net.minecraft. 之外的模型才会被转交(原版模型仍沿用 OptiFine 的路径,否则着色器会丢失 OptiFine 的额外顶点属性);

抗锯齿:FXAA 链由游戏的后处理链加载器从 post_effect/ 加载,因此这个模组早期版本曾“贴心地”删除该文件,导致每次重新加载资源时都记录 Resource not found: minecraft:post_effect/fxaa_of_2x.json,而且每次切换抗锯齿都会报 Failed to load post chain。现在 OptiFine 自带的链文件会原样保留;而在 1.21.9 / 1.21.10 中,后处理管线会通过 gl_VertexID 绘制不带属性的全屏三角形,但 OptiFine 的 fxaa_of_*.vsh 仍会读取 Position 属性(这就是“开启抗锯齿后屏幕变黑”的问题),因此这两个顶点着色器也会相应重写。

完整列表(症状 / 原因 / 修复)见更新日志;每轮修复的证据见 docs/DEVELOPMENT.md(未混淆版本的行号见 docs/PORT_26.x.md)。

已知问题

与 Sodium 冲突——两者都是渲染器;请勿同时安装。
与 RyoamicLights 不兼容——OptiFine 会替换整个视频设置界面(包括其父类),导致该模组的注入失败,并在打开界面时立即崩溃。OptiFine 内置动态光源(视频设置 → 画质 → 动态光源),因此无需安装该模组。
依赖 FRAPI/indigo 的模组在 1.20.6 和 1.21.x 上不再使用 indigo 的自定义渲染;地形由 OptiFine 渲染,而 Renderer.get() 会返回一个不起作用的占位对象(F3 中显示 OptifineRendererPlaceholder)。在 26.1.2 上则恰好相反——Indigo 会注册自己的渲染器,并渲染使用该几何体的内容。
有两个 Fabric API 钩子被有意设为不工作状态(BEFORE_BLOCK_OUTLINE 事件不会触发;移动方块的 FRAPI 路径会被绕过)。这两种情形仍可通过原版/OptiFine 正常渲染。
1.21.6 / 1.21.7 在启用光影后会立即崩溃(NullPointerException … ShadersTex.initDynamicTextureNS 中的 “multiTex” 为 null)。问题不在光影包(三个互不相关的光影包都会以相同方式崩溃,移除光影包的自定义纹理也无济于事),也不在此模组:OptiFine 为这两个版本发布的预览构建会在首次创建纹理时注入调用,却没有建立后续构建中会建立的 setParentTexture 关联。适用于这两个版本的全部七个 OptiFine 构建都会以相同方式崩溃。不启用光影时,这两个版本都可以正常启动和游玩。
26.1.2 / 26.2 需要 Java 25——使用 Java 21 启动任一版本,都会在窗口出现前失败。这是游戏本身的要求,与此模组无关。
OptiFine 无法读取 Fabric 模组内的资源——你会在日志中看到 Unknown resource pack type: …ModNioResourcePack。这是 OptiFine 本身的限制。
与 OptiFine 版本不匹配的光影包会在日志中显示 [Shaders] Invalid program name: …、Unknown macro value: IRIS_VERSION 或 ParseException: Model variable not found: …;这些问题来自光影包。
这里只涵盖上文列出的版本——其他 Minecraft 版本需要单独测试;OptiFine 根本没有为 1.21.2 / 1.21.5 发布构建;此处涵盖的最新版本是 26.2。
暮色森林尚未完全支持——它可以进入标题界面,也确实能进入世界,但成功与否**并不稳定**:一项研究中 11 次尝试有 4 次成功;后续测试中,窗口最小化时 19 次有 5 次成功,窗口可见时 18 次有 13 次成功。**加载世界时,请保持游戏窗口可见并置于最前方**,并至少等待两到三分钟再判断是否卡住。尝试卡住不会影响存档,可以直接重试。

故障排除

缓存在哪里?/如何强制重建?/.optifine//。删除 .optifine/ 文件夹即可强制重建——升级后,缓存格式 (26) 通常会自行重建缓存。

为什么我在日志中找不到 [OptiFabric]?它的输出会显示在启动器控制台中,而不是 logs/latest.log;加载器只会将 log4j 输出写入该文件。筛选 [OptiFabric] 可查看已准备的类数量,以及被 Loader 接管的类数量。

找不到游戏 JAR 文件 / 我想手动指定其位置——添加 -Doptifabric.mc-jar=。

我想检查修补后的类——添加 -Doptifabric.extract=true;重映射后的 OptiFine 类会解压到 .optifine//optifine-classes/。

卡在加载界面——使用 jstack 获取两份线程转储(间隔约 15 秒),然后进行比较。如果线程栈完全相同且 CPU 使用率一直不变,说明确实卡住了;如果线程栈停在原生调用(glfwSwapBuffers)中,则是画面呈现问题。加载时不要最小化窗口(启用垂直同步时,渲染线程会阻塞在该调用处)。

模型/物品/纹理大量消失(日志中满是 Unable to bake … model)——某个 Fabric mixin 未能转换该类;最外层的错误消息通常掩盖了真正原因。常见原因是 OptiFine 将原版方法改成了转发方法,导致注入点发生偏移。

可忽略的常见警告 — [OptiFine] (Reflector) Class not present: net.minecraftforge.* / sun.misc.SharedSecrets(OptiFine 正在探测 Forge 和旧版 JDK),Failed to locate initialiser injection point in (class_2591,…)(应用 OptiFine 的 BlockEntity 补丁时会出现此提示),[OptiFabric] Resource not found: minecraft:shaders/post/fxaa_of_{2,4}x.json(OptiFine 正在探测 1.21.6 之前使用的链路位置;当前使用的链路位于 post_effect/),以及 Skipping bad option: lastServer.

报告问题

请附上:

logs/latest.log(如果发生崩溃,还请附上 crash-reports/ 中对应的文件——文件末尾会有一个 — OptiFabric — 部分,列出 OptiFine 版本、jar 状态和重映射后的 jar 路径);
mods/ 文件夹的文件列表;
你的 OptiFine 版本(例如 OptiFine_1.21.11_HD_U_J9)以及所运行的 Minecraft 版本。

许可与致谢

这是由 Modmuss50 和 Chocohead 移植的 Chocohead/OptiFabric,采用 MPL-2.0 许可证;移植的文件保留其原始版权声明。项目不包含也不再分发 OptiFine——它由 spx(sp614x)制作,请从官方网站获取。本项目是社区移植版,与 OptiFine、Fabric 或 Mojang 团队无关,也未获其认可或支持。

常见问题

OptiFabric Reforged 支持哪些 Minecraft 版本?

OptiFabric Reforged 支持 Minecraft Java 版 1.20.6–26.2,共 13 个版本,可用的加载器:Fabric。下载时注意选择和你游戏版本一致的文件。

OptiFabric Reforged 怎么安装?

先用 PCL2 或 HMCL 启动器安装对应版本的 Fabric,再把下载的 .jar 文件放进游戏目录的 mods 文件夹,重新启动游戏即可。开启了版本隔离的,mods 文件夹在 versions/版本名 下面。

评论

登录后评论和评分

kynarain 的其他作品

查看全部 →