Zh/Counter-Strike: Global Offensive Game State Integration

这个页面介绍了在 Counter-Strike: Global Offensive 游戏中启动基本的游戏状态整合 (Game State Integration) 功能。thumb|right|360px|Counter-Strike: Global Offensive
对于第三方来说，有许多方法与 Counter-Strike: Global Offensive 游戏进行整合：在竞技比赛中启动舞台灯光与烟火效果，亦或是在游戏中激活玩家电脑连接到的灯光或者具有触觉反馈的设备；在竞技直播中让直播的画面效果与游戏状态同步，收集所有游戏状态以在录制中加入注解等。
默认状态下，游戏客户端将会暴露所有的游戏状态，当游戏状态变更时以最快的速度向本地或者远程的 HTTP POST 的接口发送游戏状态数据，并且使用标准的 JSON 格式作为游戏状态数据的结构。Game State Integration 只需要知道玩家想将数据发送至哪里，以及需要转发游戏中什么数据即可。这篇文档将会帮助第三方开发者将他们的软件或者脚本与 CS:GO 的游戏状态进行整合。
== 定位 CS:GO 安装目录==
CS:GO 游戏客户端需要读取 Game State Integration 配置文件以便找出发送的目标服务器和需要发送的游戏状态数据。竞技赛事中的系统管理员在为选手制作硬盘镜像的时候就已经知道了 CS:GO 的安装目录，因此不需要进行目录查找工作。如果你在尝试给例如：键盘，耳机 等设备驱动实现 HTTP POST 的接口来激活外围设备与游戏数据的整合，可以通过提示用户手动配置 CS:GO 的游戏安装路径，或者使用以下方式自动查找 CS:GO 的游戏安装路径。
在 Windows 里的 Steam 安装路径可以通过注册表中的键值对数据中找到: HKEY_CURRENT_USER\Software\Valve\Steam。键值对中的值："SteamPath" 包含了一个名为 "Steam install directory" 的字符串数据。例如，Steam 安装到了 D:\Steam 中，那么 Steam 的游戏库文件夹将会被列在 D:\Steam\steamapps\libraryfolders.vdf。
在 macOS 上，Steam 的游戏库文件夹大多数情况下可以在 ~/Library/Application Support/Steam/steamapps/libraryfolders.vdf 找到。
在 Linux 上，Steam 的游戏库文件夹大多数情况下可以在 ~/.local/share/Steam/steamapps/libraryfolders.vdf 找到。
自动定位 CS:GO 的流程现在可以通过遍历 libraryfolders.vdf 中发现所有用户的 Steam 游戏库文件夹。包括 libraryfolders.vdf 自身的位置以及 CS:GO 的安装目录。
如果 Steam 安装在了 D:\Steam 并且 CS:GO 直接安装在了 Steam 的安装目录下，那么 CS:GO 的配置文件路径就会是 "D:\Steam\steamapps\common\Counter-Strike Global Offensive\csgo\cfg"。
为了使你的服务与 Game state integration 一起工作，你需要在 csgo/cfg 目录下新建一个文本文件，并且命名为类似于 gamestate_integration_yourservicenamehere.cfg 这样的名字，CS:GO 将会在下次游戏启动的时候激活你的 Gate State Integration 配置文件。不同的服务可以在 csgo/cfg 目录下放置自己的 Game State Integration 配置文件，然后 CS:GO 游戏客户端将会向所有注册成功了的服务配置发送信息。
举个栗子，我有一个用来示范的服务配置文件将会放在这：
== 示例配置文件 ==
这里有一个示例配置文件，用于跟第三方程序整合玩家自己的游戏操作
以下是一个示例配置文件，通过观察模式进入比赛并且控制舞台灯光的配置文件 gamestate_integration_observerspectator.cfg:
注意： "allplayers_weapons" 载荷在 CS:GO 的 1.35.4.1 更新中可用；推荐仅在局域网的 HTTP 接口中使用本载荷，因为当启动时，载荷信息大小和频率会显著性的增加。
重要： 请确保配置文件中没有 UTF8-BOM，否则配置文件不会被正确加载。你可以在游戏启动后看看开发者控制台是否有成功加载配置文件。
== 部分设置 ==
- uri: 游戏将会发出 HTTP POST 到这个 uri。如果这个接口需要在传输过程中需要加密，那么我们推荐使用 SSL 来保护这个 uri。Steam 客户端将会自动启用 SSL 并且对接口的证书进行验证。
- timeout: 游戏期待 HTTP 接口返回 2XX 状态码 (即 200-299)，并且游戏将不会在服务器正确返回前发送下一个数据。如果 HTTP 服务器迟迟不进行返回，游戏将会考虑请求已经超时，并且在下一次发送数据时重新计算心跳，忽略任何延迟计算。单位为秒
- buffer: 因为非常多的游戏事件会相继发生，因此建议设置一个不为 0 的缓冲时间。当缓冲机制启动后，游戏将会发送一个在这么多秒时间内发生的游戏事件。对于在本地或者局域网中需要集成的程序来说这不是什么问题，你可以根据你的服务来进行调整。你可以设置为 0.0 来禁用缓冲机制，或者使用 0.1 的默认缓冲时间。单位为秒
- throttle: 对于要处理高流量的接口，节流设置可以使得游戏客户端在拿到了 HTTP 服务器的 2XX 状态码后的这么多秒内不发送请求来避免过多的游戏状态通知。如果没有设置节流，默认将会使用 1 秒。单位为秒。
- heartbeat: 即使没有游戏状态的更改，这个设置也会让游戏客户端在收到了之前的 HTTP 服务器 2XX 状态码后的多少秒内发出一个请求。这个服务可以被配置为当没有接收到心跳检测的成功消息一段时间后可以考虑为游戏已经离线或者与整合的第三方服务端开了连接。
== 游戏客户端验证 ==
在本文档提供的两个示例配置文件中都包含了一个可选的 "auth" 选项。在大多数的 localhost 或者局域网的整合场景中，这部分可以被完全忽略。当这个字段出现在请求载荷中，服务器可以对其进行游戏客户端的有效载荷验证。当然，也建议接口采用 SSL 来保护传输中的所有有效载荷
== 浮点数精度 ==
在本文档中的一份示例配置文件包含了一个可选的 "output" 选项。这个选项将会控制某些会输出浮点数的字段输出多少位小数。
"precision_time" 控制所有持续时间信息 (例如 倒计时, 存活时间, 效果时间, 等等)
"precision_position" 控制所有位置信息 (例如 玩家位置, 手榴弹位置, 等等)
"precision_vector" 控制所有矢量信息 (例如 速度, 前进量, 等等)
== 游戏状态组件 ==
所有的游戏状态都被区分为了独立的组件以便可以独立订阅不同的事件。举个例子，一个以射出的每颗子弹为基础的触觉反馈服务整合可以订阅 "player_weapons" 来跟踪武器和弹药数据。用来在直播或者视频服务中来进行演示本场比赛的信息，可以订阅 "map" 来跟踪本场匹配的地图名字和所有回合胜利的阵营。
当你开发服务时，建议使用下面提供的一个使用 NodeJS 编写的控制台应用程序来调试输出游戏发送的 JSON 数据。并且删除服务中没有必要的组件。
当任何被订阅的组件中的数据发生变化时。游戏客户端将会启动缓冲器，当缓冲器到时间或者禁用后，将会生成并发送新的游戏内状态。并且从最后一次 HTTP 服务器发送的 2XX 状态码来计算出 Delta 状态。对于上一次 HTTP POST 请求中的有效载荷，"previously" 这个键将会包含本次更变状态的前一个状态。对于出现的新数据，将会包含在 "added" 中，它将会包含上一次有效载荷中没有出现的数据。
== 示例 HTTP POST 服务器 ==
在这提供了一个使用 NodeJS 框架的简单的 HTTP POST 服务器的实现，可以将游戏的有效载荷打印到控制台中。你可以将下方的 JavaScript 脚本保存为 mysample.js 并且执行 mysample.js 来在你本地的 3000 端口启动 HTTP POST 服务器
"use strict";
const http = require("http");
const port = 3000;
const host = "127.0.0.1";
const server = http.createServer(function (req, res) {
 if (req.method == "POST") {
 console.log("Handling POST request...");
 res.writeHead(200, { "Content-Type": "text/html" });
 let body = "";
 req.on("data", function (data) {
 body += data;
 });
 req.on("end", function () {
 console.log("POST payload: " + body);
 res.end("");
 });
 } else {
 console.log("Not expecting other request types...");
 res.writeHead(200, { "Content-Type": "text/html" });
 let html =
 "<html><body>HTTP Server at http://" +
 host +
 ":" +
 port +
 "</body></html>";
 res.end(html);
 }
});
server.listen(port, host);
console.log("Listening at http://" + host + ":" + port);
== 游戏状态整合组件列表 ==
于 2018 年 8 月 18 日检查
"map_round_wins" "1" // 回合获胜历史
"map" "1" // 游戏模式, 地图, 阶段, 阵营分数
"player_id" "1" // Steam ID
"player_match_stats" "1" // 本次匹配比赛的玩家状态
"player_state" "1" // 弹药, 闪光弹, 装备价值, 生命值 等
"player_weapons" "1" // 一系列的玩家武器数据
"provider" "1" // 游戏基本数据
"round" "1" // 回合阶段和获胜阵营数据
// 以下数据必须在旁观或者观察模式才会输出
"allgrenades" "1" // 手榴弹效果时间, 爆炸时间, 拥有者, 位置, 类型, 速度
"allplayers_id" "1" // 所有玩家的 Steam ID
"allplayers_match_stats" "1" // 所有玩家在本次匹配比赛中的所有状态
"allplayers_position" "1" // 所有玩家的位置坐标
"allplayers_state" "1" // 所有玩家在本回合的状态
"allplayers_weapons" "1" // 所有玩家的武器信息
"bomb" "1" // C4 炸弹位置，携带玩家信息，以及是否有 C4 炸药掉落
"phase_countdowns" "1" // 本轮剩余时间
"player_position" "1" // 被观测玩家的的前进方向
== 第三方库 ==
下方是一个列表，包含了一些可用的第三方对于 Game State Integration 的实现或者工具。你可以用它们来轻松的实现游戏状态整合。但是请注意，这些库/服务并没有 Valve 官方批准或者认可。
=== C# ===
* https://github.com/rakijah/CSGSI
* https://github.com/kernelle/CSGSI
=== Java ===
* https://github.com/koczadly/csgo-gsi
* https://github.com/Brekcel/CSGOState
=== TypeScript ===
* https://github.com/Morabotti/csgo-gsi-types
=== NodeJS/JavaScript ===
* https://github.com/ShaunLWM/node-csgo-gsi
* https://github.com/schroffl/cs-gamestate
* https://github.com/tsuriga/csgo-gsi-events
* https://github.com/glenndehaan/csgo-gamestate
=== Python ===
* https://github.com/Erlendeikeland/csgo-gsi-python
* https://github.com/mdhedelund/CSGO-GSI
=== Go ===
* https://github.com/dank/go-csgsi
=== Tools ===
* https://github.com/tverghis/gsiconfig-cli
== 其他参考 ==
* Official site
* Wikipedia:Counter-Strike:_Global_Offensive