比赛模式:game_type / game_mode
CS:GO 用两个复制型 ConVar 组合出“经典休闲/竞技、军备竞赛、爆破、死亡竞赛、训练、自定义、合作”等模式;模式的具体参数(回合数、金钱、时间等)并不硬编码,而是由 GameModes.txt 中为每个模式列出的 cfg 与 ConVar 决定。
相关页面:CS:GO 总览 · 规则与回合 · 匹配与 GC
相关源码文件#
game/shared/cstrike15/gametypes.h、game/shared/cstrike15/gametypes.cpppublic/gametypes/igametypes.h- 调用方:
game/shared/cstrike15/cs_gamerules.cpp、game/server/gameinterface.cpp
1. 两个 ConVar 与枚举#
game_type与game_mode都带FCVAR_REPLICATED,同时在 game/client DLL 中可见:game/shared/cstrike15/gametypes.cpp:48-49。GetCurrentGameType/Mode直接返回它们的值:game/shared/cstrike15/gametypes.cpp:1641-1652。- 类型枚举:经典、军备竞赛、训练、自定义、合作;注释说明数值与
GameModes.txt中的顺序对应:game/shared/cstrike15/gametypes.h:27-38。模式枚举按类型分组,例如经典下的休闲、竞技、非排位竞技,军备竞赛下的渐进式、爆破等:game/shared/cstrike15/gametypes.h:40-60。 - 规则对象以一组
IsPlaying*谓词包装它们,例如IsPlayingClassic、IsPlayingGunGameProgressive、IsPlayingAnyCompetitiveStrictRuleset(只认经典-竞技):game/shared/cstrike15/cs_gamerules.cpp:16828-16943。
| 谓词 | 判定 | 证据 |
|---|---|---|
IsPlayingClassic | game_type == Classic | game/shared/cstrike15/cs_gamerules.cpp:16931-16934 |
IsPlayingGunGameProgressive/TRBomb/Deathmatch | GunGame 类型 + 对应模式 | game/shared/cstrike15/cs_gamerules.cpp:16828-16844 |
IsPlayingAnyCompetitiveStrictRuleset | 经典 + 竞技 | game/shared/cstrike15/cs_gamerules.cpp:16852-16855 |
IsPlayingCoopGuardian/Mission | 合作类型 + 对应模式 | game/shared/cstrike15/cs_gamerules.cpp:16857-16867 |
IsQueuedMatchmaking | sv_mmqueue_reservation 以 Q 开头(服务端) | game/shared/cstrike15/cs_gamerules.cpp:16878-16884 |
2. 模式表的加载#
GameTypes::Initialize 读取 GameModes.txt,追加 DLC 中的同名数据,再合并 gamemodes_server.txt(可用 -gamemodes_serverfile 指定);其中每个模式的 exec 段以“追加”方式合并而不是覆盖:game/shared/cstrike15/gametypes.cpp:257-330。之后按类型/模式、地图、地图组、自定义 Bot 难度分别解析:game/shared/cstrike15/gametypes.cpp:544、game/shared/cstrike15/gametypes.cpp:780、game/shared/cstrike15/gametypes.cpp:933、game/shared/cstrike15/gametypes.cpp:1026。
地图的“侧车”文件 maps/<map>.kv 不在初始化时加载,而是在规则对象 InitializeGameTypeAndMode 中按需加载(源码注释说明这是为了避免大量创意工坊订阅拖慢启动):game/shared/cstrike15/gametypes.cpp:291-294、game/shared/cstrike15/cs_gamerules.cpp:17594-17629。
3. 应用模式#
服务端 CCSGameRules 构造时调用 InitializeGameTypeAndMode:game/shared/cstrike15/cs_gamerules.cpp:4132。其中:
- 若地图未知则加载侧车文件;
CheckShouldSetDefaultGameModeAndType根据启动参数或地图默认值设置类型/模式(客户端版本直接返回):game/shared/cstrike15/cs_gamerules.cpp:17633、game/shared/cstrike15/gametypes.cpp:1535-1559;ApplyConvarsForCurrentMode遍历该模式的配置项,只处理名为exec的项,逐个执行对应 cfg(服务端用ServerCommand + ServerExecute);自定义类型直接返回。离线模式下还会应用自定义 Bot 难度,但只允许bot_difficulty、bot_dont_shoot、bot_quota三个 ConVar:game/shared/cstrike15/cs_gamerules.cpp:17636、game/shared/cstrike15/gametypes.cpp:1697-1764、game/shared/cstrike15/gametypes.cpp:1770-1815;- 再应用地图级 ConVar:
game/shared/cstrike15/cs_gamerules.cpp:17758、game/shared/cstrike15/gametypes.cpp:2161。
引擎在 Host_NewGame 时还会通过 ExecGameTypeCfg 让游戏 DLL ApplyGameSettings,并读取 members/numSlots 作为服务器槽位数:engine/host.cpp:6210、engine/sv_main.cpp:2847-2877。
4. 实例归属(跨模块)#
gametypes.cpp 被编译进 client、server、matchmaking 三个模块(game/client/client_cstrike15.vpc:239、game/server/server_cstrike15_inc.vpc:177、matchmaking/matchmaking_cstrike15_inc.vpc:35),每个模块都有自己的静态单例,并用 EXPOSE_SINGLE_INTERFACE_GLOBALVAR 以 VENGINE_GAMETYPES_VERSION 导出:game/shared/cstrike15/gametypes.cpp:206-209。源码里用 CLIENT_DLL/GAME_DLL/MATCHMAKING_DLL/MATCHMAKING_DS_DLL 分支选择执行 cfg 的方式;注释坦言这种做法“horrible design”,并说明只有 server.dll 真正调用应用函数:game/shared/cstrike15/gametypes.cpp:1728-1760。
运行时 client 与 server 拿到的是 matchmaking.dll 中的实例(静态调用链已闭环,未运行验证):
- 两个游戏 DLL 都在初始化时用传入的工厂重新赋值
g_pGameTypes:game/server/gameinterface.cpp:793-796、game/client/cdll_client_int.cpp:1528-1530。传入的都是g_GameSystemFactory,也就是GameFactory:engine/sv_main.cpp:1095、engine/cdll_engine_int.cpp:2720、engine/sys_dll.cpp:1062。 GameFactory先问 AppSystem 组,再问 matchmaking,最后才问 client.dll:engine/sys_dll.cpp:1018-1036。- 组内的 matchmaking 系统只以
IMATCHFRAMEWORK_VERSION_STRING注册,其QueryInterface也只认这个名字,所以组查找不会返回IGameTypes:engine/sys_dll2.cpp:2231、matchmaking/main.cpp:160-166、appframework/AppSystemGroup.cpp:322-358。随后 matchmaking 工厂会返回它自己导出的单例。
因此 server 调用 ApplyConvarsForCurrentMode 一类函数时,实际执行的是 matchmaking 模块编译出的 MATCHMAKING_DLL/MATCHMAKING_DS_DLL 分支;client 与 server 模块各自的静态单例在初始化后不再经由 g_pGameTypes 使用。专用服务器加载 matchmaking_ds:engine/sys_dll2.cpp:2191-2193。
约束与边界#
GameModes.txt、gamemodes_server.txt与各gamemode_*.cfg都是游戏内容文件,不在仓库中;因此本页只能描述加载与应用机制,不能给出任何模式的具体参数。- 军备竞赛的武器进阶表也来自
GameModes.txt:game/shared/cstrike15/gametypes.cpp:497-529。