CD Code Digest Hub /GTA V · v2

会话状态机:CGameSessionStateMachine

53 分钟阅读 13 个章节 1 张图

CGameSessionStateMachine 管理“游戏会话”之间的切换:首次启动时的平台检查(PC 的 Social Club/Steam/权益、主机的登录检查)、档案加载、语言选择、伽马校准、邀请检查、开局自动读档、落地页,以及运行期的新游戏、手动读档、换关、单人/多人切换。它决定何时调用 CGame::InitLevel/InitSession/ShutdownSession/ShutdownLevel,因此是 game-skeleton-and-main-loop.md 中 LEVEL/SESSION 两类 skeleton 模式的唯一驱动者。

一个重要特征:这个状态机的大多数状态是同步阻塞的——在一次 Update() 调用中自行循环(sysIpcSleep(33) + 手动“泵”少量系统),直到对话框、存档枚举、权益检查或整个关卡加载完成为止;只有淡出等待类状态跨帧推进。

背景与相关页面#

  • CApp 在首帧 RunGame_OnEnter 中第一次驱动本状态机:startup-and-app-fsm.md
  • 每个状态最终调用的 skeleton 模式与系统清单:game-skeleton-and-main-loop.md
  • 存档读写、自动读档的数据侧:../19-persistence-replay/save-load.md
  • 落地页、加载画面、引导流程 UI:../18-frontend-ui/frontend-flows.md
  • PC 权益、Steam/Epic、Social Club:../22-input-platform/platform-services-and-settings.md、../16-network-online/rline-online-services.md
  • 网络会话与过渡(多人模式的会话层,与本页的“游戏会话”不同):../16-network-online/network-sessions-and-transitions.md

相关源码文件#

文件作用
src/dev_ng/game/Core/gamesessionstatemachine.h状态枚举、公有 API、私有 Process* 函数与静态成员
src/dev_ng/game/Core/gamesessionstatemachine.cpp全部状态处理、Update 循环、SetState 守卫、泵函数
src/dev_ng/game/Core/game.h/.cppCGame 对状态机的静态包装(StartNewGame/LoadSaveGame/ChangeLevel/...)与每帧 Update 调用
src/dev_ng/game/frontend/landing_page/LandingPage.cpp、LandingPageArbiter.cpp、LandingPageConfig.h落地页进入/退出请求与开关
src/dev_ng/game/network/Live/livemanager.cpp登录状态变化 → CGame::HandleSignInStateChange

1. 角色与驱动方式#

  • 类的所有成员都是 static;CGame 持有一个实例并以静态函数转发 src/dev_ng/game/Core/gamesessionstatemachine.h:267-276、src/dev_ng/game/Core/game.h:57-74。
  • CGame::Init 调用 Init() 设定首个状态 src/dev_ng/game/Core/game.cpp:1110;CApp::RunGame_OnEnter 调用第一次 Update()(整个引导流程在这里同步完成)src/dev_ng/game/Core/app.cpp:1207;此后 CGame::Update 每帧调用一次 src/dev_ng/game/Core/game.cpp:3377。
  • Update() 是一个 do { switch(m_State) ... } while (m_State != oldState) 循环:一次调用内会连续处理状态,直到某个状态没有改变 m_State 为止;PC 上每次迭代先调 CApp::CheckExit();断言构建检测超过 MAX_STATE_CHANGES 次切换的疑似死循环 src/dev_ng/game/Core/gamesessionstatemachine.cpp:561-676。
  • IsIdle() 即 m_State == IDLE;CGame::IsChangingSessionState() 为其取反,并使 ShouldDoFullUpdateRender() 返回 false(切换期间只跑 SIMPLE_MAIN_UPDATE)src/dev_ng/game/Core/gamesessionstatemachine.h:81、src/dev_ng/game/Core/game.h:63、src/dev_ng/game/Core/game.cpp:3194-3197。
  • DEFAULT_START_LEVEL = 1,m_Level/m_ReInitLevel 初值为它 src/dev_ng/game/Core/gamesessionstatemachine.h:58-59、src/dev_ng/game/Core/gamesessionstatemachine.cpp:90-94。

2. 状态清单#

状态编译条件处理函数阻塞?证据
IDLE–ProcessIdleState:若刚完成开局自动读档,复位自动读档状态否src/dev_ng/game/Core/gamesessionstatemachine.cpp:678-686
INIT–ProcessInitState → CHECK_SIGN_IN_STATE否src/dev_ng/game/Core/gamesessionstatemachine.cpp:688-691
CHECK_FOR_RGSC__WIN32PCSocial Club 是否初始化否src/dev_ng/game/Core/gamesessionstatemachine.cpp:242-270
HANDLE_RGSC_FAILED__WIN32PC错误对话框循环是(无限循环直到选择)src/dev_ng/game/Core/gamesessionstatemachine.cpp:272-384
REQUIRE_PC_ENTITLEMENT__WIN32PC权益检查循环是src/dev_ng/game/Core/gamesessionstatemachine.cpp:447-481
REINIT__WIN32PC登录变化后重建会话 + 权益检查是src/dev_ng/game/Core/gamesessionstatemachine.cpp:167-240
TWO_FACTOR_AUTH_CHECK__WIN32PC(逻辑仅 RAGE_SEC_ENABLE_YUBIKEY)YubiKey 双因素循环是src/dev_ng/game/Core/gamesessionstatemachine.cpp:1062-1119
STEAM_VERIFICATION__WIN32PC && __STEAM_BUILDSteam 所有权验证循环是src/dev_ng/game/Core/gamesessionstatemachine.cpp:386-444
START_AUTOLOAD–决定是否尝试自动读档否src/dev_ng/game/Core/gamesessionstatemachine.cpp:693-720
CHECK_SIGN_IN_STATE–主机登录检查是src/dev_ng/game/Core/gamesessionstatemachine.cpp:722-784
LOAD_PROFILE–刷新档案设置否src/dev_ng/game/Core/gamesessionstatemachine.cpp:786-796
CHOOSE_LANGUAGE非 PS3语言选择/文本重载可能重建加载画面src/dev_ng/game/Core/gamesessionstatemachine.cpp:798-846
GAMMA_CALIBRATION–首次启动伽马校准是src/dev_ng/game/Core/gamesessionstatemachine.cpp:848-869
CHECK_FOR_INVITE–是否通过邀请启动是src/dev_ng/game/Core/gamesessionstatemachine.cpp:871-912
ENUMERATE_SAVED_GAMES–枚举存档并决定读档还是新游戏是src/dev_ng/game/Core/gamesessionstatemachine.cpp:914-959
AUTOLOAD_SAVE_GAME–读取最新存档并加载关卡/会话是src/dev_ng/game/Core/gamesessionstatemachine.cpp:961-1060
START_NEW_GAME–淡出 → 重建会话(必要时重建关卡)淡出跨帧,加载阻塞src/dev_ng/game/Core/gamesessionstatemachine.cpp:1162-1220
START_LOADING_SAVE_GAME–→ WAIT_FOR_SAVE_GAME(旧逻辑已移入存档队列)否src/dev_ng/game/Core/gamesessionstatemachine.cpp:1222-1227
WAIT_FOR_SAVE_GAME–空等,存档队列完成后调用 HandleLoadedSaveGame()跨帧src/dev_ng/game/Core/gamesessionstatemachine.cpp:1229-1234
HANDLE_LOADED_SAVE_GAME–重建会话并反序列化存档是src/dev_ng/game/Core/gamesessionstatemachine.cpp:1236-1341
LOAD_LEVEL–InitLevel(必要时)+ InitSession是src/dev_ng/game/Core/gamesessionstatemachine.cpp:1474-1538
CHANGE_LEVEL–淡出 → 关会话+关卡 → 开关卡+会话淡出跨帧,加载阻塞src/dev_ng/game/Core/gamesessionstatemachine.cpp:1343-1361
SHUTDOWN_FOR_LANDING_PAGEUI_LANDING_PAGE_ENABLED淡出并返回落地页否src/dev_ng/game/Core/gamesessionstatemachine.cpp:1365-1376
ENTER_LANDING_PAGEUI_LANDING_PAGE_ENABLEDGen9 启动落地页 / PC 旧版落地页循环PC 版阻塞src/dev_ng/game/Core/gamesessionstatemachine.cpp:1378-1407
DISMISS_LANDING_PAGEUI_LANDING_PAGE_ENABLED关闭落地页,必要时开始加载否src/dev_ng/game/Core/gamesessionstatemachine.cpp:1425-1470

枚举定义:src/dev_ng/game/Core/gamesessionstatemachine.h:21-56;UI_LANDING_PAGE_ENABLED 为 RSG_PC || IS_GEN9_PLATFORM || GEN9_UI_SIMULATION_ENABLED src/dev_ng/game/frontend/landing_page/LandingPageConfig.h:15。上表中 cpp: 均指 src/dev_ng/game/Core/gamesessionstatemachine.cpp。

3. 状态图#

图中证据(cpp = gamesessionstatemachine.cpp):

  • 初始状态:src/dev_ng/game/Core/gamesessionstatemachine.cpp:117-133;2FA 之后:src/dev_ng/game/Core/gamesessionstatemachine.cpp:1114-1118;INIT:src/dev_ng/game/Core/gamesessionstatemachine.cpp:688-691
  • CHECK_FOR_RGSC 三个出口:src/dev_ng/game/Core/gamesessionstatemachine.cpp:245-269;Steam 出口:src/dev_ng/game/Core/gamesessionstatemachine.cpp:439-443;RGSC 失败的取消分支:src/dev_ng/game/Core/gamesessionstatemachine.cpp:366-379;权益失败/结束:src/dev_ng/game/Core/gamesessionstatemachine.cpp:470-480
  • 登录检查出口:src/dev_ng/game/Core/gamesessionstatemachine.cpp:724-783;档案→语言/伽马:src/dev_ng/game/Core/gamesessionstatemachine.cpp:791-795;语言→伽马:src/dev_ng/game/Core/gamesessionstatemachine.cpp:845;伽马→邀请:src/dev_ng/game/Core/gamesessionstatemachine.cpp:868
  • 邀请三出口:src/dev_ng/game/Core/gamesessionstatemachine.cpp:884-911;自动读档决策:src/dev_ng/game/Core/gamesessionstatemachine.cpp:695-719、src/dev_ng/game/Core/gamesessionstatemachine.cpp:947-958;AUTOLOAD_SAVE_GAME 结束:src/dev_ng/game/Core/gamesessionstatemachine.cpp:1059
  • LOAD_LEVEL:src/dev_ng/game/Core/gamesessionstatemachine.cpp:1480-1537;落地页三状态:src/dev_ng/game/Core/gamesessionstatemachine.cpp:1365-1470、src/dev_ng/game/Core/gamesessionstatemachine.cpp:486-496;请求方:src/dev_ng/game/frontend/landing_page/LandingPage.cpp:56-65
  • 运行期 API:src/dev_ng/game/Core/gamesessionstatemachine.cpp:510-559;REINIT:src/dev_ng/game/Core/gamesessionstatemachine.cpp:135-164、src/dev_ng/game/Core/gamesessionstatemachine.cpp:239
  • 新游戏/读档/换关完成:src/dev_ng/game/Core/gamesessionstatemachine.cpp:1217、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1226、src/dev_ng/game/Core/gamesessionstatemachine.cpp:533-534、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1339、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1359

图中省略了次要边:PC 上 START_AUTOLOAD 在 RGSC 初始化失败过的情况下转入 HANDLE_RGSC_FAILED src/dev_ng/game/Core/gamesessionstatemachine.cpp:709-714;CHECK_SIGN_IN_STATE 在已登录时也可能转入 STEAM_VERIFICATION 或 REQUIRE_PC_ENTITLEMENT(编译条件下)src/dev_ng/game/Core/gamesessionstatemachine.cpp:759-772;Gen9 下 START_NEW_GAME、HANDLE_LOADED_SAVE_GAME、反序列化失败都可能转入 ENTER_LANDING_PAGE。

4. 首次启动的引导流程#

按平台,RunGame_OnEnter 中那一次 Update() 依次经过的状态(无邀请、正常登录、已安装):

平台状态序列
PC(无 Steam)CHECK_FOR_RGSC → REQUIRE_PC_ENTITLEMENT → LOAD_PROFILE → CHOOSE_LANGUAGE → GAMMA_CALIBRATION → CHECK_FOR_INVITE → (ENTER_LANDING_PAGE →) START_AUTOLOAD → ENUMERATE_SAVED_GAMES → AUTOLOAD_SAVE_GAME 或 LOAD_LEVEL → IDLE
PS4 / Xbox OneINIT → CHECK_SIGN_IN_STATE → LOAD_PROFILE → CHOOSE_LANGUAGE → GAMMA_CALIBRATION → CHECK_FOR_INVITE → START_AUTOLOAD → ENUMERATE_SAVED_GAMES → AUTOLOAD_SAVE_GAME 或 LOAD_LEVEL → IDLE

两点由代码直接推出:

  1. PC 不经过 CHECK_SIGN_IN_STATE:只有 INIT 会进入它,而 PC 的 Init() 直接进入 CHECK_FOR_RGSC,后者的正常出口是权益检查而非 INIT src/dev_ng/game/Core/gamesessionstatemachine.cpp:126-131、src/dev_ng/game/Core/gamesessionstatemachine.cpp:254-269。
  2. 主机没有权益/Steam 状态:RSG_ENTITLEMENT_ENABLED 在非 PC 上恒为 0,在 PC 上为 !PRELOADED_SOCIAL_CLUB(32 位构建为 1,即关闭)src/dev_ng/rage/base/src/rline/entitlement/rlrosentitlement.h:16-34;__STEAM_BUILD 默认关闭 src/dev_ng/rage/base/src/file/file_config.h:22-25;YubiKey 宏 RAGE_SEC_ENABLE_YUBIKEY 定义为 0 && RSG_PC src/dev_ng/rage/base/src/security/ragesecengine.h:47。

开发参数可短路引导:-noSocialClub(PC 非 final 或 MASTER_NO_SCUI)直接 LOAD_LEVEL;-noprofilesignin、-unattended 让登录检查直接 LOAD_LEVEL;-noautoload 禁止自动读档(除非命令行/落地页要求“直接进多人”);-skipLandingPage 跳过落地页;-stopLoadingScreensEarly 让代码而非脚本关闭加载画面 src/dev_ng/game/Core/gamesessionstatemachine.cpp:58-79、src/dev_ng/game/Core/gamesessionstatemachine.cpp:245-252、src/dev_ng/game/Core/gamesessionstatemachine.cpp:724-738、src/dev_ng/game/Core/gamesessionstatemachine.cpp:695-698。

5. PC 专属状态#

5.1 Social Club 失败处理#

HANDLE_RGSC_FAILED 是一个 while(true) 循环:每 33 ms 调 CApp::CheckExit(),根据 g_rlPc.GetInitErrorCode() 选择文本标签(无 DLL、版本过旧、未经启动器启动、通用错误;非 final 下还附带错误码枚举名),显示 OK/Cancel 警告屏 src/dev_ng/game/Core/gamesessionstatemachine.cpp:272-353。

  • OK:用 ShellExecuteA 打开 Rockstar 支持页面并请求退出到 Windows(仅一次)。
  • Cancel:final 构建请求退出;非 final 构建允许开发者继续,清除失败标志、关闭对话屏并 SetState(LOAD_PROFILE, true) src/dev_ng/game/Core/gamesessionstatemachine.cpp:355-379。
  • 每次循环调用 UpdateInitialDialogueScreensOnly() 与 gRenderThreadInterface.FlushAndDeviceCheck() src/dev_ng/game/Core/gamesessionstatemachine.cpp:381-382。

5.2 权益检查与离开守卫#

REQUIRE_PC_ENTITLEMENT 在 RSG_ENTITLEMENT_ENABLED 时 CEntitlementManager::StartCheck(),然后在 IsPending() 期间循环:CheckExit → CEntitlementManager::Update()(注释要求它在泵函数之前,否则会清掉 CheckExit 设置的警告屏标志)→ 泵函数 → D3D11 刷新;若 SCUI 处于失败态(网站加载失败),设置错误码并转 HANDLE_RGSC_FAILED src/dev_ng/game/Core/gamesessionstatemachine.cpp:447-481。IsCheckingEntitlement() 在 REINIT/REQUIRE_PC_ENTITLEMENT/HANDLE_RGSC_FAILED/STEAM_VERIFICATION 时为真,IsEntitlementUpdating() 反映是否处于自旋检查中 src/dev_ng/game/Core/gamesessionstatemachine.h:70-73。

离开守卫:PC 上若当前状态是 REQUIRE_PC_ENTITLEMENT 或 REINIT,SetState(newState) 只有在第二个参数 bCanLeaveEntitlementState == true 时才生效,否则记录日志并忽略 src/dev_ng/game/Core/gamesessionstatemachine.cpp:1683-1736。因此外部代码(如落地页、脚本触发的 StartNewGame)无法在权益检查中途把状态机“拉走”;只有这两个状态自身的出口传入 true。

5.3 ReInit / REINIT:登录身份变化#

CLiveManager::OnActionProfileChange() 调 CGame::HandleSignInStateChange() src/dev_ng/game/network/Live/livemanager.cpp:1634-1641,后者按平台分派:Gen9 → ReturnToLandingPage(Reason_SignInStateChange)(非 final 的 -netSignOutStartNewGame 改为新游戏);PC → ReInitStateMachine(当前关卡);其他 → StartNewGame(当前关卡) src/dev_ng/game/Core/game.cpp:1638-1660。

ReInit(level)(RSG_ENTITLEMENT_ENABLED):复位权益管理器;若当前在落地页或伽马校准 → REQUIRE_PC_ENTITLEMENT;若不在任一权益状态 → REINIT 并记录关卡;若已在权益状态 → 重新开始检查。若基准测试脚本是从落地页启动的,则请求重启游戏(bugstar 2307904 的修复)。未启用权益系统时退化为 StartNewGame(level) src/dev_ng/game/Core/gamesessionstatemachine.cpp:135-164。

ProcessReInitState:显示加载画面 → 若会话已初始化则 ShutdownSession → 关卡不同则 ShutdownLevel + InitLevel,或关卡未初始化且为默认关卡则 InitLevel → InitSession(level, false) → 权益自旋循环(期间手动泵 fwTimer::Update、控制线程、网络、警告屏、鼠标、Scaleform 帧末更新、渲染线程刷新,并在 g_GameRunning 时暂停/恢复外层 “Update” 计时条)→ 停加载画面 → SetState(IDLE, true) src/dev_ng/game/Core/gamesessionstatemachine.cpp:167-240。

5.4 双因素认证与 Steam#

两者都是条件编译的阻塞循环,默认构建中关闭(见第 4 节)。TWO_FACTOR_AUTH_CHECK 在法律画面结束后根据 YubiKey 插入/验证状态显示提示,直到“已验证且已拔出” src/dev_ng/game/Core/gamesessionstatemachine.cpp:1062-1112。STEAM_VERIFICATION 轮询 CSteamVerification,失败时显示错误并在确认后退出 src/dev_ng/game/Core/gamesessionstatemachine.cpp:386-438。本页不展开这些验证的判定细节。

6. 自动读档#

  1. START_AUTOLOAD:游戏未安装(CFileMgr::IsGameInstalled() 为假)或 -noautoload → 直接 LOAD_LEVEL(关卡取 CGameLogic::GetRequestedLevelIndex());否则 m_StartedAutoload = true、CSavegameAutoload::InitialiseAutoLoadAtStartOfGame(),进入 ENUMERATE_SAVED_GAMES src/dev_ng/game/Core/gamesessionstatemachine.cpp:693-720。
  2. ENUMERATE_SAVED_GAMES:(除非启用了“快速扫描 + 另一线程慢扫描”)阻塞枚举存档,再阻塞询问“读档还是新游戏”;新游戏 → LOAD_LEVEL(关卡取存档的 episode 号),读档 → AUTOLOAD_SAVE_GAME src/dev_ng/game/Core/gamesessionstatemachine.cpp:914-959。
  3. AUTOLOAD_SAVE_GAME:BeginGameAutoload() 后阻塞 CSavegameLoad::GenericLoad();成功则内联调用 ProcessLoadLevelState(true)(加载关卡与会话,bLoadingSavegame=true 使 InitSession 跳过新游戏专属步骤)→ CNetwork::Update(true) → CSavegameLoad::DeSerialize();反序列化成功调用 InitLevelAfterLoadingFromMemoryCard 与 DoGameSpecificStuffAfterSuccessLoad(true),失败则(PC 先显示“最近的存档已损坏”)关闭会话并以新游戏方式重建会话(Gen9 改为回落地页);读取失败则 ProcessLoadLevelState(false) 当作新游戏 src/dev_ng/game/Core/gamesessionstatemachine.cpp:961-1060。
  4. 回到 IDLE 后的第一次 ProcessIdleState 调用 CSavegameAutoload::ResetAfterAutoloadAtStartOfGame() src/dev_ng/game/Core/gamesessionstatemachine.cpp:678-686。

边界:ProcessLoadLevelState 自己会 SetState(IDLE),但 ProcessAutoLoadSaveGameState 在末尾再 SetState(newState) 覆盖它(newState 默认 IDLE,Gen9 反序列化失败时为 ENTER_LANDING_PAGE)src/dev_ng/game/Core/gamesessionstatemachine.cpp:967、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1536、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1059。

7. 语言选择与伽马校准#

  • CHOOSE_LANGUAGE:先显示语言选择屏(CLanguageSelect::ShowLanguageSelectScreen),取系统语言(未定义则英语)与档案中的 DISPLAY_LANGUAGE。若两者对应不同的语言文件,则关闭游戏流与加载画面、切到默认渲染函数并 Flush(true)、杀掉所有 Scaleform 影片并清空 Scaleform 存储、CText::Shutdown/Init 并重新加载文本,然后重建加载画面(LOADINGSCREEN_CONTEXT_LOADLEVEL)与游戏流;若只是语言不同而文件相同,则 TheText.ReloadAfterLanguageChange() src/dev_ng/game/Core/gamesessionstatemachine.cpp:798-846。
  • GAMMA_CALIBRATION:若 CDisplayCalibration::ShouldActivateAtStartup(),等待法律画面结束、挂起加载画面、加载并运行校准影片、把新伽马写入档案、移除影片并恢复加载画面 src/dev_ng/game/Core/gamesessionstatemachine.cpp:848-869。
  • PS3 走不同顺序:语言选择在 CGame::PreLoadingScreensInit 中已完成,LOAD_PROFILE 直接进入伽马校准 src/dev_ng/game/Core/game.cpp:1073-1076、src/dev_ng/game/Core/gamesessionstatemachine.cpp:791-795。

8. 运行期转换:新游戏、读档、换关#

8.1 触发者#

API前置条件典型调用者证据
StartNewGame(level)断言 IDLE;会话未初始化时直接返回暂停菜单“新游戏”、脚本 SHUTDOWN_AND_LAUNCH_SINGLE_PLAYER_GAME/NETWORK_GAME、回放退出、存档队列、非 PC 的登录变化src/dev_ng/game/Core/gamesessionstatemachine.cpp:510-521、src/dev_ng/game/frontend/PauseMenu.cpp:3584-3592、src/dev_ng/game/script/commands_network.cpp:7604-7616
LoadSaveGame()断言 IDLE暂停菜单读档src/dev_ng/game/Core/gamesessionstatemachine.cpp:523-527、src/dev_ng/game/frontend/PauseMenu.cpp:3690-3701
HandleLoadedSaveGame()断言 WAIT_FOR_SAVE_GAME存档队列 CSavegameQueuedOperation_ManualLoad 在 GenericLoad() 完成后调用src/dev_ng/game/Core/gamesessionstatemachine.cpp:529-535、src/dev_ng/game/SaveLoad/savegame_queued_operations.cpp:2644-2656
ChangeLevel(level)断言 IDLE脚本 SWITCH_TO_LEVEL(关卡索引 1–5)src/dev_ng/game/Core/gamesessionstatemachine.cpp:552-559、src/dev_ng/game/script/commands_misc.cpp:4008-4014
SetForceInitLevel()–冒烟测试;使下一次新游戏强制重载关卡src/dev_ng/game/Core/gamesessionstatemachine.h:123、src/dev_ng/game/tools/SmokeTest.cpp:515

值得注意:单人与多人之间的切换(SHUTDOWN_AND_LAUNCH_NETWORK_GAME)也走 StartNewGame——即关闭并重建整个游戏会话(SHUTDOWN_SESSION + INIT_SESSION);同关卡时不重载地图。

8.2 重启淡出 ProcessRestartFade#

START_NEW_GAME 与 CHANGE_LEVEL 每帧调用它直到返回 true src/dev_ng/game/Core/gamesessionstatemachine.cpp:1540-1573:

  1. 有警告屏 → 等玩家接受后移除;
  2. 画面未淡出且未在淡出 → camInterface::FadeOut(300);
  3. 淡出未到 1.0 → 等待;
  4. 暂停菜单仍打开 → 关闭它(注释:给加载渲染函数留一帧,修复 bug 176273);
  5. 否则停手柄震动,返回 true。

这是状态机中唯一真正跨帧推进的部分,期间 ShouldDoFullUpdateRender() 为 false,游戏运行 SIMPLE_MAIN_UPDATE。

8.3 START_NEW_GAME / HANDLE_LOADED_SAVE_GAME / CHANGE_LEVEL#

状态关键步骤证据
START_NEW_GAME(Gen9 先检查故事模式权益,否则转落地页)淡出完成后:结束启动条 → 显示加载画面 → ShutdownSession → 若 m_ForceInitLevel 或关卡变化则 ShutdownLevel → Bank 的 -askBeforeRestart 弹框(允许替换镜像文件)→ 必要时 InitLevel → InitSession(level, false) → 停加载画面 → IDLE。每帧还向 SuperConductor 发送 MuteGameWorld 消息src/dev_ng/game/Core/gamesessionstatemachine.cpp:1162-1220
HANDLE_LOADED_SAVE_GAME(Gen9 权益检查)显示加载画面 → 关卡号取 CSavegameList → ShutdownSession → 关卡变化则 ShutdownLevel/InitLevel → InitSession(level, true) → 停加载画面 → DeSerialize();失败时 PC 弹“无法加载损坏存档”并以新游戏方式重建,成功则 InitLevelAfterLoadingFromMemoryCard、DoGameSpecificStuffAfterSuccessLoad(true)、按新档案刷新暂停菜单选项src/dev_ng/game/Core/gamesessionstatemachine.cpp:1236-1341
CHANGE_LEVEL淡出完成后:显示加载画面 → ShutdownSession → ShutdownLevel → InitLevel → InitSession → 停加载画面 → IDLE(无条件重载关卡)src/dev_ng/game/Core/gamesessionstatemachine.cpp:1343-1361
LOAD_LEVEL(Gen9 权益检查,必要时清除脚本路由链接以免重定向循环)显示加载画面 → Gen9 下仅在关卡未初始化时 InitLevel → InitSession(level, bLoadingSavegame) → 再次 CModelIndex::MatchAllModelStrings()(DLC 可能新增模型)→ 单人游戏且无待处理脚本路由链接时停加载画面 → IDLEsrc/dev_ng/game/Core/gamesessionstatemachine.cpp:1474-1538

其他系统通过查询当前状态区分“新游戏”与“读档”:例如统计系统在 INIT_SESSION 中判断 START_NEW_GAME / HANDLE_LOADED_SAVE_GAME / AUTOLOAD_SAVE_GAME src/dev_ng/game/Stats/StatsDataMgr.cpp:1757-1787,人口流式在 START_NEW_GAME 时有特殊处理 src/dev_ng/game/streaming/populationstreaming.cpp:703。这是隐式耦合:改变状态顺序会影响这些系统。

9. 落地页#

方面PC 旧版落地页(CLegacyLandingScreen)Gen9 落地页(CLandingPage)
启用条件UI_LANDING_PAGE_ENABLED(PC 恒真)且 CLegacyLandingScreen::IsLandingPageEnabled();-benchmark、-skipLandingPage 跳过GEN9_LANDING_PAGE_ENABLED(需 RSG_GEN9)且 CLandingPage::ShouldShowOnBoot()
进入引导流程中 CHECK_FOR_INVITE → ENTER_LANDING_PAGE同左,或运行期 CLandingPage::SetShouldLaunch → SHUTDOWN_FOR_LANDING_PAGE(淡出、加载画面切到 LANDING 上下文)→ ReturnToLandingPage(清脚本路由链接、ShutdownSession、重置存储)→ ENTER_LANDING_PAGE
运行方式在 ENTER_LANDING_PAGE 内 while(!SLegacyLandingScreen::GetInstance().Update()); 阻塞,结束后 START_AUTOLOADCLandingPage::Launch() 后立即 IDLE;之后每帧由 CGame::Update 运行 LANDING_PAGE_UPDATE
退出循环结束CLandingPage::SetShouldDismiss → DISMISS_LANDING_PAGE:无会话时按脚本路由链接决定单人/多人(默认 Freeroam,Editor/Creator 视为非多人),设置“直接进多人”标志与加载画面顺序后 START_AUTOLOAD;有会话则 IDLE
证据src/dev_ng/game/frontend/landing_page/LandingPageArbiter.cpp:42-63、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1394-1406src/dev_ng/game/Core/gamesessionstatemachine.cpp:1365-1393、src/dev_ng/game/Core/gamesessionstatemachine.cpp:1409-1470、src/dev_ng/game/Core/gamesessionstatemachine.cpp:486-496

边界:在非 Gen9 构建中 ReturnToLandingPage() 的函数体为空,因此若有人在 PC 上设置 SHUTDOWN_FOR_LANDING_PAGE,状态将停留在该状态、每帧淡出并尝试初始化加载画面,不会自行离开。这表明运行期“返回落地页”只为 Gen9 设计(推断;没有找到 PC 上设置该状态的调用者,CLandingPage 只在 Gen9 编译)。

Gen9 故事模式权益:UserAllowedToEnterStoryMode() = UserHasStoryModeEntitlement() || IgnoreStoryModeEntitlement();后者在待处理的脚本路由链接目标不是故事模式时返回 true(某些非故事模式需要途经单人会话,如编辑器);无权益时 ShowStoryModeUpsell() 暂时直接回落地页(TODO 注释)src/dev_ng/game/Core/gamesessionstatemachine.cpp:1738-1777。

10. 阻塞循环中的“泵”与加载画面#

阻塞状态在循环中不会运行常规主循环,因此需要手动推进最少量的系统:

函数推进的系统证据
UpdateInitialDialogueScreensOnlyCSystem::BeginUpdate(含 fwTimer::Update)→ 控制线程启动/等待 → PC 鼠标与流式加载器(发出请求、处理异步文件)→ 警告屏 → 档案设置 → 存档对话框出现时挂起加载画面、关闭时恢复 → CNetwork::Update(true) → CSystem::EndUpdate → 初始化计时条帧src/dev_ng/game/Core/gamesessionstatemachine.cpp:1594-1626
ShutdownInitialDialogueScreensOnly关闭视口会话、最后一次警告屏更新、完全淡出、让主正交视口下次初始化会话时重新淡入、恢复加载画面src/dev_ng/game/Core/gamesessionstatemachine.cpp:1628-1642
UpdateLoadFailedDialogueScreens(PC)与 REINIT 循环相同的一组泵(含 CApp::CheckExit、Scaleform 帧末更新、FlushAndDeviceCheck)src/dev_ng/game/Core/gamesessionstatemachine.cpp:1644-1680
StartDisplayingLoadingScreens / StopDisplayingLoadingScreens前者 CLoadingScreens::Init(LOADINGSCREEN_CONTEXT_LOADLEVEL) 并允许 Scaleform 在帧末更新;后者不关闭加载画面(final 构建“交给脚本关闭”,非 final 仅 -stopLoadingScreensEarly 时关闭),只关闭帧末 Scaleform 更新src/dev_ng/game/Core/gamesessionstatemachine.cpp:1575-1592

在这些阻塞期间,主线程不调用 gRenderThreadInterface.Synchronise();渲染线程运行的是加载画面渲染函数(CLoadingScreens::RenderLoadingScreens 或 CGtaOldLoadingScreen::LoadingRenderFunction),PC 上循环中的 FlushAndDeviceCheck() 负责排空 drawlist 与处理设备状态。详见 threading-and-render-thread.md。长时间 skeleton 初始化期间则依靠 KeepAliveCallback 维持网络与加载画面(见 game-skeleton-and-main-loop.md)。

11. 未决问题 / 证据不足#

  • Gen9 代码不完整:livemanager.cpp 在 RSG_GEN9 下设置 CGameSessionStateMachine::SHUTDOWN_FOR_IIS,但状态枚举中没有该值 src/dev_ng/game/network/Live/livemanager.cpp:1602-1614、src/dev_ng/game/Core/gamesessionstatemachine.h:21-56。这表明本快照中的 Gen9(PS5/XSX)分支与当前头文件不一致,Gen9 落地页/初始交互屏流程无法完整验证。
  • TWO_FACTOR_AUTH_CHECK 的枚举只在 __WIN32PC 下存在,但 Init() 在 RAGE_SEC_ENABLE_YUBIKEY 下无条件设置它;由于该宏为 0 && RSG_PC,当前不会触发,但若在非 PC 上启用会无法编译(推断)。
  • CSavegameAutoload、CSavegameLoad、存档队列的内部状态机属于存档页面,本页只描述它们与会话状态机的接口。
  • 状态转换完全由代码阅读得出,未运行验证;各对话框文本标签只列名称,未核对本地化内容。