跳到主要内容

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:item/generated 等扁平 builtin 父模型),或
  • 输出基岩版几何体、可附着物、动画文件以及由软件渲染的 2D 背包图标(针对真正的 3D 模型)。

每对 (模型 × 基础物品) 都会生成唯一的基岩版标识符,因此同一把剑模型绑定到多个基础物品时,在 Geyser 端不会发生冲突。

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

基岩版资源包的清单 header / module UUID 是根据插件版本字符串确定性派生的(种子为 rspm_bedrock_header:<pluginVersion>rspm_bedrock_module:<pluginVersion>),因此在同一插件版本的多次重建之间保持稳定,只有插件版本变化时才会改变。资源包名本身只用于 header.name,不参与 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.zipoutput/rspm_geyser_mappings.json
  2. 后端启动一个小型 HTTP 服务器(端口自动派生,默认为 mcPort + 1),暴露 /bedrock.zip/mappings.json 两个路由,每次请求都会重新读取文件,并把它实际绑定到的确切端口公告给代理。
  3. 代理插件每 5 秒通过 If-Modified-Since 轮询一次,当文件变化时下载每个后端的输出,并等待收件箱状态稳定。
  4. 代理插件将每个后端的基岩版资源包合并为一个全网络统一的资源包,并通过代理上的 Geyser 分发给基岩版客户端。
  5. 如果代理无法直接访问某个后端的 HTTP 端口(在共享/托管服务上很常见,相邻端口往往被防火墙屏蔽),后端会把文件推送到 magmaguy.com 的中继端点,代理则通过该中继获取它们。

安装方式请参阅 Proxy networks。在代理一侧,转换(确切说是合并)是全自动的;基岩版流水线没有代理侧的配置项。

输出文件

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

plugins/ResourcePackManager/output/ResourcePackManager_Bedrock.zip
plugins/ResourcePackManager/output/rspm_geyser_mappings.json

如果启用了自动部署并在同一 JVM 中检测到 Geyser,映射文件还会被复制到:

<geyser-folder>/custom_mappings/rspm_geyser_mappings.json

基岩版资源包的 zip 不会被复制到 <geyser-folder>/packs/ 中——而是按会话实时分发。RSPM 还会移除旧版本插件可能留在该文件夹中的遗留副本,以免 Geyser 重复分发同一份资源包(重复的 UUID 会让基岩版客户端困惑)。

当转换器没找到任何可转换内容(合并后的资源包中没有物品定义)时,RSPM 会删除上一次运行残留的基岩版输出,而不是分发一个空资源包。后端的 /bedrock.zip 路由随后会干净地返回 404,这正好向代理传达了正确的信号:该后端没有任何基岩版内容可以贡献。

config.yml 设置

# 总开关:是否启用 Java 到基岩版的转换。
bedrockConversionEnabled: true

# 是否在每次合并时把 Geyser 自定义映射文件复制到已检测到的 Geyser
# 目录的 custom_mappings/ 子目录中。
bedrockAutoDeployToGeyser: true

# 手动覆盖 Geyser 数据目录路径。留空 = 自动检测。
# 可以是绝对路径,也可以是相对于 plugins/ 目录的路径。
bedrockGeyserFolder: ""

# 是否输出基岩版流水线的逐物品 / 逐骨骼详细日志。
# 默认 false — 正常运行只会输出一行 "Bedrock conversion complete: N
# mappings" 摘要,而不是几十甚至上百行逐物品的日志。仅在排查
# 具体转换问题时打开。
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 模型,但特殊情况可能需要微调。

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

# ===== 第一人称(右手,持有者视角)=====
firstPersonBaseRotationX: -90.0 # 俯仰角(向相机方向倾斜/远离)。默认值用于抵消父级旋转。
firstPersonBaseRotationY: 0.0 # 偏航角(绕垂直轴旋转)
firstPersonBaseRotationZ: 0.0 # 翻滚角(绕相机前向轴)
firstPersonBasePositionX: 0.0 # 屏幕上的纵向位置(正值 = 向上)
firstPersonBasePositionY: 12.5 # 深度(正值 = 向场景纵深方向)
firstPersonBasePositionZ: 0.0 # 屏幕上的横向位置(正值 = 向右)

# ===== 第三人称(右手,其他玩家或 F5 视角)=====
thirdPersonBaseRotationX: 90.0 # 旁观者所见的俯仰角
thirdPersonBaseRotationY: 0.0 # 偏航角
thirdPersonBaseRotationZ: 0.0 # 绕物品长轴的翻滚角
thirdPersonBasePositionX: 0.0 # 沿持有者身体的横向位置(正值 = 向外)
thirdPersonBasePositionY: 12.5 # 纵向(正值 = 抬高模型)
thirdPersonBasePositionZ: 0.0 # 相对持有者的深度(正值 = 向前)

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

基岩版客户端会在不重启的情况下热重载可附着物 JSON。如果有一个基岩版测试客户端已在线,修改数值后运行 /rspm reload,下一次基岩版玩家加入时(在某些情况下,连接中的同一玩家做一个能重新绑定可附着物的小动作之后)就会看到新的偏移。如此反复迭代直到效果合适。

调试日志

提供两种调试入口:

  • 后端:在 config.yml 中将 bedrockConverterDebug 设为 true,会启用转换器逐物品、逐可附着物、逐映射的日志行。当你需要知道某个具体物品为什么没有进入基岩版资源包时很有用。
  • 代理(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 字段仍然遵循最高优先级取胜。没有任何文件被单独特殊对待。
  • 如果转换器无法解析某个被引用的贴图或模型文件,会跳过该叶子并记录一条 [BedrockConverter] 警告(或当 bedrockConverterDebug 关闭时记一条 debug 日志);其余流水线继续执行。
  • 当合并后的资源包中没有任何可转换的物品时,根本不会生成基岩版资源包,并且会删除上一次运行残留的输出。RSPM 不会分发一个无用的、约 22 KB 的空资源包外加一个提示。