cs2-rcon-panel/modules/rcon.js
2023-10-11 23:14:29 +05:30

166 lines
6.6 KiB
JavaScript

const Rcon = require('rcon-srcds').default;
const { better_sqlite_client } = require('../db');
class RconManager {
constructor() {
this.rcons = {};
this.details = {};
this.servers = {};
this.init();
}
async init() {
try {
const servers_query = better_sqlite_client.prepare(`
SELECT * FROM servers
`);
const servers = servers_query.all();
console.log('All servers in DB:', servers);
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) {
console.error('Error connecting to MongoDB:', error);
}
}
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...")
await this.disconnect_rcon(server_id);
await this.connect(server_id, server);
}
try {
const status_promise = this.rcons[server_id].execute(`status`);
const timeout_promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(Error('Timeout - status command not received within 5 seconds'));
}, 5000); // 5 seconds timeout
});
let status = await Promise.race([status_promise, timeout_promise]);
console.log("HEARTBEAT SUCCESS", server_id)
} catch (error) {
console.log("Error in connecting to the server, reconnecting..... ERROR:", error);
await this.disconnect_rcon(server_id);
await this.connect(server_id, server);
}
}
async connect(server_id, server) {
try {
let rcon_connection = null;
rcon_connection = new Rcon({ host: server.serverIP, port: server.serverPort, timeout: 5000 });
console.log("CONNECTING RCON", server_id, server.serverIP, server.serverPort);
// Set a timeout for the authentication process
const authenticationTimeout = setTimeout(async () => {
console.error('RCON Authentication timed out', server_id);
try {
await this.disconnect_rcon(server_id); // Disconnect the RCON connection
console.log('Timed out, disconnected RCON', server_id);
} catch (error) {
console.error('Error disconnecting RCON', server_id, error);
}
}, 10000);
try {
await rcon_connection.authenticate(server.rconPassword);
clearTimeout(authenticationTimeout);
console.log('RCON Authenticated', server_id, server.serverIP, server.serverPort);
} catch (error) {
clearTimeout(authenticationTimeout);
console.error('RCON Authentication failed', server_id, error);
}
this.rcons[server_id] = rcon_connection;
this.details[server_id] = {
host: server.serverIP,
port: server.serverPort,
rcon_password: server.rconPassword,
connected: rcon_connection.isConnected(),
authenticated: rcon_connection.isAuthenticated()
};
if (rcon_connection.isConnected() && rcon_connection.isAuthenticated()) {
this.details[server_id].heartbeat_interval = setInterval(async () => this.send_heartbeat(server_id, server), 5000);
}
return;
} catch (error) {
console.error('[CONNECTION ERROR]', error);
}
}
async disconnect_rcon(server_id) {
console.log('starting disconnect', server_id)
if ( !(server_id in this.rcons) || (!this.rcons[server_id].connected)) {
return Promise.resolve();
}
clearInterval(this.details[server_id].heartbeat_interval)
this.rcons[server_id].authenticated = false;
this.rcons[server_id].connected = false;
return new Promise((resolve, reject) => {
this.rcons[server_id].connection.once('close', () => {
resolve();
});
this.rcons[server_id].connection.once('error', (e) => {
console.error('Socket error during disconnect:', e);
resolve();
});
this.rcons[server_id].connection.end(); // Close the socket gracefully
console.log("Disconnected", server_id)
});
}
}
module.exports = new RconManager();