From 53232507a76619267701ff18b8223404d194cd9b Mon Sep 17 00:00:00 2001 From: Taraman17 Date: Thu, 14 Nov 2024 19:42:35 +0000 Subject: [PATCH 1/2] Fix mapdetails for some maps and add new official maps --- OfficialMaps.json | 149 +++-- modules/apiV10.js | 1056 ++++++++++++++++++++---------------- modules/configClass.js | 328 ++++++----- modules/serverInfo.js | 534 +++++++++--------- modules/sharedFunctions.js | 576 +++++++++++--------- package.json | 94 ++-- public/favicon.ico | Bin 0 -> 3262 bytes public/gameserver.css | 38 +- public/gameserver.htm | 237 ++++---- public/js/gameserver.js | 639 ++++++++++++++-------- public/js/onload.js | 121 ----- public/maplist.txt | 18 +- serverControl.js | 566 ++++++++++--------- 13 files changed, 2355 insertions(+), 2001 deletions(-) create mode 100755 public/favicon.ico delete mode 100644 public/js/onload.js diff --git a/OfficialMaps.json b/OfficialMaps.json index 05f9570..56707b3 100755 --- a/OfficialMaps.json +++ b/OfficialMaps.json @@ -1,55 +1,94 @@ -[{ - "name": "ar_baggage", - "id": 125440026 -},{ - "name": "ar_poolday", - "id": 3263935964 -},{ - "name": "ar_shoots", - "id" : 125440261 -},{ - "name": "cs_assault", - "id": 125432575 -},{ - "name": "cs_italy", - "id": 125436057 -},{ - "name": "cs_office", - "id": 125444404 -},{ - "name": "de_ancient", - "id": 2627571649 -},{ - "name": "de_anubis", - "id": 1984883124 -},{ - "name": "de_assembly", - "id": 3071005299 -},{ - "name": "de_dust2", - "id": 125438255 -},{ - "name": "de_inferno", - "id": 125438669 -},{ - "name": "de_memento", - "id": 3165559377 -},{ - "name": "de_mills", - "id": 3152430710 -},{ - "name": "de_mirage", - "id": 152508932 -},{ - "name": "de_nuke", - "id": 125439125 -},{ - "name": "de_overpass", - "id": 205240106 -},{ - "name": "de_thera", - "id": 3121217565 -},{ - "name": "de_vertigo", - "id": 125439851 -}] +[ + { + "name": "ar_baggage", + "id": 125440026 + }, + { + "name": "ar_poolday", + "id": 3263935964 + }, + { + "name": "ar_shoots", + "id": 125440261 + }, + { + "name": "cs_assault", + "id": 125432575 + }, + { + "name": "cs_italy", + "id": 125436057 + }, + { + "name": "cs_office", + "id": 125444404 + }, + { + "name": "de_ancient", + "id": 2627571649 + }, + { + "name": "de_anubis", + "id": 1984883124 + }, + { + "name": "de_assembly", + "id": 3071005299 + }, + { + "name": "de_basalt", + "id": 3329258290 + }, + { + "name": "de_dust2", + "id": 125438255 + }, + { + "name": "de_edin", + "id": 3328169568 + }, + { + "name": "de_inferno", + "id": 125438669 + }, + { + "name": "de_memento", + "id": 3165559377 + }, + { + "name": "de_mills", + "id": 3152430710 + }, + { + "name": "de_mirage", + "id": 152508932 + }, + { + "name": "de_nuke", + "id": 125439125 + }, + { + "name": "de_overpass", + "id": 205240106 + }, + { + "name": "de_palais", + "id": 3257582863 + }, + { + "name": "de_thera", + "id": 3121217565 + }, + { + "name": "de_train", + "id": 125438372 + }, + { + "name": "de_vertigo", + "id": 125439851 + }, + { + "name": "de_whistle", + "id": 3308613773 + } +] \ No newline at end of file diff --git a/modules/apiV10.js b/modules/apiV10.js index 589d0e3..b5a7183 100755 --- a/modules/apiV10.js +++ b/modules/apiV10.js @@ -10,16 +10,15 @@ * @requires ./sharedFunctions.js */ -const { exec } = require('child_process'); -const pty = require('node-pty'); -const express = require('express'); +const { exec } = require("child_process"); +const pty = require("node-pty"); +const express = require("express"); var router = express.Router(); -const logger = require('./logger.js'); -var cfg = require('./configClass.js'); -var serverInfo = require('./serverInfo.js'); -var controlEmitter = require('./controlEmitter.js'); -const sf = require('./sharedFunctions.js'); - +const logger = require("./logger.js"); +var cfg = require("./configClass.js"); +var serverInfo = require("./serverInfo.js"); +var controlEmitter = require("./controlEmitter.js"); +const sf = require("./sharedFunctions.js"); //--------------------------- V1.0 ----------------------------// /** @@ -35,11 +34,13 @@ const sf = require('./sharedFunctions.js'); * HTTP/1.1 200 OK * { "authenticated": true/false } */ -router.get('/authenticate', (req, res) => { - sf.authenticate().then((data) => { - res.json(data); - }).catch((data) => { - res.json(data); +router.get("/authenticate", (req, res) => { + sf.authenticate() + .then((data) => { + res.json(data); + }) + .catch((data) => { + res.json(data); }); }); @@ -57,15 +58,15 @@ router.get('/authenticate', (req, res) => { * HTTP/1.1 503 Service Unavailable * { "error": "RCON not authenticated" } */ -router.get('/info/serverInfo', (req, res) => { - logger.verbose('Processing Serverinfo request.'); - if (serverInfo.serverState.authenticated) { - res.json(serverInfo.getAll()); - } else if (!serverInfo.serverState.serverRunning) { - res.status(503).json({ "error": "CS:GO Server not running." }); - } else if (!serverInfo.serverState.authenticated) { - res.status(503).json({ "error": "RCON not authenticated." }); - } +router.get("/info/serverInfo", (req, res) => { + logger.verbose("Processing Serverinfo request."); + if (serverInfo.serverState.authenticated) { + res.json(serverInfo.getAll()); + } else if (!serverInfo.serverState.serverRunning) { + res.status(503).json({ error: "CS:GO Server not running." }); + } else if (!serverInfo.serverState.authenticated) { + res.status(503).json({ error: "RCON not authenticated." }); + } }); /** @@ -81,18 +82,21 @@ router.get('/info/serverInfo', (req, res) => { * HTTP/1.1 200 OK * { "running": true/false} */ -router.get('/info/runstatus', (req, res) => { - if (serverInfo.serverState.operationPending == 'start' || serverInfo.serverState.operationPending == 'stop') { - let sendResponse = (type, action) => { - if (type == 'auth' && action == 'end') { - res.json({ "running": serverInfo.serverState.serverRunning }); - controlEmitter.removeListener('exec', sendResponse); - } - } - controlEmitter.on('exec', sendResponse) - } else { - res.json({ "running": serverInfo.serverState.serverRunning }); - } +router.get("/info/runstatus", (req, res) => { + if ( + serverInfo.serverState.operationPending == "start" || + serverInfo.serverState.operationPending == "stop" + ) { + let sendResponse = (type, action) => { + if (type == "auth" && action == "end") { + res.json({ running: serverInfo.serverState.serverRunning }); + controlEmitter.removeListener("exec", sendResponse); + } + }; + controlEmitter.on("exec", sendResponse); + } else { + res.json({ running: serverInfo.serverState.serverRunning }); + } }); /** @@ -108,18 +112,18 @@ router.get('/info/runstatus', (req, res) => { * HTTP/1.1 200 OK * { "rconauth": true/false} */ -router.get('/info/rconauthstatus', (req, res) => { - if (serverInfo.serverState.operationPending == 'auth') { - let sendResponse = (type, action) => { - if (type == 'auth' && action == 'end') { - res.json({ "rconauth": serverInfo.serverState.authenticated }); - controlEmitter.removeListener('exec', sendResponse); - } - } - controlEmitter.on('exec', sendResponse) - } else { - res.json({ "rconauth": serverInfo.serverState.authenticated }); - } +router.get("/info/rconauthstatus", (req, res) => { + if (serverInfo.serverState.operationPending == "auth") { + let sendResponse = (type, action) => { + if (type == "auth" && action == "end") { + res.json({ rconauth: serverInfo.serverState.authenticated }); + controlEmitter.removeListener("exec", sendResponse); + } + }; + controlEmitter.on("exec", sendResponse); + } else { + res.json({ rconauth: serverInfo.serverState.authenticated }); + } }); /** @@ -135,8 +139,8 @@ router.get('/info/rconauthstatus', (req, res) => { * HTTP/1.1 200 OK * { "type": {string}, "filters": {array of strings} } */ -router.get('/filter', (req, res) => { - res.json({ "type": serverInfo.mapFilterType, "filters": serverInfo.mapFilters }); +router.get("/filter", (req, res) => { + res.json({ type: serverInfo.mapFilterType, filters: serverInfo.mapFilters }); }); /** @@ -152,9 +156,9 @@ router.get('/filter', (req, res) => { * HTTP/1.1 200 OK * { "type": {string}, "filters": {array of strings} } */ -router.get('/filter/reset', (req, res) => { - serverInfo.mapFilterReset(); - res.json({ "type": serverInfo.mapFilterType, "filters": serverInfo.mapFilters }); +router.get("/filter/reset", (req, res) => { + serverInfo.mapFilterReset(); + res.json({ type: serverInfo.mapFilterType, filters: serverInfo.mapFilters }); }); /** @@ -178,18 +182,20 @@ router.get('/filter/reset', (req, res) => { * HTTP/1.1 400 Bad Request * { "error": "Submitted filter text not safe." } */ -router.post('/filter/add', (req, res) => { - if (!req.query.filter) { - return res.status(400).json({ "error": "Required parameter 'filter' is missing" }); - } +router.post("/filter/add", (req, res) => { + if (!req.query.filter) { + return res + .status(400) + .json({ error: "Required parameter 'filter' is missing" }); + } - const safe = /^[a-zA-Z0-9-_]*$/; - if (!safe.test(req.query.filter)) { - return res.status(400).json({ "error": "Submitted filter text not safe." }); - } else { - serverInfo.mapFilters.push(req.query.filter); - } - res.json({ "type": serverInfo.mapFilterType, "filters": serverInfo.mapFilters }); + const safe = /^[a-zA-Z0-9-_]*$/; + if (!safe.test(req.query.filter)) { + return res.status(400).json({ error: "Submitted filter text not safe." }); + } else { + serverInfo.mapFilters.push(req.query.filter); + } + res.json({ type: serverInfo.mapFilterType, filters: serverInfo.mapFilters }); }); /** @@ -213,17 +219,19 @@ router.post('/filter/add', (req, res) => { * HTTP/1.1 400 Bad Request * { "error": "No filter was removed." } */ -router.post('/filter/remove', (req, res) => { - if (!req.query.filter) { - return res.status(400).json({ "error": "Required parameter 'filter' is missing" }); - } +router.post("/filter/remove", (req, res) => { + if (!req.query.filter) { + return res + .status(400) + .json({ error: "Required parameter 'filter' is missing" }); + } - let oldLength = serverInfo.mapFilters.length; - serverInfo.mapFilterRemove(req.query.filter); - if (oldLength == serverInfo.mapFilters.length) { - return res.status(400).json({ "error": "No filter was removed." }); - } - res.json({ "type": serverInfo.mapFilterType, "filters": serverInfo.mapFilters }); + let oldLength = serverInfo.mapFilters.length; + serverInfo.mapFilterRemove(req.query.filter); + if (oldLength == serverInfo.mapFilters.length) { + return res.status(400).json({ error: "No filter was removed." }); + } + res.json({ type: serverInfo.mapFilterType, filters: serverInfo.mapFilters }); }); /** @@ -247,17 +255,19 @@ router.post('/filter/remove', (req, res) => { * HTTP/1.1 400 Bad Request * { "error": "Invalid type string." } */ -router.post('/filter/type', (req, res) => { - if (!req.query.type) { - return res.status(400).json({ "error": "Required parameter 'type' is missing" }); - } +router.post("/filter/type", (req, res) => { + if (!req.query.type) { + return res + .status(400) + .json({ error: "Required parameter 'type' is missing" }); + } - if (req.query.type === 'include' || req.query.type === 'exclude') { - serverInfo.mapFilterType = req.query.type; - } else { - return res.status(400).json({ "error": "Invalid type string." }); - } - res.json({ "type": serverInfo.mapFilterType, "filters": serverInfo.mapFilters }); + if (req.query.type === "include" || req.query.type === "exclude") { + serverInfo.mapFilterType = req.query.type; + } else { + return res.status(400).json({ error: "Invalid type string." }); + } + res.json({ type: serverInfo.mapFilterType, filters: serverInfo.mapFilters }); }); /** @@ -281,327 +291,400 @@ router.post('/filter/type', (req, res) => { * HTTP/1.1 503 Service Unavailable * { "error": "Server already running" } */ -router.get('/control/start', (req, res) => { - var args = req.query; +router.get("/control/start", (req, res) => { + var args = req.query; - if (!serverInfo.serverState.serverRunning && serverInfo.serverState.operationPending == 'none') { - controlEmitter.emit('exec', 'start', 'start'); - logger.verbose('Starting server.'); - let startMap = 'de_dust2'; - const safe = /^[a-zA-Z0-9-_]*$/; - if (!safe.test(args.startmap)) { - logger.warn(`Supplied mapname ${args.startmap} is not safe, using de_dust2`); - } else { - startMap = args.startmap; - } - let commandLine = `${cfg.serverCommandline} +map ${startMap}`; - logger.info(commandLine); - exec(commandLine, (error, stdout, stderr) => { - if (error) { - // node couldn't execute the command. - res.status(501).json({ "error": error.code }); - logger.error('Error Code: ' + error.code); - logger.error('Signal received: ' + error.signal); - logger.error(stderr); - serverInfo.serverState.serverRunning = false; - controlEmitter.emit('exec', 'start', 'fail'); - } else { - logger.verbose('screen started'); - controlEmitter.on('exec', function startCallback(operation, action) { - if (operation == 'auth' && action == 'end' && serverInfo.serverState.authenticated == true) { - controlEmitter.emit('exec', 'start', 'end'); - res.json({ "success": true }); - controlEmitter.removeListener('exec', startCallback); - } else if (operation == 'auth' && action == 'end' && serverInfo.serverState.authenticated == false) { - res.status(501).json({ "error": "RCON Authentication failed." }); - controlEmitter.emit('exec', 'start', 'fail'); - controlEmitter.removeListener('exec', startCallback); - } - }); - serverInfo.serverState.serverRunning = true; - } - }); - } else if (serverInfo.serverState.serverRunning) { - logger.warn('Start triggered with server already running'); - res.status(503).json({ "error": "Server already running." }); - } else if (serverInfo.serverState.operationPending != 'none') { - logger.warn(`Server Start triggered, while ${serverInfo.serverState.operationPending} pending.`); - res.status(503).json({ "error": `Another Operation is Pending: ${serverInfo.serverState.operationPending}` }); + if ( + !serverInfo.serverState.serverRunning && + serverInfo.serverState.operationPending == "none" + ) { + controlEmitter.emit("exec", "start", "start"); + logger.verbose("Starting server."); + let startMap = "de_dust2"; + const safe = /^[a-zA-Z0-9-_]*$/; + if (!safe.test(args.startmap)) { + logger.warn( + `Supplied mapname ${args.startmap} is not safe, using de_dust2` + ); + } else { + startMap = args.startmap; } + let commandLine = `${cfg.serverCommandline} +map ${startMap}`; + logger.info(commandLine); + exec(commandLine, (error, stdout, stderr) => { + if (error) { + // node couldn't execute the command. + res.status(501).json({ error: error.code }); + logger.error("Error Code: " + error.code); + logger.error("Signal received: " + error.signal); + logger.error(stderr); + serverInfo.serverState.serverRunning = false; + controlEmitter.emit("exec", "start", "fail"); + } else { + logger.verbose("screen started"); + controlEmitter.on("exec", function startCallback(operation, action) { + if ( + operation == "auth" && + action == "end" && + serverInfo.serverState.authenticated == true + ) { + controlEmitter.emit("exec", "start", "end"); + res.json({ success: true }); + controlEmitter.removeListener("exec", startCallback); + } else if ( + operation == "auth" && + action == "end" && + serverInfo.serverState.authenticated == false + ) { + res.status(501).json({ error: "RCON Authentication failed." }); + controlEmitter.emit("exec", "start", "fail"); + controlEmitter.removeListener("exec", startCallback); + } + }); + serverInfo.serverState.serverRunning = true; + } + }); + } else if (serverInfo.serverState.serverRunning) { + logger.warn("Start triggered with server already running"); + res.status(503).json({ error: "Server already running." }); + } else if (serverInfo.serverState.operationPending != "none") { + logger.warn( + `Server Start triggered, while ${serverInfo.serverState.operationPending} pending.` + ); + res + .status(503) + .json({ + error: `Another Operation is Pending: ${serverInfo.serverState.operationPending}`, + }); + } }); /** -* @apiDescription Pause round -* -* @api {get} /control/pause -* @apiVersion 1.0 -* @apiName Pause -* @apiGroup Control -* -* @apiSuccess {boolean} success -* @apiSuccessExample {json} -* HTTP/1.1 200 OK -* { "success": true } -* @apiError {string} error -* @apiErrorExample {json} -* HTTP/1.1 503 Service Unavailable -* { "error": "Pause not possible" } -*/ -router.get('/control/pause', (req,res) => { - if (serverInfo.serverState.serverRunning && serverInfo.serverState.operationPending == 'none') { - controlEmitter.emit('exec', 'pause', 'start'); - logger.verbose("Pausing round."); - sf.executeRcon('mp_pause_match').then((answer) => { - // L 10/16/2023 - 16:05:44: Match pause is enabled - mp_pause_match - logger.debug(answer); - if (answer.indexOf('Match pause is enabled - mp_pause_match') != -1) { - serverInfo.pause = true; - controlEmitter.emit('exec', 'pause', 'end'); - res.json({ "success": true }); - } else { - logger.info(`Pausing failed: ${answer}`); - controlEmitter.emit('exec', 'pause', 'end'); - res.json({ "success": false }); - } - }).catch((err) => { - logger.info(`Pausing failed. Rcon error: ${err.message}`); - controlEmitter.emit('exec', 'pause', 'end'); - res.status(503).json({ "error": `rcon error` }); - }); - } else if (!serverInfo.serverState.serverRunning) { - logger.warn('Pause triggered, although server not running'); - res.status(503).json({ "error": "Server not running." }); - } else if (serverInfo.serverState.operationPending != 'none') { - logger.warn(`Pause triggered, while ${serverInfo.serverState.operationPending} pending.`); - res.status(503).json({ "error": `Another Operation is Pending: ${serverInfo.serverState.operationPending}` }); - } + * @apiDescription Pause round + * + * @api {get} /control/pause + * @apiVersion 1.0 + * @apiName Pause + * @apiGroup Control + * + * @apiSuccess {boolean} success + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * { "success": true } + * @apiError {string} error + * @apiErrorExample {json} + * HTTP/1.1 503 Service Unavailable + * { "error": "Pause not possible" } + */ +router.get("/control/pause", (req, res) => { + if ( + serverInfo.serverState.serverRunning && + serverInfo.serverState.operationPending == "none" + ) { + controlEmitter.emit("exec", "pause", "start"); + logger.verbose("Pausing round."); + sf.executeRcon("mp_pause_match") + .then((answer) => { + // L 10/16/2023 - 16:05:44: Match pause is enabled - mp_pause_match + logger.debug(answer); + if (answer.indexOf("Match pause is enabled - mp_pause_match") != -1) { + serverInfo.pause = true; + controlEmitter.emit("exec", "pause", "end"); + res.json({ success: true }); + } else { + logger.info(`Pausing failed: ${answer}`); + controlEmitter.emit("exec", "pause", "end"); + res.json({ success: false }); + } + }) + .catch((err) => { + logger.info(`Pausing failed. Rcon error: ${err.message}`); + controlEmitter.emit("exec", "pause", "end"); + res.status(503).json({ error: `rcon error` }); + }); + } else if (!serverInfo.serverState.serverRunning) { + logger.warn("Pause triggered, although server not running"); + res.status(503).json({ error: "Server not running." }); + } else if (serverInfo.serverState.operationPending != "none") { + logger.warn( + `Pause triggered, while ${serverInfo.serverState.operationPending} pending.` + ); + res + .status(503) + .json({ + error: `Another Operation is Pending: ${serverInfo.serverState.operationPending}`, + }); + } }); /** -* @apiDescription Resume round -* -* @api {get} /control/unpause -* @apiVersion 1.0 -* @apiName Unpause -* @apiGroup Control -* -* @apiSuccess {boolean} success -* @apiSuccessExample {json} -* HTTP/1.1 200 OK -* { "success": true } -* @apiError {string} error -* @apiErrorExample {json} -* HTTP/1.1 503 Service Unavailable -* { "error": "Unpause not possible" } -*/ -router.get('/control/unpause', (req,res) => { - if (serverInfo.serverState.serverRunning && serverInfo.serverState.operationPending == 'none') { - controlEmitter.emit('exec', 'pause', 'start'); - logger.verbose("Resuming round."); - sf.executeRcon('mp_unpause_match').then((answer) => { - // L 10/16/2023 - 16:06:08: Match pause is disabled - mp_unpause_match - if (answer.indexOf('Match pause is disabled - mp_unpause_match') != -1) { - serverInfo.pause = false; - controlEmitter.emit('exec', 'pause', 'end'); - res.json({ "success": true }); - } else { - logger.info(`Unpausing failed: ${answer}`); - controlEmitter.emit('exec', 'pause', 'end'); - res.json({ "success": false }); - } - }).catch((err) => { - logger.info(`Unpausing failed. Rcon error: ${err}`); - controlEmitter.emit('exec', 'pause', 'end'); - res.status(503).json({ "error": `RCON Error: ${err.toString()}` }); - }); - } else if (!serverInfo.serverState.serverRunning) { - logger.warn('Unpause triggered, although server not running'); - res.status(503).json({ "error": "Server not running." }); - } else if (serverInfo.serverState.operationPending != 'none') { - logger.warn(`Unpause triggered, while ${serverInfo.serverState.operationPending} pending.`); - res.status(503).json({ "error": `Another Operation is Pending: ${serverInfo.serverState.operationPending}` }); - } + * @apiDescription Resume round + * + * @api {get} /control/unpause + * @apiVersion 1.0 + * @apiName Unpause + * @apiGroup Control + * + * @apiSuccess {boolean} success + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * { "success": true } + * @apiError {string} error + * @apiErrorExample {json} + * HTTP/1.1 503 Service Unavailable + * { "error": "Unpause not possible" } + */ +router.get("/control/unpause", (req, res) => { + if ( + serverInfo.serverState.serverRunning && + serverInfo.serverState.operationPending == "none" + ) { + controlEmitter.emit("exec", "pause", "start"); + logger.verbose("Resuming round."); + sf.executeRcon("mp_unpause_match") + .then((answer) => { + // L 10/16/2023 - 16:06:08: Match pause is disabled - mp_unpause_match + if ( + answer.indexOf("Match pause is disabled - mp_unpause_match") != -1 + ) { + serverInfo.pause = false; + controlEmitter.emit("exec", "pause", "end"); + res.json({ success: true }); + } else { + logger.info(`Unpausing failed: ${answer}`); + controlEmitter.emit("exec", "pause", "end"); + res.json({ success: false }); + } + }) + .catch((err) => { + logger.info(`Unpausing failed. Rcon error: ${err}`); + controlEmitter.emit("exec", "pause", "end"); + res.status(503).json({ error: `RCON Error: ${err.toString()}` }); + }); + } else if (!serverInfo.serverState.serverRunning) { + logger.warn("Unpause triggered, although server not running"); + res.status(503).json({ error: "Server not running." }); + } else if (serverInfo.serverState.operationPending != "none") { + logger.warn( + `Unpause triggered, while ${serverInfo.serverState.operationPending} pending.` + ); + res + .status(503) + .json({ + error: `Another Operation is Pending: ${serverInfo.serverState.operationPending}`, + }); + } }); /** -* @apiDescription Stop CS:GO Server -* -* @api {get} /control/stop -* @apiVersion 1.0 -* @apiName Stop -* @apiGroup Control -* -* @apiSuccess {boolean} success -* @apiSuccessExample {json} -* HTTP/1.1 200 OK -* { "success": true } -* @apiError {string} error -* @apiErrorExample {json} -* HTTP/1.1 503 Service Unavailable -* { "error": "Server not running" } -*/ -router.get('/control/stop', (req, res) => { - if (serverInfo.serverState.serverRunning && serverInfo.serverState.operationPending == 'none') { - controlEmitter.emit('exec', 'stop', 'start'); - logger.verbose("sending quit."); - sf.executeRcon('quit').then((answer) => { - 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); - res.status(501).json({ "error": `RCON Error: ${err.toString()}` }); - controlEmitter.emit('exec', 'stop', 'end'); - }); - } else if (!serverInfo.serverState.serverRunning) { - logger.warn('Stop triggered, although server not running'); - res.status(503).json({ "error": "Server not running." }); - } else if (serverInfo.serverState.operationPending != 'none') { - logger.warn(`Stop triggered, while ${serverInfo.serverState.operationPending} pending.`); - res.status(503).json({ "error": `Another Operation is Pending: ${serverInfo.serverState.operationPending}` }); - } + * @apiDescription Stop CS:GO Server + * + * @api {get} /control/stop + * @apiVersion 1.0 + * @apiName Stop + * @apiGroup Control + * + * @apiSuccess {boolean} success + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * { "success": true } + * @apiError {string} error + * @apiErrorExample {json} + * HTTP/1.1 503 Service Unavailable + * { "error": "Server not running" } + */ +router.get("/control/stop", (req, res) => { + if ( + serverInfo.serverState.serverRunning && + serverInfo.serverState.operationPending == "none" + ) { + controlEmitter.emit("exec", "stop", "start"); + logger.verbose("sending quit."); + sf.executeRcon("quit") + .then((answer) => { + 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); + res.status(501).json({ error: `RCON Error: ${err.toString()}` }); + controlEmitter.emit("exec", "stop", "end"); + }); + } else if (!serverInfo.serverState.serverRunning) { + logger.warn("Stop triggered, although server not running"); + res.status(503).json({ error: "Server not running." }); + } else if (serverInfo.serverState.operationPending != "none") { + logger.warn( + `Stop triggered, while ${serverInfo.serverState.operationPending} pending.` + ); + res + .status(503) + .json({ + error: `Another Operation is Pending: ${serverInfo.serverState.operationPending}`, + }); + } }); /** -* @apiDescription Kill CS:GO Server Process in case no RCON connection. -* -* @api {get} /control/kill -* @apiVersion 1.0 -* @apiName Kill -* @apiGroup Control -* -* @apiSuccess {boolean} success -* @apiSuccessExample {json} -* HTTP/1.1 200 OK -* { "success": true } -* @apiError {string} error -* @apiErrorExample {json} -* HTTP/1.1 501 Service Unavailable -* { "error": "Could not find csgo server process" } -*/ -router.get('/control/kill', (req, res) => { - exec('/bin/ps -A |grep cs2', (error, stdout, stderr) => { + * @apiDescription Kill CS:GO Server Process in case no RCON connection. + * + * @api {get} /control/kill + * @apiVersion 1.0 + * @apiName Kill + * @apiGroup Control + * + * @apiSuccess {boolean} success + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * { "success": true } + * @apiError {string} error + * @apiErrorExample {json} + * HTTP/1.1 501 Service Unavailable + * { "error": "Could not find csgo server process" } + */ +router.get("/control/kill", (req, res) => { + exec("/bin/ps -A |grep cs2", (error, stdout, stderr) => { + if (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.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 - serverInfo.serverState.serverRunning = false; - serverInfo.serverState.authenticated = false; - serverInfo.serverState.serverRcon = undefined; - logger.verbose('Server process killed.') - res.json({ "success": true }); - } - }); + 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 + serverInfo.serverState.serverRunning = false; + serverInfo.serverState.authenticated = false; + serverInfo.serverState.serverRcon = undefined; + logger.verbose("Server process killed."); + res.json({ success: true }); } - }); + }); + } + }); }); /** -* @apiDescription Update CS:GO Server -* -* @api {get} /control/update -* @apiVersion 1.0 -* @apiName Update -* @apiGroup Control -* -* @apiSuccess {boolean} success -* @apiSuccessExample {json} -* HTTP/1.1 200 OK -* { "success": true } -* @apiError {string} error -* @apiErrorExample {json} -* HTTP/1.1 501 Internal Server Error -* { "error": "Update could not be started." } -*/ -router.get('/control/update', (req, res) => { - if (!serverInfo.serverState.serverRunning && serverInfo.serverState.operationPending == 'none') { - controlEmitter.emit('exec', 'update', 'start'); - let updateSuccess = false; - logger.verbose('Updating Server.'); - let updateProcess = pty.spawn(cfg.steamCommand, [`+runscript ${cfg.updateScript}`]); + * @apiDescription Update CS:GO Server + * + * @api {get} /control/update + * @apiVersion 1.0 + * @apiName Update + * @apiGroup Control + * + * @apiSuccess {boolean} success + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * { "success": true } + * @apiError {string} error + * @apiErrorExample {json} + * HTTP/1.1 501 Internal Server Error + * { "error": "Update could not be started." } + */ +router.get("/control/update", (req, res) => { + if ( + !serverInfo.serverState.serverRunning && + serverInfo.serverState.operationPending == "none" + ) { + controlEmitter.emit("exec", "update", "start"); + let updateSuccess = false; + logger.verbose("Updating Server."); + let updateProcess = pty.spawn(cfg.steamCommand, [ + `+runscript ${cfg.updateScript}`, + ]); - updateProcess.on('data', (data) => { - logger.debug(data); - if (data.indexOf('Checking for available updates') != -1) { - controlEmitter.emit('progress', 'Checking Steam client updates', 0); - } else if (data.indexOf('Verifying installation') != -1) { - controlEmitter.emit('progress', 'Verifying client installation', 0); - } else if (data.indexOf('Logging in user') != -1) { - controlEmitter.emit('progress', 'Logging in steam user', 0); - } else if (data.indexOf('FAILED') != -1) { - let rex = /FAILED \((.+)\)/; - let matches = rex.exec(data); - controlEmitter.emit('progress', `Login Failed: ${matches[1]}`, 0); - } else if (data.indexOf('Logged in OK') != -1) { - controlEmitter.emit('progress', 'Login OK', 100); - } else if (data.indexOf('Update state (0x') != -1) { - let rex = /Update state \(0x\d+\) (.+), progress: (\d{1,3})\.\d{2}/; - let matches = rex.exec(data); - controlEmitter.emit('progress', matches[1], matches[2]); - } else if (data.indexOf('Downloaaction update (') != -1) { - let rex = /\[(.+)] Downloaaction update/; - let matches = rex.exec(data); - controlEmitter.emit('progress', 'Updating Steam client', matches[1].slice(0, -1)); - } else if (data.indexOf('Success!') != -1) { - controlEmitter.emit('progress', 'Update successful!', 100); - logger.verbose('Update succeeded'); - updateSuccess = true; - controlEmitter.emit('exec', 'update', 'end'); - } - }); + updateProcess.on("data", (data) => { + logger.debug(data); + if (data.indexOf("Checking for available updates") != -1) { + controlEmitter.emit("progress", "Checking Steam client updates", 0); + } else if (data.indexOf("Verifying installation") != -1) { + controlEmitter.emit("progress", "Verifying client installation", 0); + } else if (data.indexOf("Logging in user") != -1) { + controlEmitter.emit("progress", "Logging in steam user", 0); + } else if (data.indexOf("FAILED") != -1) { + let rex = /FAILED \((.+)\)/; + let matches = rex.exec(data); + controlEmitter.emit("progress", `Login Failed: ${matches[1]}`, 0); + } else if (data.indexOf("Logged in OK") != -1) { + controlEmitter.emit("progress", "Login OK", 100); + } else if (data.indexOf("Update state (0x") != -1) { + let rex = /Update state \(0x\d+\) (.+), progress: (\d{1,3})\.\d{2}/; + let matches = rex.exec(data); + controlEmitter.emit("progress", matches[1], matches[2]); + } else if (data.indexOf("Downloaaction update (") != -1) { + let rex = /\[(.+)] Downloaaction update/; + let matches = rex.exec(data); + controlEmitter.emit( + "progress", + "Updating Steam client", + matches[1].slice(0, -1) + ); + } else if (data.indexOf("Success!") != -1) { + controlEmitter.emit("progress", "Update successful!", 100); + logger.verbose("Update succeeded"); + updateSuccess = true; + controlEmitter.emit("exec", "update", "end"); + } + }); - if (updateProcess) { - if (cfg.webSockets) { - res.json(`{ "success": true }`); - updateProcess.once('close', (code) => { - if (!updateSuccess) { - logger.warn(`Update exited without success. Exit code: ${code}`); - controlEmitter.emit('progress', 'Update failed!', 100); - controlEmitter.emit('exec', 'update', 'fail'); - } - }); - } else { - updateProcess.once('close', (code) => { - if (updateSuccess) { - res.json({ "success": true }); - } else { - logger.warn(`Update exited without success. Exit code: ${code}`); - res.status(501).json({ "error": "Update was not successful" }); - } - controlEmitter.emit('exec', 'update', 'end'); - }); - } - } else { - logger.error('Update could not be started.'); - res.status(501).json({ "error": "Update could not be started." }); - controlEmitter.emit('exec', 'update', 'end'); - } - } else if (serverInfo.serverState.serverRunning) { - logger.warn('Update triggered, while server running.'); - res.status(503).json({ "error": "Server is running - stop before updating" }); - } else if (serverInfo.serverState.operationPending != 'none') { - logger.warn(`Update triggered, while ${serverInfo.serverState.operationPending} pending`); - res.status(503).json({ "error": `Another Operation is Pending: ${serverInfo.serverState.operationPending}` }); + if (updateProcess) { + if (cfg.webSockets) { + res.json(`{ "success": true }`); + updateProcess.once("close", (code) => { + if (!updateSuccess) { + logger.warn(`Update exited without success. Exit code: ${code}`); + controlEmitter.emit("progress", "Update failed!", 100); + controlEmitter.emit("exec", "update", "fail"); + } + }); + } else { + updateProcess.once("close", (code) => { + if (updateSuccess) { + res.json({ success: true }); + } else { + logger.warn(`Update exited without success. Exit code: ${code}`); + res.status(501).json({ error: "Update was not successful" }); + } + controlEmitter.emit("exec", "update", "end"); + }); + } + } else { + logger.error("Update could not be started."); + res.status(501).json({ error: "Update could not be started." }); + controlEmitter.emit("exec", "update", "end"); } + } else if (serverInfo.serverState.serverRunning) { + logger.warn("Update triggered, while server running."); + res.status(503).json({ error: "Server is running - stop before updating" }); + } else if (serverInfo.serverState.operationPending != "none") { + logger.warn( + `Update triggered, while ${serverInfo.serverState.operationPending} pending` + ); + res + .status(503) + .json({ + error: `Another Operation is Pending: ${serverInfo.serverState.operationPending}`, + }); + } }); -//change map +// change map /** * @apiDescription Change Map * @@ -623,102 +706,117 @@ router.get('/control/update', (req, res) => { * HTTP/1.1 501 Internal Server Error * { "error": "RCON error: Unable to write to socket" } */ -router.get('/control/changemap', (req, res) => { - var args = req.query; - if (serverInfo.serverState.operationPending == 'none') { - controlEmitter.emit('exec', 'mapchange', 'start'); - // only try to change map, if it exists on the server. - let map = sf.getMap(args.map); - if (map != undefined) { - let mapchangeCommand = ''; - if (map.official) { - mapchangeCommand = `map ${map.name}`; - } else { - if (map.workshopID != '') { - mapchangeCommand = `host_workshop_map ${map.workshopID}`; - } else { - mapchangeCommand = `ds_workshop_changelevel ${map.name}`; - } - } - - sf.executeRcon(mapchangeCommand).then((answer) => { - // Answer on success (unfortunately only available for official maps): - // Changelevel to de_nuke - // changelevel "de_nuke" - // CHostStateMgr::QueueNewRequest( Changelevel (de_nuke), 5 ) - // - // Answer on failure: - // changelevel de_italy: invalid map name - if (map.official && answer.indexOf(`CHostStateMgr::QueueNewRequest( Changelevel (${map.name})`) == -1) { - // If the mapchange command fails, return failure immediately - res.status(501).json({ "error": `Mapchange failed: ${answer}` }); - controlEmitter.emit('exec', 'mapchange', 'fail'); - } else { - if (!cfg.webSockets) { - // If the mapchange completed, send success and cancel timeout. - let sendCompleted = (operation, action) => { - if (operation == 'mapchange' && action == 'end') { - res.json({ "success": true }); - clearTimeout(mapchangeTimeout); - } - }; - controlEmitter.once('exec', sendCompleted); +router.get("/control/changemap", (req, res) => { + var args = req.query; + if (serverInfo.serverState.operationPending == "none") { + controlEmitter.emit("exec", "mapchange", "start"); + // only try to change map, if it exists on the server. + let map = sf.getMap(args.map); + if (map != undefined) { + let mapchangeCommand = ""; + if (map.official) { + mapchangeCommand = `map ${map.name}`; + } else { + if (map.workshopID != "") { + mapchangeCommand = `host_workshop_map ${map.workshopID}`; + } else { + mapchangeCommand = `ds_workshop_changelevel ${map.name}`; + } + } - // Failure of a mapchange is unfortunately not logged by the server, - // so we use a timeout after 30 sec. - let mapchangeTimeout = setTimeout(() => { - res.status(501).json({ "error": "Mapchange failed - timeout" }); - controlEmitter.emit('exec', 'mapchange', 'fail'); - }, 30000); - } else { - res.json({ "success": true }); - // If the mapchange is successful, cancel the timeout. - let removeTimeout = (operation, action) => { - if (operation == 'mapchange' && action == 'end') { - clearTimeout(mapchangeTimeout); - } - }; - controlEmitter.once('exec', removeTimeout); + sf.executeRcon(mapchangeCommand) + .then((answer) => { + // Answer on success (unfortunately only available for official maps): + // Changelevel to de_nuke + // changelevel "de_nuke" + // CHostStateMgr::QueueNewRequest( Changelevel (de_nuke), 5 ) + // + // Answer on failure: + // changelevel de_italy: invalid map name + if ( + map.official && + answer.indexOf( + `CHostStateMgr::QueueNewRequest( Changelevel (${map.name})` + ) == -1 + ) { + // If the mapchange command fails, return failure immediately + res.status(501).json({ error: `Mapchange failed: ${answer}` }); + controlEmitter.emit("exec", "mapchange", "fail"); + } else { + if (!cfg.webSockets) { + // If the mapchange completed, send success and cancel timeout. + let sendCompleted = (operation, action) => { + if (operation == "mapchange" && action == "end") { + res.json({ success: true }); + clearTimeout(mapchangeTimeout); + } + }; + controlEmitter.once("exec", sendCompleted); - // Failure of a mapchange is unfortunately not logged by the server, - // so we use a timeout after 30 sec. - let mapchangeTimeout = setTimeout(() => { - controlEmitter.emit('exec', 'mapchange', 'fail'); - }, 30000); - } + // Failure of a mapchange is unfortunately not logged by the server, + // so we use a timeout after 30 sec. + let mapchangeTimeout = setTimeout(() => { + res.status(501).json({ error: "Mapchange failed - timeout" }); + controlEmitter.emit("exec", "mapchange", "fail"); + }, 30000); + } else { + res.json({ success: true }); + // If the mapchange is successful, cancel the timeout. + let removeTimeout = (operation, action) => { + if (operation == "mapchange" && action == "end") { + clearTimeout(mapchangeTimeout); } - }).catch((err) => { - res.status(501).json({ "error": `RCON error: ${err.toString()}` }); - controlEmitter.emit('exec', 'mapchange', 'fail'); - }); - } else { - res.status(501).json({ "error": `Map ${args.map} not available` }); - controlEmitter.emit('exec', 'mapchange', 'fail'); - } + }; + controlEmitter.once("exec", removeTimeout); + + // Failure of a mapchange is unfortunately not logged by the server, + // so we use a timeout after 30 sec. + let mapchangeTimeout = setTimeout(() => { + controlEmitter.emit("exec", "mapchange", "fail"); + }, 30000); + } + } + }) + .catch((err) => { + res.status(501).json({ error: `RCON error: ${err.toString()}` }); + controlEmitter.emit("exec", "mapchange", "fail"); + }); } else { - logger.warn(`Mapchange triggered, while ${serverInfo.serverState.operationPending} pending.`); - res.status(503).json({ "error": `Another Operation is Pending: ${serverInfo.serverState.operationPending}` }); + res.status(501).json({ error: `Map ${args.map} not available` }); + controlEmitter.emit("exec", "mapchange", "fail"); } + } else { + logger.warn( + `Mapchange triggered, while ${serverInfo.serverState.operationPending} pending.` + ); + res + .status(503) + .json({ + error: `Another Operation is Pending: ${serverInfo.serverState.operationPending}`, + }); + } }); /** -* @apiDescription Reload availbale maps from server. -* -* @api {get} /control/reloadMaplist -* @apiVersion 1.0 -* @apiName reloadMaplist -* @apiGroup Control -* -* @apiSuccess {boolean} success -* @apiSuccessExample {json} -* HTTP/1.1 200 OK -* { "success": true } -*/ -router.get('/control/reloadMaplist', (req, res) => { - sf.reloadMaplist().then((answer) => { - res.json(answer); - }).catch((err) => { - res.json(err.message); + * @apiDescription Reload availbale maps from server. + * + * @api {get} /control/reloadMaplist + * @apiVersion 1.0 + * @apiName reloadMaplist + * @apiGroup Control + * + * @apiSuccess {boolean} success + * @apiSuccessExample {json} + * HTTP/1.1 200 OK + * { "success": true } + */ +router.get("/control/reloadMaplist", (req, res) => { + sf.reloadMaplist() + .then((answer) => { + res.json(answer); + }) + .catch((err) => { + res.json(err.message); }); }); @@ -742,16 +840,18 @@ router.get('/control/reloadMaplist', (req, res) => { * @apiErrorExample {string} * 'Error, check server logs for details.' */ -router.get('/rcon', (req, res) => { - var message = req.query.message; - res.set('Content-Type', 'text/plain'); - sf.executeRcon(message).then((answer) => { - res.send(answer); - }).catch((err) => { - res.status(501).send('Error, check server logs for details.'); - logger.error(err); +router.get("/rcon", (req, res) => { + var message = req.query.message; + res.set("Content-Type", "text/plain"); + sf.executeRcon(message) + .then((answer) => { + res.send(answer); + }) + .catch((err) => { + res.status(501).send("Error, check server logs for details."); + logger.error(err); }); }); -//------------------------ END V1.0 ----------------------------// +// ------------------------ END V1.0 ---------------------------- // -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/modules/configClass.js b/modules/configClass.js index 76ac1f8..7ce9a61 100755 --- a/modules/configClass.js +++ b/modules/configClass.js @@ -1,155 +1,187 @@ /** * Config class for CSGO Server API */ -class config { - #userOptions = require('../config.js'); - #screenCommand; - #csgoCommand; - #serverTokenCommand; - #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() { - let args = `-console -usercon -ip 0.0.0.0 +sv_logfile 1 -serverlogging +logaddress_add_http "http://${this.#localIp}:${this.#userOptions.logPort}/log" ${this.#userOptions.csgoOptionalArgs}`; - if (this.#userOptions.workshopCollection != '') { - args += ` +host_workshop_collection ${this.#userOptions.workshopCollection}`; - } - return args; - } - - get apiToken() { - return this.#userOptions.apiToken; - } - get rconPass() { - return this.#userOptions.rconPass; - } - - get admins() { - return this.#userOptions.admins; - } - - get workshopCollection() { - return this.#userOptions.workshopCollection; - } - set workshopCollection(id) { - this.#userOptions.workshopCollection = id; - } - get workshopMaps() { - return this.#userOptions.workshopMaps; - } - set workshopMaps(maps) { - this.#userOptions.workshopMaps = maps; - } - - get redirectPage() { - if (this.#userOptions.redirectPage) { - return this.#userOptions.redirectPage; - } else { - return ('/gameserver.htm'); - } - } - - get loginValidity() { - return this.#userOptions.loginValidity * 60000; - } - - get httpAuth() { - return this.#userOptions.httpAuth; - } - get httpUser() { - return this.#userOptions.httpUser; - } - - get iface() { - return this.#userOptions.iface; - } - - get localIp() { - return this.#localIp; - } - set localIp(ip) { - this.#localIp = ip; - } - get host() { - if (this.#userOptions.host != '' && this.#userOptions.useHttps) { - return this.#userOptions.host; - } else { - return this.#localIp - } +class Config { + #userOptions = require("../config.js"); + #screenCommand; + #csgoCommand; + #serverTokenCommand; + #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() { + let args = `-console -usercon -ip 0.0.0.0 +sv_logfile 1 -serverlogging +logaddress_add_http "http://${ + this.#localIp + }:${this.#userOptions.logPort}/log" ${this.#userOptions.csgoOptionalArgs}`; + if (this.#userOptions.workshopCollection !== "") { + args += ` +host_workshop_collection ${ + this.#userOptions.workshopCollection + }`; + } + return args; + } + + get apiToken() { + return this.#userOptions.apiToken; + } + + get rconPass() { + return this.#userOptions.rconPass; + } + + get admins() { + return this.#userOptions.admins; + } + + get workshopCollection() { + return this.#userOptions.workshopCollection; + } + + set workshopCollection(id) { + this.#userOptions.workshopCollection = id; + } + + get workshopMaps() { + return this.#userOptions.workshopMaps; + } + + set workshopMaps(maps) { + this.#userOptions.workshopMaps = maps; + } + + get redirectPage() { + if (this.#userOptions.redirectPage) { + return this.#userOptions.redirectPage; + } else { + return "/gameserver.htm"; + } + } + + get loginValidity() { + return this.#userOptions.loginValidity * 60000; + } + + get httpAuth() { + return this.#userOptions.httpAuth; + } + + get httpUser() { + return this.#userOptions.httpUser; + } + + get iface() { + return this.#userOptions.iface; + } + + get localIp() { + return this.#localIp; + } + + set localIp(ip) { + this.#localIp = ip; + } + + get host() { + if (this.#userOptions.host !== "" && this.#userOptions.useHttps) { + return this.#userOptions.host; + } else { + return this.#localIp; + } + } + + get apiPort() { + return this.#userOptions.apiPort; + } + + get socketPort() { + return this.#userOptions.socketPort; + } + + get logPort() { + return this.#userOptions.logPort; + } + + get serverCommandline() { + let command = `${this.#screenCommand} ${this.#csgoCommand} ${ + this.#csgoArgs + }`; + if (this._csgoToken !== "") { + command = `${command} ${this.#serverTokenCommand}`; + } + return command; + } + + get steamCommand() { + return this.#userOptions.steamExe; + } + + get updateScript() { + if (this.#userOptions.updateScript !== "") { + return this.#userOptions.updateScript; + } else { + return `${this.#userOptions.csgoDir}update_cs2.txt`; } + } + + get webSockets() { + return this.#userOptions.webSockets; + } + + get useHttps() { + return this.#userOptions.useHttps; + } + + get scheme() { + return this.#userOptions.useHttps ? "https" : "http"; + } + + get httpsCertificate() { + return this.#userOptions.httpsCertificate; + } + + get httpsPrivateKey() { + return this.#userOptions.httpsPrivateKey; + } + + get httpsCa() { + return this.#userOptions.httpsCa; + } + + get corsOrigin() { + return this.#userOptions.corsOrigin; + } + + get sessionSecret() { + return this.#userOptions.sessionSecret; + } - get apiPort() { - return this.#userOptions.apiPort; - } - get socketPort() { - return this.#userOptions.socketPort; - } - get logPort() { - return this.#userOptions.logPort; - } - - get serverCommandline() { - let command = `${this.#screenCommand} ${this.#csgoCommand} ${this.#csgoArgs}`; - if (this._csgoToken != '') { - command = `${command} ${this.#serverTokenCommand}`; - } - return command; - } - get steamCommand() { - return this.#userOptions.steamExe - } - get updateScript() { - if (this.#userOptions.updateScript != ''){ - return this.#userOptions.updateScript; - } else { - return `${this.#userOptions.csgoDir}update_cs2.txt`; - } - } - - get webSockets() { - return this.#userOptions.webSockets; - } - get useHttps() { - return this.#userOptions.useHttps; - } - get scheme() { - return (this.#userOptions.useHttps ? 'https' : 'http'); - } - get httpsCertificate() { - return this.#userOptions.httpsCertificate; - } - get httpsPrivateKey() { - return this.#userOptions.httpsPrivateKey; - } - get httpsCa() { - return this.#userOptions.httpsCa; - } - - get corsOrigin() { - return this.#userOptions.corsOrigin; - } - get sessionSecret() { - return this.#userOptions.sessionSecret; - } - - script(type) { - return this.#userOptions[`${type}Script`]; - } - - get logFile() { - return this.#userOptions.logFile; - } - get logLevel() { - return this.#userOptions.logLevel; - } - get logDays() { - return this.#userOptions.logDays; - } + script(type) { + return this.#userOptions[`${type}Script`]; + } + + get logFile() { + return this.#userOptions.logFile; + } + + get logLevel() { + return this.#userOptions.logLevel; + } + + get logDays() { + return this.#userOptions.logDays; + } } -module.exports = new config(); \ No newline at end of file +module.exports = new Config(); diff --git a/modules/serverInfo.js b/modules/serverInfo.js index 7c28629..0a835f8 100755 --- a/modules/serverInfo.js +++ b/modules/serverInfo.js @@ -1,283 +1,315 @@ -const events = require('events'); -const logger = require('./logger'); - -class serverInfo { - #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 - * @property {string} operationPending - 1 of: none, start, stop, mapchange, update, auth, pause. - * @property {boolean} serverRunning - Is the server process running. - * @property {object} serverRcon - rcon-srcds instance for the server. - * @property {boolean} authenticated - Is the rcon instance authenticated with the server. - */ - - /** @type {serverState} */ - this.#serverState = { - 'operationPending': 'none', - 'serverRunning': false, - 'serverRcon': undefined, - 'authenticated': false - } +const events = require("events"); +const logger = require("./logger"); - // data section - this.#mapFilterType = 'exclude'; // 'include / exclude', - this.#mapFilters = ['ar_', 'dz_', 'gd_', 'lobby_', 'training1']; // [ {string} ] - this.#score = { - 'T': 0, - 'C': 0 - }; +class ServerInfo { + #serverState; - // emitter to notify of changes - this.serverInfoChanged = new events.EventEmitter(); - } + #map = ""; + #mapsAvail = []; + #mapsDetails = [ + // { 'name': '', + // 'official': true/false, + // 'title': '', + // 'workshopID': '', + // 'description': '', + // 'previewLink': '', + // 'tags': [{ "tag": "" }] } + ]; - // getter / setter - get serverState() { - return this.#serverState; - } - set serverState(newVal) { - this.#serverState = newVal; - } + #mapFilterType; + #mapFilters; + #maxRounds = 0; + #pause = false; - get map() { - return this.#map; - } - set map(newMap) { - this.#map = newMap; - this.serverInfoChanged.emit('change'); - } + #players = [ + // { 'name': '', + // 'steamID': '', + // 'team': '', + // 'kills': 0, + // 'deaths': 0 } + ]; - get mapsAvail() { - return this.#mapsAvail; - } - set mapsAvail(newMapsAvail) { - this.#mapsAvail = newMapsAvail; - this.serverInfoChanged.emit('change'); - } - mapList() { - if (this.#mapFilters.length > 0) { - return this.#mapsAvail.filter((map) => { - let found = false; - this.#mapFilters.forEach((filter) => { - if (map.includes(filter)) { - found = true; - } - }); - if (this.#mapFilterType === 'include') { - return found; - } else { - return !found; - } - }); - } else { - return this.#mapsAvail; - } - } + #score; - get mapsDetails() { - return this.#mapsDetails; - } - set mapsDetails(newMapsDetails) { - this.#mapsDetails = newMapsDetails; - this.serverInfoChanged.emit('change'); - } - mapDetails() { - 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) => { - if (map.name.includes(filter)) { - found = true; - } - }); - } - if (this.#mapFilterType === 'include') { - return found; - } else { - return !found; - } - }); + constructor() { + /** + * Stores the state of the controlled server-instance. + * @typedef serverState + * @property {string} operationPending - 1 of: none, start, stop, mapchange, update, auth, pause. + * @property {boolean} serverRunning - Is the server process running. + * @property {object} serverRcon - rcon-srcds instance for the server. + * @property {boolean} authenticated - Is the rcon instance authenticated with the server. + */ + + /** @type {serverState} */ + this.#serverState = { + operationPending: "none", + serverRunning: false, + serverRcon: undefined, + authenticated: false, + }; + + // data section + this.#mapFilterType = "exclude"; // 'include / exclude', + this.#mapFilters = ["ar_", "dz_", "gd_", "lobby_", "training1"]; // [ {string} ] + this.#score = { + T: 0, + C: 0, + }; + + // emitter to notify of changes + this.serverInfoChanged = new events.EventEmitter(); + } + + // getter / setter + get serverState() { + return this.#serverState; + } + + set serverState(newVal) { + this.#serverState = newVal; + } + + get map() { + return this.#map; + } + + set map(newMap) { + this.#map = newMap; + this.serverInfoChanged.emit("change"); + } + + get mapsAvail() { + return this.#mapsAvail; + } + + set mapsAvail(newMapsAvail) { + this.#mapsAvail = newMapsAvail; + this.serverInfoChanged.emit("change"); + } + + mapList() { + if (this.#mapFilters.length > 0) { + return this.#mapsAvail.filter((map) => { + let found = false; + this.#mapFilters.forEach((filter) => { + if (map.includes(filter)) { + found = true; + } + }); + if (this.#mapFilterType === "include") { + return found; } else { - return this.#mapsDetails; + return !found; } + }); + } else { + return this.#mapsAvail; } + } - // Map Filter Methods - get mapFilterType() { - return this.#mapFilterType; - } - set mapFilterType(type) { - if (type === 'include' || type === 'exclude') { - this.#mapFilterType = type; - this.serverInfoChanged.emit('change'); - } - } - get mapFilters() { - return this.#mapFilters; - } - mapFilterAdd(filter) { - this.#mapFilters.push(filter); - this.serverInfoChanged.emit('change'); - } - mapFilterRemove(itemToRemove) { - if (this.#mapFilters.length == 0) { - return (0); + get mapsDetails() { + return this.#mapsDetails; + } + + set mapsDetails(newMapsDetails) { + this.#mapsDetails = newMapsDetails; + this.serverInfoChanged.emit("change"); + } + + mapDetails() { + 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) => { + if (map.name.includes(filter)) { + found = true; + } + }); } - if (typeof itemToRemove === 'number' && this.#mapFilters.length > parseInt(itemToRemove)) { - console.log("removing number"); - this.#mapFilters.splice(parseInt(itemToRemove), 1); - this.serverInfoChanged.emit('change'); + if (this.#mapFilterType === "include") { + return found; } else { - let newFilters = this.#mapFilters.filter((currentItem) => { - return (currentItem != itemToRemove); - }); - this.#mapFilters = newFilters; - this.serverInfoChanged.emit('change'); + return !found; } - return (this.#mapFilters.length); - } - mapFilterReset() { - this.#mapFilterType = 'exclude'; - this.#mapFilters = []; - this.serverInfoChanged.emit('change'); + }); + } else { + return this.#mapsDetails; } + } - get maxRounds() { - return this.#maxRounds; - } - set maxRounds(newMaxRounds) { - if (!Number.isNaN(newMaxRounds)) { - this.#maxRounds = newMaxRounds; - this.serverInfoChanged.emit('change'); - } else { - logger.warn('maxRounds must be a number.'); - } - } + // Map Filter Methods + get mapFilterType() { + return this.#mapFilterType; + } - get score() { - return this.#score; - } - // Accepts array with team (T or C) and score. - set score(newScoreArray) { - this.#score[newScoreArray[1]] = parseInt(newScoreArray[2]); - this.serverInfoChanged.emit('change'); + set mapFilterType(type) { + if (type === "include" || type === "exclude") { + this.#mapFilterType = type; + this.serverInfoChanged.emit("change"); } + } + + get mapFilters() { + return this.#mapFilters; + } + + mapFilterAdd(filter) { + this.#mapFilters.push(filter); + this.serverInfoChanged.emit("change"); + } - get pause() { - return this.#pause; + mapFilterRemove(itemToRemove) { + if (this.#mapFilters.length === 0) { + return 0; } - set pause(state) { - if (typeof(state) == 'boolean') { - this.#pause = state - this.serverInfoChanged.emit('change'); - } else { - logger.warn('Invalid pause state - must be of type Boolean'); - } + if ( + typeof itemToRemove === "number" && + this.#mapFilters.length > parseInt(itemToRemove) + ) { + console.log("removing number"); + this.#mapFilters.splice(parseInt(itemToRemove), 1); + this.serverInfoChanged.emit("change"); + } else { + const newFilters = this.#mapFilters.filter((currentItem) => { + return currentItem !== itemToRemove; + }); + this.#mapFilters = newFilters; + this.serverInfoChanged.emit("change"); } + return this.#mapFilters.length; + } + + mapFilterReset() { + this.#mapFilterType = "exclude"; + this.#mapFilters = []; + this.serverInfoChanged.emit("change"); + } + + get maxRounds() { + return this.#maxRounds; + } - get players() { - return this.#players; + set maxRounds(newMaxRounds) { + if (!Number.isNaN(newMaxRounds)) { + this.#maxRounds = newMaxRounds; + this.serverInfoChanged.emit("change"); + } else { + logger.warn("maxRounds must be a number."); } - addPlayer(newPlayer) { - 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.serverInfoChanged.emit('change'); + } + + get score() { + return this.#score; + } + + // Accepts array with team (T or C) and score. + set score(newScoreArray) { + this.#score[newScoreArray[1]] = parseInt(newScoreArray[2]); + this.serverInfoChanged.emit("change"); + } + + get pause() { + return this.#pause; + } + + set pause(state) { + if (typeof state === "boolean") { + this.#pause = state; + this.serverInfoChanged.emit("change"); + } else { + logger.warn("Invalid pause state - must be of type Boolean"); } - assignPlayer(name, steamID, team) { - if (this.#players.find(x => x.steamID === steamID) == undefined ) { - this.addPlayer({'name': name, 'steamID': steamID }); - } - let player = this.#players.find(x => x.steamID === steamID); - player.team = team.substr(0, 1); - this.serverInfoChanged.emit('change'); + } + + get 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; + } else { + newPlayer.team = "U"; + newPlayer.kills = 0; + newPlayer.deaths = 0; + newPlayer.disconnected = false; + this.#players.push(newPlayer); } - removePlayer(steamID) { - this.#players.find(x => x.steamID === steamID).disconnected = true; - // this.#players.splice(this.#players.findIndex(x => x.steamID === steamID), 1); - this.serverInfoChanged.emit('change'); + this.serverInfoChanged.emit("change"); + } + + assignPlayer(name, steamID, team) { + if (this.#players.find((x) => x.steamID === steamID) === undefined) { + this.addPlayer({ name, steamID }); } - clearPlayers() { - this.#players = []; - this.serverInfoChanged.emit('change'); + const 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.serverInfoChanged.emit("change"); + } + + clearPlayers() { + this.#players = []; + this.serverInfoChanged.emit("change"); + } + + recordKill(killer, victim) { + const killPlayer = this.#players.find((x) => x.steamID === killer); + if (killPlayer !== undefined) { + killPlayer.kills += 1; } - recordKill(killer, victim) { - let killPlayer = this.#players.find(x => x.steamID === killer); - if (killPlayer != undefined) - killPlayer.kills += 1; - let victimPlayer = this.#players.find(x => x.steamID === victim); - if (victimPlayer != undefined) - victimPlayer.deaths += 1; - this.serverInfoChanged.emit('change'); + const victimPlayer = this.#players.find((x) => x.steamID === victim); + if (victimPlayer !== undefined) { + victimPlayer.deaths += 1; } + this.serverInfoChanged.emit("change"); + } - // Methods - getAll() { - return { - 'map': this.#map, - 'mapsAvail': this.mapList(), - 'mapsDetails': this.mapDetails(), - 'maxRounds': this.#maxRounds, - 'score': this.#score, - 'pause': this.#pause, - 'players': this.#players - }; - } + // Methods + getAll() { + return { + map: this.#map, + mapsAvail: this.mapList(), + mapsDetails: this.mapDetails(), + 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.serverInfoChanged.emit('change'); - } - reset() { - // Method to be called on server quit. - this.#map = ''; - this.#mapsAvail = []; - this.#mapsDetails = []; - this.#maxRounds = 0; - this.#pause = false; - this.clearPlayers(); - this.newMatch(); + newMatch() { + this.#score.C = 0; + this.#score.T = 0; + for (const 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.clearPlayers(); + this.newMatch(); + } } -module.exports = new serverInfo(); \ No newline at end of file +module.exports = new ServerInfo(); diff --git a/modules/sharedFunctions.js b/modules/sharedFunctions.js index 6676915..debffcc 100755 --- a/modules/sharedFunctions.js +++ b/modules/sharedFunctions.js @@ -1,12 +1,12 @@ -const https = require('https'); -const queue = require('queue'); -const rcon = require('./rcon-srcds/rcon.js').default; -const logger = require('./logger.js'); -var cfg = require('./configClass.js'); -var serverInfo = require('./serverInfo.js'); -var controlEmitter = require('./controlEmitter.js'); +const https = require("https"); +const Rcon = require("./rcon-srcds/rcon.js").default; +const logger = require("./logger.js"); +const cfg = require("./configClass.js"); +const serverInfo = require("./serverInfo.js"); +const controlEmitter = require("./controlEmitter.js"); +const { default: Queue } = require("queue"); -const rconQ = new queue({ "autostart": true, "timeout": 500, "concurrency": 1 }); +const rconQ = new Queue({ autostart: true, timeout: 500, concurrency: 1 }); /** * Authenticate rcon with server @@ -14,57 +14,60 @@ const rconQ = new queue({ "autostart": true, "timeout": 500, "concurrency": 1 }) * @fires controlEmitter.exec */ function authenticate() { - if (serverInfo.serverState.operationPending != 'auth') { - controlEmitter.emit('exec', 'auth', 'start'); - return new Promise((resolve, reject) => { - if (!serverInfo.serverState.authenticated) { - logger.verbose("RCON authenticating..."); - // since this API is designed to run on the same machine as the server keeping - // default here which is 'localhost' - let authTimeout = setTimeout(() => { - logger.error('Authentication timed out'); - controlEmitter.emit('exec', 'auth', 'fail'); - reject({ "authenticated": false }); - }, 60000); - serverInfo.serverState.serverRcon = new rcon({}); - logger.debug('sending authentication request'); - serverInfo.serverState.serverRcon.authenticate(cfg.rconPass).then(() => { - logger.debug('received authentication'); - controlEmitter.emit('exec', 'auth', 'end'); - clearTimeout(authTimeout); - resolve({ "authenticated": true }); - }).catch((err) => { - if (err == 'Already authenticated') { - logger.verbose('Already authenticated.'); - controlEmitter.emit('exec', 'auth', 'end'); - clearTimeout(authTimeout); - resolve({ "authenticated": true }); - } else { - logger.error("authentication error: " + err); - controlEmitter.emit('exec', 'auth', 'fail'); - clearTimeout(authTimeout); - reject({ "authenticated": false }); - } - }); - - } else { - logger.info('Already authenticated.'); - controlEmitter.emit('exec', 'auth', 'end'); - resolve({ "authenticated": true }); - } - }); - } else { - return new Promise((resolve, reject) => { - if (serverInfo.serverState.authenticated) { - logger.verbose('Already authenticated.'); - resolve({ "authenticated": true }); + if (serverInfo.serverState.operationPending !== "auth") { + controlEmitter.emit("exec", "auth", "start"); + return new Promise((resolve, reject) => { + if (!serverInfo.serverState.authenticated) { + logger.verbose("RCON authenticating..."); + // since this API is designed to run on the same machine as the server keeping + // default here which is 'localhost' + const authTimeout = setTimeout(() => { + logger.error("Authentication timed out"); + controlEmitter.emit("exec", "auth", "fail"); + reject({ authenticated: false }); + }, 60000); + serverInfo.serverState.serverRcon = new Rcon({}); + logger.debug("sending authentication request"); + serverInfo.serverState.serverRcon + .authenticate(cfg.rconPass) + .then(() => { + logger.debug("received authentication"); + controlEmitter.emit("exec", "auth", "end"); + clearTimeout(authTimeout); + resolve({ authenticated: true }); + }) + .catch((err) => { + if (err === "Already authenticated") { + logger.verbose("Already authenticated."); + controlEmitter.emit("exec", "auth", "end"); + clearTimeout(authTimeout); + resolve({ authenticated: true }); } else { - logger.verbose(`Rcon authentication cancelled due to other operation Pending: ${serverInfo.serverState.operationPending}`); - reject({ "authenticated": false }); + logger.error("authentication error: " + err); + controlEmitter.emit("exec", "auth", "fail"); + clearTimeout(authTimeout); + reject({ authenticated: false }); } - }); - } - + }); + } else { + logger.info("Already authenticated."); + controlEmitter.emit("exec", "auth", "end"); + resolve({ authenticated: true }); + } + }); + } else { + return new Promise((resolve, reject) => { + if (serverInfo.serverState.authenticated) { + logger.verbose("Already authenticated."); + resolve({ authenticated: true }); + } else { + logger.verbose( + `Rcon authentication cancelled due to other operation Pending: ${serverInfo.serverState.operationPending}` + ); + reject({ authenticated: false }); + } + }); + } } /** @@ -72,200 +75,221 @@ function authenticate() { * @return {Promise} - Promise object that yields the result of reload. */ 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]=${id}&includechildren=true`, (res) => { - let resData = ''; - res.on('data', (dataChunk) => { - resData += dataChunk; - }); - res.on('end', () => { - try { - let colMaps = [] - let resJson = JSON.parse(resData); - resJson.response.publishedfiledetails[0].children.forEach((colMap) => { - colMaps.push(colMap.publishedfileid); - }) - resolve(colMaps); - } catch (e) { - reject(e); - } - }); - }).on('error', (error) => { - logger.warn(`Steam Workshop Collection request failed: ${error}`); - reject(error); - }); - }); - } + 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]=${id}&includechildren=true`, + (res) => { + let resData = ""; + res.on("data", (dataChunk) => { + resData += dataChunk; + }); + res.on("end", () => { + try { + const colMaps = []; + const resJson = JSON.parse(resData); + resJson.response.publishedfiledetails[0].children.forEach( + (colMap) => { + colMaps.push(colMap.publishedfileid); + } + ); + resolve(colMaps); + } catch (e) { + reject(e); + } + }); + } + ) + .on("error", (error) => { + logger.warn(`Steam Workshop Collection request failed: ${error}`); + reject(error); + }); + }); + } - function getMapDetails(mapIDs, official) { - return new Promise((resolve, reject) => { - let idString = ''; - let i = 0; - mapIDs.forEach( (mapId) => { - idString += `&publishedfileids[${i}]=${mapId}`; - i++; - }); + function getMapDetails(mapIDs, official) { + return new Promise((resolve, reject) => { + const omJson = require("../OfficialMaps.json"); + let idString = ""; + let i = 0; + mapIDs.forEach((mapId) => { + idString += `&publishedfileids[${i}]=${mapId}`; + i++; + }); - https.get(`https://api.steampowered.com/IPublishedFileService/GetDetails/v1?key=${cfg.apiToken}${idString}&appid=730`, (res) => { - let resData = ''; - let returnDetails = []; - res.on('data', (dataChunk) => { - resData += dataChunk; - }); - res.on('end', () => { - if (res.statusCode != 200) { - logger.warn(`getMapDetails api call failed. Status = ${res.statusCode}`); - reject('Api call was unsuccessful'); - } else { - try { - let resJson = JSON.parse(resData); - resJson.response.publishedfiledetails.forEach( details => { - if (details.result == 1) { - let _mapName = ""; - if (details.filename != "") { - let re = /\S+\/(\S+).bsp/; - let matches = details.filename.match(re); - _mapName = matches[1]; - } - returnDetails.push({ - "name": _mapName, - "official": official, - "title": details.title, - "workshopID": details.publishedfileid.toString(), - "description": details.description, - "previewLink": details.preview_url, - "tags": details.tags }); - } else { - logger.warn(`No details for map ${details.publishedfileid.toString()}. Query Result: ${details.result.toString()}`); - } - }); - resolve(returnDetails); - } catch (e) { - logger.warn(`Reading map details failed: ${e}`); - reject('Could not read map details from api response'); - } + https + .get( + `https://api.steampowered.com/IPublishedFileService/GetDetails/v1?key=${cfg.apiToken}${idString}&appid=730`, + (res) => { + let resData = ""; + const returnDetails = []; + res.on("data", (dataChunk) => { + resData += dataChunk; + }); + res.on("end", () => { + if (res.statusCode !== 200) { + logger.warn( + `getMapDetails api call failed. Status = ${res.statusCode}` + ); + reject("Api call was unsuccessful"); + } else { + try { + const resJson = JSON.parse(resData); + resJson.response.publishedfiledetails.forEach((details) => { + if (details.result === 1) { + let _mapName = ""; + if (details.filename !== "") { + const re = /\S+\/(\S+).bsp/; + const matches = details.filename.match(re); + _mapName = matches[1]; + } else if (details.filename === "" && official) { + _mapName = omJson.find((map) => map.id == details.publishedfileid).name; } - }); - }).on('error', (error) => { - logger.warn(`Steam Workshop Maps Request failed: ${error}`); - reject(error); - }); - - }); - } - - function getWorkshopCollectionMapsFromServer() { - return new Promise((resolve, reject) => { - executeRcon('ds_workshop_listmaps ').then((response) => { - let mapArray = response.split(/\r?\n/); - let details = []; - mapArray.forEach((value) => { - mapdetails.push({ - "name": value, - "official": false, - "title": value, - "workshopID": "", - "description": "", - "previewLink": "", - "tags": [], + returnDetails.push({ + name: _mapName, + official, + title: details.title, + workshopID: details.publishedfileid.toString(), + description: details.description, + previewLink: details.preview_url, + tags: details.tags, }); + } else { + logger.warn( + `No details for map ${details.publishedfileid.toString()}. Query Result: ${details.result.toString()}` + ); + } }); - - resolve(details); - }).catch((err) => { - logger.warn(`Could not get workshop collection maps from server: ${err}`); - reject(err); - }); + resolve(returnDetails); + } catch (e) { + logger.warn(`Reading map details failed: ${e}`); + reject("Could not read map details from api response"); + } + } + }); + } + ) + .on("error", (error) => { + logger.warn(`Steam Workshop Maps Request failed: ${error}`); + reject(error); + }); + }); + } + + function getWorkshopCollectionMapsFromServer() { + return new Promise((resolve, reject) => { + executeRcon("ds_workshop_listmaps ") + .then((response) => { + const mapArray = response.split(/\r?\n/); + const details = []; + mapArray.forEach((value) => { + mapdetails.push({ + name: value, + official: false, + title: value, + workshopID: "", + description: "", + previewLink: "", + tags: [], + }); }); - } + resolve(details); + }) + .catch((err) => { + logger.warn( + `Could not get workshop collection maps from server: ${err}` + ); + reject(err); + }); + }); + } + // Available maps will be built from OfficialMaps.json static file, + // workshop collection and mapsfrom config. + const officialMapIds = []; + let workshopMapIds = []; + let mapdetails = []; - // Available maps will be built from OfficialMaps.json static file, - // workshop collection and mapsfrom config. - let officialMapIds = []; - let workshopMapIds = []; - let mapdetails = []; + const omJson = require("../OfficialMaps.json"); - let omJson = require('../OfficialMaps.json'); + omJson.forEach((om) => { + officialMapIds.push(om.id); + }); - omJson.forEach( (om) => { - officialMapIds.push(om.id); - }) + logger.debug("getting official maps"); - logger.debug("getting official maps"); - - try { - mapdetails = await getMapDetails(officialMapIds, true); - } catch(error) { - logger.warn(`Getting official maps details failed: ${error}`); - logger.warn('Falling back to name and ID only'); - // As fallback use name and id from local file. - let alternateDetails = []; - omJson.forEach( (map) => { - alternateDetails.push( { - "name": map.name, - "official": true, - "title": map.name, - "workshopID": map.id, - "description": "", - "previewLink": "", - "tags": [], - }); - }); - mapdetails = alternateDetails; - } + try { + mapdetails = await getMapDetails(officialMapIds, true); + } catch (error) { + logger.warn(`Getting official maps details failed: ${error}`); + logger.warn("Falling back to name and ID only"); + // As fallback use name and id from local file. + const alternateDetails = []; + omJson.forEach((map) => { + alternateDetails.push({ + name: map.name, + official: true, + title: map.name, + workshopID: map.id, + description: "", + previewLink: "", + tags: [], + }); + }); + mapdetails = alternateDetails; + } - if (cfg.workshopCollection != '') { - logger.debug("getting collection ids"); - try { - workshopMapIds = await getWorkshopCollection(cfg.workshopCollection); - } catch (error) { - logger.warn(`Getting Workshop map IDs failed: ${error} + if (cfg.workshopCollection !== "") { + logger.debug("getting collection ids"); + try { + workshopMapIds = await getWorkshopCollection(cfg.workshopCollection); + } catch (error) { + logger.warn(`Getting Workshop map IDs failed: ${error} Trying to get names from server.`); - // As a fallback try to get workshop maps from server via rcon command. - try { - mapdetails.push(...await getWorkshopCollectionMapsFromServer()); - } catch (err) { - logger.warn(`Loading workshop maps from server failed: ${err} + // As a fallback try to get workshop maps from server via rcon command. + try { + mapdetails.push(...(await getWorkshopCollectionMapsFromServer())); + } catch (err) { + logger.warn(`Loading workshop maps from server failed: ${err} Workshop maps not available.`); - } - } } - workshopMapIds.push(...cfg.workshopMaps); + } + } + workshopMapIds.push(...cfg.workshopMaps); - if(workshopMapIds.length > 0) { - logger.debug("getting workshop maps details"); - try { - mapdetails.push(...await getMapDetails(workshopMapIds, false)); - } catch(error) { - logger.warn(`Getting Workshop maps details failed: ${error}`); - // As a fallback try to get workshop maps from server via rcon command. - try { - mapdetails.push(...await getWorkshopCollectionMapsFromServer()); - } catch (err) { - logger.warn(`Loading workshop maps from server failed: ${err} + if (workshopMapIds.length > 0) { + logger.debug("getting workshop maps details"); + try { + mapdetails.push(...(await getMapDetails(workshopMapIds, false))); + } catch (error) { + logger.warn(`Getting Workshop maps details failed: ${error}`); + // As a fallback try to get workshop maps from server via rcon command. + try { + mapdetails.push(...(await getWorkshopCollectionMapsFromServer())); + } catch (err) { + logger.warn(`Loading workshop maps from server failed: ${err} Workshop maps not available.`); - } - } - } - if (mapdetails.length > 1) { - mapdetails.sort((a, b) => a.title.localeCompare(b.title)); } + } + } + if (mapdetails.length > 1) { + mapdetails.sort((a, b) => a.title.localeCompare(b.title)); + } - serverInfo.mapsDetails = mapdetails; - // 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.'); - reject( {"success": false}); - } - }); + serverInfo.mapsDetails = mapdetails; + // 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."); + reject({ success: false }); + } + }); } /** @@ -274,13 +298,17 @@ Workshop maps not available.`); * @return {boolean} if the map was found in the details. */ function getMap(mapToFind) { - let returnMap = undefined; - serverInfo.mapsDetails.forEach( (map) => { - if (map.workshopID == mapToFind || map.name == mapToFind || map.title == mapToFind) { - returnMap = map; - } - }) - return returnMap; + let returnMap; + serverInfo.mapsDetails.forEach((map) => { + if ( + map.workshopID === mapToFind || + map.name === mapToFind || + map.title === mapToFind + ) { + returnMap = map; + } + }); + return returnMap; } /** @@ -289,52 +317,64 @@ function getMap(mapToFind) { * @return {Promise} - Promise Object that contains the rcon response or an error message. */ function executeRcon(message) { - logger.debug(`Executing rcon: ${message}`); - return new Promise((resolve, reject) => { - // To ensure proper reception of answers, we need to send requests one after another. - rconQ.push( () => { - serverInfo.serverState.serverRcon.execute(message).then((answer) => { - logger.debug(answer); - resolve(answer); - }).catch((err) => { - logger.error(`RCON Error: ${err.message}`); - reject(err.message); - }); + logger.debug(`Executing rcon: ${message}`); + return new Promise((resolve, reject) => { + // To ensure proper reception of answers, we need to send requests one after another. + rconQ.push(() => { + serverInfo.serverState.serverRcon + .execute(message) + .then((answer) => { + logger.debug(answer); + resolve(answer); + }) + .catch((err) => { + logger.error(`RCON Error: ${err.message}`); + reject(err.message); }); }); + }); } -/*------------------------- Helper Functions ----------------------------*/ +/* ------------------------- Helper Functions ---------------------------- */ /** * Cuts the bare map-name from the various representations in the servers responses. * @param {string} mapstring - The response of mapname(s) from rcon. * @returns {string} mapstring - The mapname without workshop path or .bsp */ function cutMapName(mapstring) { - if (mapstring.search('workshop') != -1) { - let re = /(\w+)/g; - let matches = mapstring.match(re); - mapstring = matches[2]; - } - if (mapstring.search(".bsp") != -1) { - mapstring = mapstring.substring(0, mapstring.length - 4); - } - return mapstring; + if (mapstring.search("workshop") !== -1) { + const re = /(\w+)/g; + const matches = mapstring.match(re); + mapstring = matches[2]; + } + if (mapstring.search(".bsp") !== -1) { + mapstring = mapstring.substring(0, mapstring.length - 4); + } + return mapstring; } /** * Query the server for mp_maxrounds.and store them in serverInfo */ function queryMaxRounds() { - executeRcon('mp_maxrounds').then((answer) => { - // "mp_maxrounds" = "30" ( def. "0" ) min. 0.000000 game notify replicated - // - max number of rounds to play before server changes maps - let rex = /mp_maxrounds = (\d+)/g; - let matches = rex.exec(answer); - serverInfo.maxRounds = matches[1]; - }).catch((err) => { - logger.error('Error getting Maxrounds: ' + err); + executeRcon("mp_maxrounds") + .then((answer) => { + // "mp_maxrounds" = "30" ( def. "0" ) min. 0.000000 game notify replicated + // - max number of rounds to play before server changes maps + const rex = /mp_maxrounds = (\d+)/g; + const matches = rex.exec(answer); + serverInfo.maxRounds = matches[1]; + }) + .catch((err) => { + logger.error("Error getting Maxrounds: " + err); }); } -module.exports = { authenticate, reloadMaplist, getMap, executeRcon, cutMapName, queryMaxRounds }; \ No newline at end of file +module.exports = { + authenticate, + reloadMaplist, + getMap, + executeRcon, + cutMapName, + queryMaxRounds, +}; diff --git a/package.json b/package.json index bf227e4..f65c240 100644 --- a/package.json +++ b/package.json @@ -1,44 +1,50 @@ -{ - "dependencies": { - "cors": ">=2.8.5", - "express": ">=4.17.1", - "express-rate-limit": ">=5.2.6", - "express-session": ">=1.17.2", - "local-ip": ">=2.0.0", - "node-pty": ">=0.10.1", - "passport": ">=0.6.0", - "passport-http": ">=0.3.0", - "passport-steam": ">=1.0.15", - "queue": "6.0", - "winston": ">=3.3.3", - "winston-daily-rotate-file": ">=4.5.5", - "ws": ">=8.0.0" - }, - "name": "nodejs-cs2-api", - "description": "## Disclaimer The use of this software is at your own risk. It exposes control of your server and shell functions to the internet. Although I did everything to secure the API, any bugs may lead to security breaches on your server. I strongly adivise to use secure connections to prevent possible man-in-the-middle attacks.", - "version": "2.0.0", - "main": "serverControl.js", - "directories": { - "public": "public", - "modules": "modules" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/taraman17/nodejs-cs2-api.git" - }, - "keywords": [ - "cs2", - "Counter-Strike 2", - "cs2_ds", - "api" - ], - "author": "Taraman17", - "license": "GPL-3.0-or-later", - "bugs": { - "url": "https://github.com/taraman17/nodejs-cs2-api/issues" - }, - "homepage": "https://github.com/taraman17/nodejs-cs2-api#readme" -} +{ + "dependencies": { + "cors": ">=2.8.5", + "express": ">=4.17.1", + "express-rate-limit": ">=5.2.6", + "express-session": ">=1.17.2", + "local-ip": ">=2.0.0", + "node-pty": ">=0.10.1", + "passport": ">=0.6.0", + "passport-http": ">=0.3.0", + "passport-steam": ">=1.0.15", + "queue": "6.0", + "winston": ">=3.3.3", + "winston-daily-rotate-file": ">=4.5.5", + "ws": ">=8.0.0" + }, + "name": "nodejs-cs2-api", + "description": "## Disclaimer The use of this software is at your own risk. It exposes control of your server and shell functions to the internet. Although I did everything to secure the API, any bugs may lead to security breaches on your server. I strongly adivise to use secure connections to prevent possible man-in-the-middle attacks.", + "version": "2.0.0", + "main": "serverControl.js", + "directories": { + "public": "public", + "modules": "modules" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/taraman17/nodejs-cs2-api.git" + }, + "keywords": [ + "cs2", + "Counter-Strike 2", + "cs2_ds", + "api" + ], + "author": "Taraman17", + "license": "GPL-3.0-or-later", + "bugs": { + "url": "https://github.com/taraman17/nodejs-cs2-api/issues" + }, + "homepage": "https://github.com/taraman17/nodejs-cs2-api#readme", + "devDependencies": { + "@eslint/js": "^9.14.0", + "eslint": "^9.14.0", + "eslint-plugin-html": "^8.1.2", + "globals": "^15.12.0" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..90dd2aa4072374e6e5b0256db0411013cce395d7 GIT binary patch literal 3262 zcmeHITWAw$6rNlrP3DqGbK9oUG#5Ha(>9mMG-=m%Ep2t%^rF?KEl68i#oe~Caf{W5 zqN|{SvWqMt=!>Wz>OQHcyHG(;1fN9kLBTgc@kQ}LeD-U8EkhV?Eqw})mmO&zLJaxSm{-Xl#uRpIPhPpLMeUNCi+N|YPvtVIa4qL!O(*}kWT$SDm zPnZ`z_7PBkCHQ5=GTw|IhYO8Bkp*`_3b<)?dZUSVx~lwvczST`{FUqf-FtNX?z4Z+ zF3Rm0hT%vq?!d<#9JE?`AieXq>8un_GAt+XA4^j*hfT`#`b06<7;edgTK2XL?7si- z>C3n8A3T2X$Ds>Wn=2NJ4G#||5($u5qN>xOK)cO8wtd(6e{XHrn7w-Iwk#*HX&yKp z8HPa{hla*NZFAD-C27b1%|j=SojCR4&AU4fo`j{Anwpw)Iz2czI6prh2n2HRS~MEA z)F>Y~d@{D_^vL+^pNEg5eR=q(6ntnySt_+Tkj$>zdS>g<8}i1#xBou#{MBpJ?r=Kl z>gte)NHltMbQG;e73g>{7?k7j*7npdvp4J8W{&@L5=}3NU*W23POhzQKTs2!ShFS+ zUANHDl{$Owf>{u3b_W?4Bw~xMt}ZkS2KeLfc6N5UJ>GcBWH6E4zi?2k4mU15(q^4u@lT+Lu{D6ZlCM?s#lUG=;+J#bkCiGiNd}%aOzYH{dsf zqse4*ZR-|!XZ~o9*=#BJ!GSo0Kl%#<9Os_izTS~cL(h@%iJfZx07ptF6q*>{ zR_PVRj=iDOKDR#tdDYDVAe6^ak`zTzO0;?-8)l|w)%sUgSEFloyK~RnKEZ6`SxYR| zXtUeYkxERZ4Fx*eI~>lRvb%Pxvt(I@o_Zba^VA9!8>$v80%}qs6tyu(o@%epBdSa8 z?(PP@PNQcy-o$bs0AD)I(Ofk?ndjU*fVP01E>EKpp{VWm`N1s@ANyu9 z8S)K>L}tElPm6*h0IXcDO316M9pf}LHAOr)^>^20e}6wdxny9JMSX^0(N}0C`J_NA mx7&?EFpZ?YMRQ9GAZQ>AaLAI^@e)Bw3{%chi77wADCz^Ju%iY5 literal 0 HcmV?d00001 diff --git a/public/gameserver.css b/public/gameserver.css index d43e405..1298e7f 100644 --- a/public/gameserver.css +++ b/public/gameserver.css @@ -62,9 +62,6 @@ p { } #buttonRestart, #buttonStop, #addControl { display: none; -} -#startMap { - } #addControl { display: flex; @@ -119,11 +116,40 @@ p { padding-left: 0px; } #playerLists li { - padding-top: 7px; - padding-bottom: 7px; + display: flex; + flex-direction: row; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 3px; margin: 2px; border: 1px solid #333333; - text-align: center; +} +#playerLists li .playerName { + display: flex; + flex-grow: 2; + align-items: center; + font-size: 1.1em; + margin: 2px; +} +#playerLists p { + margin: 2px; +} +#playerLists li .playerScore { + display: flex; + flex-direction: column; + align-content: center; + margin-right: 3px; + padding-left: 0.5em; +} +#playerLists li .playerKills { + display: flex; + font-size: 1em; + text-align: right; +} +#playerLists li .playerDeaths { + display: flex; + font-size: 1em; + text-align: right; } #mapControl { diff --git a/public/gameserver.htm b/public/gameserver.htm index 51ad123..ffdd6c1 100644 --- a/public/gameserver.htm +++ b/public/gameserver.htm @@ -1,126 +1,113 @@ - - - - CS:GO Gameserver - - - - - - -
- -
-
-
Match paused!
-
- - -
-
- - - - - - - -
- Starten mit:   - -
-
-
- -
-
-
-
-
current Map:
-
Rounds: 30 / Left: 30
-
Score: CT: 0 / T: 0
-
-
-

Players:

-
-
-

Counter Terrorists

-
    -
-
-
-

Terrorists

-
    -
-
-
-

Unassigned

-
    -
-
-
-

Spectators

-
    -
-
-
-
-
-
- - - - -
- -
-
- - - - - - + + + + + CS:GO Gameserver + + + + + + +
+ +
+
+
Match paused!
+
+ + +
+
+ + + + + + + +
+ Starten mit:   + +
+
+
+ +
+
+
+
+
current Map:
+
Rounds: 30 / Left: 30
+
Score: CT: 0 / T: 0
+
+
+

Players:

+
+
+

Counter Terrorists

+
    +
+
+
+

Terrorists

+
    +
+
+
+

Unassigned

+
    +
+
+
+

Spectators

+
    +
+
+
+
+
+
+ + + + +
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/public/js/gameserver.js b/public/js/gameserver.js index d5d1fb0..15da11b 100644 --- a/public/js/gameserver.js +++ b/public/js/gameserver.js @@ -1,315 +1,498 @@ // Change here if you don't host the webInterfae on the same host as the NodeJS API -var host = window.location.hostname; -var protocol = window.location.protocol -var address = `${protocol}//${host}:8090/csgoapi`; -var apiPath = `${address}/v1.0` -var maplistFile = './maplist.txt'; +const host = window.location.hostname; +const protocol = window.location.protocol; +const address = `${protocol}//${host}:8090/csgoapi`; +const apiPath = `${address}/v1.0`; +const maplistFile = "./maplist.txt"; // Titles for throbber window. -var titles = { - 'start': 'Starting server', - 'stop': 'Stopping server', - 'auth': 'Authenticating RCON', - 'update': 'Updating server', - 'mapchange': 'Changing map', - 'pause': 'Pausing/Unpausing match' -} +const titles = { + start: "Starting server", + stop: "Stopping server", + auth: "Authenticating RCON", + update: "Updating server", + mapchange: "Changing map", + pause: "Pausing/Unpausing match", +}; // Redirect to login page. function doLogin() { - window.location.href = `${address}/login`; + window.location.href = `${address}/login`; } // Sends a get Request with the headers needed for authentication with the seesion cookie. function sendGet(address, data, callback) { - return $.ajax({ - type: "GET", - url: address, - data: data, - cache: false, - crossDomain: true, - xhrFields: { - withCredentials: true - }, - success: callback - }); + return $.ajax({ + type: "GET", + url: address, + data: data, + cache: false, + crossDomain: true, + xhrFields: { + withCredentials: true, + }, + success: callback, + }); } // Load the maplist for serverstart from maplist.txt function loadMaplist() { - // The Maplist file can be taken from the csgo folder. - $.get(maplistFile, (data) => { - let lines = data.split(/\r\n|\n/); - lines.forEach((map) => { - $("#mapAuswahl").append(``); - }); + // The Maplist file can be taken from the csgo folder. + $.get(maplistFile, (data) => { + let lines = data.split(/\r\n|\n/); + lines.forEach((map) => { + $("#mapAuswahl").append(``); }); + }); } // Setup the Elements according to server status. function setupPage() { - $('#popupCaption').text('Querying Server'); - let getPromise = (path) => { - return Promise.resolve(sendGet(`${address}/${path}`)); - } + $("#popupCaption").text("Querying Server"); + let getPromise = (path) => { + return Promise.resolve(sendGet(`${address}/${path}`)); + }; - let loginCheck = getPromise('loginStatus'); - loginCheck.then((data) => { - if (data.login) { - let authenticated = getPromise('v1.0/info/rconauthstatus'); - authenticated.then((data) => { - if (data.rconauth) { - setupServerRunning(); + let loginCheck = getPromise("loginStatus"); + loginCheck + .then((data) => { + if (data.login) { + let authenticated = getPromise("v1.0/info/rconauthstatus"); + authenticated + .then((data) => { + if (data.rconauth) { + setupServerRunning(); + } else { + let serverRunning = getPromise("v1.0/info/runstatus"); + serverRunning.then((data) => { + if (data.running) { + window.location.href = "./notauth.htm"; } else { - let serverRunning = getPromise('v1.0/info/runstatus'); - serverRunning.then((data) => { - if (data.running) { - window.location.href = './notauth.htm'; - } else { - setupServerStopped(); - } - }); + setupServerStopped(); } - }).catch(() => { - setupServerStopped(); - }); - } else { - setupNotLoggedIn(); - } - }).catch(() => { + }); + } + }) + .catch(() => { + setupServerStopped(); + }); + } else { setupNotLoggedIn(); + } + }) + .catch(() => { + setupNotLoggedIn(); }); - $('#container-popup').css('display', 'none'); + $("#container-popup").css("display", "none"); } function setupNotLoggedIn() { - $('#power-image').hide(0); - $('#startMap').hide(0); - $('#buttonStop').hide(0); - $('#buttonStart').hide(0); - $('#buttonUpdate').hide(0); - $('#buttonLogin').show(0); - $('#addControl').hide(0); - $('#serverInfo').hide(0); - $('#mapControl').hide(0); + $("#power-image").hide(0); + $("#startMap").hide(0); + $("#buttonStop").hide(0); + $("#buttonStart").hide(0); + $("#buttonUpdate").hide(0); + $("#buttonLogin").show(0); + $("#addControl").hide(0); + $("#serverInfo").hide(0); + $("#mapControl").hide(0); } function setupServerRunning() { - $('#power-image').attr('src', 'pic/power-on.png'); - if (socket.readyState != 1) { // if websocket not connected - getMaps(); - } else if ($("#mapSelector div").length < 2) { - socket.send('infoRequest'); - } - $('#startMap').hide(0); - $('#buttonStop').show(0); - $('#buttonStart').hide(0); - $('#buttonUpdate').hide(0); - $('#buttonLogin').hide(0); - $('#addControl').show(0); - $('#serverInfo').css('display', 'flex'); - $('#mapControl').show(0); + $("#power-image").attr("src", "pic/power-on.png"); + if (socket.readyState != 1) { + // if websocket not connected + getMaps(); + } else if ($("#mapSelector div").length < 2) { + socket.send("infoRequest"); + } + $("#startMap").hide(0); + $("#buttonStop").show(0); + $("#buttonStart").hide(0); + $("#buttonUpdate").hide(0); + $("#buttonLogin").hide(0); + $("#addControl").show(0); + $("#serverInfo").css("display", "flex"); + $("#mapControl").show(0); } function setupServerStopped() { - $('#power-image').attr('src', 'pic/power-off.png'); - $('#startMap').show(0); - $('#buttonStart').show(0); - $('#buttonStop').hide(0); - $('#buttonUpdate').show(0); - $('#buttonLogin').hide(0); - $('#serverInfo').hide(0); - $('#addControl').hide(0); - $('#mapControl').hide(0); - $('#mapSelector').hide('fast'); + $("#power-image").attr("src", "pic/power-off.png"); + $("#startMap").show(0); + $("#buttonStart").show(0); + $("#buttonStop").hide(0); + $("#buttonUpdate").show(0); + $("#buttonLogin").hide(0); + $("#serverInfo").hide(0); + $("#addControl").hide(0); + $("#mapControl").hide(0); + $("#mapSelector").hide("fast"); } function clickButton(aButton) { - let action = aButton.value.toLowerCase(); - $('#popupCaption').text(`${titles[action]}`); - $('#popupText').text('Moment bitte!'); - $('#container-popup').css('display', 'flex'); - let startMap = document.getElementById('mapAuswahl').value; - - sendGet(`${apiPath}/control/${action}`, `startmap=${startMap}`).done((data) => { - if (socket.readyState != 1) { // if websocket not connected - if (action != 'update') { - setupPage(); - } - $('#container-popup').hide(); - } - }).fail((err) => { - let errorText = err.responseJSON.error; - if (errorText.indexOf('Another Operation is pending:') != -1) { - let operation = errorText.split(':')[1]; - alert(`${operation} running.\nTry again in a moment.`); - } else { - alert(`command ${action} failed!\nError: ${errorText}`); - window.location.href = './notauth.htm'; - } - if (socket.readyState != 1) { - $('#container-popup').css('display', 'none'); + let action = aButton.value.toLowerCase(); + $("#popupCaption").text(`${titles[action]}`); + $("#popupText").text("Moment bitte!"); + $("#container-popup").css("display", "flex"); + let startMap = document.getElementById("mapAuswahl").value; + + sendGet(`${apiPath}/control/${action}`, `startmap=${startMap}`) + .done((data) => { + if (socket.readyState != 1) { + // if websocket not connected + if (action != "update") { + setupPage(); } + $("#container-popup").hide(); + } + }) + .fail((err) => { + let errorText = err.responseJSON.error; + if (errorText.indexOf("Another Operation is pending:") != -1) { + let operation = errorText.split(":")[1]; + alert(`${operation} running.\nTry again in a moment.`); + } else { + alert(`command ${action} failed!\nError: ${errorText}`); + window.location.href = "./notauth.htm"; + } + if (socket.readyState != 1) { + $("#container-popup").css("display", "none"); + } }); } function showPlayerMenu(event) { - $('#playerDropdown').css({ 'top': event.pageY, 'left': event.pageX, 'display': 'block' }); - $('#playerDropdown').attr('player', event.target.textContent); - // Close the dropdown menu if the user clicks outside of it - window.onclick = function(event) { - if (!event.target.matches('.dropbtn')) { - $('#playerDropdown').css('display', 'none'); - window.onclick = ''; - } + $("#playerDropdown").css({ + top: event.pageY, + left: event.pageX, + display: "block", + }); + $("#playerDropdown").attr("player", event.target.textContent); + // Close the dropdown menu if the user clicks outside of it + window.onclick = function (event) { + if (!event.target.matches(".dropbtn")) { + $("#playerDropdown").css("display", "none"); + window.onclick = ""; } + }; } function movePlayer(event) { - // This function uses sourcemod plugin "moveplayers" -> https://forums.alliedmods.net/showthread.php?p=2471466 - /* "sm_movect" - Move a player to the counter-terrorist team. + // This function uses sourcemod plugin "moveplayers" -> https://forums.alliedmods.net/showthread.php?p=2471466 + /* "sm_movect" - Move a player to the counter-terrorist team. "sm_movespec" - Move a player to the spectators team. "sm_movet" - Move a player to the terrorist team. */ - let player = event.target.parentElement.getAttribute('player') - let command = event.target.getAttribute('command'); - sendGet(`${apiPath}/rcon`, `message=sm_move${command} "${player}"`, (data) => { - // no actions for now. - }); + let player = event.target.parentElement.getAttribute("player"); + let command = event.target.getAttribute("command"); + sendGet( + `${apiPath}/rcon`, + `message=sm_move${command} "${player}"`, + (data) => { + // no actions for now. + } + ); } function getMaps() { - function getServerInfo() { - return Promise.resolve(sendGet(`${apiPath}/info/serverInfo`)); - } - let serverInfo = getServerInfo(); - serverInfo.then((data) => { - $("#currentMap").html(`Current map: ${data.map}`); - maplist = data.mapsDetails; - $("#mapSelector").empty(); - maplist.forEach((map) => { - if ('content' in document.createElement('template')) { - var mapDiv = document.querySelector('#maptemplate'); - mapDiv.content.querySelector('.mapname').textContent = map.title; - mapDiv.content.querySelector('.mapimg').setAttribute("src", map.previewLink); - mapDiv.content.querySelector('.map').setAttribute("id", map.workshopID); - $('#mapSelector').append(document.importNode(mapDiv.content, true)); - } else { - let alttext = createElement('h2'); - text.html("Your browser does not have HTML template support - please use another browser."); - $('#mapSelector').append(alttext); - } - }); - }).catch((error) => { - // do nothing for now + function getServerInfo() { + return Promise.resolve(sendGet(`${apiPath}/info/serverInfo`)); + } + let serverInfo = getServerInfo(); + serverInfo + .then((data) => { + $("#currentMap").html(`Current map: ${data.map}`); + maplist = data.mapsDetails; + $("#mapSelector").empty(); + maplist.forEach((map) => { + if ("content" in document.createElement("template")) { + var mapDiv = document.querySelector("#maptemplate"); + mapDiv.content.querySelector(".mapname").textContent = map.title; + mapDiv.content + .querySelector(".mapimg") + .setAttribute("src", map.previewLink); + mapDiv.content + .querySelector(".map") + .setAttribute("id", map.workshopID); + $("#mapSelector").append(document.importNode(mapDiv.content, true)); + } else { + let alttext = createElement("h2"); + text.html( + "Your browser does not have HTML template support - please use another browser." + ); + $("#mapSelector").append(alttext); + } + }); + }) + .catch((error) => { + // do nothing for now }); } function toggleMaplist() { - $('#mapSelector').toggle('fast'); + $("#mapSelector").toggle("fast"); } function showPlay(event) { - if (event.currentTarget.classList.contains('active')) { - changeMap(event); - $('.map').removeClass('active'); - } else { - $('.active > .playicon').hide(0); - $('.active').removeClass('active'); - event.currentTarget.classList.add('active'); - event.currentTarget.children[1].style.display = 'block'; - } + if (event.currentTarget.classList.contains("active")) { + changeMap(event); + $(".map").removeClass("active"); + } else { + $(".active > .playicon").hide(0); + $(".active").removeClass("active"); + event.currentTarget.classList.add("active"); + event.currentTarget.children[1].style.display = "block"; + } } function changeMap(event) { - let map = event.currentTarget.firstElementChild.textContent; - $('#mapSelector').hide('fast'); - //$('#popupCaption').text(titles['mapchange']); - //$('#container-popup').css('display', 'flex'); - 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); - - } - }); + let map = event.currentTarget.firstElementChild.textContent; + $("#mapSelector").hide("fast"); + // $('#popupCaption').text(titles['mapchange']); + // $('#container-popup').css('display', 'flex'); + 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); + } + }); } function restartRound() { - sendGet(`${apiPath}/rcon`, `message=mp_restartgame 1`, (data) => { - $('#popupCaption').text(`Restart Round`); - $('#popupText').html(`Round Restarted!`); - $('#container-popup').css('display', 'flex'); - window.setTimeout(() => { - $('#container-popup').css('display', 'none'); - }, 1000); - }); + sendGet(`${apiPath}/rcon`, `message=mp_restartgame 1`, (data) => { + $("#popupCaption").text(`Restart Round`); + $("#popupText").html(`Round Restarted!`); + $("#container-popup").css("display", "flex"); + window.setTimeout(() => { + $("#container-popup").css("display", "none"); + }, 1000); + }); } function pauseGame() { - sendGet(`${apiPath}/control/pause`).done((data) => { - if (data.success) { - if (socket.readyState != 1) { // if websocket not connected - $('#pause-overlay').css('top', $('#serverControl').position().top); - $('#pause-overlay').css('height', $('#serverInfo').height() + $('#serverControl').height()); - $('#pause-overlay').css('display', 'flex'); - } - } else { - alert('Pausing the match failed!'); - } - }); + sendGet(`${apiPath}/control/pause`).done((data) => { + if (data.success) { + if (socket.readyState != 1) { + // if websocket not connected + $("#pause-overlay").css("top", $("#serverControl").position().top); + $("#pause-overlay").css( + "height", + $("#serverInfo").height() + $("#serverControl").height() + ); + $("#pause-overlay").css("display", "flex"); + } + } else { + alert("Pausing the match failed!"); + } + }); } function resumeGame() { - sendGet(`${apiPath}/control/unpause`).done((data) => { - if (data.success) { - if (socket.readyState != 1) { // if websocket not connected - $('#pause-overlay').hide(); - } - } else { - alert('Unpausing the match failed!'); - } - }); + sendGet(`${apiPath}/control/unpause`).done((data) => { + if (data.success) { + if (socket.readyState != 1) { + // if websocket not connected + $("#pause-overlay").hide(); + } + } else { + alert("Unpausing the match failed!"); + } + }); } function authenticate(caller) { - sendGet(`${apiPath}/authenticate`).done((data) => { - if (data.authenticated) { - window.location.href = './gameserver.htm'; - } else { - caller.disabled = true; - $('#autherror').show('fast'); - } - }); + sendGet(`${apiPath}/authenticate`).done((data) => { + if (data.authenticated) { + window.location.href = "./gameserver.htm"; + } else { + caller.disabled = true; + $("#autherror").show("fast"); + } + }); } function kill(caller) { - sendGet(`${apiPath}/control/kill`).done((data) => { - window.location.href = './gameserver.htm'; - }).fail((error) => { - caller.disabled = true; - $('#killerror').show('fast'); + sendGet(`${apiPath}/control/kill`) + .done((data) => { + window.location.href = "./gameserver.htm"; + }) + .fail((error) => { + caller.disabled = true; + $("#killerror").show("fast"); }); } - // Bot Training functions function setBotRules() { - sendGet(`${apiPath}/rcon`, `message=mp_autoteambalance 0`); - sendGet(`${apiPath}/rcon`, `message=mp_limitteams 0`); - sendGet(`${apiPath}/rcon`, `message=bot_difficulty 3`); + sendGet(`${apiPath}/rcon`, `message=mp_autoteambalance 0`); + sendGet(`${apiPath}/rcon`, `message=mp_limitteams 0`); + sendGet(`${apiPath}/rcon`, `message=bot_difficulty 3`); } function addBots(team, quantity) { - for (let i = 0; i < quantity; i++) { - setTimeout(sendGet(`${apiPath}/rcon`, `message=bot_add_${team}`), 100); - } + for (let i = 0; i < quantity; i++) { + setTimeout(sendGet(`${apiPath}/rcon`, `message=bot_add_${team}`), 100); + } } function kickBots() { - sendGet(`${apiPath}/rcon`, `message=bot_kick all`); -} \ No newline at end of file + sendGet(`${apiPath}/rcon`, `message=bot_kick all`); +} + +// what to do after document is loaded. +var socket = null; +$(document).ready(() => { + let startSocket = () => { + try { + if (protocol == "https:") { + socket = new WebSocket(`wss://${host}:8091`); + } else { + socket = new WebSocket(`ws://${host}:8091`); + } + } catch (err) { + console.error("Connection to websocket failed:\n" + err); + } + + socket.onopen = () => { + socket.send("infoRequest"); + }; + + socket.onmessage = (e) => { + let data = JSON.parse(e.data); + + if (data.type == "serverInfo") { + let serverInfo = data.payload; + + $("#currentMap").html(`Current map: ${serverInfo.map}`); + $("#scoreT").text(serverInfo.score.T); + $("#scoreCT").text(serverInfo.score.C); + $("#rounds").html( + `Rounds: ${serverInfo.maxRounds} / Left: ${ + serverInfo.maxRounds - (serverInfo.score.T + serverInfo.score.C) + }` + ); + + $(".playerDiv ul").empty(); + $(".playerDiv").hide(0); + if (serverInfo.players.length > 0) { + for (let i = 0; i < serverInfo.players.length; i++) { + let player = serverInfo.players[i]; + if (player.disconnected) { + break; + } + if ("content" in document.createElement("template")) { + var playerLi = document.querySelector("#playerTemplate"); + playerLi.content.querySelector(".playerName").textContent = + player.name; + playerLi.content.querySelector( + ".playerKills" + ).textContent = `K: ${player.kills}`; + playerLi.content.querySelector( + ".playerDeaths" + ).textContent = `D: ${player.deaths}`; + $(`#${player.team.toLowerCase()}List`).append( + document.importNode(playerLi.content, true) + ); + } else { + let alttext = document.createElement("li"); + alttext.html( + "Your browser does not have HTML template support - please use another browser." + ); + $(`#${player.team.toLowerCase()}List`).append(alttext); + } + $(`#${player.team.toLowerCase()}Players`).show(0); + } + } + if (serverInfo.pause) { + $("#pause-overlay").css("top", $("#serverControl").position().top); + $("#pause-overlay").css( + "height", + $("#serverInfo").height() + $("#serverControl").height() + ); + $("#pause-overlay").css("display", "flex"); + } else { + $("#pause-overlay").hide(); + } + if ($("#mapSelector .map").length != serverInfo.mapsDetails.length) { + if (serverInfo.mapsDetails) { + let maplist = serverInfo.mapsDetails; + $("#mapSelector").empty(); + maplist.forEach((map) => { + if ("content" in document.createElement("template")) { + var mapDiv = document.querySelector("#maptemplate"); + mapDiv.content.querySelector(".mapname").textContent = + map.title; + mapDiv.content + .querySelector(".mapimg") + .setAttribute("src", map.previewLink ? map.previewLink : ""); + mapDiv.content + .querySelector(".map") + .setAttribute("id", map.workshopID); + $("#mapSelector").append( + document.importNode(mapDiv.content, true) + ); + } else { + let alttext = document.createElement("h2"); + alttext.html( + "Your browser does not have HTML template support - please use another browser." + ); + $("#mapSelector").append(alttext); + } + }); + } + } + } else if (data.type == "commandstatus") { + if (data.payload.state == "start") { + $("#popupCaption").text(`${titles[data.payload.operation]}`); + $("#popupText").text("Moment bitte!"); + $("#container-popup").css("display", "flex"); + } else if ( + data.payload.state == "end" && + data.payload.operation != "start" + ) { + $("#popupText").html(`${data.payload.operation} success!`); + setTimeout(() => { + $("#container-popup").css("display", "none"); + setupPage(); + }, 1500); + } else if (data.payload.state == "fail") { + $("#popupText").html(`${data.payload.operation} failed!`); + setTimeout(() => { + $("#container-popup").css("display", "none"); + if ( + data.payload.operation != "update" && + data.payload.operation != "mapchange" + ) { + window.location.href = "./notauth.htm"; + } + }, 3000); + } + } else if (data.type == "progress") { + $("#popupText").html(`${data.payload.step}: ${data.payload.progress}%`); + } else if (data.type == "mapchange") { + if ( + data.payload.success$ && + $("#popupCaption").text() == "Changing Map" + ) { + socket.send("infoRequest"); + $("#container-popup").css("display", "none"); + } else if (!data.payload.success) { + $("#popupText").html("Mapchange failed!"); + } + } + }; + + socket.onclose = () => { + // connection closed, discard old websocket and create a new one in 5s + socket = null; + setTimeout(startSocket, 5000); + }; + }; + startSocket(); + loadMaplist(); + setupPage(); +}); diff --git a/public/js/onload.js b/public/js/onload.js deleted file mode 100644 index c1f3c90..0000000 --- a/public/js/onload.js +++ /dev/null @@ -1,121 +0,0 @@ -// what to do after document is loaded. -var socket = null; -$(document).ready(() => { - let startSocket = () => { - try { - if (protocol == 'https:') { - socket = new WebSocket(`wss://${host}:8091`); - } else { - socket = new WebSocket(`ws://${host}:8091`); - } - } catch (err) { - console.error('Connection to websocket failed:\n' + err); - } - - socket.onopen = () => { - socket.send('infoRequest'); - } - - socket.onmessage = (e) => { - let data = JSON.parse(e.data); - - if (data.type == "serverInfo") { - let serverInfo = data.payload; - - $("#currentMap").html(`Current map: ${serverInfo.map}`); - $('#scoreT').text(serverInfo.score.T); - $('#scoreCT').text(serverInfo.score.C); - $('#rounds').html( - `Rounds: ${serverInfo.maxRounds} / Left: ${serverInfo.maxRounds - (serverInfo.score.T + serverInfo.score.C)}` - ); - - $('.playerDiv ul').empty(); - $('.playerDiv').hide(0); - if (serverInfo.players.length > 0) { - for (let i = 0; i < serverInfo.players.length; i++) { - let player = serverInfo.players[i]; - if (player.disconnected) { - break; - } - if ('content' in document.createElement('template')) { - var playerLi = document.querySelector('#playerTemplate'); - playerLi.content.querySelector('.playerName').textContent = player.name; - playerLi.content.querySelector('.playerKills').textContent = `K: ${player.kills}`; - playerLi.content.querySelector('.playerDeaths').textContent = `D: ${player.deaths}`; - $(`#${player.team.toLowerCase()}List`).append(document.importNode(playerLi.content, true)); - } else { - let alttext = document.createElement('li'); - alttext.html("Your browser does not have HTML template support - please use another browser."); - $(`#${player.team.toLowerCase()}List`).append(alttext); - } - $(`#${player.team.toLowerCase()}Players`).show(0); - } - } - if (serverInfo.pause) { - $('#pause-overlay').css('top', $('#serverControl').position().top); - $('#pause-overlay').css('height', $('#serverInfo').height() + $('#serverControl').height()); - $('#pause-overlay').css('display', 'flex'); - } else { - $('#pause-overlay').hide(); - } - if ($('#mapSelector .map').length != serverInfo.mapsDetails.length) { - if (serverInfo.mapsDetails) { - let maplist = serverInfo.mapsDetails; - $("#mapSelector").empty(); - maplist.forEach((map) => { - if ('content' in document.createElement('template')) { - var mapDiv = document.querySelector('#maptemplate'); - mapDiv.content.querySelector('.mapname').textContent = map.title; - mapDiv.content.querySelector('.mapimg').setAttribute("src", map.previewLink ? map.previewLink : ''); - mapDiv.content.querySelector('.map').setAttribute("id", map.workshopID); - $('#mapSelector').append(document.importNode(mapDiv.content, true)); - } else { - let alttext = document.createElement('h2'); - alttext.html("Your browser does not have HTML template support - please use another browser."); - $('#mapSelector').append(alttext); - } - }); - } - } - } else if (data.type == "commandstatus") { - if (data.payload.state == 'start') { - $('#popupCaption').text(`${titles[data.payload.operation]}`); - $('#popupText').text('Moment bitte!'); - $('#container-popup').css('display', 'flex'); - } else if (data.payload.state == 'end' && data.payload.operation != 'start') { - $('#popupText').html(`${data.payload.operation} success!`); - setTimeout(() => { - $('#container-popup').css('display', 'none'); - setupPage(); - }, 1500); - } else if (data.payload.state == 'fail') { - $('#popupText').html(`${data.payload.operation} failed!`); - setTimeout(() => { - $('#container-popup').css('display', 'none'); - if (data.payload.operation != 'update' && data.payload.operation != 'mapchange') { - window.location.href = './notauth.htm'; - } - }, 3000); - } - } else if (data.type == "progress") { - $('#popupText').html(`${data.payload.step}: ${data.payload.progress}%`); - } else if (data.type == "mapchange") { - if (data.payload.success$ && $('#popupCaption').text() == 'Changing Map') { - socket.send('infoRequest'); - $('#container-popup').css('display', 'none'); - } else if (!data.payload.success) { - $('#popupText').html(`Mapchange failed!`); - } - } - } - - socket.onclose = () => { - // connection closed, discard old websocket and create a new one in 5s - socket = null; - setTimeout(startSocket, 5000); - } - } - startSocket(); - loadMaplist(); - setupPage(); -}); \ No newline at end of file diff --git a/public/maplist.txt b/public/maplist.txt index 79a0f27..4d41605 100644 --- a/public/maplist.txt +++ b/public/maplist.txt @@ -1,10 +1,10 @@ -cs_italy -cs_office -de_ancient -de_anubis -de_dust2 -de_inferno -de_mirage -de_nuke -de_overpass +cs_italy +cs_office +de_ancient +de_anubis +de_dust2 +de_inferno +de_mirage +de_nuke +de_overpass de_vertigo \ No newline at end of file diff --git a/serverControl.js b/serverControl.js index 819ca3b..14014a2 100755 --- a/serverControl.js +++ b/serverControl.js @@ -19,171 +19,189 @@ * @requires ./modules/sharedFunctions.js */ -const express = require('express'); -const session = require('express-session'); -const rateLimit = require('express-rate-limit'); -const cors = require('cors'); -const passport = require('passport'); -const SteamStrategy = require('passport-steam').Strategy; -const BasicStrategy = require('passport-http').BasicStrategy; -const webSocket = require('ws'); -const fs = require('fs'); -const { exec } = require('child_process'); -const logger = require('./modules/logger.js'); -var serverInfo = require('./modules/serverInfo.js'); -var cfg = require('./modules/configClass.js'); -const sf = require('./modules/sharedFunctions.js'); - -cfg.localIp = require('local-ip')(cfg.iface); -var http = undefined; -var httpsCredentials = {}; +const express = require("express"); +const session = require("express-session"); +const rateLimit = require("express-rate-limit"); +const cors = require("cors"); +const passport = require("passport"); +const SteamStrategy = require("passport-steam").Strategy; +const BasicStrategy = require("passport-http").BasicStrategy; +const webSocket = require("ws"); +const fs = require("fs"); +const { exec } = require("child_process"); +const logger = require("./modules/logger.js"); +const serverInfo = require("./modules/serverInfo.js"); +const cfg = require("./modules/configClass.js"); +const sf = require("./modules/sharedFunctions.js"); + +cfg.localIp = require("local-ip")(cfg.iface); +let http; +let httpsCredentials = {}; // if configured for https, we fork here. if (cfg.useHttps) { - http = require('https'); - httpsCredentials = { - key: fs.readFileSync(cfg.httpsPrivateKey), - cert: fs.readFileSync(cfg.httpsCertificate), - }; - if (cfg.httpsCa != '') { - httpsCredentials.ca = fs.readFileSync(cfg.httpsCa) - } + http = require("https"); + httpsCredentials = { + key: fs.readFileSync(cfg.httpsPrivateKey), + cert: fs.readFileSync(cfg.httpsCertificate), + }; + if (cfg.httpsCa !== "") { + httpsCredentials.ca = fs.readFileSync(cfg.httpsCa); + } } else { - http = require('http'); + http = require("http"); } // check for running Server on Startup -exec('/bin/ps -A', (error, stdout, stderr) => { - if (error) { - logger.error(`exec error: ${error}`); - logger.error(stderr) - return; - } - if (stdout.match(/cs2/) != null) { - serverInfo.serverState.serverRunning = true; - logger.verbose('Found running server'); - sf.authenticate().then((data) => { - logger.verbose(`authentication ${data.authenticated}`); - sf.executeRcon(`logaddress_add_http "http://${cfg.localIp}:${cfg.logPort}/log`); - sf.executeRcon(`host_workshop_collection ${cfg.workshopCollection}`); - }).catch((data) => { - logger.verbose(`authentication ${data.authenticated}`); - }); - } +exec("/bin/ps -A", (error, stdout, stderr) => { + if (error) { + logger.error(`exec error: ${error}`); + logger.error(stderr); + return; + } + if (stdout.match(/cs2/) != null) { + serverInfo.serverState.serverRunning = true; + logger.verbose("Found running server"); + sf.authenticate() + .then((data) => { + logger.verbose(`authentication ${data.authenticated}`); + sf.executeRcon( + `logaddress_add_http "http://${cfg.localIp}:${cfg.logPort}/log` + ); + sf.executeRcon(`host_workshop_collection ${cfg.workshopCollection}`); + }) + .catch((data) => { + logger.verbose(`authentication ${data.authenticated}`); + }); + } }); // Event Emitters -var controlEmitter = require('./modules/controlEmitter.js'); +const controlEmitter = require("./modules/controlEmitter.js"); /** * Sets the operationPending variable on events. Gathers Information on RCON authentication. * @listens controlEmitter#exec */ -controlEmitter.on('exec', (operation, action) => { - serverInfo.serverState.operationPending = (action == 'start') ? operation : 'none'; - logger.debug('serverInfo.serverState.operationPending = ' + serverInfo.serverState.operationPending); - if (operation == 'auth' && action == 'end') { - serverInfo.serverState.authenticated = true; - logger.debug('serverInfo.serverState.authenticated = ' + serverInfo.serverState.authenticated); - logger.verbose("RCON Authenticate success"); - // Get current and available maps and store them. - sf.executeRcon('status').then((answer) => { - let re = /\[1: (\w+) \|/; - let matches = re.exec(answer); - let mapstring = matches[1]; - serverInfo.map = sf.cutMapName(mapstring); - }); - sf.reloadMaplist().then(() => { - // Be happy and do nothing - }).catch((err) => { - logger.warn(`Maps could not be loaded: ${err}`); - }); - sf.queryMaxRounds(); - } +controlEmitter.on("exec", (operation, action) => { + serverInfo.serverState.operationPending = + action === "start" ? operation : "none"; + logger.debug( + "serverInfo.serverState.operationPending = " + + serverInfo.serverState.operationPending + ); + if (operation === "auth" && action === "end") { + serverInfo.serverState.authenticated = true; + logger.debug( + "serverInfo.serverState.authenticated = " + + serverInfo.serverState.authenticated + ); + logger.verbose("RCON Authenticate success"); + // Get current and available maps and store them. + sf.executeRcon("status").then((answer) => { + const re = /\[1: (\w+) \|/; + const matches = re.exec(answer); + const mapstring = matches[1]; + serverInfo.map = sf.cutMapName(mapstring); + }); + sf.reloadMaplist() + .then(() => { + // Be happy and do nothing + }) + .catch((err) => { + logger.warn(`Maps could not be loaded: ${err}`); + }); + sf.queryMaxRounds(); + } }); -/*----------------- HTTP Server Code -------------------*/ +/* ----------------- HTTP Server Code ------------------- */ /** * Creates an express server to handle the API requests */ const app = express(); -var apiV10 = require('./modules/apiV10.js'); +const apiV10 = require("./modules/apiV10.js"); const limit = rateLimit({ - max: 50, // max requests - windowMs: 60 * 1000, // 1 Minute - message: 'Too many requests' // message to send + max: 50, // max requests + windowMs: 60 * 1000, // 1 Minute + message: "Too many requests", // message to send }); app.use(limit); -app.use(session({ +app.use( + session({ secret: cfg.sessionSecret, name: `csgo-api-${cfg.host}`, cookie: { - expires: cfg.loginValidity, - secure: cfg.useHttps + expires: cfg.loginValidity, + secure: cfg.useHttps, }, resave: true, - saveUninitialized: true -})); -app.use(cors({ + saveUninitialized: true, + }) +); +app.use( + cors({ origin: cfg.host, - credentials: true -})); + credentials: true, + }) +); app.use(passport.initialize()); app.use(passport.session()); -app.use(express.static('public')); +app.use(express.static("public")); -app.disable('x-powered-by'); +app.disable("x-powered-by"); -//--------------------------- Steam authentication ----------------------------// +// --------------------------- Steam authentication ---------------------------- // // Setup Passport for SteamStrategy passport.serializeUser((user, done) => { - done(null, user); + done(null, user); }); passport.deserializeUser((obj, done) => { - done(null, obj); + done(null, obj); }); passport.use( - new SteamStrategy({ - returnURL: `${cfg.scheme}://${cfg.host}:${cfg.apiPort}/csgoapi/login/return`, - realm: `${cfg.scheme}://${cfg.host}:${cfg.apiPort}/`, - profile: false - }, - (identifier, profile, done) => { - process.nextTick( () => { - - // Cut the SteamID64 from the returned User-URI - let steamID64 = identifier.split('/')[5]; - profile.identifier = steamID64; - logger.http({ - 'user': `${steamID64}`, - 'message': 'logged in' - }); - return done(null, profile); - }); - } - )); + new SteamStrategy( + { + returnURL: `${cfg.scheme}://${cfg.host}:${cfg.apiPort}/csgoapi/login/return`, + realm: `${cfg.scheme}://${cfg.host}:${cfg.apiPort}/`, + profile: false, + }, + (identifier, profile, done) => { + process.nextTick(() => { + // Cut the SteamID64 from the returned User-URI + const steamID64 = identifier.split("/")[5]; + profile.identifier = steamID64; + logger.http({ + user: `${steamID64}`, + message: "logged in", + }); + return done(null, profile); + }); + } + ) +); function ensureAuthenticated(req, res, next) { - if (req.isAuthenticated()) { - if (cfg.admins.includes(req.user.identifier)) { - logger.http({ - 'user': `${req.user.identifier}`, - 'message': `${req.method}:${req.url}` - }); - return next(); - } else { - logger.info({ - 'user': `${req.user.identifier}`, - 'message': 'User not in Admin list.' - }); - return res.status(401).send('User not in Admin list.'); - } + if (req.isAuthenticated()) { + if (cfg.admins.includes(req.user.identifier)) { + logger.http({ + user: `${req.user.identifier}`, + message: `${req.method}:${req.url}`, + }); + return next(); + } else { + logger.info({ + user: `${req.user.identifier}`, + message: "User not in Admin list.", + }); + return res.status(401).send("User not in Admin list."); } - logger.warn({ - 'user': 'unknown', - 'message': `Unauthorized Access from ${req.ip}.` - }); - return res.status(401).send('Not logged in.'); + } + logger.warn({ + user: "unknown", + message: `Unauthorized Access from ${req.ip}.`, + }); + return res.status(401).send("Not logged in."); } /** @@ -195,13 +213,10 @@ function ensureAuthenticated(req, res, next) { * @apiSuccess (302) Redirect to confiured page. * @apiError (302) Redirect to /csgoapi/loginStatus */ -app.get('/csgoapi/login', - passport.authenticate('steam'), - () => { - // The request will be redirected to Steam for authentication, so - // this function will not be called. - } -); +app.get("/csgoapi/login", passport.authenticate("steam"), () => { + // The request will be redirected to Steam for authentication, so + // this function will not be called. +}); /** * @api {get} /csgoapi/login/return * @apiVersion 1.0 @@ -211,11 +226,12 @@ app.get('/csgoapi/login', * @apiSuccess (302) Redirect to confiured page. * @apiError (302) Redirect to /csgoapi/loginStatus */ -app.get('/csgoapi/login/return', - passport.authenticate('steam', { failureRedirect: '/csgoapi/loginStatus' }), - (req, res) => { - res.redirect(cfg.redirectPage); - } +app.get( + "/csgoapi/login/return", + passport.authenticate("steam", { failureRedirect: "/csgoapi/loginStatus" }), + (req, res) => { + res.redirect(cfg.redirectPage); + } ); /** * @api {get} /csgoapi/logout @@ -225,20 +241,20 @@ app.get('/csgoapi/login/return', * * @apiSuccess (302) Redirect to configured page. */ -app.get('/csgoapi/logout', (req, res) => { - logger.http({ - 'user': `${req.user.identifier}`, - 'message': 'logged out' - }); - req.logout((err) => { - if (err) { - logger.warn({ - 'user': `${req.user.identifier}`, - 'message': `logout failed: ${err}` - }); - } - res.redirect(cfg.redirectPage); - }); +app.get("/csgoapi/logout", (req, res) => { + logger.http({ + user: `${req.user.identifier}`, + message: "logged out", + }); + req.logout((err) => { + if (err) { + logger.warn({ + user: `${req.user.identifier}`, + message: `logout failed: ${err}`, + }); + } + res.redirect(cfg.redirectPage); + }); }); /** @@ -254,160 +270,174 @@ app.get('/csgoapi/logout', (req, res) => { * HTTP/1.1 200 OK * { "login": true/false } */ -app.get('/csgoapi/loginStatus', (req, res) => { - if (req.user && cfg.admins.includes(req.user.identifier)) { - res.json({ "login": true }); - } else { - res.json({ "login": false }); - } +app.get("/csgoapi/loginStatus", (req, res) => { + if (req.user && cfg.admins.includes(req.user.identifier)) { + res.json({ login: true }); + } else { + res.json({ login: false }); + } }); -app.use('/csgoapi/v1.0/', ensureAuthenticated, apiV10); -//------------------------ END Steam authentication ----------------------------// +app.use("/csgoapi/v1.0/", ensureAuthenticated, apiV10); +// ------------------------ END Steam authentication ---------------------------- // -//------------------------ Basic authentication ----------------------------// +// ------------------------ Basic authentication ---------------------------- // if (cfg.httpAuth) { - passport.use(new BasicStrategy({ qop: 'auth', passReqToCallback: true }, - (req, username, password, done) => { - if (username == cfg.httpUser.username) { - if (password == cfg.httpUser.password) { - logger.http({ - "user": username, - "message": `${req.method}:${req.url}` - }); - return done(null, cfg.httpUser.username); - } else { - logger.warn({ - 'user': username, - 'message': `Unauthorized http Access - wrong Password - from ${req.ip}.` - }); - return done(null, false); - } - } else { - logger.warn({ - 'user': username, - 'message': `Unauthorized http Access - unknown user - from ${req.ip}.` - }); - return done(null, false); - } + passport.use( + new BasicStrategy( + { qop: "auth", passReqToCallback: true }, + (req, username, password, done) => { + if (username === cfg.httpUser.username) { + if (password === cfg.httpUser.password) { + logger.http({ + user: username, + message: `${req.method}:${req.url}`, + }); + return done(null, cfg.httpUser.username); + } else { + logger.warn({ + user: username, + message: `Unauthorized http Access - wrong Password - from ${req.ip}.`, + }); + return done(null, false); + } + } else { + logger.warn({ + user: username, + message: `Unauthorized http Access - unknown user - from ${req.ip}.`, + }); + return done(null, false); } - )); - - app.use('/csgoapi/http/v1.0/', passport.authenticate('basic', { session: false }), apiV10); + } + ) + ); + + app.use( + "/csgoapi/http/v1.0/", + passport.authenticate("basic", { session: false }), + apiV10 + ); } -//--------------------- END Basic authentication --------------------------// +// --------------------- END Basic authentication -------------------------- // let server; if (cfg.useHttps) { - server = http.createServer(httpsCredentials, app); + server = http.createServer(httpsCredentials, app); } else { - server = http.createServer(app); + server = http.createServer(app); } server.listen(cfg.apiPort); -//------------------------------- Log receiver ----------------------------// -var logreceive = express(); +// ------------------------------- Log receiver ---------------------------- // +const logreceive = express(); logreceive.use(express.text({ limit: "50mb" })); let logserver; if (cfg.useHttps) { - let loghttp = require('http'); - logserver = loghttp.createServer(logreceive); + const loghttp = require("http"); + logserver = loghttp.createServer(logreceive); } else { - logserver = http.createServer(logreceive); + logserver = http.createServer(logreceive); } -const logroute = require('./modules/logreceive.js'); -logreceive.use('/', logroute); +const logroute = require("./modules/logreceive.js"); +logreceive.use("/", logroute); logserver.listen(cfg.logPort, () => { - logger.info('Logserver listening!'); + logger.info("Logserver listening!"); }); -//----------------------------- END Log receiver --------------------------// +// ----------------------------- END Log receiver -------------------------- // -/*----------------- WebSockets Code -------------------*/ +/* ----------------- WebSockets Code ------------------- */ if (cfg.webSockets) { - let wssServer - if (cfg.useHttps) { - wssServer = http.createServer(httpsCredentials); - } else { - wssServer = http.createServer(); - } - const wss = new webSocket.Server({ server: wssServer }); + let wssServer; + if (cfg.useHttps) { + wssServer = http.createServer(httpsCredentials); + } else { + wssServer = http.createServer(); + } + const wss = new webSocket.Server({ server: wssServer }); + + wssServer.listen(cfg.socketPort, () => { + const host = cfg.host; + logger.verbose(host); + }); + + /** + * Websocket to send data updates to a webClient. + * @listens ws#connection + */ + wss.on("connection", (ws) => { + /** + * Sends updated serverInfo to clients. + */ + const sendUpdate = () => { + ws.send( + `{ "type": "serverInfo", "payload": ${JSON.stringify( + serverInfo.getAll() + )} }` + ); + }; - wssServer.listen(cfg.socketPort, () => { - let host = cfg.host; - logger.verbose(host); + /** + * Listens for messages on Websocket. + * @listens ws#message + */ + ws.on("message", (message) => { + if (message.toString().search("infoRequest") !== -1) { + sendUpdate(); + } }); - /** - * Websocket to send data updates to a webClient. - * @listens ws#connection + * Listens for changed serverInfo and calls function to forward them. + * @listens serverInfo.serverInfoChanged#change */ - wss.on('connection', (ws) => { - /** - * Sends updated serverInfo to clients. - */ - var sendUpdate = () => { - ws.send(`{ "type": "serverInfo", "payload": ${JSON.stringify(serverInfo.getAll())} }`); - } + serverInfo.serverInfoChanged.on("change", sendUpdate); - /** - * Listens for messages on Websocket. - * @listens ws#message - */ - ws.on('message', (message) => { - if (message.toString().search('infoRequest') != -1) { - sendUpdate(); - } - }); - - /** - * Listens for changed serverInfo and calls function to forward them. - * @listens serverInfo.serverInfoChanged#change - */ - serverInfo.serverInfoChanged.on('change', sendUpdate); - - /** - * Notifies clients of start or end of a control operation - * @param {string} operation (start, stop, update, mapchange) - * @param {string} action (start, end, fail) - */ - var sendControlNotification = (operation, action) => { - ws.send(`{ "type": "commandstatus", "payload": { "operation": "${operation}", "state": "${action}" } }`); - } + /** + * Notifies clients of start or end of a control operation + * @param {string} operation (start, stop, update, mapchange) + * @param {string} action (start, end, fail) + */ + const sendControlNotification = (operation, action) => { + ws.send( + `{ "type": "commandstatus", "payload": { "operation": "${operation}", "state": "${action}" } }` + ); + }; - /** - * Listens for execution notification of control operations. - * @listens controlEmitter#exec - */ - controlEmitter.on('exec', sendControlNotification); + /** + * Listens for execution notification of control operations. + * @listens controlEmitter#exec + */ + controlEmitter.on("exec", sendControlNotification); - /** - * Reports update progress to clients. - * @param {string} action - Reports, which action is in progress during the update. - * @param {int} progress - Integer representing the percentage of the action that is completed. - */ - var reportProgress = (action, progress) => { - ws.send(`{ "type": "progress", "payload": { "step": "${action}", "progress": ${progress} } }`); - } + /** + * Reports update progress to clients. + * @param {string} action - Reports, which action is in progress during the update. + * @param {int} progress - Integer representing the percentage of the action that is completed. + */ + const reportProgress = (action, progress) => { + ws.send( + `{ "type": "progress", "payload": { "step": "${action}", "progress": ${progress} } }` + ); + }; - /** - * Listens for progress reporst from update process and sends them to the client. - * @listens controlEmitter#progress - */ - controlEmitter.on('progress', reportProgress); + /** + * Listens for progress reporst from update process and sends them to the client. + * @listens controlEmitter#progress + */ + controlEmitter.on("progress", reportProgress); - /** - * Listens for Websocket to close and removes listeners. - * @listens ws#close - */ - ws.on('close', (code, reason) => { - serverInfo.serverInfoChanged.removeListener('change', sendUpdate); - controlEmitter.removeListener('exec', sendControlNotification); - controlEmitter.removeListener('progress', reportProgress); - logger.info(`websocket closed with code ${code}. Reason: ${reason}`); - }); + /** + * Listens for Websocket to close and removes listeners. + * @listens ws#close + */ + ws.on("close", (code, reason) => { + serverInfo.serverInfoChanged.removeListener("change", sendUpdate); + controlEmitter.removeListener("exec", sendControlNotification); + controlEmitter.removeListener("progress", reportProgress); + logger.info(`websocket closed with code ${code}. Reason: ${reason}`); }); + }); } From c841b93121011b9f310ad595431c2fa3e29a4cf3 Mon Sep 17 00:00:00 2001 From: Taraman17 Date: Thu, 14 Nov 2024 19:55:39 +0000 Subject: [PATCH 2/2] add all official maps to maplist.txt for server start --- public/maplist.txt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/public/maplist.txt b/public/maplist.txt index 4d41605..ae31b87 100644 --- a/public/maplist.txt +++ b/public/maplist.txt @@ -1,10 +1,23 @@ +ar_baggage +ar_poolday +ar_shoots +cs_assault cs_italy cs_office de_ancient de_anubis +de_assembly +de_basalt de_dust2 +de_edin de_inferno +de_memento +de_mills de_mirage de_nuke de_overpass -de_vertigo \ No newline at end of file +de_palais +de_thera +de_train +de_vertigo +de_whistle \ No newline at end of file