技术博客

大厅与匹配:Steam/Nakama 集成思路,Godot 只管传输不管社交

第 122 / 149 篇 · 2025-12-17 · godot · 网络同步大厅匹配后端集成

把一个能跑的 ENet 多人原型推到公网,你立刻会撞上一堵墙:玩家从哪拿到对端的 IP?公网服务器要不要自己架?好友想一起玩怎么进同一个房间?这些都是大厅(lobby)与匹配(matchmaking)问题,而 Godot 的态度很明确——不管。SceneMultiplayer 的边界到 create_server/create_client 为止,前面那一大段"玩家怎么找到彼此"被刻意留给了第三方。这篇讲这块缺口的常见补法。

引擎的边界画在哪

看 MultiplayerPeer 的接口就明白这个边界:create_server(port)、create_client(ip, port),完毕。它不回答"ip 和 port 从哪来",也没有房间、席位、准备状态这些概念。这不是偷懒,是分层:传输层(ENet/WebSocket,见七-4、七-7)和会话层(谁和谁玩、在哪个房间)是两种生命周期完全不同的问题——传输连接秒级生灭,房间数据要持久化、要跨对端可见、要做权限。硬塞进引擎反而绑死一种后端。

所以标准架构是三段式:大厅服务(房间目录 + 玩家资料)→ 连接信息交换(IP/relay token)→ 游戏内传输(ENet/WebRTC P2P)。Godot 只负责最后一段。

三段式架构:大厅服务负责找人,传输层只管连上 大厅服务(第三方) 房间目录 / 玩家资料 Steam Lobby 或 Nakama 会话交换 服务器 IP+端口 或 WebRTC offer/answer 游戏内传输(引擎) ENet 服务器/客户端 或 WebRTC P2P 数据通道 职责切分 大厅:房间 CRUD、席位、准备状态、跨对端可见 —— 数据要在玩家之间广播,不适合点对点直连 交换:房主把连接信息发进大厅元数据,加入者读出来调 create_client 传输:连上之后完全是七-1 的 SceneMultiplayer 世界,大厅服务可以全程旁观或退出

路线一:Steam 大厅

如果游戏上 Steam,这是阻力最小的路。Steamworks 的 Lobby 本质是一个挂在 Steam 后端上的房间目录:房主 CreateLobby,拿到一个 64 位 SteamID,把 ENet 监听端口写进大厅元数据(SetLobbyData),其他玩家通过好友列表或大厅浏览器拿到 SteamID、读元数据、调 create_client(ip, port)——IP 从 Steam 的 P2P 会话协商里来,房主甚至可以不开公网端口(走 Steam Datagram Relay 时配合 ENetConnection 的代理模式,4.3+)。

Godot 侧没有官方 Steam 模块,社区方案是 GodotSteam(GDExtension 封 Steamworks API)。它工作的前提是你的发布渠道就是 Steam:开发期可以直接跑,但导出的非 Steam 版本(itch、Demo 独立包)要内置一套 fallback,否则大厅功能直接消失。这是我看到的最多抱怨:大厅代码和平台 SDK 耦合太深,换平台等于重写匹配层。

路线二:Nakama(或自建房间服务)

想要跨平台(iOS/Android/Steam/itch 同一套账号体系),自己架一个后端服务,或者直接用 Nakama——开源的游戏服务器,带账户、房间(party)、匹配(matchmaker)全套。架构上它替代上图左段:玩家先走 REST/gRPC 登录拿 token,用 socket 连上 Nakama,进 party 后用 Nakama 的 relay 或者直接交换 IP 走 ENet。

Nakama 的好处是匹配逻辑可写服务端脚本(Lua/Go):按段位分池、按延迟选区、按队伍平衡,这些是 Steam Lobby 给不了的——Steam 的大厅搜索只有简单的字符串过滤,匹配等于没有。代价是运维:一台 Nakama + 一个数据库跑起来不难,难的是 SLA、扩容和作弊治理——房间目录一旦是你自己的,反作弊、防刷分也就都是你的了。

还有一条中间路线:WebRTC + 公共信令。房间本身极轻(只需要交换 SDP offer/answer),可以塞进任何现成的实时后端——Firebase Realtime、Supabase、甚至一个静态 JSON 托管加轮询。适合小到不需要真匹配的游戏(合作房间制),不适合需要分池的大 DAU 项目。

共同的工程问题

不管选哪条路,有三件事躲不掉:

房主迁移。房主(权威服务器)退了怎么办?Steam Lobby 有 owner migration,但 ENet 的 listen socket 可不会跟着迁移——实务上要么把房主限为专用服务器(无迁移问题),要么接受"房主退 = 房间散"。移动端项目尤其要注意后台杀进程。

大厅数据与游戏状态分离。大厅里的"准备状态、角色选择"是对端可见的房间元数据,不该走游戏内 RPC——还没连上 ENet 的玩家也要能看到。Godot 项目里常见错误是把选角界面做成连上之后再同步,结果断线重连的玩家拿到一片默认。

匹配时延预算。匹配算法再精巧,玩家从点击"开始匹配"到进入 ENet 会话有几百毫秒的 SDK 往返(登录、进房、读元数据、建连)。把这段做成显式的 loading 界面并埋点统计,比优化任何渲染都直接影响留存——玩家在等的时候最容易流失。

本文架构与 SDK 行为基于 Steamworks 1.5x、Nakama 3.x、Godot 4.3–4.4;GodotSteam 为社区 GDExtension,非官方模块,版本兼容性以仓库 README 为准。