CD Code Digest Hub /CS:GO

服务器运维面:RCON、查询、Steam 认证、封禁、纯净服务器与下载

38 分钟阅读 11 个章节 5 张图

本页解释服务器上与“玩法”无关、但决定一台服务器能否被找到、被管理、被安全连接的那部分引擎代码:远程控制台(RCON)的认证与失败封禁、无连接查询(A2S)与限流、Steam 游戏服务器的登录与心跳、客户端 Steam 票据的校验与踢出、IP/SteamID 封禁列表、sv_pure 文件一致性校验、资源下载,以及 net_ws.cpp 中的 Steam 数据报中继与通道加密密钥交换。连接握手与签入流程本身见 网络通道、协议与连接,这里只补充握手中与认证、加密相关的分支。

相关页面:网络通道与协议 · 专用服务器启动链 · 输入与命令(命令缓冲与权限) · 文件系统与资源缓存 · 匹配框架与 Game Coordinator · Demo 与 GOTV

相关源码文件#

  • RCON:engine/sv_rcon.cpp、engine/sv_rcon.h、engine/sv_remoteaccess.cpp、common/igameserverdata.h、engine/socketcreator.cpp、engine/cl_rcon.cpp
  • 无连接包与查询:engine/baseserver.cpp、common/proto_oob.h、engine/sv_ipratelimit.cpp、engine/host_cmd.cpp
  • Steam 游戏服务器:engine/sv_steamauth.cpp、engine/sv_master.cpp
  • 封禁:engine/sv_filter.cpp
  • 纯净服务器:engine/sv_main.cpp、engine/pure_server.cpp、engine/sv_client.cpp、engine/client.cpp、engine/cl_main.cpp
  • 下载:engine/download.cpp、engine/net_chan.cpp
  • 传输与加密:engine/net_ws.cpp、engine/net_steamsocketmgr.cpp、engine/baseclientstate.cpp、public/steamdatagram/isteamdatagramserver.h

1. 总览:一个服务器端口上的几类流量#

证据:服务器每帧 RunFrame 先处理套接字,再检查超时、更新主服务器:engine/baseserver.cpp:3261-3275;NET_ProcessSocket 中先按 IP 过滤并回送封禁提示,再区分无连接包与通道包:engine/net_ws.cpp:2003-2043、engine/sv_filter.cpp:35-38;通道包在接收阶段按通道密钥做 ICE 解密:engine/net_ws.cpp:1555-1578;无连接包分派:engine/baseserver.cpp:895-1420;RCON 在 NET_RunFrame 中驱动:engine/net_ws.cpp:3640-3648。

2. RCON:远程控制台#

2.1 监听与协议#

  • 只有专用服务器或带 -usercon 的进程才创建 RCON 监听,地址取本机地址 + 服务器 UDP 端口号:engine/net_ws.cpp:3734-3741。监听由 CSocketCreator 实现,是 TCP 流套接字:engine/sv_rcon.h:83、engine/socketcreator.cpp:78-106。
  • 每个已接受的连接分配一个 listenerID,初始未认证:engine/sv_rcon.cpp:190-197。
  • 帧格式为“4 字节长度 + 负载”;单个请求超过 RCON_MAX_RCON_COMMAND_LEN(1024 字节)时按认证失败计数并关闭连接:engine/sv_rcon.cpp:280-321、engine/sv_rcon.h:25。
  • 负载是 requestID + requestType + 两个字符串,类型枚举为 SERVERDATA_*:engine/sv_remoteaccess.cpp:96-116、common/igameserverdata.h:45-66。

证据:分包后在 SV_RedirectStart/End 之间交给远程访问层:engine/sv_rcon.cpp:323-327;认证分支 engine/sv_remoteaccess.cpp:228-241、engine/sv_remoteaccess.cpp:435-457;错误密码分支 engine/sv_remoteaccess.cpp:474-502;执行分支 engine/sv_remoteaccess.cpp:187-226、engine/sv_remoteaccess.cpp:649-654。

2.2 认证规则#

规则证据
rcon_password 为空时任何密码都不通过(即 RCON 关闭)engine/sv_rcon.cpp:153-161
修改 rcon_password 会关闭全部已接受连接,迫使重新认证;该 ConVar 带 FCVAR_SERVER_CANNOT_QUERY 与 FCVAR_DONTRECORDengine/sv_rcon.cpp:81-91、engine/sv_rcon.cpp:142-146
认证状态按连接保存;EXECCOMMAND、REQUESTVALUE、SETVALUE、远程 bug 提交都先检查 IsAuthenticatedengine/sv_remoteaccess.cpp:118-120、engine/sv_remoteaccess.cpp:152-154、engine/sv_remoteaccess.cpp:189、engine/sv_remoteaccess.cpp:342、engine/sv_remoteaccess.cpp:462-466
未认证的 EXECCOMMAND 只记录 Bad Password,不计入失败次数;只有 AUTH 失败与超长请求会计数engine/sv_remoteaccess.cpp:210-224、engine/sv_rcon.cpp:316-321
SETVALUE 的 map 被翻译为 changelevel,mapcycle 会改写地图循环文件,其余拼成 <var> <value> 进入 CBUF_SERVERengine/sv_remoteaccess.cpp:606-644

2.3 失败封禁#

HandleFailedRconAuth 按来源 IP(不含端口)记录失败:engine/sv_rcon.cpp:527-626

  • sv_rcon_whitelist_address 命中的地址永不封禁;最多跟踪 32 个地址,满了淘汰最早失败的;每个地址最多保留 20 个失败时间戳。
  • 累计失败次数超过 sv_rcon_maxfailures(默认 10),或 sv_rcon_minfailuretime(默认 30 秒)内的失败超过 sv_rcon_minfailures(默认 5),即执行 addip <sv_rcon_banpenalty> <ip>;sv_rcon_banpenalty 默认为 0,而 addip 的 0 分钟表示永久:engine/sv_rcon.cpp:501-505、engine/sv_filter.cpp:145-149。
  • 封禁成功时 BadPassword 关闭该连接,否则回一个 id=-1 的认证失败响应:engine/sv_remoteaccess.cpp:480-501。

客户端侧的 rcon 命令通过 CRConClient 建立 TCP 连接并发送命令:engine/cl_main.cpp:2380、engine/cl_rcon.cpp:319、engine/cl_rcon.cpp:709-715。

3. 无连接查询(A2S)与限流#

限流在最前面:任何无连接包先经过 CheckConnectionLessRateLimits,按单 IP(默认平均 10 次/秒、30 秒窗口)和全局(默认 500 次/秒)限流,超限丢弃;线程化套接字泵在收包线程里也做同样检查:engine/baseserver.cpp:903-906、engine/sv_ipratelimit.cpp:22-26、engine/sv_ipratelimit.cpp:82-97、engine/net_steamsocketmgr.cpp:257-263。

服务器未激活时:专用服务器丢弃全部无连接包,监听服务器只处理首字节为 0 的局域网匹配包:engine/baseserver.cpp:912-922。

请求(首字节)行为证据
A2S_INFO('T')仅当 Steam 心跳已启用时处理;host_info_show=1(默认)由引擎自己回一个精简的 S2A_INFO_SRC:协议号 17、主机名(host_name_store 为假时换成游戏名)、地图、csgo、AppID、玩家/最大真人/Bot 数、d/l、操作系统、是否有密码、是否 VAC 安全、版本号,以及游戏端口、GOTV 端口与名称、游戏标签、GameID;=2 转交 Steam;=0 不回复engine/baseserver.cpp:1218-1346、engine/host_cmd.cpp:111-113
A2S_PLAYER('U')host_players_show=1(默认)只回一条伪记录“Max Players”(值为最大真人数,附运行时长),不暴露真实玩家;=2 转交 Steamengine/baseserver.cpp:1348-1383
A2S_RULES('V')host_rules_show 为真时转交 Steam,否则不回复engine/baseserver.cpp:1385-1392
A2S_SERVERQUERY_GETCHALLENGE('W')回复查询挑战值engine/baseserver.cpp:946-947
A2S_GETCHALLENGE / C2S_CONNECT连接握手(还没拿到 Steam 登录结果时丢弃挑战请求),见第 9 节与 网络通道engine/baseserver.cpp:936-944、engine/baseserver.cpp:965-1176
A2S_PING、A2S_RESERVE_CHECK 等版本匹配时回 S2A_PING_RESPONSE;预约检查交给匹配逻辑engine/baseserver.cpp:1186-1207
其他只有 -enablelegacypackets 时转交 Steam 的 HandleIncomingPacketengine/baseserver.cpp:1394-1418

“挑战”其实是固定字符串:A2S_INFO 的校验 ValidInfoChallenge 只比较请求附带的字符串是否等于 A2S_KEY_STRING("Source Engine Query"),sv_enableoldqueries(默认 0)为真时连这一步也跳过:engine/baseserver.cpp:875-889、engine/baseserver.cpp:838-846、engine/baseserver.cpp:195、common/proto_oob.h:130。数字挑战值 CheckChallengeNr(按 IP 记录、有效期 CHALLENGE_LIFETIME)用于连接请求:engine/baseserver.cpp:370-400、engine/baseserver.cpp:561-565。

推断:因为 A2S_INFO/PLAYER/RULES 都以 g_bSteamMasterHeartbeatsEnabled 为前提,局域网(sv_lan 1)或带 -nomaster 的服务器不会回答这些查询;该标志只在主服务器更新逻辑中被赋值(engine/baseserver.cpp:3148-3156)。

4. Steam 游戏服务器:激活、登录与心跳#

阶段行为证据
何时激活SV_ActivateServer 中,多人游戏或游戏 DLL 偏好 Steam 认证时调用 Steam3Server().Activate(),随后设置查询端口与服务器标签;模式未变时重复调用直接返回engine/sv_main.cpp:2568、engine/sv_main.cpp:2675-2687、engine/sv_steamauth.cpp:156-163
选择模式sv_lan → 无认证;不允许安全模式或 -insecure → 仅认证;否则认证 + VAC 安全engine/sv_steamauth.cpp:112-132、engine/sv_main.cpp:253
端口与地址默认 Steam 端口 26900(-steamport 覆盖),ip/ip_steam 指定网卡;sv_master_share_game_socket(默认 1)时主服务器更新复用游戏套接字,查询端口即游戏端口engine/sv_steamauth.cpp:171-226、engine/sv_steamauth.cpp:63-64
初始化失败SteamGameServer_InitSafe 失败、NO_STEAM 构建或 Xbox 专用服务器:退回无认证并强制 sv_lan 1engine/sv_steamauth.cpp:228-243、engine/sv_steamauth.cpp:268-288
登录未设置 sv_setsteamaccount 令牌时匿名登录(专用服务器会打印“仅限局域网”警告),否则用令牌持久账号登录;登录后开始监听 Steam 数据报中继engine/sv_steamauth.cpp:312-349、engine/sv_steamauth.cpp:1150-1167
回调泵CSteam3Server::RunFrame 至多每 0.1 秒调用一次 SteamGameServer_RunCallbacks;有无玩家变化时推送服务器详情engine/sv_steamauth.cpp:1037-1056、engine/sv_main.cpp:3492
心跳UpdateMasterServer 每帧转发主服务器更新器的待发包,并每 2 秒检查一次:活动的多人非单人服务器(无 -nomaster、未隐藏)才 EnableHeartbeats(true),同时刷新规则(仅 FCVAR_NOTIFY 变量)、玩家与详情;之后通知游戏 DLL 更新 GC 信息engine/baseserver.cpp:3114-3168、engine/baseserver.cpp:3170-3190
手动心跳heartbeat 命令调用 ForceHeartbeat(-fork 的父进程不发布自己);sv_search_key 变化时自动触发engine/sv_master.cpp:25-60
过期提示Steam 要求重启时提示服务器过期;Valve 官方服务器在非 -noautoupdate 时直接 sv_shutdownengine/baseserver.cpp:3068-3112

5. 客户端 Steam 票据校验与踢出#

证据与要点:

  • ConnectClient 依次检查协议、挑战值、仅限大厅/预约、监听服务器的背景图/单人限制、局域网 IP 限制(仅 sv_lan 时要求同 B 类网段或私有地址)与服务器密码:engine/baseserver.cpp:553-620、engine/baseserver.cpp:2784-2813。
  • CheckChallengeType:拒绝非法认证协议;Steam 协议要求票据长度在 (0, STEAM_KEYSIZE) 内,然后调用 NotifyClientConnect,失败且非局域网模式时拒绝连接:engine/baseserver.cpp:2713-2782、engine/baseserver.cpp:677-681。
  • NotifyClientConnect:从票据前 8 字节取出 SteamID 后 BeginAuthSession,无效票据、重复请求、版本不符、游戏不符、过期票据都拒绝;队列匹配预约(sv_mmqueue_reservation 以 Q 开头)要求账号在名单中;用户信息里的 accountid 与经 SDR 代理的地址都必须与票据账号一致;SDR 客户端直接标记为已完全认证:engine/sv_steamauth.cpp:870-949。
  • 后端回调 OnValidateAuthTicketResponse:结果不是 k_EAuthSessionResponseOK 时交给失败处理并直接返回;结果为 OK 时,若 SteamID 在封禁列表中则拒绝并断开,SteamID 重复则断开,否则记录日志并通知插件与游戏 DLL,最后统一 SetFullyAuthenticated:engine/sv_steamauth.cpp:687-736。注释说明“收不到回调时默认视为通过”,即客户端在回调到达前保持连接:engine/sv_steamauth.cpp:689-692。
  • 失败处理 OnValidateAuthTicketResponseHelper:超时中的客户端只报超时;“在别处登录”“VAC 封禁”在非局域网模式断开;“未拥有游戏”直接断开;“未连接 Steam / 票据取消 / 票据无效”交给 OnInvalidSteamLogonErrorForClient;VAC 检查超时视 sv_steamauth_enforce 而定:engine/sv_steamauth.cpp:780-834。
  • sv_steamauth_enforce(默认 2):对已完全认证的客户端,0 不踢,1 询问游戏 DLL(InvalidSteamLogon 键值命令,用于“下次重生时踢”),2 立即踢;局域网模式永不因此踢人:engine/sv_steamauth.cpp:68-70、engine/sv_steamauth.cpp:742-773。
  • 断开时对真实账号调用 EndAuthSession,对匿名账号(Bot、直连 GOTV)发送 SendUserDisconnect:engine/sv_steamauth.cpp:974-1008。

6. 封禁列表#

命令/机制行为证据
IP 过滤生效点每个收到的数据报先过 Filter_ShouldDiscard;sv_filterban=1(默认)为黑名单模式,=0 关闭,其他值为白名单模式;过期条目在检查时顺带删除engine/sv_filter.cpp:21、engine/sv_filter.cpp:45-78、engine/net_ws.cpp:2005-2009
addip <分钟> <ip>(别名 banip)0 分钟为永久;新增条目时踢掉第一个匹配的在线真人玩家并触发 server_addban 事件,原地更新已有条目则不踢;列表有上限 MAX_IPFILTERSengine/sv_filter.cpp:131-175、engine/sv_filter.cpp:193-254
writeip只把永久 IP 封禁写成 cfg/banned_ip.cfg 中的 addip 0 x.x.x.x 行engine/sv_filter.cpp:388-423
banid <分钟> <userid 或 SteamID> [kick]局域网模式拒绝;支持 STEAM_x:y:z 与 STEAM64BITID_ 前缀;触发 server_addban 游戏事件,带 kick 且玩家在线时踢出engine/sv_filter.cpp:698-760、engine/sv_filter.cpp:882-926
writeid只把永久 SteamID 封禁写成 cfg/banned_user.cfg 中的 banid 0 <id> 行engine/sv_filter.cpp:508-539
SteamID 封禁生效点不在握手时检查,而是在 Steam 票据校验成功回调中检查 Filter_IsUserBannedengine/sv_steamauth.cpp:707-711、engine/sv_filter.cpp:430
预约清空服务器被匹配预约(非 R 类负载)时执行 removeallidsengine/vengineserver_impl.cpp:1600-1607

谁加载 banned_user.cfg / banned_ip.cfg? 引擎只定义文件名并负责写出:engine/sv_filter.cpp:26-28;Filter_Init 是空函数:engine/sv_filter.cpp:934-936;全仓库没有执行这两个文件的代码。按 Source 惯例由 server.cfg 中的 exec 加载(推断,仓库不含 cfg 内容,证据不足)。

7. sv_pure 与文件一致性校验#

证据与要点:

  • sv_pure 是命令而不是 ConVar,值保存在静态 g_sv_pure_mode(默认 1);若命令行把它设为 0,之后不能再在控制台开启;2 被视为过时并改为 1;修改在下一张地图生效:engine/sv_main.cpp:174-240。
  • ReloadWhitelist:监听服务器强制关闭纯净模式;单人或模式 0 时不建白名单;否则加载全局 pure_server_whitelist.txt 与 maps/<地图>_whitelist.txt:engine/sv_main.cpp:2776-2840。白名单编码后放入启动字符串表:engine/sv_main.cpp:697-705;专用服务器 ModInit 时开启文件系统的白名单跟踪(见 文件系统)。
  • 客户端从字符串表解码白名单;单人、HLTV、回放时不处理:engine/cl_main.cpp:1486-1542。进入 SIGNONSTATE_FULL 后,CheckFileCRCsWithServer 每 0.2 秒最多发送 5 个文件系统报告的“未验证文件哈希”:engine/client.cpp:1844-1885。
  • 服务端 CLCMsg_FileCRCCheck:不在纯净模式时忽略;交给 g_PureFileTracker.DoesFileMatch;不匹配时通知游戏 DLL OnPureServerFileValidationFailure,sv_pure_kick_clients(默认 1)为真则断开,否则只警告:engine/sv_client.cpp:421-483、engine/sv_main.cpp:247。
  • DoesFileMatch 的判定顺序:不在 VPK 中且不在“强制匹配”列表的文件直接通过(由服务器决定是否白名单,客户端无法把修改文件藏进白名单目录);服务器认为应在 VPK 中却以散文件出现则失败;双方都有整文件哈希时直接比较;VPK 内未识别的文件失败;有主哈希时与之比较;都没有时记录下来等待“共识”,暂时判为通过:engine/pure_server.cpp:617-778。

8. 资源下载#

环节行为证据
可下载清单服务器的 downloadables 字符串表(建表见 网络通道)engine/sv_main.cpp:587-656
何时下载客户端在 SIGNONSTATE_NEW 等待 Steam 资源就绪后遍历清单;cl_downloadfilter 可设为 none/nosounds;创意工坊地图的 .bsp 改走 UGC 下载;队列非空时显示进度并暂停签入,否则继续 FinishSignonState_Newengine/client.cpp:1624-1641、engine/client.cpp:1663-1790、engine/client.cpp:61
HTTP 还是通道CL_QueueDownload 以 sv_downloadurl(FCVAR_REPLICATED)为基址;以 http:///https:// 开头时先尝试下载 .bz2 压缩版本并在线程中进行,否则通过网络通道 RequestFile;本机就是服务器或文件已存在时跳过engine/download.cpp:589-649、engine/download.cpp:846-874、engine/download.cpp:953-957、engine/sv_main.cpp:261
服务器响应CGameClient::FileRequested:sv_allowdownload(默认 1)为真则 SendFile,否则 DenyFile;上传开关为 sv_allowuploadengine/sv_client.cpp:2168-2180、engine/sv_client.cpp:52-53
文件名安全检查IsValidFileForTransfer:拒绝绝对路径与 ..、cfg/、scripts/、addons/ 等目录、maps/ 下除 .bsp/.ain/.nav 外的文件、扩展名含空白,以及 .cfg/.exe/.dll/.bat/.vdf/... 等扩展名;队列、发送与接收三处都调用engine/net_chan.cpp:3585-3662、engine/download.cpp:591-592、engine/net_chan.cpp:627、engine/net_chan.cpp:2268
大小限制通道上传上限 net_maxfilesize(默认 16 MB,最大 64)engine/net_chan.cpp:52

9. 传输层:Steam 数据报中继与通道加密#

9.1 Steam 数据报中继(SDR)与线程化套接字#

  • 地址类型除普通 IP 外还有 NSAT_PROXIED_GAMESERVER(客户端看到的经中继的服务器)与 NSAT_PROXIED_CLIENT(服务器看到的经中继的客户端)。发送时分别走 ISteamDatagramTransportClient::SendDatagram 与 ISteamDatagramTransportGameserver::SendDatagram:engine/net_ws.cpp:2097-2133。
  • 服务器端:sv_steamdatagramtransport_port 非 0 时,Steam 登录后 SteamDatagram_GameserverListen 打开一个中继接口,失败则把该 ConVar 清零以免对外宣称:engine/net_ws.cpp:95、engine/net_ws.cpp:4450-4474、engine/sv_steamauth.cpp:349。接收时普通套接字没有数据才轮询中继,并按服务器、GOTV、GOTV1 三个通道区分来源:engine/net_ws.cpp:1380-1421。
  • 客户端:NET_InitSteamDatagramProxiedGameserverConnection 复用或新建到目标服务器 SteamID 的中继连接;数据报客户端库按 -perfectworld 选择合作方配置:engine/net_ws.cpp:3015-3040、engine/net_ws.cpp:2950-2983、engine/net_ws.cpp:1423-1437。
  • 中继库只以头文件与预编译库形式存在(public/steamdatagram/、lib/public/*steamdatagramlib*),实现不在仓库中:public/steamdatagram/isteamdatagramserver.h:18。
  • 另有一个与 SDR 无关的线程化套接字泵:默认启用(-nothreadedsockets 关闭),收包线程对无连接包做限流,对通道包按客户端做突发上限:engine/net_steamsocketmgr.cpp:21-29、engine/net_steamsocketmgr.cpp:65、engine/net_steamsocketmgr.cpp:225-275。

9.2 通道加密密钥交换#

证据:

  • 证书来自 -certificate <文件>:文件头必须是 CSv1,目录项依次给出公钥、签名、私钥的偏移与长度;任何格式错误都直接退出进程:engine/net_ws.cpp:4807-4864。指定 -certificate 或作为 Valve 官方服务器运行时,缺证书即退出:engine/host.cpp:5854-5869。登录 Steam 后服务器用自己的公网 IP 自检证书,失败同样退出(-ignore_certificate_address 跳过):engine/sv_steamauth.cpp:503-528。
  • 服务器在挑战回复中写入“是否要求加密通道”与公钥、签名:engine/baseserver.cpp:1849-1866。
  • 客户端用内置主公钥验证签名:被签名的数据是“服务器公钥 + IP/掩码位”,掩码位从 32 递减到 16 逐个尝试;通过后用随机数生成会话密钥并以服务器公钥做 RSA-OAEP 加密:engine/net_ws.cpp:4562-4660。客户端把密钥按递增索引保存,验签失败则断开:engine/baseclientstate.cpp:1763-1806。
  • 服务器用私钥解密并检查长度;插件可通过 -externalnetworkcryptkey 接管密钥管理;sv 实例持有私钥但未得到客户端密钥时拒绝连接:engine/baseserver.cpp:1068-1151、engine/net_ws.cpp:4715-4805。
  • 有密钥的通道在发送时附加随机填充并按 ICE 块加密,接收时先解密:engine/net_ws.cpp:2690-2700、engine/net_ws.cpp:1555-1578;密钥在创建通道时传入:engine/net_ws.cpp:755-786。
  • 本快照中主公钥被置空(Removed for partner depot),只记录其存在:engine/net_ws.cpp:4567。因此本仓库构建出的客户端无法验证任何要求加密的服务器证书,带 -certificate 的服务器在 Steam 登录后的自检也会失败退出(推断,依据是验签以该常量为唯一信任根)。

约束与边界#

  • 裁剪:主服务器 Steam 详情中“官方地图”判断被删空(Removed for partner depot),bOfficialMap 恒为假:engine/sv_steamauth.cpp:1106-1108;加密主公钥被置空(见 9.2)。
  • 观察:未认证可调用的 RCON 请求:非 DEDICATED 构建中,SERVERDATA_TAKE_SCREENSHOT 与 SERVERDATA_SEND_CONSOLE_LOG 分支没有 IsAuthenticated 检查:engine/sv_remoteaccess.cpp:257-285。客户端进程只有带 -usercon(或启用 ENABLE_RPT 的 RPT 服务器)时才监听,影响范围有限(推断)。
  • 观察:挑战回复中的长度未与缓冲比较:客户端把服务器给出的公钥/签名长度直接用于读入 1024 字节的栈缓冲,只在读完后检查位流是否溢出:engine/baseclientstate.cpp:1767-1778。是否可被利用取决于 bf_read::ReadBytes 的越界行为,本次未验证。
  • 弱随机源(观察):客户端会话密钥用以当前时间为种子的 RandomInt 生成,而不是密码学随机数:engine/net_ws.cpp:4642-4648。
  • 认证的乐观默认:握手阶段只要 BeginAuthSession 被受理就放行,真正的校验结果异步到达;“已完全认证”标志只在回调为 OK 时设置(SDR 客户端在握手时即设置),而 sv_steamauth_enforce 的宽限只适用于已完全认证的客户端,未完全认证者出错时总是立即断开:engine/sv_steamauth.cpp:701-735、engine/sv_steamauth.cpp:943-944、engine/sv_steamauth.cpp:747-767。
  • 条件编译:NO_STEAM 构建强制局域网模式;ENGINE_CONNECT_VIA_MMS 才处理 C2S_VALIDATE_SESSION、A2S_RESERVE;ENABLE_RPT 才驱动 RPT 服务器:engine/sv_steamauth.cpp:240-243、engine/baseserver.cpp:949-963、engine/baseserver.cpp:1181-1184、engine/net_ws.cpp:3646-3648。
  • 本页未展开:日志系统(sv_log.cpp、log_address)、sv_uploaddata.cpp 的统计上报、HLTV/GOTV 中继的认证与 tv_password、服务器预约(reservation)与匹配框架交互的细节(见 匹配与 GC)、net_steamsocketmgr.cpp 中 Steam P2P 伪套接字(USE_STEAM_SOCKETS)的实现。