CD Code Digest Hub /CS:GO

客户端启动链:从 csgo.exe 到第一帧

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

本页追踪 Windows 客户端(同时也是“监听服务器”)的启动顺序:可执行文件如何找到 DLL、三层 AppSystemGroup 如何嵌套、引擎何时加载游戏 DLL、以及主循环从哪里开始。

相关页面:接口工厂与 AppSystem · 专用服务器 · 主循环与 Tick

相关源码文件#

  • launcher_main/main.cpp:最外层可执行入口
  • launcher/launcher.cpp:LauncherMain、CSourceAppSystemGroup
  • appframework/WinApp.cpp:CSteamApplication
  • engine/sys_dll2.cpp:CEngineAPI、CModAppSystemGroup
  • engine/sys_engine.cpp、engine/sys_dll.cpp、engine/host.cpp:引擎加载与 Host_Init

1. 总体时序#

下文逐步给出每条箭头的证据。

2. 可执行入口 WinMain#

  1. 用可执行文件路径推出根目录,把 <root>\bin(64 位为 bin\x64)前置到 PATH:launcher_main/main.cpp:118-149。
  2. 用 LoadLibraryEx(..., LOAD_WITH_ALTERED_SEARCH_PATH) 加载 bin\launcher.dll;失败时弹出系统错误信息并返回 0:launcher_main/main.cpp:151-173。
  3. 取导出函数 LauncherMain 并直接调用:launcher_main/main.cpp:175-176。

同一文件还包含 PS3 与 POSIX 版本的入口(POSIX 用 dlopen/dlsym):launcher_main/main.cpp:619、launcher_main/main.cpp:681。

3. LauncherMain:命令行整理与重启循环#

LauncherMain 由 extern "C" __declspec(dllexport) 导出:launcher/launcher.cpp:1479-1485。在进入 AppSystem 之前它会修补命令行,例如非 Steam 启动时自动追加 -insecure:launcher/launcher.cpp:1847-1851;构建 cubemap 时追加 -nosound -noasync:launcher/launcher.cpp:1861-1866。

核心是一个重启循环:每轮构造新的 CSourceAppSystemGroup 与包裹它的 CSteamApplication 并运行;若在初始化阶段返回 INIT_RESTART 或运行阶段返回 RUN_RESTART 就重来,并清除与窗口尺寸相关的命令行覆盖项:launcher/launcher.cpp:1870-1922。这就是“修改视频设置后引擎整体重启”的实现位置。

4. 第一层:CSteamApplication#

Create 先通过 FileSystem_GetFileSystemDLLName 决定文件系统模块,然后把 vstdlib 的 cvar 工厂与文件系统加入本组:appframework/WinApp.cpp:148-171。Main 设置基础路径后,把自己作为父组传给 CSourceAppSystemGroup 并运行它:appframework/WinApp.cpp:202-210。

5. 第二层:CSourceAppSystemGroup#

Create:决定加载哪些模块#

模块表以 {DLL 名, 接口版本} 的形式列出,注释特别说明 engine 必须排第一:launcher/launcher.cpp:727-770。随后:

  • 定义 INCLUDE_SCALEFORM 时,按 -sf3 选择 scaleformui_3 或 scaleformui:launcher/launcher.cpp:784-810。
  • -tools/-p4 时加载 p4lib;Windows PC 上加载 vstdlib 的 IProcessUtils;-dev 时加载资源访问控制:launcher/launcher.cpp:812-840。
  • 取得 IMaterialSystem 与 IEngineAPI 指针;-edit 模式额外加载 hammer_dll:launcher/launcher.cpp:842-858。
  • 必须在 Connect 之前选择 Shader API:默认 shaderapidx9,-noshaderapi 改用 shaderapiempty,-shaderapi <dll> 可任意指定:launcher/launcher.cpp:860-872。材质系统随即加载该 DLL 并保存其工厂:materialsystem/cmaterialsystem.cpp:746-774。

PreInit:连接基础库并挂载内容#

依次 ConnectTier1Libraries、ConVar_Register、ConnectTier2/3Libraries,若文件系统或材质系统指针为空直接失败:launcher/launcher.cpp:880-889。然后设置 Steam 环境、挂载 gameinfo 内容、可选 -vpk 追加 VPK、加入 platform 搜索路径:launcher/launcher.cpp:895-936。最后把实例句柄、基础目录、初始 mod/game、父组指针打包成 StartupInfo_t 交给引擎:launcher/launcher.cpp:958-966。

Main#

只有一行:return g_pEngineAPI->Run();:launcher/launcher.cpp:969-972。

6. 引擎:CEngineAPI::Run → RunListenServer#

  • Run 在 -insecure 时禁止连接安全服务器;Windows 非调试会话下用 SE translator 捕获结构化异常并写 minidump,异常时 TerminateProcess(…,100):engine/sys_dll2.cpp:1736-1779。
  • RunListenServer 调 ModInit(设置 host_parms.mod/game、注册表路径、读取预启动配置并创建游戏窗口:engine/sys_dll2.cpp:1319-1355),然后构造以启动器组为父的 CModAppSystemGroup 并运行,结束后 ModShutdown:engine/sys_dll2.cpp:1409-1447。

7. 第三层:CModAppSystemGroup#

Create 的顺序:

  1. 非 Perforce 运行且需要时,通过 SteamAPI_RestartAppIfNecessary 让 Steam 重启进程:engine/sys_dll2.cpp:2161-2165。
  2. 校验并加载 matchmaking(专用服务器为 matchmaking_ds)。签名校验失败时仍会加载,但把客户端置为不能连接安全服务器;拿不到 IMatchFramework 则 Sys_Error:engine/sys_dll2.cpp:2180-2231。
  3. 非纯服务器时 ClientDLL_Load + ClientDLL_Connect,然后 ServerDLL_Load:engine/sys_dll2.cpp:2240-2259。
  4. 读取 client/server 声明的共享系统列表,合并去重后 AddSystems;再把 engine 自身的工具框架接口加入:engine/sys_dll2.cpp:2261-2330。

Main 在非纯服务器分支中调用 eng->Load(false, …),再进入 s_EngineAPI.MainLoop();MainLoop 返回 true 表示需要重启(RUN_RESTART):engine/sys_dll2.cpp:2398-2425。

8. eng->Load → Sys_InitGame → Host_Init#

CEngine::Load 把 DLL 状态置为 active 并调用 Sys_InitGame:engine/sys_engine.cpp:237-257。Sys_InitGame 依次初始化平台、内存、Host_Init、认证:engine/sys_dll.cpp:1133-1157。Host_Init 中与游戏模块相关的顺序:

步骤证据
控制台、命令缓冲、cvar 等基础设施engine/host.cpp:5557-5565
网络、游戏事件管理器、sv.Initengine/host.cpp:5611-5616
未加 -nogamedll 时 SV_InitGameDLL(调用 server 的 DLLInit)engine/host.cpp:5618-5621、engine/sv_main.cpp:1095-1097
HLTV 初始化engine/host.cpp:5625
客户端:CL_Init、材质系统、Scaleform 全屏/光标、模型加载器、studiorender、匹配框架、VGUI、ClientDLL_Initengine/host.cpp:5636-5661
声音 S_Init,执行 valve.rc、配置文件engine/host.cpp:5713、engine/host.cpp:5745-5760

client.dll 的 Init 收到的是引擎的 g_GameSystemFactory:engine/cdll_engine_int.cpp:2720。这个工厂依次询问 AppSystem 组、matchmaking、client、GameUI:engine/sys_dll.cpp:1014-1062。

9. 主循环#

CEngineAPI::MainLoop 在退出标志未设时,每轮泵消息(编辑模式走另一套泵),然后 eng->Frame(),编辑模式下再驱动 Hammer:engine/sys_dll2.cpp:1126-1167。eng->Frame() 之后的逻辑见 主循环与 Tick。

约束与边界#

  • 启动器按名字加载 vphysics、shaderapidx9、scaleformui 等模块,而这些模块要么不在 csgo_partner 组、要么源码缺失(见 构建与物理组织)。任一 AddSystems 失败都会让 Create 返回 false,从而整个启动失败:launcher/launcher.cpp:778-779、appframework/AppSystemGroup.cpp:301-316。
  • 签名校验失败的 matchmaking 模块仍被加载,但会触发 Host_DisallowSecureServers:engine/sys_dll2.cpp:2182-2188。
  • 本次没有运行启动链;上述顺序来自静态阅读,平台分支(OSX Cocoa、SDL、PS3、X360)只做了存在性确认。