CD Code Digest Hub /CS:GO

比赛规则与回合流程

15 分钟阅读 7 个章节 1 张图

CCSGameRules(game/shared/cstrike15/cs_gamerules.cpp,18,555 行)是一局比赛的“裁判”:它推进热身、冻结时间、回合计时、半场与加时,判定胜负,发放团队奖励,并在回合之间重置地图与玩家。本页按时间顺序拆解它的主循环。

相关页面:CS:GO 总览 · 比赛模式 · 玩家 · 投掷物与 C4 · 实体系统

相关源码文件#

  • game/shared/cstrike15/cs_gamerules.cpp、game/shared/cstrike15/cs_gamerules.h
  • 调用方:game/server/gameinterface.cpp、game/server/cstrike15/cs_player.cpp、game/shared/cstrike15/weapon_c4.cpp、game/server/cstrike15/hostage/cs_simple_hostage.cpp

1. 对象与网络化#

服务端在关卡初始化时通过 InstallGameRules 创建 CCSGameRules:game/server/cstrike15/cs_client.cpp:207-210、game/shared/cstrike15/cs_gamerules.cpp:591。规则状态(冻结期、热身期、回合时长、比赛阶段、已打回合数、回合开始时间、重启时间、C4 是否已下/已掉、回合胜者与原因等)放在 DT_CSGameRules 中,经代理实体 cs_gamerules 复制到客户端:game/shared/cstrike15/cs_gamerules.cpp:593-640、game/shared/cstrike15/cs_gamerules.cpp:779-780。客户端也构造一个 CCSGameRules,用于共享代码中的查询:game/shared/cstrike15/cs_gamerules.cpp:17168-17175。

规则基类 CGameRules 本身是一个 CAutoGameSystemPerFrame,其 FrameUpdatePostEntityThink 直接转发到 Think(),因此服务端每个 GameFrame 在实体 Think 之后调用一次规则 Think:game/shared/gamerules.h:123、game/shared/gamerules.cpp:584-588、game/server/gameinterface.cpp:1494。

2. 回合状态机#

证据(按图中边):

  • 热身期结束(或关闭热身)时设置 m_flRestartRoundTime、m_bGameRestart,冻结玩家并广播 CS_UM_WarmupHasEnded;重启时间到达后关闭热身标志:game/shared/cstrike15/cs_gamerules.cpp:9449-9538。
  • RestartRound 把 m_bFreezePeriod 置为 true,并设置 m_fRoundStartTime = curtime + m_iFreezeTime:game/shared/cstrike15/cs_gamerules.cpp:7937、game/shared/cstrike15/cs_gamerules.cpp:8128-8134。
  • 冻结期内 Think 调用 CheckFreezePeriodExpired,否则调用 CheckRoundTimeExpired:game/shared/cstrike15/cs_gamerules.cpp:9542-9550。冻结期到期后清除标志并触发 round_freeze_end:game/shared/cstrike15/cs_gamerules.cpp:10543-10548;比赛等待恢复或战术暂停期间会不断把回合开始时间往后推:game/shared/cstrike15/cs_gamerules.cpp:10419-10451。
  • TerminateRound 记录胜者与原因、设置 m_flRestartRoundTime = curtime + delay:game/shared/cstrike15/cs_gamerules.cpp:13445-13447。
  • 距重启 0.3 秒时调用 PreRestartRound,到时调用 RoundEnd 与 RestartRound;若有 Bot 正在语音或有人在看回放,会推迟(但有上限):game/shared/cstrike15/cs_gamerules.cpp:9584-9659。
  • Think 开头依次检查 CheckGameOver、帧数上限、m_endMatchOnThink:game/shared/cstrike15/cs_gamerules.cpp:9275-9293。

3. Think 的主要职责#

段落内容证据
冷却封禁对 GC 下发的冷却名单中仍在服务器上的玩家执行踢出/封禁game/shared/cstrike15/cs_gamerules.cpp:9042-9094
新比赛事件首次满足条件时触发 begin_new_matchgame/shared/cstrike15/cs_gamerules.cpp:9117-9141
阶段计时半场、比赛结束(掉落展示、投票)阶段的倒计时game/shared/cstrike15/cs_gamerules.cpp:9163-9246
购买时间购买时间结束时触发事件game/shared/cstrike15/cs_gamerules.cpp:9295-9303
半场换边上半场达到 mp_maxrounds/2(加时另算)或时间过半时进入半场,换边并冻结game/shared/cstrike15/cs_gamerules.cpp:9311-9343
比赛结束下半场达到总回合数、提前锁定胜局或超时;平局且开启加时则进入加时game/shared/cstrike15/cs_gamerules.cpp:9345-9390
无半场模式按 mp_maxrounds、锁定胜局或 mp_timelimit 结束game/shared/cstrike15/cs_gamerules.cpp:9420-9445
回合计时冻结期/回合时间到期检查、关卡初始化检查、剩余时间警告事件game/shared/cstrike15/cs_gamerules.cpp:9542-9560
回合重启见上节game/shared/cstrike15/cs_gamerules.cpp:9584-9659

4. 胜负判定#

CheckWinConditions 不是每帧轮询,而是在关键事件发生时被调用:玩家死亡或换队、断开连接、C4 下包/爆炸/拆除、人质被救等:game/server/cstrike15/cs_player.cpp:2791、game/server/cstrike15/cs_player.cpp:10001、game/shared/cstrike15/cs_gamerules.cpp:6302-6303、game/shared/cstrike15/weapon_c4.cpp:748、game/shared/cstrike15/weapon_c4.cpp:852、game/server/cstrike15/hostage/cs_simple_hostage.cpp:581。

它的检查顺序:

  1. 忽略胜利条件或正在载入回合备份时直接返回;热身期或已有胜者时只更新人数统计:game/shared/cstrike15/cs_gamerules.cpp:6320-6334。
  2. 统计双方存活/死亡人数:game/shared/cstrike15/cs_gamerules.cpp:6336-6338。
  3. 军备竞赛(GunGame)专用检查:game/shared/cstrike15/cs_gamerules.cpp:6341-6352。
  4. “人数不足”检查:首个玩家加入时以 Game_Commencing 立即结束回合以开始比赛:game/shared/cstrike15/cs_gamerules.cpp:6355-6357、game/shared/cstrike15/cs_gamerules.cpp:6391-6445。
  5. 依次检查越狱、炸弹、团灭、人质、守护者模式:game/shared/cstrike15/cs_gamerules.cpp:6359-6387。

代表性分支:

  • 炸弹:炸弹爆炸 → T 胜并发放 TERRORIST_WIN_BOMB;拆除 → CT 胜,T 也获得“下包未爆”奖励:game/shared/cstrike15/cs_gamerules.cpp:6909-6936。
  • 团灭:T 全部阵亡且不重生时,只要场上仍有激活的 C4,CT 就不能因团灭获胜:game/shared/cstrike15/cs_gamerules.cpp:7058-7087。
  • 超时:有炸弹点的地图在 C4 未下时 CT 胜(Target_Saved),下包后计时器不再结束回合;人质图超时 T 胜;没有目标时判平局。mp_default_team_winner_no_objective 可覆盖:game/shared/cstrike15/cs_gamerules.cpp:10626-10769。

TerminateRound 的其它行为:已有胜者时直接返回(因此同一回合只结束一次);投降投票会改写原因;把原因映射到胜方与提示文本;计算 MVP;可选保存回合备份;最后触发 round_end 事件:game/shared/cstrike15/cs_gamerules.cpp:13323-13447、game/shared/cstrike15/cs_gamerules.cpp:13449-13464、game/shared/cstrike15/cs_gamerules.cpp:13599-13605。

5. 回合重置与经济#

  • PreRestartRound 处理连败补偿:胜方打破连败时重置 m_iLoserBonus,连败方的补偿每轮增加直至上限,然后向败方发放 LOSER_BONUS、按营救人质数奖励 CT:game/shared/cstrike15/cs_gamerules.cpp:7506-7550。(RestartRound 中有一段同样逻辑已被注释掉:game/shared/cstrike15/cs_gamerules.cpp:8057-8121。)
  • 团队奖励通过 AddTeamAccount:mp_teamcashawards 关闭时只打印警告;合作模式下不给 Bot 一方发钱:game/shared/cstrike15/cs_gamerules.cpp:16617-16641。
  • RestartRound:触发 round_prestart,重置炸弹状态,进入冻结期,按 mp_afterroundmoney 给所有玩家发钱,CleanUpMap 重生地图实体(会销毁玩家正指向的地图实体,因此之后要刷新指针),重生/观察玩家,给 T 随机发 C4,清零重启时间与胜者,触发 round_start,最后 PostRestartRound:game/shared/cstrike15/cs_gamerules.cpp:7681-7685、game/shared/cstrike15/cs_gamerules.cpp:7784、game/shared/cstrike15/cs_gamerules.cpp:8146-8173、game/shared/cstrike15/cs_gamerules.cpp:8500-8520、game/shared/cstrike15/cs_gamerules.cpp:8661、game/shared/cstrike15/cs_gamerules.cpp:8670-8703、game/shared/cstrike15/cs_gamerules.cpp:8715-8739、game/shared/cstrike15/cs_gamerules.cpp:8845。
  • 回合内的个人收入(击杀奖励等)走玩家的 AddAccountAward/AddAccount,见 玩家。

约束与边界#

  • 回合结束是“幂等”的:TerminateRound 在 m_iRoundWinStatus != WINNER_NONE 时直接返回:game/shared/cstrike15/cs_gamerules.cpp:13325-13326。
  • 下包后的超时规则依赖 m_bBombPlanted:game/shared/cstrike15/cs_gamerules.cpp:10739-10750。
  • 军备竞赛、死亡竞赛、合作任务/守护者、训练、越狱等模式分支只在本页点到;它们的完整规则需要结合 GameModes.txt 与 cfg(不在仓库中)理解。