Skip to content

Instantly share code, notes, and snippets.

@aikar
Created March 28, 2020 16:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aikar/f860f804ddbe1779dd2f211f7f87566a to your computer and use it in GitHub Desktop.
Save aikar/f860f804ddbe1779dd2f211f7f87566a to your computer and use it in GitHub Desktop.
#!/usr/bin/env _node
var Rcon = require('rcon');
var exec = require('child_process').exec;
var rconPass = require('fs').readFileSync(__dirname + '/rconpass').toString();
var servers = {};
[
"smp1", "smp2", "smp3",
"smp4", "smp5", "smp6",
"smp7", "smp8", "smp9",
"utopia",
"games",
"stage"
].forEach(initServer);
function generateReport(server, _) {
var lag = rconCommand(server, "_empireserver servermonitor", _);
var tps = rconCommand(server, "tps", _);
var load= '';
//var uptime = exec('ssh ' + server + '.emc uptime', _);
//var load = match(uptime, /load average: (.*)/);
var m;
var lcolor = 'a';
if (m = load.match(/[\d\.]+/)) {
if (parseFloat(m[0]) > 8) {
lcolor = 'c';
}
}
tps = match(tps, /15m: (.*)/);
var players = match(lag, /Players: (\d+)/);
var chunks = match(lag, /Chunks: (\d+)/);
var entities = match(lag, /Entities: (\d+)/);
var tileentities = match(lag, /TileEnt: (\d+)/);
var memocnt = parseInt(match(lag, /GCOldCount: (\d+)/));
var memolast = match(lag, /GCOldLast: (\d+)/);
var memydur = match(lag, /GCYoungDur: (\d+)/);
var memyint = Math.round(Number(match(lag, /GCYoungInt: (\d+)/)/1000));
var old = memocnt > 0 ? c(4, " (OLD: " + memocnt + ")") : "";
var report =
c('e', server + ": ") + c(6, "TPS: ") + c('a', tps) + c(6, " - Load: ") + c(lcolor, load) +"\n" +
c('e', server + ": ") + c(6, memyint + "s(" + memydur +"ms)") + old
+ c(6, " P: ") + c('a', players)
+ c(6, " E: ") + c('a', entities)
+ c(6, " TE: ") + c('a', tileentities)
+ c(6, " Ch: ") + c('a', chunks);
//servers[server].membars = membars;
servers[server].players = players;
servers[server].report = report;
servers[server].report_dupe = report;
servers[server].lastupdate = new Date;
reprint();
}
setInterval(function() {
var now = new Date().getTime();
forEachServer(function(s, t) {
if (now - s.lastupdate.getTime() > 60*1000) {
s.report = c(4, t + ": ERROR: NOT RESPONDING: " + s.lastupdate + "\n" + (s.report_dupe||""));
beep();
reprint();
}
});
}, 10000);
function initServer(server) {
servers[server] = {
authed: false,
rcon: initRcon(server),
cb: null,
queue: [],
lastupdate: new Date,
player: 0,
membars: 5
};
setInterval(renderReport, 1000);
renderReport();
function renderReport() {
generateReport(server, errstub);
}
}
var scheduled = false;
var reprintdelay = 500;
function reprint() {
if (scheduled) {
return;
}
scheduled = true;
setTimeout(function() {
resetConsole();
var players = 0;
forEachServer(function(v) {
if (v.report) {
console.log(mccolor(v.report));
players += parseInt(v.players || 0);
}
});
console.log(mccolor(
c('e', 'Total Players: ') +
c('a', players)
));
scheduled = false;
}, reprintdelay);
}
function match(src, ptn, idx) {
var m;
if ((m = src.match(ptn))) {
return m[idx || 1] || m[0];
}
return m;
}
function forEachServer(cb) {
Object.keys(servers).forEach(function (v) {
cb(servers[v], v);
});
}
function initRcon(server) {
var rcon = new Rcon(server + '.emc', 25576, rconPass);
rcon.on('response', function(str) {
receiveResponse(server, str);
});
rcon.on('auth', function() {
servers[server].authed = true;
processQueue(server);
});
rcon.on('end', function() {
reconnect();
});
rcon.on("error", function(e){
rcon.disconnect();
reconnect();
});
try {
rcon.connect();
} catch (e) {
console.error(e.message);
reconnect()
}
function reconnect() {
setTimeout(function() {
servers[server].rcon = initRcon(server);
}, 1000);
}
return rcon;
}
function rconCommand(server, cmd, cb) {
var obj = servers[server];
if (obj.cb == null && obj.authed) {
obj.cb = cb;
obj.cmd = cmd;
executeRcon(obj);
} else {
var nxt = {
cb: cb,
cmd: cmd
};
obj.queue.push(nxt);
}
}
function executeRcon(obj) {
obj.rcon.send(obj.cmd);
}
function receiveResponse(server, str) {
var obj = servers[server];
if (obj.cb) {
obj.cb(null, str);
}
processQueue(server);
}
function processQueue(server) {
var obj = servers[server];
if (obj.queue.length) {
var next = obj.queue.shift();
obj.cb = next.cb;
obj.cmd = next.cmd;
executeRcon(obj);
} else {
obj.cb = null;
obj.cmd = null;
}
}
function beep() {
// return exec('ssh laptop bin/alert 0.5');
}
function resetConsole() {
process.stdout.write('\x1Bc');
}
function errstub(err) {
if (err) {
throw err;
}
}
var colors = {
0: "\x1B[0;30m", /* 00 BLACK 0x30 */
1: "\x1B[0;34m", /* 01 BLUE 0x31 */
2: "\x1B[0;32m", /* 02 GREEN 0x32 */
3: "\x1B[0;36m", /* 03 CYAN 0x33 */
4: "\x1B[0;31m", /* 04 RED 0x34 */
5: "\x1B[0;35m", /* 05 PURPLE 0x35 */
6: "\x1B[0;33m", /* 06 GOLD 0x36 */
7: "\x1B[0;37m", /* 07 GREY 0x37 */
8: "\x1B[1;30m", /* 08 DGREY 0x38 */
9: "\x1B[1;34m", /* 09 LBLUE 0x39 */
a: "\x1B[1;32m", /* 10 LGREEN 0x61 */
b: "\x1B[1;36m", /* 11 LCYAN 0x62 */
c: "\x1B[1;31m", /* 12 LRED 0x63 */
d: "\x1B[1;35m", /* 13 LPURPLE 0x64 */
e: "\x1B[1;33m", /* 14 YELLOW 0x65 */
f: "\x1B[1;37m", /* 15 WHITE 0x66 */
};
function mccolor(str) {
return str.replace(/§([0-9a-fr])/g, function(m, contents) {
return colors[contents] || "\x1B[0m";
});
}
function c(r, msg) {
return '§' + r + msg + '§r';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment