游戏 DLL 契约:client.dll 与 server.dll
引擎通过一组版本化接口调用游戏代码,游戏代码再通过另一组接口回调引擎。理解这些契约就能知道“某段玩法代码在什么时候、被谁、在哪个进程里执行”。
子页面:
相关页面:引擎总览 · 主循环与 Tick · CS:GO 玩法总览
相关源码文件#
- 契约:
public/eiface.h(服务端)、public/cdll_int.h(客户端) - 服务端实现:
game/server/gameinterface.cpp - 客户端实现:
game/client/cdll_client_int.cpp - 引擎侧加载:
engine/sys_dll2.cpp、engine/sys_dll.cpp、engine/sv_main.cpp、engine/cdll_engine_int.cpp
1. 接口一览#
| 方向 | 接口(版本) | 实现 | 证据 |
|---|---|---|---|
| 引擎→服务端 | IServerGameDLL(ServerGameDLL005) | CServerGameDLL | public/eiface.h:547-574、game/server/gameinterface.cpp:696 |
| 引擎→服务端 | IServerGameClients | CServerGameClients | public/eiface.h:767、game/server/gameinterface.cpp:3098 |
| 引擎→服务端 | IServerGameEnts(含 CheckTransmit) | CServerGameEnts | public/eiface.h:736-756、game/server/gameinterface.cpp:2650 |
| 服务端→引擎 | IVEngineServer | CVEngineServer | public/eiface.h:139、engine/vengineserver_impl.cpp:2059 |
| 引擎→客户端 | IBaseClientDLL(VClient018) | CHLClient | public/cdll_int.h:878、public/cdll_int.h:1177、game/client/cdll_client_int.cpp:1058 |
| 客户端→引擎 | IVEngineClient | CEngineClient | public/cdll_int.h:298、engine/cdll_engine_int.cpp:736 |
2. 加载与初始化#
- 引擎在 Mod 组
Create中加载 client 与 server 模块,并读取二者声明的共享 AppSystem:engine/sys_dll2.cpp:2240-2321。 - server 的
DLLInit由SV_InitGameDLL调用,传入的第一个工厂是g_GameSystemFactory:engine/sv_main.cpp:1095-1097。它先连接 tier 库,再逐个获取引擎接口(任一缺失返回 false),获取IGameTypes,初始化数学库与游戏系统,最后初始化游戏模式表:game/server/gameinterface.cpp:702-786、game/server/gameinterface.cpp:794-795、game/server/gameinterface.cpp:843-845、game/server/gameinterface.cpp:923-935。 - client 的
Connect连接 tier 库、获取 Scaleform 接口并注册 ConVar(FCVAR_CLIENTDLL):game/client/cdll_client_int.cpp:1387-1437;Init获取各类引擎接口与IGameTypes、初始化游戏系统与粒子管理器:game/client/cdll_client_int.cpp:1452、game/client/cdll_client_int.cpp:1529、game/client/cdll_client_int.cpp:1651-1699。它由引擎的ClientDLL_Init以g_GameSystemFactory调用:engine/cdll_engine_int.cpp:2720。
一个跨模块的细节:谁提供 IGameTypes?#
gametypes.cpp 同时编译进 client、server、matchmaking 三个模块,每个模块都用 EXPOSE_SINGLE_INTERFACE_GLOBALVAR 暴露 VENGINE_GAMETYPES_VERSION:game/shared/cstrike15/gametypes.cpp:209、matchmaking/matchmaking_cstrike15_inc.vpc:35。游戏 DLL 通过 g_GameSystemFactory 获取它,而该工厂的查询顺序是 AppSystem 组 → matchmaking → client → GameUI:engine/sys_dll.cpp:1014-1044;matchmaking 作为 AppSystem 的 QueryInterface 只回答 IMatchFramework:matchmaking/main.cpp:160-166。由此推断:client 与 server 拿到的都是 matchmaking.dll 中的那份 GameTypes 单例,三方共享同一份模式状态(推断,未运行验证)。
3. 服务端回调时间线#
| 时机 | 回调 | 证据 |
|---|---|---|
| 换图开始 | LevelInit:解析实体字符串、创建并 Spawn 地图实体 | game/server/gameinterface.cpp:1192-1275 |
| 地图激活 | ServerActivate(同时激活 Bot 管理器) | game/server/gameinterface.cpp:1338、game/server/gameinterface.cpp:1382 |
| Steam 就绪 | GameServerSteamAPIActivated:初始化 Steam 上下文与 GC 客户端系统 | game/server/gameinterface.cpp:1389-1411 |
| 每个服务器 tick | GameFrame(simulating) | game/server/gameinterface.cpp:1418-1536 |
| 每帧最后一个 tick | Think(finalTick)、PreClientUpdate | game/server/gameinterface.cpp:1593-1620、game/server/gameinterface.cpp:1542 |
收到 clc_Move | ProcessUsercmds | game/server/gameinterface.cpp:3565-3651 |
| 打快照 | CheckTransmit、发送后 PostClientMessagesSent | game/server/gameinterface.cpp:2784、game/server/gameinterface.cpp:3654-3658 |
| 换图结束 | LevelShutdown | game/server/gameinterface.cpp:1627 |
4. 客户端回调时间线#
| 时机 | 回调 | 证据 |
|---|---|---|
| 每帧开头 | FrameStageNotify(FRAME_START) | engine/sys_engine.cpp:530-535 |
| 每个 tick 输入阶段 | CreateMove → CInput::CreateMove | engine/cl_main.cpp:2789-2793、game/client/cdll_client_int.cpp:2169-2185 |
| 收到实体包 | FRAME_NET_UPDATE_START … _END | engine/cl_ents_parse.cpp:631-698 |
| 渲染前 | FRAME_RENDER_START → OnRenderStart:插值、客户端动画、数据变化事件、SimulateEntities | engine/gl_screen.cpp:287、game/client/cdll_client_int.cpp:3234-3323 |
| 渲染 | View_Render → CViewRender::Render | game/client/cdll_client_int.cpp:2222-2235 |
| 渲染后 | FRAME_RENDER_END | engine/gl_screen.cpp:311 |
帧阶段枚举定义见 public/cdll_int.h:218-237,client.dll 对各阶段的处理见 game/client/cdll_client_int.cpp:3400-3521。
5. 共享代码的双端编译#
game/shared 下的文件同时编译进两端,靠 CLIENT_DLL/GAME_DLL 区分。阅读时要注意三种常见模式:
| 模式 | 例子 | 证据 |
|---|---|---|
同名类在客户端加 C_ 前缀 | FX_FireBullets 中 C_CSPlayer 与 CCSPlayer | game/shared/cstrike15/fx_cs_shared.cpp:169-173 |
| 只在服务端执行的权威逻辑 | 延迟补偿仅在 !CLIENT_DLL 下启用 | game/shared/cstrike15/fx_cs_shared.cpp:380-383 |
| 只在客户端执行的表现/防重入 | 客户端同一发子弹 20ms 内不重复追踪 | game/shared/cstrike15/fx_cs_shared.cpp:201-214 |
约束与边界#
- 只有
IServerGameDLL等核心接口被逐条核对;IServerGameTags、IServerGameDLL的存档/读档回调(SaveInit等)对 CS:GO 多人模式意义不大,未展开。 - client.dll 中大量
C_实体(视觉效果、HUD、GameUI)只按需在其它页面引用。