女儿七岁,Minecraft 玩得相当溜——创造模式里能盖出像模像样的大房子,红石机关、复杂建筑,玩得津津有味,完全不需要人教。但切到生存模式就完全不一样了:天还没黑,她就缩回屋子里不敢出门了,窗户都不敢靠近,僵尸在外面敲门她能吓得直叫。

我就想,如果我能在游戏里陪着她呢?有爸爸在旁边帮她打僵尸,也许她就敢玩生存了?

问题是,我玩的是 Mac 上的 Java 版,她玩的是 Switch 上的 Bedrock 版,这两个版本自古以来就不互通。这个坑我知道,所以虽然她玩 Minecraft 已经好几年了,我一直没往联机这方面想过。

直到今年春节,我决定好好折腾一下。

第一条路:虚拟机

第一个想法很自然:在 Mac 上跑个虚拟机,启动 Windows 版的 Bedrock,不就能联机了?

调研了一圈,死胡同:

  • Crossover:我之前买过,试了,不支持 Minecraft
  • VirtualBox:网上没见有人跑通 Bedrock 版
  • Parallels:理论上可能行,但要额外收费,还要找 Windows 镜像,麻烦;而且用虚拟机打游戏总觉得体验会很差

这条路放弃了。

转机:Geyser

虚拟机走不通,换个思路——能不能让 Java 版服务器同时接受 Bedrock 客户端连接?

搜了一圈,发现还真有这个方向的方案:Geyser。它是一个协议转换层,可以让 Bedrock 客户端(包括 Switch)连入 Java 版服务器。反过来的方向(让 Java 客户端连 Bedrock 服务器)倒是没有,但这个方向刚好满足我的需求。

最终方案的架构非常简洁:一个 Docker 容器,跑 PaperMC(Java 版服务器),同时加载 Geyser 和 Floodgate 两个插件。Java 版走 25565 TCP 端口,Switch(Bedrock)走 19132 UDP 端口。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
services:
  mc:
    image: docker.io/itzg/minecraft-server:latest
    tty: true
    stdin_open: true
    container_name: mc
    environment:
      EULA: "TRUE"
      TYPE: "PAPER"
      MEMORY: "2G"
      ONLINE_MODE: "true"
      TZ: "Asia/Shanghai"
      PLUGINS: |
        https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot
        https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot
    ports:
      - "0.0.0.0:25565:25565/tcp"
      - "0.0.0.0:19132:19132/udp"
    volumes:
      - ./paper:/data
    restart: unless-stopped

听起来很简单。实际上踩了不少坑。

坑一:同一个账号,不能同时在线

服务器起来之后,我先用自己的 Java 版连上了。然后把 Switch 上配好,女儿也连进来了——但立刻被踢出去了。

原因:Switch 上的 Bedrock 版需要登录微软账号才能进服务器,而我当时随手用了自己的微软账号——这个账号同时也是我 Java 版的账号。同一个账号不能在同一服务器上存在两个玩家,当然会被踢出去。

另外这里还有个小坑:Switch 版 Minecraft 联机需要开通 Nintendo Switch Online 会员,没有会员是没法进服务器的。我给女儿的账号先开了个免费试用期。

解决办法:给女儿单独注册一个微软账号。但新账号没有购买 Java 版,所以不能通过 Java 正版验证,这时候就需要 Floodgate 插件了——它专门用来处理 Bedrock 玩家的认证,让没有 Java 账号的 Bedrock 玩家也能正常接入 Java 服务器。

在 Geyser 配置里把认证方式改成 floodgate

1
2
java:
  auth-type: floodgate

这个问题按照文档来就能解决,不算太难。

坑二:Switch 没有”添加服务器”的入口

Switch 上的 Minecraft Bedrock 版,有一个很反人类的设计:它的服务器列表里只有 Minecraft 官方预置的几个服务器,没有让你手动输入 IP 地址的入口

这个问题的解法是 BedrockConnect:通过 DNS 劫持,把 Switch 去访问那几个官方服务器的 DNS 请求拦截掉,替换成一个中间服务器的地址。连上这个中间服务器之后,会弹出一个界面让你输入真实想连的服务器地址,然后帮你转发连接。

BedrockConnect 有公共服务器可以直接用,在路由器上把 Switch 的 DNS 改掉指向 BedrockConnect 即可。这一步照着说明做,也不算复杂。

坑三:Podman 的文件权限

这个算是小坑。用 Podman(而非 Docker)跑的服务,在容器外面直接修改 Geyser 的配置文件之后,容器里的服务读不到——因为容器内 Minecraft 服务是以 minecraft 用户运行的,没有权限读取宿主机用户修改过的文件。

解决方法是修改完配置后,进容器用 root 把文件 chown 给容器内的 minecraft 用户:

1
podman exec -u root mc chown minecraft:minecraft /data/plugins/Geyser-Spigot/config.yml

坑四(终极Boss):Switch 连上了,但又连不上

这才是真正折磨人的地方。

Switch 能 ping 通服务器,Geyser 的 debug 日志里能看到 ping 请求,用 tcpdump 抓包也能抓到 Switch 发来的 UDP 包——但 ping 之后就没有下文了,尝试加入游戏一直超时。成功率大概是几十次里能连上一次,完全像玄学。

为了排查这个问题,我先后:

  • 从 Podman 换成 Docker,又试了直接裸跑 Java 服务器——没有区别
  • 开启 Geyser debug 日志,确认数据包到达了服务器
  • 用 tcpdump 抓包,确认 Switch 到 19132 端口的 UDP 包能到
  • 试了手机 App「MC Connector」,用附近好友的方式接入——也不行
  • 排查了 Mac 防火墙、NAT 设置、Podman 网络转发——逐一排除

最后怀疑到 UDP 握手阶段的数据包大小上:Switch 是比较老的硬件,2.4G 频段,Bedrock 协议的握手过程数据量不小,MTU 如果偏大就可能导致数据包在传输过程中被截断,握手就永远完不成。

在 Geyser 配置里调整 MTU:

1
2
3
advanced:
  bedrock:
    mtu: 1100  # 默认是 1400

从 1400 调到 1200,再调到 1100。同时把 Switch 搬到离路由器更近的地方。两个调整加在一起,连接稳定了很多。

深夜的小房子

折腾了整整一天,到了晚上女儿先睡觉了。我在凌晨把 MTU 从 1400 调到 1200,Switch 竟然连上了。

当时有点激动过了头,没急着叫醒任何人,而是自己先进了游戏,造了一间小房子,然后把两个账号都操作进去,确认父女俩站在同一个屋子里——才放心地退出去睡觉。那时候已经凌晨两点了。

第二天早上起来,第一件事就是告诉女儿。她瞬间清醒,立刻要联机。

然后就又连不上了。

又折腾了大半天,到下午才再次连上。两个人第一次真正在同一个世界里出生在同一个房间的时候,都激动坏了。

女儿偷偷哭了

一起玩了几个小时之后,女儿不小心在 Switch 上切出去看了一下电量,再切回来,又进不去游戏了。

她没说什么,悄悄躲起来哭了。妈妈发现了问她,她才说:联机好难,总也连不上。

这让我有点意外。她一开始对联机这件事其实没什么期待,我在折腾的时候她还几次劝我:”爸爸别弄了,连不上就算了。”但真的一起玩过之后,感受到了不一样的乐趣,现在反倒比我还在乎能不能连上了。

我当时心里暗暗下定决心:这个问题一定要彻底搞定。

于是继续调,MTU 降到 1100,Switch 物理上移到路由器旁边。之后连接稳定了很多,再次连上,这次没有断。

从躲僵尸到冲掠夺者

女儿第一次进生存模式其实是被我”骗”进去的——我说我不知道这个服务器怎么开创造模式(其实是没开)。她将信将疑地跟着进来,一开始还是很小心。

但有我在旁边,她慢慢放开了。天黑了我帮她守着,僵尸来了我帮她打,她负责跑去挖矿或者搭房子。几天游戏时间过去,房子搭起来了,装备也有了。

有一天天刚亮,她不等我,自己主动出门去打僵尸了。后来遇到小白和掠夺者,她竟然主动往前冲。

完全不像以前那个躲在屋里怕僵尸的小孩了。


给想复现的人:关键步骤速查

整体方案: - PaperMC Java 服务器 + Geyser + Floodgate 插件 - 用 itzg/minecraft-server 镜像一键启动 - Java 版连 <局域网IP>:25565,Switch 通过 BedrockConnect 连 <局域网IP>:19132

几个关键配置: 1. Geyser 配置 auth-type: floodgate,让没有 Java 账号的 Bedrock 玩家可以加入 2. Switch 联机走 BedrockConnect:在 Switch 网络设置里把主 DNS 改为 104.238.130.180,副 DNS 改为 8.8.8.8;然后进 Minecraft → 服务器列表,加入任意一个 redirect-compatible 的官方服务器(Mineville、Lifeboat、Enchanted、Galaxite、The Hive 均可),会弹出输入自定义服务器地址的界面 3. Geyser 的 MTU 调低(尝试从 1400 → 1200 → 1100),并把 Switch 靠近路由器

如果 Switch 连不上,优先排查 UDP MTU 问题。 这个坑最隐蔽,没有明确的报错,就是握手超时,很容易被误导去排查其他方向。


最后说一句:这次折腾前前后后用了好几个 AI 工具辅助——ChatGPT、Gemini、Claude,每个都派上了用场,帮我趟方案、辅助排查。AI 在这种”有明确目标但方案不确定”的折腾场景里,真的挺有用。

两天折腾的成就感,和之后每晚一起在游戏里打怪的快乐相比,微不足道。