From d277b8106caa0de1563c2c3a1aa425803a751300 Mon Sep 17 00:00:00 2001 From: Shobhit Pathak Date: Wed, 11 Oct 2023 23:14:29 +0530 Subject: [PATCH] Improved command execution on server --- cfg/knife.cfg | 9 +++- cfg/live.cfg | 24 ++++++--- cfg/warmup.cfg | 7 ++- modules/rcon.js | 45 +++++++++++++++++ public/js/console.js | 57 +++++++++++++++------ routes/game.js | 117 +++++++++++++++++++++++++------------------ routes/server.js | 12 +++-- views/manage.ejs | 19 ++++++- 8 files changed, 209 insertions(+), 81 deletions(-) diff --git a/cfg/knife.cfg b/cfg/knife.cfg index a050a24..64c870f 100644 --- a/cfg/knife.cfg +++ b/cfg/knife.cfg @@ -11,7 +11,12 @@ mp_roundtime_defuse 1.92 mp_roundtime_hostage 1.92 mp_t_default_secondary "" mp_round_restart_delay 3 - +mp_team_intro_time 0 say Knife! say Knife! -say Knife! \ No newline at end of file +say Knife! +say Knife! +say Knife! +say Knife! +mp_restartgame 1 +mp_warmup_end \ No newline at end of file diff --git a/cfg/live.cfg b/cfg/live.cfg index 69ad265..a70ee3a 100644 --- a/cfg/live.cfg +++ b/cfg/live.cfg @@ -11,7 +11,6 @@ cash_player_killed_enemy_factor 1 cash_player_killed_hostage -1000 cash_player_killed_teammate -300 cash_player_rescued_hostage 1000 -cash_team_bonus_shorthanded 1000 cash_team_elimination_bomb_map 3250 cash_team_elimination_hostage_map_ct 3000 cash_team_elimination_hostage_map_t 3000 @@ -50,7 +49,7 @@ mp_display_kill_assists 1 mp_endmatch_votenextmap 0 mp_forcecamera 1 mp_free_armor 0 -mp_freezetime 15 +mp_freezetime 18 mp_friendlyfire 1 mp_give_player_c4 1 mp_halftime 1 @@ -59,7 +58,7 @@ mp_halftime_pausetimer 0 mp_ignore_round_win_conditions 0 mp_limitteams 0 mp_match_can_clinch 1 -mp_match_end_restart 1 +mp_match_end_restart 0 mp_maxmoney 16000 mp_maxrounds 24 mp_molotovusedelay 0 @@ -72,7 +71,7 @@ mp_randomspawn 0 mp_respawn_immunitytime 0 mp_respawn_on_death_ct 0 mp_respawn_on_death_t 0 -mp_round_restart_delay 7 +mp_round_restart_delay 5 mp_roundtime 1.92 mp_roundtime_defuse 1.92 mp_roundtime_hostage 1.92 @@ -94,7 +93,7 @@ spec_freeze_panel_extended_time 0 spec_freeze_time 2 spec_freeze_time_lock 2 spec_replay_enable 0 -sv_allow_votes 0 +sv_allow_votes 1 sv_auto_full_alltalk_during_warmup_half_end 0 sv_coaching_enabled 1 sv_competitive_official_5v5 1 @@ -108,7 +107,18 @@ sv_occlude_players 1 sv_talk_enemy_dead 0 sv_talk_enemy_living 0 sv_voiceenable 1 -tv_relayvoice 0 +tv_relayvoice 1 +mp_team_timeout_max 4 +mp_team_timeout_time 30 +sv_vote_command_delay 0 +cash_team_bonus_shorthanded 0 +cash_team_loser_bonus_shorthanded 0 +mp_spectators_max 20 +mp_team_intro_time 0 say Match is live! say Match is live! -say Match is live! \ No newline at end of file +say Match is live! +say use call timeout for TAC and type Tech ( Admin will pause the game ) +say use call timeout for TAC and type Tech ( Admin will pause the game ) +mp_restartgame 3 +mp_warmup_end \ No newline at end of file diff --git a/cfg/warmup.cfg b/cfg/warmup.cfg index 34bfddd..6cd68d3 100644 --- a/cfg/warmup.cfg +++ b/cfg/warmup.cfg @@ -16,12 +16,12 @@ mp_spectators_max 20 mp_maxmoney 16000 mp_startmoney 16000 mp_timelimit 0 -sv_alltalk 1 +sv_alltalk 0 sv_auto_full_alltalk_during_warmup_half_end 0 sv_coaching_enabled 1 sv_competitive_official_5v5 1 sv_deadtalk 1 -sv_full_alltalk 1 +sv_full_alltalk 0 sv_grenade_trajectory 0 sv_hibernate_when_empty 0 mp_weapons_allow_typecount -1 @@ -38,9 +38,12 @@ mp_ct_default_primary "" mp_t_default_melee weapon_knife mp_t_default_secondary weapon_glock mp_t_default_primary +mp_maxrounds 24 mp_warmup_start mp_warmup_pausetimer 1 mp_warmuptime 9999 +cash_team_bonus_shorthanded 0 +cash_team_loser_bonus_shorthanded 0 say Warmup! say Warmup! say Warmup! \ No newline at end of file diff --git a/modules/rcon.js b/modules/rcon.js index b126648..f21e04f 100644 --- a/modules/rcon.js +++ b/modules/rcon.js @@ -6,6 +6,7 @@ class RconManager { constructor() { this.rcons = {}; this.details = {}; + this.servers = {}; this.init(); } @@ -19,6 +20,7 @@ class RconManager { for (const server of servers) { const server_id = server.id.toString(); if (server_id in this.rcons) continue; + this.servers[server_id] = server; await this.connect(server_id, server); } } catch (error) { @@ -26,6 +28,49 @@ class RconManager { } } + async execute_command(server_id, command) { + try { + let rcon_connection = this.rcons[server_id]; + let server = this.servers[server_id]; + if (!rcon_connection.isConnected() || !rcon_connection.isAuthenticated() || !rcon_connection.connection.writable) { + console.log("Connection issue detected, reconnecting to the server:", server_id) + await this.disconnect_rcon(server_id); + await this.connect(server_id, server); + } + rcon_connection = this.rcons[server_id]; + if (rcon_connection.isConnected() && rcon_connection.isAuthenticated() && rcon_connection.connection.writable) { + const executePromise = new Promise(async (resolve, reject) => { + try { + const response = await Promise.race([ + rcon_connection.execute(command), + new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ error: 'Command execution timed out' }); + }, 200); // 200ms timeout + }), + ]); + resolve(response); + } catch (error) { + reject(error); + } + }); + + const response = await executePromise; + + if (response.error) { + return 200; + } + return response.toString(); + } else { + console.log(`Unable to establish connection to the server id: ${server_id}, cannot execute command: ${command}`) + return 400 + } + } catch (error) { + console.error('Error in execute_command:', error); + return 400 + } + } + async send_heartbeat(server_id, server) { if (!this.rcons[server_id].connection.writable) { console.log("Connection unwritable, reconnecting...") diff --git a/public/js/console.js b/public/js/console.js index 7272c7d..fd8dc21 100644 --- a/public/js/console.js +++ b/public/js/console.js @@ -108,7 +108,10 @@ $(document).ready(function () { const data = await response.json(); console.log(data.message) alert(data.message); - } else { + } else if (response.status == 401) { + alert('Unauthorized, please reload and relogin.'); + } + else { alert('Failed to perform the action'); } } catch (error) { @@ -118,31 +121,45 @@ $(document).ready(function () { } $('#pause_game').on('click', function () { - send_post_request('/api/pause'); + if (confirm("Are you sure you want to pause the game?")) { + send_post_request('/api/pause'); + } }); - + $('#unpause_game').on('click', function () { - send_post_request('/api/unpause'); + if (confirm("Are you sure you want to unpause the game?")) { + send_post_request('/api/unpause'); + } }); - + $('#restart_game').on('click', function () { - send_post_request('/api/restart'); + if (confirm("Are you sure you want to restart the game?")) { + send_post_request('/api/restart'); + } }); - + $('#start_warmup').on('click', function () { - send_post_request('/api/start-warmup'); + if (confirm("Are you sure you want to start the warm-up?")) { + send_post_request('/api/start-warmup'); + } }); - + $('#knife_start').on('click', function () { - send_post_request('/api/start-knife'); + if (confirm("Are you sure you want to start the knife round?")) { + send_post_request('/api/start-knife'); + } }); - + $('#swap_team').on('click', function () { - send_post_request('/api/swap-team'); + if (confirm("Are you sure you want to swap teams?")) { + send_post_request('/api/swap-team'); + } }); - + $('#go_live').on('click', function () { - send_post_request('/api/go-live'); + if (confirm("Are you sure you want to go live?")) { + send_post_request('/api/go-live'); + } }); $('#rconInputBtn').on('click', function () { @@ -153,12 +170,22 @@ $(document).ready(function () { $('#rconInput').val(''); }); + $('#say_input_btn').on('click', function () { + let data = { + message: $('#say_input').val() + }; + send_post_request('/api/say-admin', data); + $('#say_input').val(''); + }); + $('#list_backups').on('click', function () { send_post_request('/api/list-backups'); }); $('#restore_latest_backup').on('click', function () { - send_post_request('/api/restore-latest-backup'); + if (confirm("Are you sure you want to restore the latest round backup?")) { + send_post_request('/api/restore-latest-backup'); + } }); $('#restore_backup').on('click', function () { diff --git a/routes/game.js b/routes/game.js index 17d7d8a..db7236f 100644 --- a/routes/game.js +++ b/routes/game.js @@ -15,16 +15,25 @@ router.post('/api/setup-game', is_authenticated, async (req, res) => { const team2 = req.body.team2; const selected_map = req.body.selectedMap; const game_mode = req.body.game_mode.toString(); - rcon.rcons[server_id].execute(`mp_teamname_1 "${team1}"`); - rcon.rcons[server_id].execute(`mp_teamname_2 "${team2}"`); - rcon.rcons[server_id].execute(`game_mode ${game_mode}`); + // rcon.rcons[server_id].execute(`mp_teamname_1 "${team1}"`); + // rcon.rcons[server_id].execute(`mp_teamname_2 "${team2}"`); + // rcon.rcons[server_id].execute(`game_mode ${game_mode}`); + if (team1.trim() != "") { + await rcon.execute_command(server_id, `mp_teamname_1 "${team1}"`); + } + if (team2.trim() != "") { + await rcon.execute_command(server_id, `mp_teamname_2 "${team2}"`); + } + await rcon.execute_command(server_id, `game_mode ${game_mode}`); if (game_mode == "1") { execute_cfg_on_server(server_id, './cfg/live.cfg'); } else if (game_mode == "2") { execute_cfg_on_server(server_id, './cfg/live_wingman.cfg'); } - rcon.rcons[server_id].execute(`mp_warmup_pausetimer 1`); - rcon.rcons[server_id].execute(`changelevel ${selected_map}`); + // rcon.rcons[server_id].execute(`mp_warmup_pausetimer 1`); + // rcon.rcons[server_id].execute(`changelevel ${selected_map}`); + await rcon.execute_command(server_id, `mp_warmup_pausetimer 1`); + await rcon.execute_command(server_id, `changelevel ${selected_map}`); // Adding 1 second delay in executing warmup.cfg to make it effective after map has been changed. setTimeout(() => { @@ -41,7 +50,8 @@ router.post('/api/setup-game', is_authenticated, async (req, res) => { router.post('/api/restart', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_restartgame 1'); + // rcon.rcons[server_id].execute('mp_restartgame 1'); + await rcon.execute_command(server_id, `mp_restartgame 1`); return res.status(200).json({ message: 'Game restarted' }); } catch (error) { console.log(error); @@ -49,10 +59,11 @@ router.post('/api/restart', is_authenticated, async (req, res) => { } }); -router.post('/api/start-warmup', is_authenticated, (req, res) => { +router.post('/api/start-warmup', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_restartgame 1'); + // rcon.rcons[server_id].execute('mp_restartgame 1'); + await rcon.execute_command(server_id, `mp_restartgame 1`); execute_cfg_on_server(server_id, './cfg/warmup.cfg'); return res.status(200).json({ message: 'Warmup started!' }); @@ -65,8 +76,10 @@ router.post('/api/start-warmup', is_authenticated, (req, res) => { router.post('/api/start-knife', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_warmup_end'); - rcon.rcons[server_id].execute('mp_restartgame 1'); + // rcon.rcons[server_id].execute('mp_warmup_end'); + // rcon.rcons[server_id].execute('mp_restartgame 1'); + await rcon.execute_command(server_id, `mp_warmup_end`); + await rcon.execute_command(server_id, `mp_restartgame 1`); execute_cfg_on_server(server_id, './cfg/knife.cfg'); return res.status(200).json({ message: 'Knife started!' }); @@ -79,7 +92,8 @@ router.post('/api/start-knife', is_authenticated, async (req, res) => { router.post('/api/swap-team', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_swapteams'); + // rcon.rcons[server_id].execute('mp_swapteams'); + await rcon.execute_command(server_id, `mp_swapteams`); return res.status(200).json({ message: 'Teams Swapped!' }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); @@ -89,8 +103,10 @@ router.post('/api/swap-team', is_authenticated, async (req, res) => { router.post('/api/go-live', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_warmup_end'); - const response = await rcon.rcons[server_id].execute('game_mode'); + // rcon.rcons[server_id].execute('mp_warmup_end'); + await rcon.execute_command(server_id, `mp_warmup_end`); + // const response = await rcon.rcons[server_id].execute('game_mode'); + const response = await rcon.execute_command(server_id, `game_mode`); const game_mode = response.split("=")[1].trim().toString(); if (game_mode == "1") { console.log("Executing live.cfg") @@ -99,7 +115,8 @@ router.post('/api/go-live', is_authenticated, async (req, res) => { console.log("Executing live_wingman.cfg") execute_cfg_on_server(server_id, './cfg/live_wingman.cfg'); } - rcon.rcons[server_id].execute('mp_restartgame 1'); + // rcon.rcons[server_id].execute('mp_restartgame 1'); + await rcon.execute_command(server_id, `mp_restartgame 1`); return res.status(200).json({ message: 'Match is live!!' }); } catch (error) { @@ -112,7 +129,8 @@ router.post('/api/go-live', is_authenticated, async (req, res) => { router.post('/api/list-backups', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - const response = await rcon.rcons[server_id].execute('mp_backup_restore_list_files'); + // const response = await rcon.rcons[server_id].execute('mp_backup_restore_list_files'); + const response = await rcon.execute_command(server_id, "mp_backup_restore_list_files"); console.log('Server response:', response); return res.status(200).json({ message: response }); } catch (error) { @@ -130,8 +148,10 @@ router.post('/api/restore-round', is_authenticated, async (req, res) => { round_number = "0" + round_number; } console.log(`SENDING mp_backup_restore_load_file backup_round${round_number}.txt`) - rcon.rcons[server_id].execute(`mp_backup_restore_load_file backup_round${round_number}.txt`); - rcon.rcons[server_id].execute('mp_pause_match'); + // rcon.rcons[server_id].execute(`mp_backup_restore_load_file backup_round${round_number}.txt`); + // rcon.rcons[server_id].execute('mp_pause_match'); + await rcon.execute_command(server_id, `mp_backup_restore_load_file backup_round${round_number}.txt`); + await rcon.execute_command(server_id, `mp_pause_match`); return res.status(200).json({ message: 'Round Restored!' }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); @@ -141,11 +161,14 @@ router.post('/api/restore-round', is_authenticated, async (req, res) => { router.post('/api/restore-latest-backup', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - const response = await rcon.rcons[server_id].execute('mp_backup_round_file_last'); + // const response = await rcon.rcons[server_id].execute('mp_backup_round_file_last'); + const response = await rcon.execute_command(server_id, `mp_backup_round_file_last`); const last_round_file = response.split("=")[1].trim().toString(); if (last_round_file.includes('.txt')) { - rcon.rcons[server_id].execute(`mp_backup_restore_load_file ${last_round_file}`); - rcon.rcons[server_id].execute('mp_pause_match'); + // rcon.rcons[server_id].execute(`mp_backup_restore_load_file ${last_round_file}`); + // rcon.rcons[server_id].execute('mp_pause_match'); + await rcon.execute_command(server_id, `mp_backup_restore_load_file ${last_round_file}`); + await rcon.execute_command(server_id, `mp_pause_match`); return res.status(200).json({ message: `Latest Round Restored! (${last_round_file})` }); } else { return res.status(200).json({ message: 'No latest backup found!' }); @@ -161,7 +184,8 @@ router.post('/api/restore-latest-backup', is_authenticated, async (req, res) => router.post('/api/pause', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_pause_match'); + // rcon.rcons[server_id].execute('mp_pause_match'); + const response = await rcon.execute_command(server_id, 'mp_pause_match'); return res.status(200).json({ message: 'Game paused' }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); @@ -172,7 +196,8 @@ router.post('/api/pause', is_authenticated, async (req, res) => { router.post('/api/unpause', is_authenticated, async (req, res) => { try { const server_id = req.body.server_id; - rcon.rcons[server_id].execute('mp_unpause_match'); + // rcon.rcons[server_id].execute('mp_unpause_match'); + const response = await rcon.execute_command(server_id, 'mp_unpause_match'); return res.status(200).json({ message: 'Game unpaused' }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); @@ -184,28 +209,9 @@ router.post('/api/rcon', is_authenticated, async (req, res) => { const server_id = req.body.server_id; const command = req.body.command; - // Wrap the await call in a Promise and add a timeout - const executePromise = new Promise(async (resolve, reject) => { - try { - const response = await Promise.race([ - rcon.rcons[server_id].execute(command), - new Promise((resolve, reject) => { - setTimeout(() => { - resolve({ error: 'Command execution timed out' }); - }, 1000); // 1 seconds timeout - }), - ]); - resolve(response); - } catch (error) { - reject(error); - } - }); + const response = await rcon.execute_command(server_id, command); - // Wait for the wrapped Promise to resolve - const response = await executePromise; - console.log(response) - // Check if the result is an error or the actual response - if (response.error) { + if (response == 200) { return res.status(200).json({ message: 'Command sent!' }); } @@ -215,6 +221,18 @@ router.post('/api/rcon', is_authenticated, async (req, res) => { } }); +router.post('/api/say-admin', is_authenticated, async (req, res) => { + try { + const server_id = req.body.server_id; + const message = req.body.message; + const message_to_send = "say " + message; + await rcon.execute_command(server_id, message_to_send); + return res.status(200).json({ message: 'Message sent!' }); + } catch (error) { + res.status(500).json({ error: 'Internal server error' }); + } +}); + function check_whitelisted_players() { rcon.rcons[server_id].execute('status_json') .then((response) => { @@ -259,7 +277,7 @@ function splitByByteLength(data, length) { return exportedLines; } -function execute_cfg_on_server(server_id, cfg_path) { +async function execute_cfg_on_server(server_id, cfg_path) { fs.readFile(cfg_path, 'utf8', (err, data) => { if (err) { @@ -281,16 +299,17 @@ function execute_cfg_on_server(server_id, cfg_path) { } const exported_lines = splitByByteLength(data, 512) - function execute_next_item(item) { + async function execute_next_item(item) { try { if (item < exported_lines.length) { - console.log(exported_lines[item]); - rcon.rcons[server_id].execute(exported_lines[item]); + console.log("Executing on server:", exported_lines[item]); + // rcon.rcons[server_id].execute(exported_lines[item]); + await rcon.execute_command(server_id, exported_lines[item]); - // Wait for 200ms before moving to the next iteration + // Wait for 100ms before moving to the next iteration setTimeout(() => { execute_next_item(item + 1); - }, 200); + }, 100); } } catch (error) { console.log("[execute_next_item] Error:", error) diff --git a/routes/server.js b/routes/server.js index e5d3a51..44e7d29 100644 --- a/routes/server.js +++ b/routes/server.js @@ -26,7 +26,8 @@ router.get('/manage/:server_id', is_authenticated, async (req, res) => { return res.status(404).send('Server not found'); } - const response = await rcon.rcons[server_id].execute('hostname'); + // const response = await rcon.rcons[server_id].execute('hostname'); + const response = await rcon.execute_command(server_id, "hostname"); const hostname = response.toString().split("=")[1].trim(); const host = rcon.details[server_id].host; const port = rcon.details[server_id].port; @@ -71,11 +72,14 @@ router.get('/api/servers', is_authenticated, async (req, res) => { let hostname = "-"; if (server_id in rcon.rcons) { - servers[i].connected = rcon.rcons[server_id].isConnected(); - servers[i].authenticated = rcon.rcons[server_id].isAuthenticated(); + // servers[i].connected = rcon.rcons[server_id].isConnected(); + // servers[i].authenticated = rcon.rcons[server_id].isAuthenticated(); + servers[i].connected = true; + servers[i].authenticated = true; if (servers[i].connected && servers[i].authenticated) { - const response = await rcon.rcons[server_id].execute('hostname'); + const response = await rcon.execute_command(server_id, "hostname"); + // const response = await rcon.rcons[server_id].execute('hostname'); hostname = response.toString().split("=")[1].trim(); } } else { diff --git a/views/manage.ejs b/views/manage.ejs index a3eedfe..a1acfa2 100644 --- a/views/manage.ejs +++ b/views/manage.ejs @@ -87,12 +87,12 @@
- +
- +
@@ -233,6 +233,21 @@ Go Live
+
+ + +