主题
随进随出模式与开放局
概述
“随进随出”是创作者中心游戏大厅中的一种模式配置。玩家从大厅进入该模式时,平台可以先查找同一匹配池中仍有自动匹配名额的进行中游戏局;找到后加入旧局,找不到时再创建新局。
由这类模式创建、允许玩家中途加入的普通游戏局,在本文中称为开放局。开放局表示“该局具有中途加入能力”,不表示人数无限,也不表示任何玩家都能绕过项目自己的邀请和权限规则。
本系统包含两条不同的进入路径:
- 大厅自动匹配:按模式配置寻找一个未达到后台自动匹配容量的开放局。
- 定向加入开放局:项目把某个具体游戏局的
OpenGameRef交给目标玩家,目标玩家调用LobbyService.LeaveAndJoinOpenGame切换到该局。
这两条路径共用同一种开放局,但容量判断不同。理解这一点,是设计单人世界、组队副本、好友邀请和多个大厅模式的关键。
创作者中心配置
配置入口:
运营配置 → 游戏基础配置 → 游戏大厅配置
测试版本创作者中心:<https://developer-alpha.spark.xd.com/>
正式版本创作者中心:<https://developer.spark.xd.com/>
创建或编辑大厅模式时,重点关注以下字段:
| 配置 | 作用 | 建议 |
|---|---|---|
| 模式类型 | 选择“随进随出”后,该模式创建的普通游戏局会登记为可供后续玩家查找和中途加入的开放局。 | 需要加入进行中游戏时选择“随进随出”。 |
| 模式 key | 隔离大厅自动匹配池,也会透传给项目逻辑用于识别当前玩法模式。 | 每个规则不兼容的模式使用不同、稳定且非空的 key。 |
| 房间最大人数 | 限制从大厅自动匹配进入同一个开放局的人数。 | 按模式明确填写,不要把它当作地图的物理槽位数。 |
| 匹配开局人数 | 普通匹配模式用于决定等待多少人后开局;随进随出模式不会为了凑齐该人数而等待。 | 随进随出模式通常填写 1。 |
| 大厅进入时创建全新的游戏局 | 为“否”时先查找可加入旧局;为“是”时每次从大厅进入都直接创建新开放局。 | 需要“一人一世界”时建议设为“是”。 |
后台页面会读取地图配置中的真人玩家槽位数,并阻止把模式人数配置得高于地图槽位数。因此,地图槽位仍是所有模式共同遵守的基础上限,而房间最大人数可以在这个上限内按模式分别设置。
模式 key 的作用
服务端脚本可以通过 Game.LobbyModeKey 读取当前游戏局对应的创作者中心大厅模式 key,再按后台配置的入口区分玩法:
csharp
#if SERVER
if (Game.LobbyModeKey == "co-op")
{
// 使用大厅 co-op 入口对应的规则。
}
#endifGame.LobbyModeKey 表示平台大厅传入的后台模式 key,可能为空(例如编辑器调试或没有通过大厅入口创建的游戏局)。它与项目数编配置 Game.GameModeLink 是两个独立概念:前者识别创作者中心入口,后者选择项目内的 GameDataGameMode 数据。
| 属性 | 来源 | 用途 | 读取时机与范围 |
|---|---|---|---|
Game.LobbyModeKey | 创作者中心游戏大厅配置;平台在通过大厅入口创建游戏局时传入 | 区分后台配置的大厅入口、匹配池和入口规则 | 仅服务端;服务端游戏局启动后可读取。没有大厅入口时为空字符串 |
Game.GameModeLink | 项目数编中的 GameDataGameMode link | 选择项目内部的玩法数据、默认场景、规则和 UI 等配置 | 按数编初始化流程赋值;在 OnRegisterGameClass() 阶段不要读取 |
两者可以同时使用,但不能互相替代:大厅 key 是平台入口元数据,数编 link 是项目内部玩法配置。不要把大厅 key 当作 GameDataGameMode link 比较,也不要根据 GameModeLink 推断玩家从哪个后台大厅入口进入。需要同时约束入口和项目玩法时,分别读取两个属性并明确处理空的 LobbyModeKey。
大厅自动找房会按游戏、运行环境 tag 和模式 key 等信息分池。对同一游戏和同一运行环境而言:
- 两个大厅模式使用相同的模式 key,会进入同一个自动匹配池。
- 两个大厅模式使用不同的模式 key,不会自动匹配到彼此创建的开放局。
- 模式显示名不同,不等于匹配池一定不同;真正用于模式分池的是模式 key。
- 运行环境 tag 由平台运行和发布链路携带,不能替代同一游戏内部的模式 key。
例如,同一张地图同时提供“双人合作”和“三人合作”两个大厅入口时,应给它们配置不同的模式 key,并分别设置房间最大人数 2 和 3。如果两者共用一个 key,平台会把它们视为同一自动匹配池,容易让规则不同的玩家进入同一个游戏局。
模式 key 只参与大厅自动匹配。通过 OpenGameRef 定向加入某个具体开放局时,平台使用目标地图和 JoinKey 定位游戏局,不再按模式 key 重新选房。
发布版本与匹配池隔离
随进随出自动匹配不会替项目判断新客户端、新服务器逻辑与仍在运行的旧开放局之间是否兼容。不要假设客户端版本、AppBundle 版本或服务器版本会自动把开放局分开;模式 key 是项目方主动控制大厅自动匹配池边界的配置。
发布新版本时,应由项目方根据实际的协议、游戏逻辑、数据和资源兼容性做决定:
- 可以共存时:继续使用原模式 key,新用户可以继续进入同一自动匹配池中的旧开放局。
- 不能共存时:为新版本配置新的、稳定且非空的模式 key,使大厅自动匹配进入新的池;旧 key 下已经运行的开放局不会因为改 key 自动迁移或关闭,应让它们自然结束或由项目主动排空。
- 尚未确认时:先完成新客户端/服务器与旧开放局的兼容性验证,不要把“版本号不同”或“版本号相同”当作平台已经替你做出的判断。
更换模式 key 只隔离大厅自动匹配,不会改变已经发出的 OpenGameRef。OpenGameRef 定向加入会直接定位目标游戏局并绕过模式 key 分池;如果项目不允许跨版本定向加入,应在项目自己的邀请、队伍或引用分发逻辑中做版本判断,或不要继续分发旧局引用。
**最佳实践:**如果兼容性测试难以充分实施,应优先为新版本使用新的模式 key,例如在 key 中带上协议或内容版本标识。这样可能暂时放弃加入旧开放局,但通常比把不兼容的新代码匹配进旧局后再出现运行错误更安全。待新旧版本的兼容性得到确认后,再决定是否恢复共用原 key。
三层人数模型
不要把“地图玩家槽位”和“房间最大人数”理解成重复配置。它们控制不同层级:
| 层级 | 含义 | 是否可被定向加入突破 |
|---|---|---|
| 地图真人玩家槽位 | 游戏局实际能够分配的真人玩家位置,是运行时硬上限。 | 不可突破。没有空闲槽位时加入失败。 |
| 后台房间最大人数 | 大厅自动匹配何时停止继续把新玩家放进这个开放局。 | 可以。定向加入具体开放局不使用这个自动匹配上限。 |
| 当前开放局人数 | 当前已经占用或为连入流程保留的人数。 | 必须始终小于等于地图真人玩家槽位数。 |
后台配置了有效的正数房间最大人数时,平台按该值控制大厅自动匹配。只有没有得到有效正数配置时,Host 才会以地图真人玩家槽位数作为兼容兜底;正常项目应显式配置每个随进随出模式的人数,不要依赖兜底值。
大厅自动匹配流程
当“大厅进入时创建全新的游戏局”为“否”时,流程是:
- 玩家点击一个随进随出模式。
- 平台按当前游戏、环境 tag 和模式 key 查询开放局。
- 如果找到当前人数低于该模式房间最大人数的开放局,则加入该局。
- 如果没有可加入的开放局,则创建一个新的开放局并让当前玩家进入。
- 游戏局持续同步人数,使后续大厅查询能够判断是否仍可自动加入。
当“大厅进入时创建全新的游戏局”为“是”时,平台跳过第 2、3 步,每次都创建新开放局。
定向加入开放局流程
项目可以使用 开放局加入系统 实现好友邀请、队伍集结或跨局传送:
- 队长在当前开放局中调用
LobbyService.TryGetCurrentOpenGameRef获取OpenGameRef。 - 项目通过自己的好友、队伍、局内消息或 跨服务器频道系统 把引用交给目标玩家。
- 目标玩家调用
LobbyService.LeaveAndJoinOpenGame(openGame)。 - 平台退出目标玩家当前所在游戏局,并按
MapName + JoinKey定向加入队长的开放局。
定向加入不会重新执行大厅自动找房,也不会用后台房间最大人数筛掉目标局;最终能否进入取决于目标局是否仍存在、JoinKey 是否仍有效,以及地图是否还有真人玩家槽位。
LeaveAndJoinOpenGame 返回 true 只表示宿主接受并排队了切局请求,不表示目标玩家已经完成加入。项目仍应处理加载失败、目标局结束、槽位耗尽等结果,并向玩家提供明确反馈。
槽位清理与离开注意事项
开放局的当前人数同时反映两类资源:大厅自动匹配所看到的开放局占位,以及地图中实际占用的真人 Player 槽位。玩家进入流程时,框架可能会先为他保留名额;项目不需要、也不应该自行修改房间人数或手工重置 Player 槽位。应使用正式的退出、踢出或切局 API,让框架在对应的离开流程中完成清理。
| 场景 | 槽位清理时机 | 现行语义 |
|---|---|---|
客户端调用 LobbyService.QuitCurrentGame() | 主动退出流程完成并断开当前连接后 | 明确离开当前局,不保留重连入口;开放局占位和地图真人槽位都会释放。该 API 是 fire-and-forget,返回 true 只表示退出请求已排队。 |
服务端调用 Player.Disconnect() | 主动断开流程完成后 | 适用于正常离开;不保留重连入口,框架负责释放开放局占位和地图真人槽位。 |
服务端调用 Player.Kick(...) | 踢出流程完成后 | 强制移出且不保留重连入口。在线玩家会先收到踢出通知,再断开并释放;已经离线的玩家会直接释放。 |
| 普通网络断线、客户端崩溃或进程被杀 | 重连窗口结束且玩家没有重连时 | 断线不等于离开。开放局会暂时保留该玩家的重连名额,当前实现通常保留约 30 秒;窗口内大厅不会把该名额当作空位。 |
| 玩家放弃重连 | 放弃重连流程完成后 | 明确放弃旧局,释放开放局占位和地图真人槽位;已经没有连接的玩家会直接释放。 |
调用 LeaveAndJoinOpenGame 切换到另一局 | 源局完成切换交接时 | 框架释放源局的开放局占位和真人槽位;目标局只有在实际加入成功后才占用目标槽位。项目不要根据 API 返回值自行增减人数。 |
| 已登记但没有完成连接的进入流程 | 预约过期时 | 框架会自动清理未完成连接的预约,当前实现的保护时长通常约为 5 分钟。 |
因此,客户端已经回到大厅、连接暂时断开或 LeaveAndJoinOpenGame 返回 true,都不能单独作为“目标槽位已经释放/占用”的判断依据。项目自己的玩法资源应监听相应的玩家事件做清理,但不要在事件中重复维护开放局人数。
大厅房间信息通过异步方式传播,离开后的查询结果可能有很短的同步延迟。排查“人数没有下降”时,应先确认离开类型:普通断线可能仍处于重连窗口,在线踢人可能还在发送踢出通知,未完成连接可能仍在预约保护期内;只有超过对应窗口后仍不下降,才需要进一步检查服务端日志和房间同步链路。
示例:平时单人,组队时进入同一个副本局
假设一张地图有 100 个真人玩家槽位,但希望玩家从大厅进入时默认各自在自己的世界中游戏,只有组队下副本时才进入队长所在的服务器:
- 地图保留
100个真人玩家槽位,作为开放局的实际硬上限。 - 后台随进随出模式的房间最大人数设为
1。 - 匹配开局人数设为
1。 - “大厅进入时创建全新的游戏局”建议设为“是”,明确表达“一人一世界”。
- 玩家组队时,由队长分享当前局的
OpenGameRef。 - 队员调用
LeaveAndJoinOpenGame,定向进入队长局。
这样,从大厅进入的玩家不会被自动塞进别人的世界;但项目主动邀请时,队长局的人数可以超过后台自动匹配上限 1,直至地图的 100 个真人玩家槽位耗尽。
如果“大厅进入时创建全新的游戏局”设为“否”,房间最大人数 1 也会让已有单人局立即达到自动匹配上限,因此下一位大厅玩家通常仍会创建新局。不过,对于明确的一人一世界玩法,设为“是”更直观,也不会依赖自动匹配查询结果来表达产品意图。
与其他系统的关系
| 系统 | 负责什么 | 不负责什么 |
|---|---|---|
| 随进随出大厅配置 | 创建开放局、按模式 key 自动分池、按房间最大人数自动找房。 | 不负责好友、队伍或邀请协议。 |
OpenGameRef / 开放局加入 API | 引用并定向加入某个具体开放局。 | 不负责把引用发送给其他玩家,也不保证最终加入成功。 |
ServerChannel | 在不同游戏局的服务端之间传递邀请或组队消息。 | 不是客户端通信,也不自动实现好友关系或权限校验。 |
UserSessionHandoff | 同一用户切到同游戏其他局前,让旧局保存云数据并停止继续写该用户状态。 | 不负责选房、排队、邀请或进房裁决。 |
项目如果在切局时维护 CloudData、用户级锁或周期保存任务,应同时阅读 同用户游戏局切换清理。
配置与排查清单
- [ ] 模式类型为“随进随出”。
- [ ] 每个规则不兼容的大厅模式使用不同的稳定模式 key。
- [ ] 发布新版本前已判断它与仍在运行的旧开放局是否兼容:兼容时保留原 key,不兼容时改用新 key 隔离大厅自动匹配池。
- [ ] 房间最大人数符合该模式的自动匹配需求,并且不高于地图真人玩家槽位数。
- [ ] 随进随出模式的匹配开局人数通常设为
1。 - [ ] 已明确选择大厅进入时“先找旧局”还是“总是新建局”。
- [ ] 项目没有把后台房间最大人数误当作开放局的实际硬上限。
- [ ] 定向加入前确认
OpenGameRef是短期有效引用,并为最终加入失败提供 UI 反馈。 - [ ] 客户端正常退出使用
LobbyService.QuitCurrentGame();服务端正常移出使用Player.Disconnect(),强制移出使用Player.Kick()。 - [ ] 没有把普通网络断线立即当作空槽位,也没有在项目侧手工增减开放局人数。
- [ ] 使用
LeaveAndJoinOpenGame时按最终加入结果处理,不把返回true当作切局完成。 - [ ] 如果项目不允许跨版本定向加入,已在分发
OpenGameRef前自行检查版本兼容性;不要把更换模式 key 当作定向加入的版本校验。 - [ ] 跨局切换涉及云数据时,旧局通过
UserSessionHandoff停止继续写该用户状态。