Improved command execution on server

This commit is contained in:
Shobhit Pathak 2023-10-11 23:14:29 +05:30
parent e9c9ca8d34
commit d277b8106c
8 changed files with 209 additions and 81 deletions

View File

@ -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!
say Knife!
say Knife!
say Knife!
mp_restartgame 1
mp_warmup_end

View File

@ -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!
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

View File

@ -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!

View File

@ -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...")

View File

@ -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 () {

View File

@ -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)

View File

@ -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 {

View File

@ -87,12 +87,12 @@
<form id="server_setup_form" class="server-setup-form">
<div class="mb-3">
<label for="team1" class="form-label">Team 1:</label>
<input type="text" id="team1" name="team1" class="form-control" required>
<input type="text" id="team1" name="team1" class="form-control">
</div>
<div class="mb-3">
<label for="team2" class="form-label">Team 2:</label>
<input type="text" id="team2" name="team2" class="form-control" required>
<input type="text" id="team2" name="team2" class="form-control">
</div>
<div class="mb-3">
@ -233,6 +233,21 @@
</svg> Go Live
</button>
</div>
<div class="input-group mb-3">
<input type="text" id="say_input" name="sayInput" class="form-control" placeholder="Say as admin on server" required style="flex: 2;">
<button type="submit" id="say_input_btn" class="btn btn-primary" style="flex: 1;">
Say
<svg viewBox="0 0 24 24" fill="none" width="16" height="16" xmlns="http://www.w3.org/2000/svg" stroke="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path
d="M11.5003 12H5.41872M5.24634 12.7972L4.24158 15.7986C3.69128 17.4424 3.41613 18.2643 3.61359 18.7704C3.78506 19.21 4.15335 19.5432 4.6078 19.6701C5.13111 19.8161 5.92151 19.4604 7.50231 18.7491L17.6367 14.1886C19.1797 13.4942 19.9512 13.1471 20.1896 12.6648C20.3968 12.2458 20.3968 11.7541 20.1896 11.3351C19.9512 10.8529 19.1797 10.5057 17.6367 9.81135L7.48483 5.24303C5.90879 4.53382 5.12078 4.17921 4.59799 4.32468C4.14397 4.45101 3.77572 4.78336 3.60365 5.22209C3.40551 5.72728 3.67772 6.54741 4.22215 8.18767L5.24829 11.2793C5.34179 11.561 5.38855 11.7019 5.407 11.8459C5.42338 11.9738 5.42321 12.1032 5.40651 12.231C5.38768 12.375 5.34057 12.5157 5.24634 12.7972Z"
stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</svg>
</button>
</div>
<div class="input-group mb-3">
<input type="text" id="rconInput" name="sayInput" class="form-control" placeholder="Send RCON command to server" required style="flex: 2;">
<button type="submit" id="rconInputBtn" class="btn btn-primary" style="flex: 1;">