自托管资源包
ResourcePackManager 内置了自己的小型 HTTP 服务器。当 autoHost: true(默认)且 preferSelfHost: true(默认)时,插件会尝试在与 Minecraft 服务器同一个 JVM 中托管合并后的资源包——无需外部文件托管服务、无需独立的 Web 服务器、也无需手动粘贴 URL。
本页介绍该自托管路径的工作原理、各项完整性检查的作用、端口与主机名的选择方式,以及当默认设置不合适时该如何配置。
如果你反而希望通过自己已有的、独立于 RSPM 的 Web 服务器托管 zip,请参阅疑难排查中关于 autoHost: false 的一节。
分发决策树
当玩家加入时,RSPM 按以下优先级选择分发 URL:
selfHostForce: true— 直接走自托管,不做任何探测,也不做远程上传。主要用于测试自托管路径。会绕过其他所有开关。preferSelfHost: true且selfHostEnabled: true且不在网络模式 — 先尝试带三项完整性检查的自托管(见下文)。若全部通过,则确认走自托管;若任一项失败,则回退到远程路径。- 否则 — 将资源包上传到
https://magmaguy.com/rsp/并公告该 URL。如果上传失败或 SHA1 校验返回SESSION_NOT_FOUND,则回退到自托管(前提是selfHostEnabled: true)。
一旦拿到 URL,RSPM 会使用 Minecraft 的多资源包 API,因此它的 Java 资源包可以与服务器发送的其他资源包共存。当前的 Bukkit 插件描述符要求 Minecraft 1.21.4 或更高版本。
三项完整性检查
当 preferSelfHost: true 时,RSPM 在确认走自托管之前会按顺序运行下面三项检查:
第 1 层 — 对解析出的外部主机名做启发式检查
如果解析出的主机名(参见下文 “外部主机检测”)属于 RFC1918(10.*、172.16-31.*、192.168.*)、回环(127.*)、链路本地(169.254.*)或未指定(0.0.0.0),那么自托管对互联网客户端根本不可能可用。立即跳过,使用远程托管。
这能捕获非常常见的“ipify 查询失败、回退到 LAN IP”的失败模式。
第 2 层 — 本地回环自探测
对 http://127.0.0.1:<port>/rspm.zip 发起 HEAD 请求,验证返回 HTTP 200 并且响应体非空。可捕获:
- 端口绑定冲突(其他东西占用了所选端口)
- 资源包文件缺失(路由已注册但 zip 尚未在磁盘上就位)
- 路由注册的 bug
该探测使用激进的超时(3 秒),以免一个慢探测拖延启动。
第 3 层 — 外部可达性探测
向 magmaguy.com 托管端的 POST /rsp/probe 提交公告出的 URL。该托管端会从一个公共可视点(带 SSRF 防护和较紧的超时)尝试拉取该 URL,并把可达情况回报。
这能捕获生产环境中最常见的失败模式:服务器有公网 IP,但 HTTP 端口没有在路由器或防火墙上放通。第 2 层会通过(服务器在 127.0.0.1 上有响应),但任何真实客户端都根本下载不到资源包。
针对探测结果的决策策略:
- reachable=true → 外部客户端可以访问到我们的 URL。确认走自托管。
- reachable=false → 外部客户端访问不到。拆掉自托管,改用远程托管(从 magmaguy.com 看是普遍可达的)。
- 探测通信本身失败(IOException) → 无法证实任何一种结果。默认保留自托管:以“无法探测”为由拒绝是自相矛盾的,因为远程路径同样需要 magmaguy.com。
这些检查仍然检测不到的情况
NAT 回流(hairpin)边缘情况:端口对公网完全开放(第 3 层通过),但运维人员自家路由器不会把 LAN 内的流量回环回来。外部客户端正常工作,但运维人员从同一台机器测试时失败。
如果只是想在宿主机上快速测试,请设置 preferSelfHost: false 使用远程回退,或者从蜂窝网络/外部网络访问那个公网 URL。若要长期使用自托管,请采用分离视图 DNS(split-horizon DNS,让同一个公网主机名在你的内网中解析到服务器的 LAN 地址)。不要把 selfHostExternalHost 设为 127.0.0.1:回环地址与私有地址会被公网主机名完整性检查拒绝,而且它们本来也无法为外部客户端提供服务。
端口解析
两个设置会互相影响:
selfHostPort— 显式端口(任意正整数)或-1(默认)以启用自动派生。networkHttpOffset-v2— 仅在selfHostPort = -1时才参考。加到 Minecraft 服务器端口上以派生出 HTTP 端口。默认1。(在代理网络中,这同一个值也是代理对某个后端 HTTP 端口的回退猜测——见下文。)
默认值是 selfHostPort: -1 + networkHttpOffset-v2: 1,所以:
- MC 端口 25565 → HTTP 端口 25566
- MC 端口 25584 → HTTP 端口 25585
这样在单主机网络上的多个后端之间会自动错开 HTTP 端口,无需任何管理员配置——每个后端本来就有唯一的 MC 端口,因此各自得到一个唯一的 HTTP 端口。
为什么偏移是 1?
大多数共享 / 托管型 Minecraft 服务(基于 Pterodactyl 的面板等)会为每个容器分配一段较窄的端口范围(通常只有 4–10 个端口)。更大的偏移会落到范围之外,宿主防火墙会静默屏蔽 HTTP 端口。偏移 1 即使在最紧的分配下也能放得下。
拥有完整端口控制权的自托管管理员可以把它改成任意值。在代理网络中,后端会把它实际绑定到的确切 HTTP 端口自动公告给代理,因此即便你更改了偏移或设置了显式的 selfHostPort,代理也会随之跟进。只有在后端尚未完成公告的那个短暂窗口内(或公告无法送达代理时),把代理的 network-http-offset-v2 调成一致才有意义,那只是作为回退使用。
注意:RCON 端口冲突
如果你的宿主默认在 MC port + 1 上启用 RCON,请将偏移改为 2 或 3 以避免端口冲突。在 server.properties 中查看 rcon.port=。
带版本号的配置键
config.yml 中的设置项名称就是字面的 networkHttpOffset-v2。v1 的键是 networkHttpOffset,默认值 100——在共享 / 托管型服务上会出问题,因为每个游戏容器只分到 4–10 个连续端口,MC + 100 落在范围外,HTTP 服务器在内部能够绑定但宿主防火墙会丢弃外部流量,代理会得到一个永远静默的 CONNECT_FAILED。v2 默认值为 1,让 MC + 1 即使在最窄的容器分配下也能稳稳落在范围内。
如果你从 v1 升级,废弃的 v1 键会以无害遗留物的形式留在你的配置中,等你手动清理——RSPM 故意不读取它。
外部主机检测
selfHostExternalHost 控制客户端在 URL 中看到的主机名。留空(默认)则按以下优先级自动检测:
- api.ipify.org / checkip.amazonaws.com — 返回本主机的公网 IPv4。每次
/rspm reload之间只缓存一次,避免反复请求 IP 服务。 Bukkit.getIp()— 服务器绑定地址,仅在非空且不为0.0.0.0时使用。通常是 LAN 地址。InetAddress.getLocalHost()— 尽力而为。localhost— 最后的兜底。盒子外的客户端访问不到。
如果自动检测落在了不可路由的地址上,且 preferSelfHost: true,第 1 层启发式检查会失败,插件会切换到远程托管。
要获得最可靠的自托管设置,请把 selfHostExternalHost 显式设为你的公网主机名(例如 play.example.com)。这会完全跳过 ipify/AWS 检测,探测会针对你显式给出的值运行。
配置项参考
# Whether the built-in HTTP server may be used as a delivery path at all.
# When false, ordinary self-host attempts are disabled. selfHostForce still overrides it.
selfHostEnabled: true
# Port for the self-host HTTP server.
# -1 (default) = auto-derive: HTTP port = Minecraft server port + networkHttpOffset-v2.
# Set to any positive value to force an explicit port.
selfHostPort: -1
# Added to the Minecraft server port when selfHostPort = -1.
# Default 1 (MC 25565 -> HTTP 25566). On a proxy network this is only a FALLBACK:
# the backend announces the exact HTTP port it actually bound to the proxy, so
# you no longer have to match this against the proxy's network-http-offset-v2.
networkHttpOffset-v2: 1
# Public hostname or IP clients use to reach the self-host server.
# Leave empty for auto-detect (api.ipify.org / checkip.amazonaws.com).
selfHostExternalHost: ""
# Try self-host FIRST with three sanity checks, fall back to remote if any fail.
# When false, use the legacy order: remote upload first, self-host only on upload failure.
preferSelfHost: true
# Skip ALL other delivery paths and force self-hosting.
# Bypasses sanity checks AND remote upload. Mainly for testing.
selfHostForce: false
# Print every step of pack preparation and the hosting handshake.
# Default false — see "What the console actually prints" below.
# `/rspm verbose on|off` flips this same key at runtime and saves it here.
verboseLogging: false
控制台实际会打印什么
在正常启动时,RSPM 刻意只打印一行关于托管的信息 —— 只讲结果,不讲过程:
[ResourcePackManager] Resource pack is live via self-hosting — http://play.example.com:25566/rspm.zip
或者,当走的是远程路径时:
[ResourcePackManager] Resource pack is live via automatic hosting — https://magmaguy.com/rsp/<uuid>
这一行只在每次有分发路径被全新初始化时打印一次,玩家重试或保活心跳都不会重复打印。如果一次 reload 后的资源包没有变化,它可以复用已有的托管注册,而不会打印新的结果行。
失败的完整性检查不是警告。 探测失败是 RSPM 会自行恢复的一种状况,因此它被记录为细节而不是故障,并且每条消息都明确这么说(Self-host check: local pack link is not public. This is OK.)。除非你主动要求,否则你不会看到它 —— 为插件已经处理好的事情发出警告,读起来就像出了故障,会让服主去追一个根本不存在的问题。
要查看完整的决策链 —— 哪一项检查失败、探测 URL、原因代码、暂存的每个资源包与合并的每个集群 —— 请运行:
/rspm verbose on
然后执行 /rspm reload。这是在为托管问题提交 bug 报告之前应该先打开的开关。该命令会把 verboseLogging: true 写入 config.yml,且该设置会在重启后保留,所以排查完毕后请运行 /rspm verbose off;手动编辑这个配置键的效果完全相同。
请注意,verboseLogging 覆盖的是资源包准备与托管。基岩版转换器有自己独立的开关 bedrockConverterDebug —— 参见基岩版转换。
后端 HTTP 服务器路由
内置的 HTTP 服务器始终在下面这个地址提供资源包 zip:
http://<host>:<port>/rspm.zip
在网络模式下(RSPM 处于 Velocity / BungeeCord / Waterfall 代理之后),还会额外注册供代理插件拉取的路由:
http://<host>:<port>/bedrock.zip # the Bedrock-converted pack
http://<host>:<port>/mappings.json # the Geyser custom-mappings JSON
http://<host>:<port>/rspm-update.jar # the universal RSPM update jar (authenticated)
资源包路由与基岩版构件路由都由文件支持:每次请求都会重新读取对应文件,所以即使重新合并写入了同一个路径,也会被自动采纳,无需重启 HTTP 服务器。基岩版路由使用强 ETag,并同时支持 If-Modified-Since 以保持兼容,因此当内容未变时代理的轮询会收到 304,几乎零带宽。这些路由在底层文件缺失时(例如首次合并尚未完成)都会干净地返回 404。更新路由由从共享网络密钥派生的 bearer 令牌单独保护,并且只有在能够提供有效更新 jar 时才可用。
验证自托管是否启用
/rspm status 会显示:
Active delivery: SELF-HOSTED— 当前使用自托管Active delivery: REMOTE (magmaguy.com)— 当前使用远程 auto-hostURL: ...— 客户端实际看到的 URLResolved external host—selfHostExternalHost解析得到的值Public IP (auto-detected)— ipify/AWS 报告的结果(如有)selfHostPort— 是自动还是显式,以及解析得到的值
如果你预期走自托管但看到的是远程,/rspm status 就是该看的地方 —— 启动日志按设计对已自愈的探测失败保持沉默。运行 /rspm verbose on 并执行 /rspm reload,即可看到是哪一项完整性检查失败以及原因。
常见错误
- 在路由器存在 hairpin 问题时仍然相信第 3 层探测的结果 — 该探测从 magmaguy.com 的可视点运行,所以无法捕获“外部客户端可用、但运维人员自家 LAN 无法回环”的情况。不确定时请用蜂窝网络下的手机测试。
- 把
networkHttpOffset-v2调过你托管商的端口范围 — 症状是代理侧永远静默的 CONNECT_FAILED。后端确实会把它真实绑定的端口公告给代理,但 HTTP 服务器仍然必须绑定到一个可达的端口,代理也仍然必须能够访问它;如果mcPort + offset落到容器分配的端口区间之外,那么无论是否公告,绑定/防火墙都会失败。在提高偏移之前,先确认该端口落在容器分配的端口区间内。