Skip to content

常驻游戏局 ​

常驻游戏局允许作者把自己创建并由项目代码识别的服务端游戏局保留在运行状态,即使其中已经没有在线玩家。它适合承载跨游戏局协调、项目级权威云数据处理、异步任务汇总等不能依赖一般玩家对局生命周期的服务端逻辑。

常驻只改变当前游戏局的自然回收行为:它会跳过游戏局自然运行时限和全员离线回收。显式结束游戏、管理员终止、平台安全保护和服务器关闭仍然可以结束常驻局。因此,常驻游戏局不是永久进程或高可用服务,业务状态仍应保存到 CloudData 等持久化系统,并能够在新游戏局中重新初始化。

> 特别重要:调试环境不能使用常驻游戏局。 Game.SetSessionResident(...) 在 debug 调试环境中会记录警告、返回 SessionResidenceResult.InvalidSessionState,且不会向 Host 申请常驻。调试服务器本身不持久,并且缺少常驻能力所依赖的部分 Redis 与平台服务。项目必须发布到至少 test 内测环境,才能测试常驻申请、跨服消息、离线存活、主动关闭和故障恢复;不能把编辑器调试成功当作这项功能的验收结果。

> 特别重要:第一次启用常驻前,必须先实现服务器状态管理和主动关闭机制。 常驻局不会因为发布了新版代码而自动退出或升级。如果旧版本没有预埋受信任的状态查询、排空和关闭入口,它可能一直运行,导致新版常驻服务无法正常接管。最终仍可通过后台“全服公告”的立即关服功能强制关闭该游戏在对应环境中的所有游戏局,但这会同时影响普通玩家对局,副作用明显,只应作为失控后的紧急兜底,而不是常规更新流程。

基本用法 ​

API 仅在服务端可用。作者可以先创建一个用于长期服务的房间,再由服务端根据创建者 UserId、房间参数或其他项目条件判断当前局是否应申请常驻,并始终检查结果枚举。用于识别作者房间的条件应来自服务端可信数据,不应直接信任客户端自行声明的标记。

csharp
#if SERVER
using EngineInterface.Enum;
using GameCore.GameSystem;

var result = Game.SetSessionResident(true);
switch (result)
{
    case SessionResidenceResult.Success:
        Game.Logger.LogInformation("当前游戏局已进入常驻状态");
        break;

    case SessionResidenceResult.ProjectLimitReached:
        Game.Logger.LogWarning("当前项目的常驻游戏局数量已达到上限");
        break;

    case SessionResidenceResult.ServiceUnavailable:
    case SessionResidenceResult.InvalidSessionState:
        Game.Logger.LogWarning("游戏局无法进入常驻状态: {Result}", result);
        break;
}
#endif

以上代码必须在 test 或 formal 环境运行。debug 环境中的调用会被拒绝;调用方不应在调试环境循环重试。

申请成功后,Game.IsSessionResident 返回 true。不再需要常驻时调用:

csharp
#if SERVER
var result = Game.SetSessionResident(false);
if (result != SessionResidenceResult.Success)
{
    Game.Logger.LogWarning("取消常驻失败: {Result}", result);
}
#endif

数量限制 ​

同一个项目最多同时有三个常驻游戏局。超过上限的申请返回 SessionResidenceResult.ProjectLimitReached,调用方应保持普通游戏局行为,或按项目自己的策略稍后重试。

三个名额不代表其中任何一个游戏局会被自动选为唯一协调者。如果业务只能有一个权威协调者,项目仍需自行设计角色认领、重复执行保护和状态恢复逻辑。

结果枚举 ​

结果含义建议处理
Success请求的状态已经生效,或本来就是该状态继续启动或停止常驻业务
ProjectLimitReached项目已有三个常驻游戏局保持普通局行为,或按业务策略稍后重试
ServiceUnavailable当前无法处理申请记录结果并有限重试,不要高频循环调用
InvalidSessionState当前游戏局正在结束、当前运行环境不支持常驻,或其他状态不允许切换停止申请;若当前为 debug,发布到至少 test 环境后再验证

生命周期边界 ​

  • Game.SetSessionResident(true) 只作用于当前游戏局,不会创建新游戏局,也不会自动发现其他常驻局。
  • debug 调试环境不支持常驻;必须在至少 test 内测环境验证完整生命周期。
  • 常驻成功后,即使全部玩家退出,服务端逻辑仍可继续运行。
  • 常驻不会覆盖显式结束、管理员操作、资源保护、异常保护或服务器关闭。
  • Host 或服务器故障后,原游戏局不会自动恢复。需要长期保留的数据必须写入持久化存储。
  • 建议由作者自行创建用于长期服务的房间,再由服务端根据创建者 UserId、房间参数或其他项目条件识别该房间并申请常驻。一般玩家创建的对局不需要执行这段逻辑。

服务器状态管理与升级 ​

服务器状态管理不是常驻服务上线后的增强项,而是启用常驻前的前置能力。至少应包含:

  • 明确的服务状态,例如 Starting、Serving、Draining、Stopping,并能查询当前游戏局 ID、服务版本和职责。
  • 受信任的控制消息,例如状态查询、停止接收新任务、保存状态、取消常驻和主动结束游戏局。
  • 服务端鉴权和目标校验。即使管理入口只对特定用户显示,服务端仍必须验证操作者 UserId、权限、目标实例和消息版本,不能只依赖客户端隐藏界面。
  • 持久化与幂等处理。关闭命令可能重发,服务应能重复进入排空/关闭流程,并在退出前把必要状态写入 CloudData 等持久化存储。
  • 明确的升级流程。发布新版后,先通知旧常驻局排空并退出,再让新版本实例接管;不能假设平台会自动替换仍在运行的旧局。

推荐通过 ServerChannel 建立项目自己的管理协议。普通游戏局、运维入口或仅对特定授权用户显示的游戏内服务器管理界面可以发送控制消息,常驻局订阅后执行状态查询、排空和关闭。收到关闭命令时,常驻局通常应按顺序:

  1. 标记为 Draining,停止领取新任务并拒绝新的职责认领。
  2. 完成或安全取消正在处理的任务,把必要状态持久化。
  3. 回报最终状态并停止相关频道订阅。
  4. 调用 Game.RequestSessionEnd() 主动结束当前游戏局。

仅调用 Game.SetSessionResident(false) 只是取消自然回收保护,不等于立即关闭;需要确定退出时应使用 Game.RequestSessionEnd()。

如果旧常驻局已经上线且没有任何可用的主动关闭协议,后台“全服公告 → 立即关服”仍是最终可用的手段。它会关闭该游戏在所选环境中的所有游戏局,而不是只关闭目标常驻局,因此可能中断所有在线对局。标准操作仍然是预先完成上述服务器状态管理,再启用常驻。

跨游戏局协调建议 ​

常见结构是让普通游戏局通过 ServerChannel 向常驻协调局发送事件,由协调局执行项目级权威处理并写入 CloudData。仍需注意:

  • ServerChannel 是消息通道,不是持久化存储;重要状态应保存到 CloudData。
  • 消息处理应具备幂等性,避免重发或故障恢复造成重复结算。
  • 常驻局可能同时存在多个,不能仅凭“申请成功”推断自己是唯一实例。
  • 玩家云数据仍应使用 User.UserId,并遵守 CloudData ready 与错误处理规则。
  • 协调频道必须同时覆盖状态查询、排空和主动关闭;不能只设计业务消息而没有运维控制消息。

相关文档: