环境:Lophine (LuminolMC) / AdvancedServerList / Via系列插件 / Wireshark / tcpdump
问题现象
Minecraft 服务器列表中,自建服务器的 MOTD 无法正常刷出。具体表现为:
- 服务器列表中版本信息与玩家数量无法显示
- MOTD 文字在约 30 秒延迟后才出现
- 所有协议版本客户端均能复现,不限于特定版本
- 其他玩家同样能复现,排除本地环境因素
- 回退至最简单的占位符 MOTD 后,问题消失
- 重置插件配置文件重新生成后,问题临时消失
- 将配置搬至本机 localhost 测试,问题消失
- 此前从未出现过类似问题,属于首次发生
初步排查:客户端侧
首先在控制端排查本地因素:
- 检查 TUN 代理 DNS 分流配置
- 检查游戏客户端本身(内置 DNS、代理设置)
- 测试连接其他服务器的 MOTD → 正常
- 对比连接自建服务器与第三方服务器的行为差异
结论:客户端侧未发现异常,问题不在本地。
抓包分析:客户端视角
在本地对 Minecraft 流量进行 Wireshark 抓包,发现一个关键异常包:
Internet Control Message Protocol
Type: Destination unreachable (3)
Code: 3 (Port unreachable)
该 ICMP 错误包内嵌了一个 TCP RST 数据包,发向游戏服务器端口。
关键时序:
ICMP Port Unreachable(超时后触发)
↓ 约 30 秒后
MOTD 数据流(此后才出现)
MOTD 延迟约 30 秒出现的现象与此吻合。结合 TCP 流的完整性标志(RFDASS:RST / FIN / DATA / ACK / SYN-ACK / SYN 全部出现),可以判断:
- 客户端发起了一次完整的 TCP 状态查询连接
- 该连接在约 28 秒后因等待超时,客户端主动发出 RST
- 部分 NAT 或防火墙设备在连接会话失效后,对延迟回包回复了 ICMP Port Unreachable
- 此时客户端才回退并发起第二次 MOTD 请求,最终成功
阶段性结论:问题根源在服务端,与 DNS、Via 插件、域名解析无关。
服务端抓包:两次连接,两种命运
在服务端使用 tcpdump 抓取游戏端口流量,触发一次 MOTD 刷新:
sudo tcpdump -i [网卡] -n port [游戏端口]
观察到两组连接:
第一次连接(失败):
握手完成
客户端发送状态请求(约 21–23 字节)
服务端回包:约 3283 字节
客户端:立刻 RST,连接中断
第二次连接(约 15 秒后,成功):
握手完成
客户端发送状态请求(约 58–62 字节)
服务端回包:约 1223 字节
客户端:正常 FIN,干净关闭
关键数据对比:
| 连接 | 服务端响应大小 | 客户端行为 |
|---|---|---|
| 第一次 | 3283 字节 | 立刻 RST |
| 第二次 | 1223 字节 | 正常 FIN |
服务端确实有响应,不是静默丢弃。客户端 RST 发生在收到完整数据之后。因此问题不是"服务端没有响应",而是"服务端响应的内容有问题"。
定位响应内容:JSON 结构分析
使用带 ASCII 输出的 tcpdump 抓取服务端响应内容:
sudo tcpdump -i [网卡] -A -n 'dst [客户端IP] and port [游戏端口]'
截取到的 JSON 片段显示:
{"text":"I","color":"#F8F8F9"},
{"text":"N","color":"#F5F5F6"},
{"text":"F","color":"#F2F2F3"},
...
找到了。 MOTD 标题使用了 MiniMessage 的 gradient 标签:
'<gradient:#ffffff:#a1a1aa>▲ INFINITYCRAFT | NextGen 2.0</gradient>'
AdvancedServerList 在将 gradient 序列化为 JSON Text Component 时,每个字符单独生成一个带 hex 颜色的 JSON 对象。约 30 个字符的标题,每个对象约 35 字节,仅此一行就产生约 1000 字节。两行 MOTD 加上 player sample 条目与 favicon base64,响应体积膨胀至 3283 字节。
Gradient 渲染方式对比:
| 渲染方式 | 示例 | 单字符体积 |
|---|---|---|
| Legacy §色码 | §fI§7N | ~3 字节/字符 |
| 现代 hex component | {"text":"I","color":"#F8F8F9"} | ~35 字节/字符 |
相差约 10 倍。30 个字符的渐变从约 90 字节暴涨至约 1000 字节。
初步假设:响应包体积过大导致客户端解析失败或被中间设备截断。
排查假设 A:ViaBackwards 转换 Bug
假设:ViaBackwards 在处理含 hex 颜色的 Status 响应包时,转换逻辑产生了畸形 JSON,导致客户端 RST。
依据:
- ViaBackwards 某版本 changelog 中有一条:"Fixed reading of massive clientbound text components"
- 当前运行版本为较新发布版
验证:临时移除 ViaBackwards,重新测试。
结果:问题依旧。
结论:ViaBackwards 排除。
排查假设 B:hover 字段使用 hex 颜色
AdvancedServerList 官方文档明确指出:
Hexadecimal (RGB) colors are supported, however, only the motd option may display them and only for MC 1.16+ clients. Any other option will have the color down-sampled to the nearest supported color code.
配置文件的 playerCount.hover 字段中存在 hex 颜色,属于文档中明确不支持的用法。
验证:将 hover 中的 hex 全部替换为命名颜色,重新测试。
结果:问题依旧。
结论:hover hex 排除。
排查假设 C:Gradient 导致响应体积超限
将 gradient 替换为三段固定 hex 色块,减少 JSON 对象数量:
# 修改前
'<gradient:#ffffff:#a1a1aa>▲ INFINITYCRAFT | NextGen 2.0</gradient>'
# 修改后
'<color:#ffffff>▲ INFIN</color><color:#d0d0d4>ITYCRAFT | </color><color:#a1a1aa>NextGen 2.0</color>'
结果:问题依旧。
此时发现,MOTD 中所有 hex 颜色(包括固定颜色如 <color:#a1a1aa>、<color:#FF0000> 等)移除后,问题才消失。Gradient 膨胀并非唯一因素。
设计隔离测试——保留 gradient,仅将第二行改为命名色:
profiles:
- motd:
- '<center><bold><gradient:#ffffff:#a1a1aa>▲ INFINITYCRAFT | NextGen 2.0</gradient></bold>'
- '<center><bold><gray>Production Environment</gray> <red>•</red> <blue>Ready</blue>'
测试准备就绪,正要执行——
根因确认:机房的问题
联系云服务商客服后得到回复:
服务器近期遭受了 MOTD 扫描攻击(Server List Ping 大规模自动扫描)。
为保护平台安全,运营方临时启用了针对游戏端口 Status 响应包的流量限制。
该限制现已解除。
完整链路还原:
MOTD 响应包约 3283 字节
↓
云服务商临时安全策略:Status 响应包大小超过阈值
↓
响应包在传输层被截断
↓
客户端收到不完整数据,JSON 解析失败
↓
客户端发出 RST,连接中断
↓
30 秒超时后触发第二次回退请求(体积更小)
↓
第二次请求低于阈值,通过,MOTD 延迟显示
简单 MOTD 体积低于阈值,始终可以通过。复杂 MOTD(含 gradient 和 hex)体积超出阈值,被截断。一切现象完美自洽。
为什么排查过程仍然有价值
尽管根因与代码无关,本次排查过程建立了以下有效结论:
| 结论 | 价值 |
|---|---|
| Gradient 会导致 JSON 响应体积约膨胀 10 倍 | 可指导 MOTD 配置优化,避免不必要的大包 |
| AdvancedServerList hover 字段不支持 hex | 避免未来踩坑 |
| 客户端 RST 出现在收到完整数据之后 | 说明是解析失败或截断,而非网络丢包 |
| 本机测试正常 ≠ 云端正常 | 云端存在不透明的基础设施策略 |
| 简单 MOTD 可通过 ≠ 端口无限制 | 安全策略可能基于包体积,而非端口或协议 |
同类问题判断方法
若出现以下现象,应优先排查基础设施层面,而非应用层:
本地测试正常
其他服务器正常
自建服务器 MOTD 延迟出现或不出现
更换插件 / 客户端 / 版本均无效
响应包在网络层被截断或触发 RST
优先检查:
云服务商是否存在临时安全策略
服务商防火墙对特定流量的包大小限制
IDC / 运营商层面的 DDoS 防护规则
不应优先归因于:
MOTD 插件配置
Via 协议转换插件
客户端版本兼容性
服务端本地防火墙规则
DNS 解析
这些因素仍可能导致其他问题,但本案例中均非根因。
排查路径回顾
客户端 DNS / 代理 → 排除
Via 插件 → 排除
服务端连通性 → 排除(服务端有响应)
Gradient JSON 膨胀 → 发现响应过大现象,方向正确
ViaBackwards 转换 Bug → 排除
hover hex 字段不兼容 → 排除
MOTD fixed hex 问题 → 准备验证,被打断
云服务商临时 MOTD 扫描防护限制 → ✅ 根因
附:本次排查用到的命令
服务端抓包(确认响应是否到达客户端)
sudo tcpdump -i [网卡] -n port [游戏端口]
服务端响应内容查看
sudo tcpdump -i [网卡] -A -n 'dst [客户端IP] and port [游戏端口]'
RST 时序判断方法
RST 出现在服务端回包之前 → 客户端主动中断,服务端可能无响应
RST 出现在服务端回包之后 → 客户端收到数据但解析失败或被截断
最终结论
MOTD 刷不出来的根因是:
云服务商因 MOTD 扫描攻击临时启用了 Status 响应包大小限制,
导致含 gradient 的大体积 MOTD 响应包被截断,
客户端 JSON 解析失败并 RST。
但我个人认为本次故障排查相当具备参考价值,
其中的经验值得我去借鉴和记录,
当然...最后我还是想说一句。
AI真是太好用了
远离MC开服,这会让你变得不幸