跳到主要内容

Java 到基岩版转换

ResourcePackManager 可以将合并后的 Java 资源包转换为基岩版资源包,让 GeyserMC 客户端看到与 Java 客户端相同的自定义内容。该功能默认启用。

转换的触发条件

转换只在存在基岩版目标时才会运行。RSPM 在以下任一条件成立时认为目标存在:

  1. Geyser-Spigot 安装在本后端(基岩版玩家在本地通过 Geyser 接入)。
  2. Floodgate 安装在本后端(典型的代理-后端架构 —— Floodgate 在本地运行,Geyser 部署在其他位置)。
  3. 网络模式处于激活状态 —— RSPM 检测到自身处于 Velocity / BungeeCord / Waterfall 代理之后。后端会生成自己的基岩版资源包,并通过一个小型 HTTP 服务器暴露出来,供代理插件拉取。

如果上述条件都不成立,转换器就是纯粹的开销,会被静默跳过。当资源包没有生成时,/rspm status 会准确解释原因。

哪些内容会被转换

转换器与命名空间无关。它会递归遍历每一个符合 1.21.4+ 物品定义格式的 assets/<namespace>/items/**/*.json 文件,包括 minecraft 命名空间。

扁平与 3D 的分流

对每个叶子模型,转换器会决定走两条流水线中的哪一条。以 minecraft:item/generatedminecraft:builtin/generated 为根的模型留在扁平路径上。对于其他所有父链,只有当合并后的模型携带非空的 elements 数组时,该模型才会走 3D 流水线;没有几何体的模型就是 2D 精灵图。

  • 扁平 —— 模型的 layer0 贴图会被直接复制到 textures/items/<hash>.png 并注册为 Geyser 图标。在基岩版上,该物品在背包中和手持时都会显示正确的 2D 精灵图,与 Java 的渲染完全一致。
  • 3D —— 转换器会拼合一张贴图图集、把 Java 立方体转换为基岩版几何体、生成手持/头部动画、软件渲染一个 64×64 的背包图标,并为每一组 (model × base item × predicate shape) 映射写出一个可附着物。

为什么几何体检查很重要:一件扁平的手持工具(父模型为 minecraft:item/handheld,只有 layer0 贴图而没有 elements)仍然是 2D 精灵图。早期版本会把扁平手持物品推进 3D 流水线,它们在几何体这一步失败,于是在基岩版上要么消失,要么退回显示原版基础物品的图标。这尤其影响 ItemsAdder 资源包,因为它们提供了大量扁平手持物品。由于 elements 是从合并后的父链中读取的,因此一个非 generated 的模型若从父模型继承了几何体,仍然会被分流到 3D。

每组 (model × base item × predicate shape) 映射都会生成唯一的基岩版标识符,因此同一把剑模型注册到多个基础物品或多个谓词分支时,在 Geyser 端不会发生冲突。生成的文件名是简短的内容哈希而非可读名称,因为完整的命名空间+路径名经常超出 Geyser 80 个字符的资源包路径限制。

1.21.4 之前的旧资源包

仍在使用旧的 assets/minecraft/models/item/*.json + overrides[].predicate.custom_model_data 格式的资源包同样会被识别,并合成为现代的 range-dispatch 形式。这是尽力而为的处理:控制台会记录有多少物品使用了旧格式,因为它们在基岩版上往往无法正确渲染。真正的解决办法是把源资源包迁移到 1.21.4+ 的物品定义格式。

手工编写的基岩版实体资源包

插件可以在其 Java 资源包中的 assets/<namespace>/rspm_bedrock_pack/ 下直接放置原生基岩版实体资源。RSPM 会把这些文件原样复制进生成的基岩版资源包。只接受与实体相关的目录(entitymodels/entityanimationsanimation_controllersrender_controllersmaterialstextures/entity),这样贡献内容的插件就无法覆盖资源包清单或图标图集。过长的路径会被自动缩短,并同步重写 JSON 中的交叉引用,因此几何体和贴图引用仍然可以正常解析。两个命名空间向同一目标位置写入不同字节是硬性错误,而不会被静默覆盖。

这正是实现真正的基岩版自定义实体的机制 —— 参见 Geyser 扩展与自定义实体。只包含实体资源包、完全没有物品映射的资源包同样会被正常分发。

assets/<namespace>/equipment/<material>.json 同级存在时,会被识别为自定义盔甲套装。转换器会装配一个将原版盔甲几何体与 Java 贴图作为可见层组合的盔甲可附着物,使基岩版玩家穿戴该物品时能看到正确的盔甲贴图。

基岩版资源包的清单 header / module UUID 是根据插件版本字符串确定性派生的(种子为 rspm_bedrock_header:<pluginVersion>rspm_bedrock_module:<pluginVersion>),因此在同一插件版本的多次重建之间保持稳定,只有插件版本变化时才会改变。可见的 header 名称固定为 ResourcePackManager Bedrock Pack,它不参与 UUID。版本三元组在每次构建时根据一个 cache-bust 令牌递增,该令牌取自暂存基岩版资源包内容的 SHA-256 摘要——内容完全相同则版本相同(因此空操作的重建不会扰动 Geyser 的缓存),而真正的内容变更则会使基岩版以 (uuid, version) 为键的资源包缓存失效。仅在无法计算内容摘要时,才退而使用构建时间(System.currentTimeMillis())作为兜底。

按会话实时分发(独立服务器)

当检测到 Geyser-Spigot 与本后端在同一台服务器上时,RSPM 会注册一个 SessionLoadResourcePacksEvent 订阅者。每个在新一次合并之后加入的基岩版玩家,都会直接从磁盘获取最新的基岩版资源包——对现有物品的贴图或模型编辑无需重启服务器。

Geyser 的自定义物品映射(即 custom_mappings/ 中的 JSON)仍然是启动时冻结的,因此新增或移除自定义物品仍需重启服务器,基岩版客户端才能看到这些变更。RSPM 会在启动早期预先部署上一次运行生成的映射文件,让 Geyser 在启动时的自定义物品注册流程能够自动拾取它。

当前已连接的基岩版玩家会保留各自加入时收到的资源包——这是基岩版协议的限制,插件无法在会话中途绕过它。

在代理模式下的工作原理

在代理网络中,后端本身不会注册 SessionLoadResourcePacks 订阅者(本地没有 Geyser 可以订阅)。流程是:

  1. 后端生成 output/ResourcePackManager_Bedrock.zip,并在存在物品映射时生成 output/rspm_geyser_mappings.json
  2. 后端启动一个小型 HTTP 服务器(端口自动派生,默认为 mcPort + 1),暴露 /bedrock.zip/mappings.json 两个路由,每次请求都会重新读取文件,并把它实际绑定到的确切端口公告给代理。
  3. 代理插件每 5 秒轮询一次,优先使用强 ETag 配合 If-None-Match,同时保留 If-Modified-Since 以兼容较旧的后端。当文件变化时它会下载每个后端的输出,并等待收件箱状态稳定。
  4. 代理插件将每个后端的基岩版资源包合并为一个全网络统一的资源包,并通过代理上的 Geyser 分发给基岩版客户端。
  5. 如果代理无法直接访问某个后端的 HTTP 端口(在共享/托管服务上很常见,相邻端口往往被防火墙屏蔽),后端会把文件推送到 magmaguy.com 的中继端点,代理则通过该中继获取它们。

安装方式请参阅 代理网络。代理侧的合并是自动进行的,没有启用开关。代理配置只有两项设置:network-http-offset-v2,即在后端端点公告可用之前使用的回退端口偏移;以及 geyser-extension-auto-install,它是 geyserExtensionAutoInstall 在代理侧的对应项。

输出文件

合并成功后,基岩版相关文件位于:

plugins/ResourcePackManager/output/ResourcePackManager_Bedrock.zip
plugins/ResourcePackManager/output/rspm_geyser_mappings.json # only when item mappings exist

如果启用了自动部署并检测到本地 Geyser 数据目录,映射文件还会被复制到:

<geyser-folder>/custom_mappings/rspm_geyser_mappings.json

基岩版资源包的 zip 不会被复制到 <geyser-folder>/packs/ 中——而是按会话实时分发。如果某个较旧的 RSPM 版本曾在 Geyser 的资源包目录中留下 ResourcePackManager_Bedrock.zip,当前插件在 Geyser 运行期间不会删除它:Geyser 已经扫描过该路径,并且可能仍将其保留在内存中。RSPM 会在那次启动中跳过实时分发提供器的注册,并打印出确切的遗留文件路径。请完全停止服务器,只删除那个遗留文件,然后再启动服务器。执行 /reload 是不够的。

当转换器没找到任何可发布的内容(既没有可转换的物品映射,也没有被允许的实体资源包文件)时,RSPM 会删除上一次运行残留的基岩版输出,而不是分发一个空资源包。后端的 /bedrock.zip 路由随后会干净地返回 404,这正好向代理传达了正确的信号:该后端没有任何基岩版内容可以贡献。仅含实体的资源包仍会被正常发布。

config.yml 设置

# Toggles Java-to-Bedrock conversion altogether.
bedrockConversionEnabled: true

# Copies the Geyser custom mappings file into the detected Geyser folder's
# custom_mappings/ directory on each mix.
bedrockAutoDeployToGeyser: true

# Manual override for the Geyser data folder. Empty = auto-detect.
# Relative values are tried from the server working directory first, then
# relative to plugins/. Absolute paths work directly.
bedrockGeyserFolder: ""

# Installs and updates the universal ResourcePackManager.jar in Geyser's
# extensions/ folder, which is what makes custom Bedrock ENTITIES render
# instead of armor stands. Separate from pack conversion above — items,
# textures and models convert and serve normally either way.
# Setting this false stops future installation and update staging; it does NOT
# remove an extension jar that is already installed. Delete that by hand while
# Geyser is stopped. See the Geyser extension page.
geyserExtensionAutoInstall: true

# Verbose per-item / per-bone progress logging from the Bedrock pipeline.
# Default false — a clean run emits a single "Bedrock conversion complete: N
# mappings" summary instead of dozens to hundreds of per-item lines. Flip on
# when debugging a specific conversion issue.
bedrockConverterDebug: false

自动检测 Geyser 目录会依次查找:

  1. bedrockGeyserFolder 已设置,则先按字面值使用(因此相对路径从服务器工作目录解析;如果该位置不存在,再尝试相对于 plugins/ 解析)。绝对路径同样可用。
  2. plugins/Geyser-Spigot/
  3. plugins/Geyser-*/(任何变体)
  4. config/Geyser-*/(用于 Fabric/NeoForge 部署)

调整手持物品的显示:bedrock_display_offsets.yml

基岩版通过一个父骨骼来渲染手持物品,而该骨骼的静止姿势与 Java 的第一人称/第三人称变换并不相同,因此算法在转换时必须在 Java 模型的 display 变换之上额外施加一个基础偏移。默认偏移适用于典型的右手 Java 模型,但特殊情况可能需要微调。

第一人称与第三人称是两个完全独立的基岩版渲染通道(父骨骼不同,静止姿势不同),所以各有一组互相独立的六个旋钮。调整一个不会影响另一个。

# ===== First-person (right hand, seen by the holder) =====
firstPersonBaseRotationX: -60.0 # pitch (tipping toward/away from camera)
firstPersonBaseRotationY: 123.0 # yaw (spinning around vertical line)
firstPersonBaseRotationZ: 170.0 # roll (around camera-forward axis)
firstPersonBasePositionX: -8.0 # vertical on screen (positive = up)
firstPersonBasePositionY: 7.5 # depth (positive = further into the scene)
firstPersonBasePositionZ: -5.0 # horizontal on screen (positive = right)

# ===== Third-person (right hand, seen by other players / F5) =====
thirdPersonBaseRotationX: 90.0 # pitch as observers see it
thirdPersonBaseRotationY: 0.0 # yaw
thirdPersonBaseRotationZ: 0.0 # roll around the item's long axis
thirdPersonBasePositionX: 0.0 # horizontal across the holder's body (positive = outward)
thirdPersonBasePositionY: 6.0 # vertical (positive = raises the model)
thirdPersonBasePositionZ: -10.0 # depth relative to holder (positive = forward)

位置值以像素为单位,1 像素 = 1/16 个方块。旋转以度为单位。

修改数值后运行 /rspm reload,并让基岩版测试客户端重新连接,这样它下一次加入时就会收到重建后的资源包。如此反复迭代,直到手持物品看起来合适为止。

调试日志

提供两种调试入口:

  • 后端:在 config.yml 中设置 bedrockConverterDebug: true,会启用转换器逐物品、逐可附着物、逐映射的日志行。当你需要知道某个具体物品为什么没有进入基岩版资源包时很有用。它与 verboseLogging相互独立的:后者覆盖的是资源包合并与托管,而不是基岩版流水线 —— 排查转换问题你要用的是 bedrockConverterDebug
  • 代理(Velocity 与 BungeeCord)/rspm debug bedrock on 切换代理端 GeyserBinder 输出的 [RSPM-BedrockDebug] 日志流。适用于基岩版玩家已经接入代理但看不到资源包的情况。该设置在代理重启时会重置为关闭,避免意外保持开启状态。

局限性与已知行为

  • 3D 背包图标由软件根据 Java 模型的 display.gui 变换渲染。效果合理但并非像素级精确;如果图标看起来不对,最常见的原因是模型引用的贴图文件缺失或命名错误。
  • 用作物品图标的连拍贴图(flipbook)会被裁剪到第 0 帧——基岩版的 item_texture.json 不支持动画图标,只能通过 flipbook_textures.json 支持动画方块/地形贴图。
  • 可附着物几何体的格式版本固定为 1.21.0;如果你的安装无法解析它,请升级 Geyser。
  • 旧式原版物品模型覆盖文件(assets/minecraft/models/item/ 下的任何文件,包括 shield.jsoncrossbow.json)会跨资源包合并,而不是让某一个资源包完全取胜:只有 overrides 数组会被合并(按 override 键去重),而非 override 字段仍然遵循最高优先级取胜。没有任何文件被单独特殊对待。
  • 如果转换器无法解析某个被引用的贴图或模型文件,会跳过该叶子,其余流水线继续执行。启用 bedrockConverterDebug 可获得详细的解析追踪。当某个物品最终完全没有有效的自定义贴图,或某个被引用的模型 JSON 无法解析时,会输出一条普通警告。
  • 格式错误的物品定义则是另一回事,现在它对本次转换周期是致命的:无法解析的 JSON 会以 Bedrock conversion failed: ... 中止整个转换,而不是悄悄产出一个不完整的资源包。以前“大体上”能转换成功的资源包,现在会明确告诉你它没有成功。
  • 横幅与红石基础物品会被跳过。 以 16 种染色横幅之一或 minecraft:redstone 为基础物品的自定义物品不会获得 Geyser 自定义物品映射,因为 Geyser 会从这些基础物品推导出无效的方块放置器。基岩版上会为它们显示原版图标。发生这种情况时,控制台会列出受影响的物品。
  • 转换是可协作取消的:中途执行 /rspm reload 或关服会干净地中止,而不会留下写了一半的输出。Geyser 映射文件只有在资源包 zip 成功之后才会发布,因此你绝不会得到一个新资源包配上过期映射的组合。
  • 当合并后的资源包既没有可转换的物品映射、也没有被允许的实体资源包文件时,不会生成任何基岩版资源包,并且会删除上一次运行残留的输出。仅含实体的资源包依然有效,即使物品映射数量为零也会被生成。

资源包体积优化

在发布基岩版资源包之前,RSPM 会对扩展名相同且字节完全一致的贴图文件去重,并把精确的 JSON 贴图引用重写为保留下来的那个文件。当某个引用存在歧义、嵌入在更长的字符串中,或出现在格式错误/不透明的 JSON 中时,它会有意保留别名,从而确保优化不会悄无声息地破坏资源引用。