├── .gitignore ├── LICENSE ├── README.md ├── example └── index.js ├── gamestate_integration_node.cfg ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | payload.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Shaun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-csgo-gsi 2 | 3 | [![npm](https://img.shields.io/npm/dt/node-csgo-gsi.svg)](https://github.com/ShaunLWM/node-csgo-gsi/releases) 4 | [![npm](https://img.shields.io/npm/v/node-csgo-gsi.svg)](https://www.npmjs.com/package/node-csgo-gsi) 5 | 6 | Counter-Strike: Global Offensive [Game State Integration](https://developer.valvesoftware.com/wiki/Counter-Strike:_Global_Offensive_Game_State_Integration) for node.js. 7 | 8 | ## Usage 9 | 10 | Install `gamestate_integration_node.cfg` into your CS:GO cfg directory. 11 | 12 | Example: `C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\csgo\cfg\gamestate_integration_node.cfg` 13 | 14 | This file will automatically be executed on client start. Look into the console to check if it has executed successfully. As per the [documentation](https://developer.valvesoftware.com/wiki/Counter-Strike:_Global_Offensive_Game_State_Integration#Locating_CS:GO_Install_Directory), the file name should start with `gamestate_integration_` and ends with `.cfg`. 15 | 16 | ``` 17 | gamestate_integration_yourservicenamehere.cfg 18 | ``` 19 | 20 | The console should have an output like this the moment you open CS:GO 21 | 22 | ``` 23 | Loading Game State Integration: gamestate_integration_node.cfg 24 | ``` 25 | 26 | ## Installation 27 | `npm install node-csgo-gsi --save` 28 | 29 | or 30 | 31 | ```yarn add node-csgo-gsi``` 32 | 33 | ``` js 34 | const CSGOGSI = require("node-csgo-gsi"); 35 | let gsi = new CSGOGSI({ options }); 36 | gsi.on("EVENT_NAME", function("optional data") { 37 | // EVENT_NAME is the individual event name below. Look into the example/index.js for more information 38 | }); 39 | ``` 40 | 41 | 42 | ## Example 43 | 44 | A sample script is in the `example` folder. 45 | 46 | 47 | ## Options 48 | - ```port``` - Set the server port (default: 3000) 49 | - ``` authToken``` - An array of authentication token to accept from client. You can have multiple tokens (default: [] - no authentication needed) 50 | - Example: Team 1 can have "token1". Team 2 can have "token2". Team1's sub team can have "token1sub" etc. 51 | 52 | # Events 53 | 54 | #### all 55 | - Returns the full posted data (use this if you do not want to use the given events provided below) 56 | 57 | ## Game 58 | 59 | #### gameMap (returns String) 60 | - Returns current map. 61 | 62 | #### gamePhase (returns String) 63 | - Returns current game state. 64 | - `live` 65 | - `warmup` 66 | 67 | #### gameRounds (returns Integer) 68 | - Returns the current round number. 69 | - eg: 10 70 | 71 | #### gameCTscore (returns Object) 72 | - Returns the current Counter Terrorist team's score. 73 | ``` 74 | "score": int, 75 | "consecutive_round_losses": int, 76 | "timeouts_remaining": int, 77 | "matches_won_this_series": int 78 | ``` 79 | 80 | #### gameTscore (returns Object) 81 | - Returns the current Terrorist team's score. 82 | ``` 83 | "score": int, 84 | "consecutive_round_losses": int, 85 | "timeouts_remaining": int, 86 | "matches_won_this_series": int 87 | ``` 88 | 89 | ## Round 90 | #### roundPhase (returns String) 91 | - Returns the current round state. 92 | - `live` 93 | - `freezetime` 94 | - `over` 95 | 96 | #### roundWinTeam (returns String) 97 | - Returns the latest round's winner. 98 | - `CT` 99 | - `T` 100 | 101 | #### roundWins (returns Object) 102 | - Returns a key-value object of round number and reason 103 | Example 104 | ``` 105 | { 106 | "1": "ct_win_elimination", 107 | "2": "ct_win_time", 108 | "3": "ct_win_elimination", 109 | "4": "ct_win_time", 110 | "5": "t_win_bomb" 111 | } 112 | ``` 113 | 114 | ## C4 115 | 116 | #### bombState (returns String) 117 | - Returns C4 state. 118 | - `planted` 119 | - `exploded` 120 | - `defused` 121 | 122 | #### bombTimeStart (returns Float) 123 | - Returns when C4 is planted. 124 | 125 | #### bombTimeLeft (returns Float) 126 | - Returns planted C4 time left. 127 | 128 | ## WARNING ⚠️ 129 | C4 timer (`bombTimeLeft` event) is not accurate. CS:GO does not return a real time update when the bomb is planted. It's just an estimation on the library's side. Thus, do just look for the `bombState` event instead of relying on the timer. 130 | 131 | ## Todo 132 | - None for now. Open an issue! 133 | 134 | ## Credits 135 | - [Double0negative/CSGO-HUD](https://github.com/Double0negative/CSGO-HUD) 136 | 137 | 138 | # License 139 | 140 | The MIT License (MIT) 141 | 142 | Copyright (c) 2021 Shaun -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const CSGOGSI = require("../index"); // const CSGOGSI = require("node-csgo-gsi"); 3 | 4 | let gsi = new CSGOGSI({ 5 | port: 3000, 6 | authToken: ["Q79v5tcxVQ8u", "Team2Token", "Team2SubToken"] // this must match the cfg auth token 7 | }); 8 | 9 | gsi.on("all", function (data) { 10 | fs.appendFileSync("./payload.txt", JSON.stringify(data, null, 2)); 11 | }); 12 | 13 | gsi.on("gameMap", (map) => console.log(`gameMap: ${map}`)); 14 | gsi.on("gamePhase", (phase) => console.log(`gamePhase: ${phase}`)); 15 | gsi.on("gameRounds", (rounds) => console.log(`gameRounds: ${rounds}`)); 16 | gsi.on("gameCTscore", (score) => console.log(`gameCTscore: ${score}`)); 17 | gsi.on("gameTscore", (score) => console.log(`gameTscore: ${score}`)); 18 | gsi.on("roundWins", (wins) => console.log(`roundWins: ${roundWins}`)); 19 | gsi.on("player", (player) => console.log(`player: ${player}`)); 20 | gsi.on("roundPhase", (phase) => console.log(`roundPhase: ${phase}`)); 21 | gsi.on("roundWinTeam", (team) => console.log(`roundWinTeam HUAT AH: ${team}`)); 22 | gsi.on("bombState", (state) => console.log(`bombState: ${state}`)); 23 | gsi.on("bombTimeStart", (time) => console.log(`bombTimeStart: ${time}`)); 24 | gsi.on("bombExploded", () => console.log(`bombExploded`)); 25 | gsi.on("bombTimeLeft", (time) => console.log(`bombTimeLeft: ${time}`)); -------------------------------------------------------------------------------- /gamestate_integration_node.cfg: -------------------------------------------------------------------------------- 1 | "node-csgo-gsi" 2 | { 3 | "uri" "http://127.0.0.1:3000" 4 | "timeout" "5.0" 5 | "buffer" "0.1" 6 | "throttle" "0.1" 7 | "heartbeat" "30.0" 8 | "auth" 9 | { 10 | "token" "Q79v5tcxVQ8u" 11 | } 12 | "data" 13 | { 14 | "provider" "1" // info about the game providing info 15 | "map" "1" // mode, map, phase, team scores 16 | "round" "1" // round phase and the winning team 17 | "player_id" "1" // steamid 18 | "player_state" "1" // armor, flashed, equip_value, health, etc. 19 | "map_round_wins" "1" // history of round wins 20 | "player_match_stats" "1" // scoreboard info 21 | "player_weapons" "1" // list of player weapons and weapon state 22 | 23 | "allplayers_id" "1" // the steam id of each player 24 | "allplayers_state" "1" // the player_state for each player 25 | "allplayers_match_stats" "1" // the scoreboard info for each player 26 | "allplayers_weapons" "1" // the player_weapons for each player 27 | "allplayers_position" "1" // player_position but for each player 28 | "phase_countdowns" "1" // time remaining in tenths of a second, which phase 29 | "allgrenades" "1" // grenade effecttime, lifetime, owner, position, type, velocity 30 | "bomb" "1" // location of the bomb, who's carrying it, dropped or not 31 | "player_position" "1" // forward direction, position for currently spectated player 32 | } 33 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const http = require("http"); 2 | const EventEmitter = require("events"); 3 | 4 | class CSGOGSI extends EventEmitter { 5 | constructor({ port = 3000, authToken = [] }) { 6 | super(); 7 | let tokens = authToken; 8 | if (!Array.isArray(tokens)) { 9 | tokens = []; 10 | } 11 | 12 | this.authToken = tokens; 13 | this.app = http.createServer((req, res) => { 14 | if (req.method !== "POST") { 15 | res.writeHead(404, { "Content-Type": "text/plain" }); 16 | return res.end("404 Not Found"); 17 | } 18 | 19 | let body = ""; 20 | req.on("data", data => { 21 | body += data; 22 | }); 23 | 24 | req.on("end", () => { 25 | this.processJson(body); 26 | return res.writeHead(200).end(); 27 | }); 28 | }); 29 | 30 | this.bombTime = 40; 31 | this.isBombPlanted = false; 32 | this.bombTimer = null; 33 | this.server = this.app.listen({ port }, () => { 34 | let addr = this.server.address(); 35 | console.log(`[@] CSGO GSI server listening on ${addr.address}:${addr.port}`); 36 | }); 37 | } 38 | 39 | processJson(json) { 40 | try { 41 | let data = JSON.parse(json); 42 | if (!this.isAuthenticated(data)) return; 43 | this.emit("all", data); 44 | this.process(data); 45 | } catch (error) { } 46 | } 47 | 48 | isAuthenticated(data) { 49 | return this.authToken.length < 1 || (data["auth"]["token"] && this.authToken.length > 0 && this.authToken.includes(data["auth"]["token"])) 50 | } 51 | 52 | process(data) { 53 | if (data["map"]) { 54 | this.emit("gameMap", data["map"]["name"]); 55 | this.emit("gamePhase", data["map"]["phase"]); //warmup etc 56 | this.emit("gameRounds", data["map"]["round"]); 57 | this.emit("gameCTscore", data["map"]["team_ct"]); 58 | this.emit("gameTscore", data["map"]["team_t"]); 59 | } 60 | 61 | if (data["round_wins"]) { 62 | this.emit("roundWins", data["round_wins"]); 63 | } 64 | 65 | if (data["player"]) { 66 | this.emit("player", data["player"]); 67 | } 68 | 69 | if (data["round"]) { 70 | this.emit("roundPhase", data["round"]["phase"]); 71 | switch (data["round"]["phase"]) { 72 | case "live": 73 | break; 74 | case "freezetime": 75 | break; 76 | case "over": 77 | if (this.isBombPlanted) { 78 | this.isBombPlanted = false; 79 | this.stopC4Countdown(); 80 | } 81 | 82 | this.emit("roundWinTeam", data["round"]["win_team"]); 83 | break; 84 | } 85 | 86 | if (data["round"]["bomb"]) { 87 | this.emit("bombState", data["round"]["bomb"]); 88 | switch (data["round"]["bomb"]) { 89 | case "planted": 90 | if (!this.isBombPlanted) { 91 | this.isBombPlanted = true; 92 | let timeleft = this.bombTime - (new Date().getTime() / 1000 - data["provider"]["timestamp"]); 93 | this.emit("bombTimeStart", timeleft); 94 | this.startC4Countdown(timeleft); 95 | } 96 | 97 | break; 98 | case "defused": 99 | case "exploded": 100 | this.isBombPlanted = false; 101 | this.stopC4Countdown(); 102 | break; 103 | } 104 | } 105 | 106 | } 107 | } 108 | 109 | stopC4Countdown() { 110 | if (this.bombTimer) clearInterval(this.bombTimer); 111 | } 112 | 113 | startC4Countdown(time) { 114 | this.bombTimer = setInterval(() => { 115 | time = time - 1; 116 | if (time <= 0) { 117 | this.stopC4Countdown() 118 | this.isBombPlanted = false; 119 | return this.emit("bombExploded"); 120 | } 121 | 122 | this.emit("bombTimeLeft", time); 123 | }, 1000); 124 | } 125 | } 126 | 127 | module.exports = CSGOGSI; 128 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-csgo-gsi", 3 | "version": "0.0.5", 4 | "description": "Counter-Strike: Global Offensive Game State Integration for nodejs. https://developer.valvesoftware.com/wiki/Counter-Strike:_Global_Offensive_Game_State_Integration", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/ShaunLWM/node-csgo-gsi.git" 9 | }, 10 | "keywords": [ 11 | "steam", 12 | "csgo", 13 | "csgo-gsi", 14 | "steam-csgo", 15 | "hlds" 16 | ], 17 | "author": "Shaun", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/ShaunLWM/node-csgo-gsi/issues" 21 | }, 22 | "homepage": "https://github.com/ShaunLWM/node-csgo-gsi#readme" 23 | } 24 | --------------------------------------------------------------------------------