diff --git a/OfficialMaps.json b/OfficialMaps.json index 71956c4..7c76b85 100644 --- a/OfficialMaps.json +++ b/OfficialMaps.json @@ -1,45 +1,37 @@ -{ - "ar_baggage": 125440026, - "ar_monastery": 125440154, - "ar_shoots": 125440261, - "cs_agency": 1464919827, - "cs_assault": 125432575, - "de_boyard": 2926953717, - "de_chalice": 2926952684, - "cs_climb": 2537983994, - "cs_insertion2": 2650330155, - "cs_italy": 125436057, - "cs_militia": 133256570, - "cs_office": 125444404, - "de_ancient": 2627571649, - "de_anubis": 1984883124, - "de_crete": 1220681096, - "de_bank": 125440342, - "de_basalt": 2627569615, - "de_blagai": 2791116183, - "de_breach": 1258599704, - "de_cache": 2606407435, - "de_canals": 951287718, - "de_cbble": 205239595, - "de_dust2": 125438255, - "de_extraction": 2650340943, - "de_hive": 2539316567, - "de_inferno": 125438669, - "de_iris": 1591780701, - "de_lake": 125440557, - "de_mirage": 152508932, - "de_nuke": 125439125, - "de_overpass": 205240106, - "de_prime": 2831565855, - "de_ravine": 2615546425, - "de_safehouse": 125440714, - "de_shortnuke": 2131550446, - "de_stmarc": 125441004, - "de_sugarcane": 125440847, - "de_train": 125438372, - "de_tuscan": 2458920550, - "de_vertigo": 125439851, - "dz_ember": 2681770529, - "dz_vineyard": 2587298130, - "gd_cbble": 782012846 -} \ No newline at end of file +[{ + "name": "ar_baggage", + "id": 125440026 +},{ + "name": "ar_shoots", + "id" : 125440261 +},{ + "name": "cs_italy", + "id": 125436057 +},{ + "name": "cs_office", + "id": 125444404 +},{ + "name": "de_ancient", + "id": 2627571649 +},{ + "name": "de_anubis", + "id": 1984883124 +},{ + "name": "de_dust2", + "id": 125438255 +},{ + "name": "de_inferno", + "id": 125438669 +},{ + "name": "de_mirage", + "id": 152508932 +},{ + "name": "de_nuke", + "id": 125439125 +},{ + "name": "de_overpass", + "id": 205240106 +},{ + "name": "de_vertigo", + "id": 125439851 +}] diff --git a/modules/apiV10.js b/modules/apiV10.js index 28e2721..065a274 100755 --- a/modules/apiV10.js +++ b/modules/apiV10.js @@ -294,7 +294,7 @@ router.get('/control/start', (req, res) => { } let commandLine = `${cfg.serverCommandline} +map ${startMap}`; logger.info(commandLine); - let serverProcess = exec(commandLine, (error, stdout, stderr) => { + exec(commandLine, (error, stdout, stderr) => { if (error) { // node couldn't execute the command. res.status(501).json({ "error": error.code }); @@ -443,12 +443,17 @@ router.get('/control/stop', (req, res) => { controlEmitter.emit('exec', 'stop', 'start'); logger.verbose("sending quit."); sf.executeRcon('quit').then((answer) => { - // CHostStateMgr::QueueNewRequest( Quitting, 8 ) - // TODO: find out if command quit can fail. - serverInfo.serverState.serverRunning = false; - serverInfo.serverState.authenticated = false; - serverInfo.reset(); - res.json({ "success": true }); + if (answer.indexOf("CHostStateMgr::QueueNewRequest( Quitting") != -1) { + // CHostStateMgr::QueueNewRequest( Quitting, 8 ) + // TODO: find out if command quit can fail. + serverInfo.serverState.serverRunning = false; + serverInfo.serverState.authenticated = false; + serverInfo.reset(); + res.json({ "success": true }); + } else { + res.status(501).json({ "error": `RCON response not correct.` }); + logger.warn("Stopping the server failed - rcon command not successful"); + } controlEmitter.emit('exec', 'stop', 'end'); }).catch((err) => { logger.error('Stopping server Failed: ' + err); @@ -484,12 +489,13 @@ router.get('/control/stop', (req, res) => { router.get('/control/kill', (req, res) => { exec('/bin/ps -A |grep cs2', (error, stdout, stderr) => { if (error) { - logger.error(`exec error: ${error}`); + logger.error(`exec error: ${error}, ${stderr}`); res.status(501).json({ "error": "Could not find csgo server process" }); } else if (stdout.match(/cs2/) != null) { let pid = stdout.split(/\s+/)[1]; exec(`/bin/kill ${pid}`, (error, stdout, stderr) => { if (error) { + logger.warn(`Server process could not be killed: ${error}: ${stderr}`); res.status(501).json({ "error": "Could not kill csgo server process" }); } else { // reset API-State @@ -563,7 +569,7 @@ router.get('/control/update', (req, res) => { res.json(`{ "success": true }`); updateProcess.once('close', (code) => { if (!updateSuccess) { - logger.warn('Update exited without success.'); + logger.warn(`Update exited without success. Exit code: ${code}`); controlEmitter.emit('progress', 'Update failed!', 100); controlEmitter.emit('exec', 'update', 'fail'); } @@ -573,7 +579,7 @@ router.get('/control/update', (req, res) => { if (updateSuccess) { res.json({ "success": true }); } else { - logger.warn('Update exited without success.'); + logger.warn(`Update exited without success. Exit code: ${code}`); res.status(501).json({ "error": "Update was not successful" }); } controlEmitter.emit('exec', 'update', 'end'); diff --git a/modules/configClass.js b/modules/configClass.js index 4749f1d..899fdfb 100755 --- a/modules/configClass.js +++ b/modules/configClass.js @@ -2,146 +2,150 @@ * Config class for CSGO Server API */ class config { - constructor() { - this._userOptions = require('../config.js'); + #userOptions = require('../config.js'); + #screenCommand; + #csgoCommand; + #serverTokenCommand; + #localIp; - this._screenCommand = `${this._userOptions.screen} -L -Logfile ${this._userOptions.screenLog} -dmS ${this._userOptions.screenName}`; - this._csgoCommand = `${this._userOptions.csgoDir}game/bin/linuxsteamrt64/cs2 -dedicated`; - this._serverTokenCommand = `+sv_setsteamaccount ${this._userOptions.serverToken}`; - this._localIp = ''; + constructor() { + this.#screenCommand = `${this.#userOptions.screen} -L -Logfile ${this.#userOptions.screenLog} -dmS ${this.#userOptions.screenName}`; + this.#csgoCommand = `${this.#userOptions.csgoDir}game/bin/linuxsteamrt64/cs2 -dedicated`; + this.#serverTokenCommand = `+sv_setsteamaccount ${this.#userOptions.serverToken}`; + this.#localIp = ''; } - get _csgoArgs() { - return `-console -usercon -ip 0.0.0.0 +sv_logfile 1 -serverlogging +logaddress_add_http "http://${this._localIp}:${this.logPort}/log" ${this._userOptions.csgoOptionalArgs}`; + get #csgoArgs() { + return `-console -usercon -ip 0.0.0.0 +sv_logfile 1 -serverlogging +logaddress_add_http "http://${this.#localIp}:${this.#userOptions.logPort}/log" ${this.#userOptions.csgoOptionalArgs}`; } get apiToken() { - return this._userOptions.apiToken; + return this.#userOptions.apiToken; } get rconPass() { - return this._userOptions.rconPass; + return this.#userOptions.rconPass; } get admins() { - return this._userOptions.admins; + return this.#userOptions.admins; } get workshopCollection() { - return this._userOptions.workshopCollection; + return this.#userOptions.workshopCollection; } set workshopCollection(id) { - this._userOptions.workshopCollection = id; + this.#userOptions.workshopCollection = id; } get workshopMaps() { - return this._userOptions.workshopMaps; + return this.#userOptions.workshopMaps; } set workshopMaps(maps) { - this._userOptions.workshopMaps = maps; + this.#userOptions.workshopMaps = maps; } get redirectPage() { - if (this._userOptions.redirectPage) { - return this._userOptions.redirectPage; + if (this.#userOptions.redirectPage) { + return this.#userOptions.redirectPage; } else { return ('/gameserver.htm'); } } get loginValidity() { - return this._userOptions.loginValidity * 60000; + return this.#userOptions.loginValidity * 60000; } get httpAuth() { - return this._userOptions.httpAuth; + return this.#userOptions.httpAuth; } get httpUser() { - return this._userOptions.httpUser; + return this.#userOptions.httpUser; } get iface() { - return this._userOptions.iface; + return this.#userOptions.iface; } get localIp() { - return this._localIp; + return this.#localIp; } set localIp(ip) { - this._localIp = ip; + this.#localIp = ip; } get host() { - if (this._userOptions.host != '') { - return this._userOptions.host; + if (this.#userOptions.host != '' && this.#userOptions.useHttps) { + return this.#userOptions.host; } else { - return this._localIp + return this.#localIp } } get apiPort() { - return this._userOptions.apiPort; + return this.#userOptions.apiPort; } get socketPort() { - return this._userOptions.socketPort; + return this.#userOptions.socketPort; } get logPort() { - return this._userOptions.logPort; + return this.#userOptions.logPort; } get serverCommandline() { - let command = `${this._screenCommand} ${this._csgoCommand} ${this._csgoArgs}`; + let command = `${this.#screenCommand} ${this.#csgoCommand} ${this.#csgoArgs}`; if (this._csgoToken != '') { - command = `${command} ${this._serverTokenCommand}`; + command = `${command} ${this.#serverTokenCommand}`; } return command; } get steamCommand() { - return this._userOptions.steamExe + return this.#userOptions.steamExe } get updateScript() { - if (this._userOptions.updateScript != ''){ - return this._userOptions.updateScript; + if (this.#userOptions.updateScript != ''){ + return this.#userOptions.updateScript; } else { - return `${this._userOptions.csgoDir}update_cs2.txt`; + return `${this.#userOptions.csgoDir}update_cs2.txt`; } } get webSockets() { - return this._userOptions.webSockets; + return this.#userOptions.webSockets; } get useHttps() { - return this._userOptions.useHttps; + return this.#userOptions.useHttps; } get scheme() { - return (this._userOptions.useHttps ? 'https' : 'http'); + return (this.#userOptions.useHttps ? 'https' : 'http'); } get httpsCertificate() { - return this._userOptions.httpsCertificate; + return this.#userOptions.httpsCertificate; } get httpsPrivateKey() { - return this._userOptions.httpsPrivateKey; + return this.#userOptions.httpsPrivateKey; } get httpsCa() { - return this._userOptions.httpsCa; + return this.#userOptions.httpsCa; } get corsOrigin() { - return this._userOptions.corsOrigin; + return this.#userOptions.corsOrigin; } get sessionSecret() { - return this._userOptions.sessionSecret; + return this.#userOptions.sessionSecret; } script(type) { - return this._userOptions[`${type}Script`]; + return this.#userOptions[`${type}Script`]; } get logFile() { - return this._userOptions.logFile; + return this.#userOptions.logFile; } get logLevel() { - return this._userOptions.logLevel; + return this.#userOptions.logLevel; } get logDays() { - return this._userOptions.logDays; + return this.#userOptions.logDays; } -}; +} module.exports = new config(); \ No newline at end of file diff --git a/modules/logreceive.js b/modules/logreceive.js index d83ad52..b50782b 100755 --- a/modules/logreceive.js +++ b/modules/logreceive.js @@ -31,7 +31,7 @@ router.post('/log', (req, res) => { } else if (line.indexOf('Loading map ') != -1) { // Start of map. // L 10/13/2023 - 14:28:38: Loading map "de_anubis" - let rex = /Loading map \"(\S+)\"/g; + let rex = /Loading map "(\S+)"/g; let matches = rex.exec(line); let mapstring = matches[1]; mapstring = sf.cutMapName(mapstring); @@ -65,11 +65,11 @@ router.post('/log', (req, res) => { if (cfg.script('roundStart') != '') { exec(cfg.script('roundStart')); } - } else if (/Team \"\S+\" scored/.test(line)) { + } else if (/Team "\S+" scored/.test(line)) { // Team scores at end of round. // L 02/10/2019 - 21:31:15: Team "CT" scored "1" with "2" players // L 02/10/2019 - 21:31:15: Team "TERRORIST" scored "1" with "2" players - rex = /Team \"(\S)\S+\" scored \"(\d+)\"/g; + let rex = /Team "(\S)\S+" scored "(\d+)"/g; let matches = rex.exec(line); serverInfo.score = matches; } else if (line.indexOf('World triggered "Round_End"') != -1) { @@ -84,25 +84,25 @@ router.post('/log', (req, res) => { if (cfg.script('matchEnd') != '') { exec(cfg.script('matchEnd')); } - } else if (/\".{1,32}<\d{1,3}><\[\w:\d:\d{1,10}\]>/.test(line)) { + } else if (/".{1,32}<\d{1,3}><\[\w:\d:\d{1,10}\]>/.test(line)) { // Player join or teamchange. // 10/12/2023 - 16:06:38: "[Klosser] Taraman<2><[U:1:12610374]><>" entered the game // 10/12/2023 - 18:57:47: "[Klosser] Taraman<2><[U:1:12610374]>" switched from team to // 10/12/2023 - 18:59:25: "[Klosser] Taraman<2><[U:1:12610374]>" switched from team to // 10/16/2023 - 16:31:59.699 - "[Klosser] Taraman<2><[U:1:12610374]>" disconnected (reason "NETWORK_DISCONNECT_DISCONNECT_BY_USER") // "Strapper<6>" - let rex = /\"(.{1,32})<\d{1,3}><\[(\w:\d:\d{1,10})\]><\[(\w:\d:\d{1,10})\]><\[(\w:\d:\d{1,10})\]>\" switched from team <\S{1,10}> to <(\S{1,10})>/g; + rex = /"(.{1,32})<\d{1,3}><\[(\w:\d:\d{1,10})\]>" switched from team <\S{1,10}> to <(\S{1,10})>/g; matches = rex.exec(line); serverInfo.assignPlayer(matches[1], matches[2], matches[3]); - } else if (line.search(/\[\w:\d:\d{1,10}\]><\w{1,10}>\" \[.{1,5} .{1,5} .{1,5}\] killed \".{1,32}<\d{1,3}><\[\w:\d:\d{1,10}\]/) != -1) { - rex = /\[(\w:\d:\d{1,10})\]><\w{1,10}>\" \[.{1,5} .{1,5} .{1,5}\] killed \".{1,32}<\d{1,3}><\[(\w:\d:\d{1,10})\]/ + } else if (line.search(/\[\w:\d:\d{1,10}\]><\w{1,10}>" \[.{1,5} .{1,5} .{1,5}\] killed ".{1,32}<\d{1,3}><\[\w:\d:\d{1,10}\]/) != -1) { + rex = /\[(\w:\d:\d{1,10})\]><\w{1,10}>" \[.{1,5} .{1,5} .{1,5}\] killed ".{1,32}<\d{1,3}><\[(\w:\d:\d{1,10})\]/ matches = rex.exec(line); serverInfo.recordKill(matches[1], matches[2]); } diff --git a/modules/rcon-srcds/LICENSE b/modules/rcon-srcds/LICENSE index 786d1c5..d3216cf 100644 --- a/modules/rcon-srcds/LICENSE +++ b/modules/rcon-srcds/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2018 Enrique Carpintero - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2018 Enrique Carpintero + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/modules/rcon-srcds/packet.js b/modules/rcon-srcds/packet.js index dee157c..abb4335 100644 --- a/modules/rcon-srcds/packet.js +++ b/modules/rcon-srcds/packet.js @@ -1,37 +1,37 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.decode = exports.encode = void 0; -/** - * Encode data to packet buffer - * @param type Packet Type - * @param id Packet ID - * @param body Packet body (payload) - * @param encoding Body encoding - * @returns Encoded packet buffer - */ -exports.encode = function (type, id, body, encoding) { - if (encoding === void 0) { encoding = 'ascii'; } - var size = Buffer.byteLength(body) + 14; // body size + 10 + 4 (Null) - var buffer = Buffer.alloc(size); - buffer.writeInt32LE(size - 4, 0); - buffer.writeInt32LE(id, 4); - buffer.writeInt32LE(type, 8); - buffer.write(body, 12, size - 2, encoding); - buffer.writeInt16LE(0, size - 2); - return buffer; -}; -/** - * Decodes packet buffer to data - * @param buf Buffer to decode - * @param encoding Body encoding - * @returns Decoded packet object - */ -exports.decode = function (buf, encoding) { - if (encoding === void 0) { encoding = 'ascii'; } - return { - size: buf.readInt32LE(0), - id: buf.readInt32LE(4), - type: buf.readInt32LE(8), - body: buf.toString(encoding, 12, buf.byteLength - 2) - }; -}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decode = exports.encode = void 0; +/** + * Encode data to packet buffer + * @param type Packet Type + * @param id Packet ID + * @param body Packet body (payload) + * @param encoding Body encoding + * @returns Encoded packet buffer + */ +exports.encode = function (type, id, body, encoding) { + if (encoding === void 0) { encoding = 'ascii'; } + var size = Buffer.byteLength(body) + 14; // body size + 10 + 4 (Null) + var buffer = Buffer.alloc(size); + buffer.writeInt32LE(size - 4, 0); + buffer.writeInt32LE(id, 4); + buffer.writeInt32LE(type, 8); + buffer.write(body, 12, size - 2, encoding); + buffer.writeInt16LE(0, size - 2); + return buffer; +}; +/** + * Decodes packet buffer to data + * @param buf Buffer to decode + * @param encoding Body encoding + * @returns Decoded packet object + */ +exports.decode = function (buf, encoding) { + if (encoding === void 0) { encoding = 'ascii'; } + return { + size: buf.readInt32LE(0), + id: buf.readInt32LE(4), + type: buf.readInt32LE(8), + body: buf.toString(encoding, 12, buf.byteLength - 2) + }; +}; diff --git a/modules/rcon-srcds/protocol.js b/modules/rcon-srcds/protocol.js index d0e8d67..47cfd21 100644 --- a/modules/rcon-srcds/protocol.js +++ b/modules/rcon-srcds/protocol.js @@ -1,17 +1,17 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Packet Types - * Reference: https://developer.valvesoftware.com/wiki/Source_RCON#Requests_and_Responses - * - * @readonly - */ -var protocol = Object.freeze({ - SERVERDATA_AUTH: 0x03, - SERVERDATA_EXECCOMMAND: 0x02, - SERVERDATA_AUTH_RESPONSE: 0x02, - SERVERDATA_RESPONSE_VALUE: 0x00, - ID_AUTH: 0x999, - ID_REQUEST: 0x123, -}); -exports.default = protocol; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Packet Types + * Reference: https://developer.valvesoftware.com/wiki/Source_RCON#Requests_and_Responses + * + * @readonly + */ +var protocol = Object.freeze({ + SERVERDATA_AUTH: 0x03, + SERVERDATA_EXECCOMMAND: 0x02, + SERVERDATA_AUTH_RESPONSE: 0x02, + SERVERDATA_RESPONSE_VALUE: 0x00, + ID_AUTH: 0x999, + ID_REQUEST: 0x123, +}); +exports.default = protocol; diff --git a/modules/rcon-srcds/rcon.js b/modules/rcon-srcds/rcon.js index 469136c..560c034 100644 --- a/modules/rcon-srcds/rcon.js +++ b/modules/rcon-srcds/rcon.js @@ -1,250 +1,250 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var net_1 = require("net"); -var protocol_1 = __importDefault(require("./protocol")); -var packets = __importStar(require("./packet")); -var RCON = /** @class */ (function () { - /** - * Source RCON (https://developer.valvesoftware.com/wiki/Source_RCON) - * @param options Connection options - */ - function RCON(options) { - this.host = options.host || '127.0.0.1'; - this.port = options.port || 27015; - this.maxPacketSize = options.maxPacketSize || 4096; - this.encoding = options.encoding || 'ascii'; - this.timeout = options.timeout || 1000; - this.authenticated = false; - this.connected = false; - } - /** - * Authenticates the connection - * @param password Password string - */ - RCON.prototype.authenticate = function (password) { - return __awaiter(this, void 0, void 0, function () { - var _this = this; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!!this.connected) return [3 /*break*/, 2]; - return [4 /*yield*/, this.connect()]; - case 1: - _a.sent(); - _a.label = 2; - case 2: return [2 /*return*/, new Promise(function (resolve, reject) { - if (_this.authenticated) { - reject(Error('Already authenticated')); - return; - } - _this.write(protocol_1.default.SERVERDATA_AUTH, protocol_1.default.ID_AUTH, password) - .then(function (data) { - if (data === true) { - _this.authenticated = true; - resolve(true); - } - else { - _this.disconnect(); - reject(Error('Unable to authenticate')); - } - }).catch(reject); - })]; - } - }); - }); - }; - /** - * Executes command on the server - * @param command Command to execute - */ - RCON.prototype.execute = function (command) { - var _this = this; - return new Promise(function (resolve, reject) { - if (!_this.connected) { - reject(Error('Already disconnected. Please reauthenticate.')); - return; - } - var packetId = Math.floor(Math.random() * (256 - 1) + 1); - if (!_this.connection.writable) { - reject(Error('Unable to write to socket')); - return; - } - if (!_this.authenticated) { - reject(Error('Not authorized')); - return; - } - _this.write(protocol_1.default.SERVERDATA_EXECCOMMAND, packetId, command) - .then(resolve) - .catch(reject); - }); - }; - /** - * Creates a connection to the socket - */ - RCON.prototype.connect = function () { - var _this = this; - return new Promise(function (resolve, reject) { - _this.connection = net_1.createConnection({ - host: _this.host, - port: _this.port - }, function () { - if (_this.connection) - _this.connection.removeListener('error', reject); - _this.connected = true; - resolve(); - }); - _this.connection.once('error', reject); - _this.connection.setTimeout(_this.timeout); - }); - }; - /** - * Destroys the socket connection - */ - RCON.prototype.disconnect = function () { - var _this = this; - this.authenticated = false; - this.connected = false; - this.connection.destroy(); - return new Promise(function (resolve, reject) { - var onError = function (e) { - _this.connection.removeListener('close', onClose); - reject(e); - }; - var onClose = function () { - _this.connection.removeListener('error', onError); - resolve(); - }; - _this.connection.once('close', onClose); - _this.connection.once('error', onError); - }); - }; - RCON.prototype.isConnected = function () { - return this.connected; - }; - RCON.prototype.isAuthenticated = function () { - return this.authenticated; - }; - /** - * Writes to socket connection - * @param type Packet Type - * @param id Packet ID - * @param body Packet payload - */ - RCON.prototype.write = function (type, id, body) { - var _this = this; - return new Promise(function (resolve, reject) { - var response = ''; - var decodedPacket = undefined - var onData = function (packet) { - decodedPacket = packets.decode(packet, _this.encoding); - const fs = require('fs'); - // Server will respond twice (0x00 and 0x02) if we send an auth packet (0x03) - // but we need 0x02 to confirm - if (type === protocol_1.default.SERVERDATA_AUTH && decodedPacket.type !== protocol_1.default.SERVERDATA_AUTH_RESPONSE) { - // TODO: It seems, the CS2 server does not send this additional packet. - console.log('Does this still get called anyway?'); - return; - } else if (type === protocol_1.default.SERVERDATA_AUTH && decodedPacket.type === protocol_1.default.SERVERDATA_AUTH_RESPONSE) { - if (decodedPacket.id === protocol_1.default.ID_AUTH) { - resolve(true); - } - else { - resolve(false); - } - _this.connection.removeListener('data', onData); - } else if (id === decodedPacket.id) { - // if decoded packet has the correct id, and a termination string at the end, - // it is a single packet answer - if (packet.indexOf("0000", -2 , "hex") != -1) { - response = decodedPacket.body.replace(/\n\s*$/, ''); // delete trailing empty line - _this.connection.removeListener('data', onData); - resolve(response); - } - } else { - // If the sent packet is not of type SERVERDATA_AUTH - // AND - // The decoded packet ID does not match the sent id, it is the continuation of a multipacket response, - // so we keep reading until a termination string is detected. - response = response.concat(packet.toString(_this.encoding, 0, packet.byteLength - 2)); - if (packet.indexOf("0000", -2 , "hex") != -1) { - _this.connection.removeListener('data', onData); - resolve(response.replace(/\n\s*$/, '')); // delete trailing empty line - } - } - _this.connection.removeListener('error', onError); - }; - var onError = function (e) { - _this.connection.removeListener('data', onData); - reject(e); - }; - var encodedPacket = packets.encode(type, id, body, _this.encoding); - if (_this.maxPacketSize > 0 && encodedPacket.length > _this.maxPacketSize) { - reject(Error('Packet size too big')); - return; - } - _this.connection.on('data', onData); - _this.connection.on('error', onError); - _this.connection.write(encodedPacket); - }); - }; - return RCON; -}()); -exports.default = RCON; +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var net_1 = require("net"); +var protocol_1 = __importDefault(require("./protocol")); +var packets = __importStar(require("./packet")); +var RCON = /** @class */ (function () { + /** + * Source RCON (https://developer.valvesoftware.com/wiki/Source_RCON) + * @param options Connection options + */ + function RCON(options) { + this.host = options.host || '127.0.0.1'; + this.port = options.port || 27015; + this.maxPacketSize = options.maxPacketSize || 4096; + this.encoding = options.encoding || 'ascii'; + this.timeout = options.timeout || 1000; + this.authenticated = false; + this.connected = false; + } + /** + * Authenticates the connection + * @param password Password string + */ + RCON.prototype.authenticate = function (password) { + return __awaiter(this, void 0, void 0, function () { + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!!this.connected) return [3 /*break*/, 2]; + return [4 /*yield*/, this.connect()]; + case 1: + _a.sent(); + _a.label = 2; + case 2: return [2 /*return*/, new Promise(function (resolve, reject) { + if (_this.authenticated) { + reject(Error('Already authenticated')); + return; + } + _this.write(protocol_1.default.SERVERDATA_AUTH, protocol_1.default.ID_AUTH, password) + .then(function (data) { + if (data === true) { + _this.authenticated = true; + resolve(true); + } + else { + _this.disconnect(); + reject(Error('Unable to authenticate')); + } + }).catch(reject); + })]; + } + }); + }); + }; + /** + * Executes command on the server + * @param command Command to execute + */ + RCON.prototype.execute = function (command) { + var _this = this; + return new Promise(function (resolve, reject) { + if (!_this.connected) { + reject(Error('Already disconnected. Please reauthenticate.')); + return; + } + var packetId = Math.floor(Math.random() * (256 - 1) + 1); + if (!_this.connection.writable) { + reject(Error('Unable to write to socket')); + return; + } + if (!_this.authenticated) { + reject(Error('Not authorized')); + return; + } + _this.write(protocol_1.default.SERVERDATA_EXECCOMMAND, packetId, command) + .then(resolve) + .catch(reject); + }); + }; + /** + * Creates a connection to the socket + */ + RCON.prototype.connect = function () { + var _this = this; + return new Promise(function (resolve, reject) { + _this.connection = net_1.createConnection({ + host: _this.host, + port: _this.port + }, function () { + if (_this.connection) + _this.connection.removeListener('error', reject); + _this.connected = true; + resolve(); + }); + _this.connection.once('error', reject); + _this.connection.setTimeout(_this.timeout); + }); + }; + /** + * Destroys the socket connection + */ + RCON.prototype.disconnect = function () { + var _this = this; + this.authenticated = false; + this.connected = false; + this.connection.destroy(); + return new Promise(function (resolve, reject) { + var onError = function (e) { + _this.connection.removeListener('close', onClose); + reject(e); + }; + var onClose = function () { + _this.connection.removeListener('error', onError); + resolve(); + }; + _this.connection.once('close', onClose); + _this.connection.once('error', onError); + }); + }; + RCON.prototype.isConnected = function () { + return this.connected; + }; + RCON.prototype.isAuthenticated = function () { + return this.authenticated; + }; + /** + * Writes to socket connection + * @param type Packet Type + * @param id Packet ID + * @param body Packet payload + */ + RCON.prototype.write = function (type, id, body) { + var _this = this; + return new Promise(function (resolve, reject) { + var response = ''; + var decodedPacket = undefined + var onData = function (packet) { + decodedPacket = packets.decode(packet, _this.encoding); + const fs = require('fs'); + // Server will respond twice (0x00 and 0x02) if we send an auth packet (0x03) + // but we need 0x02 to confirm + if (type === protocol_1.default.SERVERDATA_AUTH && decodedPacket.type !== protocol_1.default.SERVERDATA_AUTH_RESPONSE) { + // TODO: It seems, the CS2 server does not send this additional packet. + console.log('Does this still get called anyway?'); + return; + } else if (type === protocol_1.default.SERVERDATA_AUTH && decodedPacket.type === protocol_1.default.SERVERDATA_AUTH_RESPONSE) { + if (decodedPacket.id === protocol_1.default.ID_AUTH) { + resolve(true); + } + else { + resolve(false); + } + _this.connection.removeListener('data', onData); + } else if (id === decodedPacket.id) { + // if decoded packet has the correct id, and a termination string at the end, + // it is a single packet answer + if (packet.indexOf("0000", -2 , "hex") != -1) { + response = decodedPacket.body.replace(/\n\s*$/, ''); // delete trailing empty line + _this.connection.removeListener('data', onData); + resolve(response); + } + } else { + // If the sent packet is not of type SERVERDATA_AUTH + // AND + // The decoded packet ID does not match the sent id, it is the continuation of a multipacket response, + // so we keep reading until a termination string is detected. + response = response.concat(packet.toString(_this.encoding, 0, packet.byteLength - 2)); + if (packet.indexOf("0000", -2 , "hex") != -1) { + _this.connection.removeListener('data', onData); + resolve(response.replace(/\n\s*$/, '')); // delete trailing empty line + } + } + _this.connection.removeListener('error', onError); + }; + var onError = function (e) { + _this.connection.removeListener('data', onData); + reject(e); + }; + var encodedPacket = packets.encode(type, id, body, _this.encoding); + if (_this.maxPacketSize > 0 && encodedPacket.length > _this.maxPacketSize) { + reject(Error('Packet size too big')); + return; + } + _this.connection.on('data', onData); + _this.connection.on('error', onError); + _this.connection.write(encodedPacket); + }); + }; + return RCON; +}()); +exports.default = RCON; diff --git a/modules/serverInfo.js b/modules/serverInfo.js index cb8ad1b..7c28629 100755 --- a/modules/serverInfo.js +++ b/modules/serverInfo.js @@ -1,7 +1,35 @@ const events = require('events'); +const logger = require('./logger'); class serverInfo { - constructor(options = {}) { + #serverState; + + #map = ''; + #mapsAvail = []; + #mapsDetails = [ + //{ 'name': '', + // 'official': true/false, + // 'title': '', + // 'workshopID': '', + // 'description': '', + // 'previewLink': '', + // 'tags': [{ "tag": "" }] } + ]; + #mapFilterType; + #mapFilters; + #maxRounds = 0 + #pause = false; + + #players = [ + //{ 'name': '', + // 'steamID': '', + // 'team': '', + // 'kills': 0, + // 'deaths': 0 } + ]; + #score; + + constructor() { /** * Stores the state of the controlled server-instance. * @typedef serverState @@ -12,7 +40,7 @@ class serverInfo { */ /** @type {serverState} */ - this._serverState = { + this.#serverState = { 'operationPending': 'none', 'serverRunning': false, 'serverRcon': undefined, @@ -20,32 +48,12 @@ class serverInfo { } // data section - this._map = ''; - this._mapsAvail = [] - this._mapsDetails = [ - //{ 'name': '', - // 'official': true/false, - // 'title': '', - // 'workshopID': '', - // 'description': '', - // 'previewLink': '', - // 'tags': [{ "tag": "" }] } - ]; - this._mapFilterType = 'exclude'; // 'include / exclude', - this._mapFilters = ['ar_', 'dz_', 'gd_', 'lobby_', 'training1']; // [ {string} ] - this._maxRounds = 0; - this._pause = false // Is the match paused? - this._score = { + this.#mapFilterType = 'exclude'; // 'include / exclude', + this.#mapFilters = ['ar_', 'dz_', 'gd_', 'lobby_', 'training1']; // [ {string} ] + this.#score = { 'T': 0, 'C': 0 }; - this._players = [ - //{ 'name': '', - // 'steamID': '', - // 'team': '', - // 'kills': 0, - // 'deaths': 0 } - ]; // emitter to notify of changes this.serverInfoChanged = new events.EventEmitter(); @@ -53,178 +61,186 @@ class serverInfo { // getter / setter get serverState() { - return this._serverState; + return this.#serverState; } set serverState(newVal) { - this._serverState[expr] = newVal; + this.#serverState = newVal; } get map() { - return this._map; + return this.#map; } set map(newMap) { - this._map = newMap; + this.#map = newMap; this.serverInfoChanged.emit('change'); } get mapsAvail() { - return this._mapsAvail; + return this.#mapsAvail; } set mapsAvail(newMapsAvail) { - this._mapsAvail = newMapsAvail; + this.#mapsAvail = newMapsAvail; this.serverInfoChanged.emit('change'); } mapList() { - if (this._mapFilters.length > 0) { - return this._mapsAvail.filter((map) => { + if (this.#mapFilters.length > 0) { + return this.#mapsAvail.filter((map) => { let found = false; - this._mapFilters.forEach((filter) => { + this.#mapFilters.forEach((filter) => { if (map.includes(filter)) { found = true; } }); - if (this._mapFilterType === 'include') { + if (this.#mapFilterType === 'include') { return found; } else { return !found; } }); } else { - return this._mapsAvail; + return this.#mapsAvail; } } get mapsDetails() { - return this._mapsDetails; + return this.#mapsDetails; } set mapsDetails(newMapsDetails) { - this._mapsDetails = newMapsDetails; + this.#mapsDetails = newMapsDetails; this.serverInfoChanged.emit('change'); } mapDetails() { - if (this._mapFilters.length > 0) { - return this._mapsDetails.filter((map) => { + if (this.#mapFilters.length > 0) { + return this.#mapsDetails.filter((map) => { let found = false; if (map.name) { // sometimes map.name is undefined for some reason. - this._mapFilters.forEach((filter) => { + this.#mapFilters.forEach((filter) => { if (map.name.includes(filter)) { found = true; } }); } - if (this._mapFilterType === 'include') { + if (this.#mapFilterType === 'include') { return found; } else { return !found; } }); } else { - return this._mapsDetails; + return this.#mapsDetails; } } // Map Filter Methods get mapFilterType() { - return this._mapFilterType; + return this.#mapFilterType; } set mapFilterType(type) { if (type === 'include' || type === 'exclude') { - this._mapFilterType = type; + this.#mapFilterType = type; this.serverInfoChanged.emit('change'); } } get mapFilters() { - return this._mapFilters; + return this.#mapFilters; } mapFilterAdd(filter) { - this._mapFilters.push(filter); + this.#mapFilters.push(filter); this.serverInfoChanged.emit('change'); } mapFilterRemove(itemToRemove) { - if (this._mapFilters.length == 0) { + if (this.#mapFilters.length == 0) { return (0); } - if (typeof itemToRemove === 'number' && this._mapFilters.length > parseInt(itemToRemove)) { + if (typeof itemToRemove === 'number' && this.#mapFilters.length > parseInt(itemToRemove)) { console.log("removing number"); - this._mapFilters.splice(parseInt(itemToRemove), 1); + this.#mapFilters.splice(parseInt(itemToRemove), 1); this.serverInfoChanged.emit('change'); } else { - let newFilters = this._mapFilters.filter((currentItem) => { + let newFilters = this.#mapFilters.filter((currentItem) => { return (currentItem != itemToRemove); }); - this._mapFilters = newFilters; + this.#mapFilters = newFilters; this.serverInfoChanged.emit('change'); } - return (this._mapFilters.length); + return (this.#mapFilters.length); } mapFilterReset() { - this._mapFilterType = 'exclude'; - this._mapFilters = []; + this.#mapFilterType = 'exclude'; + this.#mapFilters = []; this.serverInfoChanged.emit('change'); } get maxRounds() { - return this._maxRounds; + return this.#maxRounds; } set maxRounds(newMaxRounds) { - this._maxRounds = newMaxRounds; - this.serverInfoChanged.emit('change'); + if (!Number.isNaN(newMaxRounds)) { + this.#maxRounds = newMaxRounds; + this.serverInfoChanged.emit('change'); + } else { + logger.warn('maxRounds must be a number.'); + } } get score() { - return this._score; + return this.#score; } // Accepts array with team (T or C) and score. set score(newScoreArray) { - this._score[newScoreArray[1]] = parseInt(newScoreArray[2]); + this.#score[newScoreArray[1]] = parseInt(newScoreArray[2]); this.serverInfoChanged.emit('change'); } get pause() { - return this._pause; + return this.#pause; } set pause(state) { - this._pause = state - this.serverInfoChanged.emit('change'); + if (typeof(state) == 'boolean') { + this.#pause = state + this.serverInfoChanged.emit('change'); + } else { + logger.warn('Invalid pause state - must be of type Boolean'); + } } get players() { - return this._players; + return this.#players; } addPlayer(newPlayer) { - if (this._players.find(x => x.steamID === newPlayer.steamID) != undefined) { - this._players.find(x => x.steamID === newPlayer.steamID).disconnected = false + if (this.#players.find(x => x.steamID === newPlayer.steamID) != undefined) { + this.#players.find(x => x.steamID === newPlayer.steamID).disconnected = false } else { newPlayer.team = 'U'; newPlayer.kills = 0; newPlayer.deaths = 0; newPlayer.disconnected = false; - this._players.push(newPlayer); + this.#players.push(newPlayer); } this.serverInfoChanged.emit('change'); } assignPlayer(name, steamID, team) { - if (this._players.find(x => x.steamID === steamID) == undefined ) { + if (this.#players.find(x => x.steamID === steamID) == undefined ) { this.addPlayer({'name': name, 'steamID': steamID }); } - let player = this._players.find(x => x.steamID === steamID); + let player = this.#players.find(x => x.steamID === steamID); player.team = team.substr(0, 1); this.serverInfoChanged.emit('change'); } removePlayer(steamID) { - this._players.find(x => x.steamID === steamID).disconnected = true; - // this._players.splice(this._players.findIndex(x => x.steamID === steamID), 1); + this.#players.find(x => x.steamID === steamID).disconnected = true; + // this.#players.splice(this.#players.findIndex(x => x.steamID === steamID), 1); this.serverInfoChanged.emit('change'); } clearPlayers() { - this._players = []; + this.#players = []; this.serverInfoChanged.emit('change'); } recordKill(killer, victim) { - let killPlayer = this._players.find(x => x.steamID === killer); + let killPlayer = this.#players.find(x => x.steamID === killer); if (killPlayer != undefined) killPlayer.kills += 1; - let victimPlayer = this._players.find(x => x.steamID === victim); + let victimPlayer = this.#players.find(x => x.steamID === victim); if (victimPlayer != undefined) victimPlayer.deaths += 1; this.serverInfoChanged.emit('change'); @@ -233,35 +249,35 @@ class serverInfo { // Methods getAll() { return { - 'map': this._map, + 'map': this.#map, 'mapsAvail': this.mapList(), 'mapsDetails': this.mapDetails(), - 'maxRounds': this._maxRounds, - 'score': this._score, - 'pause': this._pause, - 'players': this._players + 'maxRounds': this.#maxRounds, + 'score': this.#score, + 'pause': this.#pause, + 'players': this.#players }; } newMatch() { - this._score.C = 0; - this._score.T = 0; - for (let i in this._players) { - this._players[i].kills = 0; - this._players[i].deaths = 0; + this.#score.C = 0; + this.#score.T = 0; + for (let i in this.#players) { + this.#players[i].kills = 0; + this.#players[i].deaths = 0; } this.serverInfoChanged.emit('change'); } reset() { // Method to be called on server quit. - this._map = ''; - this._mapsAvail = []; - this._mapsDetails = []; - this._maxRounds = 0; - this._pause = false; + this.#map = ''; + this.#mapsAvail = []; + this.#mapsDetails = []; + this.#maxRounds = 0; + this.#pause = false; this.clearPlayers(); this.newMatch(); } -}; +} module.exports = new serverInfo(); \ No newline at end of file diff --git a/modules/sharedFunctions.js b/modules/sharedFunctions.js index 4780e9c..386762e 100755 --- a/modules/sharedFunctions.js +++ b/modules/sharedFunctions.js @@ -5,10 +5,8 @@ const logger = require('./logger.js'); var cfg = require('./configClass.js'); var serverInfo = require('./serverInfo.js'); var controlEmitter = require('./controlEmitter.js'); -const config = require('../config.js'); -const { response } = require('express'); -rconQ = new queue({ "autostart": true, "timeout": 500, "concurrency": 1 }); +const rconQ = new queue({ "autostart": true, "timeout": 500, "concurrency": 1 }); /** * Authenticate rcon with server @@ -77,7 +75,7 @@ async function reloadMaplist() { return new Promise( async (resolve, reject) => { function getWorkshopCollection(id) { return new Promise((resolve, reject) => { - https.get(`https://api.steampowered.com/IPublishedFileService/GetDetails/v1?key=${cfg.apiToken}&publishedfileids[0]=${cfg.workshopCollection}&includechildren=true`, (res) => { + https.get(`https://api.steampowered.com/IPublishedFileService/GetDetails/v1?key=${cfg.apiToken}&publishedfileids[0]=${id}&includechildren=true`, (res) => { let resData = ''; res.on('data', (dataChunk) => { resData += dataChunk; @@ -257,6 +255,7 @@ Workshop maps not available.`); // TODO: Check if this is still needed. // serverInfo.mapsAvail = maplist; if(mapdetails.length > 0) { + logger.info('Maps reloaded'); resolve({ "success": true }); } else { logger.warn('Update maps failed: Maplist is empty.'); @@ -302,23 +301,6 @@ function executeRcon(message) { } /*------------------------- Helper Functions ----------------------------*/ -/** - * Extracts all matches for a regex. - * @param {string} string - String to search. - * @param {regex} regex - Regex to execute on the string. - * @param {integer} index - Optional index which capturing group should be retreived. - * @returns {string[]} matches - Array holding the found matches. - */ -function getMatches(string, regex, index) { - index || (index = 1); // default to the first capturing group - var matches = []; - var match; - while (match = regex.exec(string)) { - matches.push(match[index]); - } - return matches; -} - /** * Cuts the bare map-name from the various representations in the servers responses. * @param {string} mapstring - The response of mapname(s) from rcon. @@ -326,8 +308,8 @@ function getMatches(string, regex, index) { */ function cutMapName(mapstring) { if (mapstring.search('workshop') != -1) { - re = /(\w+)/g; - matches = mapstring.match(re); + let re = /(\w+)/g; + let matches = mapstring.match(re); mapstring = matches[2]; } if (mapstring.search(".bsp") != -1) { diff --git a/public/js/gameserver.js b/public/js/gameserver.js index b9bb5e3..d5d1fb0 100644 --- a/public/js/gameserver.js +++ b/public/js/gameserver.js @@ -14,8 +14,6 @@ var titles = { 'mapchange': 'Changing map', 'pause': 'Pausing/Unpausing match' } -var running = false; -var authenticated = false; // Redirect to login page. function doLogin() { @@ -51,7 +49,7 @@ function loadMaplist() { // Setup the Elements according to server status. function setupPage() { $('#popupCaption').text('Querying Server'); - getPromise = (path) => { + let getPromise = (path) => { return Promise.resolve(sendGet(`${address}/${path}`)); } @@ -72,13 +70,13 @@ function setupPage() { } }); } - }).catch((error) => { + }).catch(() => { setupServerStopped(); }); } else { setupNotLoggedIn(); } - }).catch((error) => { + }).catch(() => { setupNotLoggedIn(); }); @@ -128,11 +126,11 @@ function setupServerStopped() { } function clickButton(aButton) { - action = aButton.value.toLowerCase(); + let action = aButton.value.toLowerCase(); $('#popupCaption').text(`${titles[action]}`); $('#popupText').text('Moment bitte!'); $('#container-popup').css('display', 'flex'); - startMap = document.getElementById('mapAuswahl').value; + let startMap = document.getElementById('mapAuswahl').value; sendGet(`${apiPath}/control/${action}`, `startmap=${startMap}`).done((data) => { if (socket.readyState != 1) { // if websocket not connected @@ -224,25 +222,21 @@ function showPlay(event) { } function changeMap(event) { - let map = event.currentTarget.getAttribute("id"); + let map = event.currentTarget.firstElementChild.textContent; $('#mapSelector').hide('fast'); //$('#popupCaption').text(titles['mapchange']); //$('#container-popup').css('display', 'flex'); - if (event.currentTarget.children[2].attributes["src"].nodeValue != "") { - sendGet(`${apiPath}/control/changemap`, `map=${map}`, (data) => { - if (data.success) { - $('#popupText').html(`Changing map to ${map}`); - } else { - $('#popupText').html(`Mapchange failed!`); - window.setTimeout(() => { - $('#container-popup').css('display', 'none'); - }, 2000); + sendGet(`${apiPath}/control/changemap`, `map=${map}`, (data) => { + if (data.success) { + $('#popupText').html(`Changing map to ${map}`); + } else { + $('#popupText').html(`Mapchange failed!`); + window.setTimeout(() => { + $('#container-popup').css('display', 'none'); + }, 2000); - } - }); - } else { - sendGet(`${apiPath}/rcon`, `message=ds_workshop_changelevel ${map}`); - } + } + }); } function restartRound() { @@ -294,7 +288,6 @@ function authenticate(caller) { } function kill(caller) { - sendGet(`${apiPath}/control/kill`).done((data) => { window.location.href = './gameserver.htm'; }).fail((error) => { diff --git a/public/notauth.htm b/public/notauth.htm index 127b37c..292fe04 100644 --- a/public/notauth.htm +++ b/public/notauth.htm @@ -1,36 +1,36 @@ - - - - Klosser Gameserver - - - - - -

Server not running correctly

-

- The CS:GO Server is started, but RCON is not authenticated.
- You can either try to authenticate again or kill the server-process to start over. -

-

- The attempt was also unsuccessful. See your server logs for details and try to fix there. -

-

- Kill command failed! Further troubleshooting needs to take place on your server. -

- -
- - -
- - + + + + Klosser Gameserver + + + + + +

Server not running correctly

+

+ The CS:GO Server is started, but RCON is not authenticated.
+ You can either try to authenticate again or kill the server-process to start over. +

+

+ The attempt was also unsuccessful. See your server logs for details and try to fix there. +

+

+ Kill command failed! Further troubleshooting needs to take place on your server. +

+ +
+ + +
+ + \ No newline at end of file diff --git a/serverControl.js b/serverControl.js index f512bfd..c4f28e8 100755 --- a/serverControl.js +++ b/serverControl.js @@ -1,7 +1,7 @@ /** * @file CS:GO Dedicated Server Control * @author Markus Adrario - * @version 1.0 + * @version 2.0 * @requires express * @requires express-session * @requires express-rate-limit @@ -11,8 +11,6 @@ * @requires http * @requires https * @requires ws - * @requires url - * @requires events * @requires child_process * @requires rcon-srcds * @requires ./modules/logger.js @@ -29,9 +27,7 @@ const passport = require('passport'); const SteamStrategy = require('passport-steam').Strategy; const BasicStrategy = require('passport-http').BasicStrategy; const webSocket = require('ws'); -const url = require('url'); const fs = require('fs'); -const events = require('events'); const { exec } = require('child_process'); const logger = require('./modules/logger.js'); var serverInfo = require('./modules/serverInfo.js'); @@ -59,6 +55,7 @@ if (cfg.useHttps) { exec('/bin/ps -A', (error, stdout, stderr) => { if (error) { logger.error(`exec error: ${error}`); + logger.error(stderr) return; } if (stdout.match(/cs2/) != null) { @@ -94,8 +91,8 @@ controlEmitter.on('exec', (operation, action) => { let mapstring = matches[1]; serverInfo.map = sf.cutMapName(mapstring); }); - sf.reloadMaplist().then((answer) => { - logger.info('Maps reloaded'); + sf.reloadMaplist().then(() => { + // Be happy and do nothing }).catch((err) => { logger.warn(`Maps could not be loaded: ${err}`); }); @@ -199,7 +196,7 @@ function ensureAuthenticated(req, res, next) { */ app.get('/csgoapi/login', passport.authenticate('steam'), - (req, res) => { + () => { // The request will be redirected to Steam for authentication, so // this function will not be called. } @@ -229,11 +226,18 @@ app.get('/csgoapi/login/return', */ app.get('/csgoapi/logout', (req, res) => { logger.http({ - 'user': `${steamID64}`, + 'user': `${req.user.identifier}`, 'message': 'logged out' }); - req.logout(); - res.redirect(cfg.redirectPage); + req.logout((err) => { + if (err) { + logger.warn({ + 'user': `${req.user.identifier}`, + 'message': `logout failed: ${err}` + }); + } + res.redirect(cfg.redirectPage); + }); }); /** @@ -292,25 +296,27 @@ if (cfg.httpAuth) { } //--------------------- END Basic authentication --------------------------// +let server; if (cfg.useHttps) { - var server = http.createServer(httpsCredentials, app); + server = http.createServer(httpsCredentials, app); } else { - var server = http.createServer(app); + server = http.createServer(app); } server.listen(cfg.apiPort); //------------------------------- Log receiver ----------------------------// -logreceive = express(); +var logreceive = express(); logreceive.use(express.text({ limit: "50mb" })); +let logserver; if (cfg.useHttps) { - loghttp = require('http'); + let loghttp = require('http'); logserver = loghttp.createServer(logreceive); } else { logserver = http.createServer(logreceive); } -logroute = require('./modules/logreceive.js'); +const logroute = require('./modules/logreceive.js'); logreceive.use('/', logroute); logserver.listen(cfg.logPort, () => { @@ -320,9 +326,20 @@ logserver.listen(cfg.logPort, () => { /*----------------- WebSockets Code -------------------*/ if (cfg.webSockets) { - const wssServer = http.createServer(httpsCredentials); + let wssServer + if (cfg.useHttps) { + wssServer = http.createServer(httpsCredentials); + } else { + wssServer = http.createServer(); + } const wss = new webSocket.Server({ server: wssServer }); + wssServer.listen(cfg.socketPort, () => { + let host = cfg.host; + logger.verbose(host); + }); + + /** * Websocket to send data updates to a webClient. * @listens ws#connection @@ -389,17 +406,7 @@ if (cfg.webSockets) { serverInfo.serverInfoChanged.removeListener('change', sendUpdate); controlEmitter.removeListener('exec', sendControlNotification); controlEmitter.removeListener('progress', reportProgress); + logger.info(`websocket closed with code ${code}. Reason: ${reason}`); }); }); - - wssServer.listen(cfg.socketPort, () => { - let host = cfg.host; - logger.verbose(host); - - if (cfg.useHttps) { - const ws = new webSocket(`wss://${host}:${wssServer.address().port}`); - } else { - const ws = new webSocket(`ws://${host}:${wssServer.address().port}`); - } - }); }