多人会话与模式切换
返回总览。多人架构必须同时理解会话控制、玩法脚本实例、实体同步和在线服务。本文聚焦前两层的建立与退出;数据复制详见多人同步与实体所有权。
1. 架构判断与证据边界#
本地代码具有会话 owner、peer、主机迁移、脚本参与者握手、实体 clone 和各对象 owner 等机制。因此,实时玩法表现为客户端之间协作、分布持有实体并由脚本主机协调的架构,不能当成所有玩法都在一台权威专用服务器执行。[S4][S5]
另一方面,账户资格、匹配/邀请、云内容、角色统计、商品目录和交易有服务接口。这些服务不是脚本主机的别名。客户端中的 serverBD 或 PROCESS_SERVER 一般表示当前网络脚本的主机逻辑;NET_GAMESERVER_* 交易接口则属于在线服务链路。仓库不能据此证明包含完整后端实现。
2. 会话和脚本实例不是一回事#
| 层次 | 所需身份/状态 | 可发生的变化 |
|---|---|---|
| 平台/在线资格 | 登录、订阅/权限、服务可用性 | 下线、权限变化、服务失败 |
| 游戏会话 | session、会话 owner、活动玩家 | 加入、离开、会话迁移 |
| 网络脚本 | script ID、instance ID、participants、script host | 加入某活动、活动主机迁移、脚本终止 |
| 网络实体 | network ID、owner、clone | 复制、控制权转移、移除 |
一个玩家已进入在线会话,不代表已经成为某任务脚本的 participant。CNetworkSession 分别绑定 Physical 和 Transition session 的加入及迁移回调,说明会话层内部也有多种用途。[S4,L924]
注意“migration”在代码中还可能指旧平台存档/角色迁移。例如 Transition 控制头中的 save transfer 与会话 host migration 不是同一机制,阅读时必须看对象和调用链。
3. Transition 是跨系统状态机#
MainTransition.sc、Transition_Controller.sch、Transition_Joining.sch 把目标模式、HUD 状态和切换状态分别维护。切换中涉及天空镜头、界面、角色选择、在线资格、会话确认、脚本加载、玩家创建和落地恢复。[S1][S2][S3]
这是主要路径的概括,不是完整枚举。源码有重试、邀请、角色创建、不同模式和平台分支,不能把切换视为一次阻塞的 Join()。
Transition_Joining 中可沿 PREJOINING_FM_SESSION_CHECKS → LOOK_FOR_FRESH_JOIN_FM → 等待/确认加入 → PRE_FM_LAUNCH_SCRIPT → START_FM_LAUNCH_SCRIPT 阅读。这能区分卡在资格、网络、资源还是玩家初始化阶段。
4. 错误处理必须与正常路径同等完整#
MainTransition 的 IS_PROCESSING_A_BAIL_EVENT 处理网络连接、在线登录、权限、服务拒绝等条件;DO_BAIL_CLEANUP 会关闭/复位相关前端状态,通知 creator 清理,执行玩家离开清理,并在特定视频切换路径恢复 streaming 和音频场景。[S1]
这表明用户看到的“连接失败返回单机”实际是一段补偿流程:屏幕、输入、玩家可见性、资源加载暂停、音频、活动脚本都可能已被部分改变。单纯退出网络连接不会自动恢复所有这些状态。
失败处理也不能无限重复触发。源码使用当前 bailing 状态和目标切换状态避免同时运行互相冲突的退出流程。设计新切换入口时,应复用现有状态管理,而不是另建只覆盖成功分支的快捷路径。
5. 网络脚本的启动屏障#
脚本登记 NETWORK_SET_THIS_SCRIPT_IS_NETWORK_SCRIPT 后,需要等待网络运行状态。HANDLE_NET_SCRIPT_INITIALISATION 持续查询 NETWORK_GET_SCRIPT_STATUS,直到 NETSCRIPT_PLAYING;终止、会话满、禁止中途加入、队伍满等状态走失败路径。[S6]
一个容易误读的细节是:该函数中 50000 毫秒相关超时逻辑主要用于调试/最终日志提示,旧的“超过时间主动终止”代码已被注释,当前注释要求等待原生层给出结论。因此不能把这里写成固定 50 秒后必定退出。
原生 scriptHandlerNetComponent 维护 candidates、participants、slot、ACK、host token 和内部加入状态。没有主机时会在满足条件的候选中决定谁主持;加入过程中原主机离开还可能使本地退回候选重新加入。[S5] 玩法初始化必须等这一身份稳定,不能仅看 script 已加载。
6. 迟加入和迁移对内容的要求#
迟加入玩家需要当前任务阶段、选定变体、目标实体和参与规则,不能依赖“开始时给所有人发过一次事件”。玩法退出时还要处理玩家离开脚本与离开会话两种不同通知。
迁移期间原生组件允许暂时没有有效脚本主机,并维护 pending host 与迁移间隔。内容层应按当前主机身份执行全局推进,依赖同步状态恢复,而不是假定最初启动任务的玩家永久主持。
本文没有运行匹配、邀请或断网测试,也未验证各平台后端可用性。会话机制由本地代码支持,真实网络拓扑中的中继、平台服务和部署细节不在本次证据范围内。
源码证据#
| 编号 | 位置 |
|---|---|
| [S1] | MainTransition.sc:DO_BAIL_CLEANUP、IS_PROCESSING_A_BAIL_EVENT |
| [S2] | Transition_Controller.sch L855/860 HUD 状态,以及 Transition/Gamemode 状态接口 |
| [S3] | Transition_Joining.sch L2975、L3233、L3706、L3964、L4079、L4293 |
| [S4] | NetworkSession.cpp L924–934,Physical/Transition 会话回调 |
| [S5] | scripthandlernetcomponent.cpp L194、L222、L256 握手/参与者;L612 迁移 |
| [S6] | net_include.sch L7514,初始化等待与失败处理 |
[S1]: <../../../GTAV Source/script/dev_ng/multiplayer/scripts/Transition/MainTransition.sc> [S2]: <../../../GTAV Source/script/dev_ng/multiplayer/scripts/Transition/Transition_Controller.sch> [S3]: <../../../GTAV Source/script/dev_ng/multiplayer/scripts/Transition/Transition_Joining.sch> [S4]: <../../../GTAV Source/src/dev_ng/game/network/Sessions/NetworkSession.cpp> [S5]: <../../../GTAV Source/src/dev_ng/rage/framework/src/fwscript/scripthandlernetcomponent.cpp> [S6]: <../../../GTAV Source/script/dev_ng/multiplayer/include/public/net_include.sch>