diff --git a/app.js b/app.js index f0bee41..965fd47 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ var server = require('./lib/server'); var ping = require('./lib/ping'); var logger = require('./lib/logger'); +var mojang = require('./lib/mojang_services'); var config = require('./config.json'); @@ -13,10 +14,10 @@ function pingAll() { for (var i = 0; i < servers.length; i++) { // Make sure we lock our scope. (function(network) { - ping.ping(network.ip, network.port, network.type, 2500, function(err, res) { + ping.ping(network.ip, network.port, network.type, config.rates.connectTimeout, function(err, res) { // Handle our ping results, if it succeeded. if (err) { - logger.log('error', 'Failed to ping ' + network.ip + ': ' + err); + logger.log('error', 'Failed to ping ' + network.ip + ': ' + JSON.stringify(err)); } server.io.sockets.emit('update', res); @@ -47,6 +48,23 @@ function pingAll() { } } +// Start our main loop that does everything. +function startMainLoop() { + setInterval(pingAll, config.rates.pingAll); + + setInterval(function() { + mojang.update(config.rates.mojangStatusTimeout); + + server.io.sockets.emit('updateMojangServices', mojang.toMessage()); + }, config.rates.upateMojangStatus); + + // Manually fire the first round of our tasks. + mojang.update(config.rates.mojangStatusTimeout); + server.io.sockets.emit('updateMojangServices', mojang.toMessage()); + + pingAll(); +} + server.start(function() { // Track how many people are currently connected. server.io.on('connect', function(client) { @@ -74,8 +92,5 @@ server.start(function() { }); }); - // Start our main loop that fires off pings. - setInterval(pingAll, 2500); - - pingAll(); + startMainLoop(); }); \ No newline at end of file diff --git a/config.json b/config.json index fc08d4d..009a7a2 100644 --- a/config.json +++ b/config.json @@ -4,7 +4,7 @@ "name": "Hypixel", "ip": "mc.hypixel.net", "type": "PC" - }, + }, { "name": "Mineplex", "ip": "us.mineplex.com", @@ -30,5 +30,11 @@ "site": { "port": 80, "ip": "0.0.0.0" + }, + "rates": { + "upateMojangStatus": 5000, + "mojangStatusTimeout": 3500, + "pingAll": 2500, + "connectTimeout": 2500 } -} \ No newline at end of file +} diff --git a/lib/mojang_services.js b/lib/mojang_services.js new file mode 100644 index 0000000..adfdf3b --- /dev/null +++ b/lib/mojang_services.js @@ -0,0 +1,80 @@ +var request = require('request'); + +var logger = require('./logger'); + +var serviceNameLookup = { + 'minecraft.net': 'Website', + 'sessionserver.mojang.com': 'Sessions', + 'authserver.mojang.com': 'Auth', + 'textures.minecraft.net': 'Skins', + 'api.mojang.com': 'API' +}; + +var serviceStates = { + // Lazy populated. +}; + +function updateService(name, status) { + // Only update if we need to. + if (!(name in serviceStates) || serviceStates[name].status !== status) { + var newEntry = { + name: serviceNameLookup[name], // Send the clean name, not the URL. + status: status + }; + + // If it's an outage, track when it started. + if (status === 'yellow'|| status === 'red') { + newEntry.startTime = (new Date).getTime(); + } + + // Generate a nice title from the color. + if (status === 'green') { + newEntry.title = 'Online'; + } else if (status === 'yellow') { + newEntry.title = 'Unstable'; + } else if (status === 'red') { + newEntry.title = 'Offline'; + } else { + throw new Error('Unknown Mojang status: ' + status); + } + + // Wipe the old status in favor of the new one. + serviceStates[name] = newEntry; + } +} + +exports.update = function(timeout) { + request({ + uri: 'http://status.mojang.com/check', + method: 'GET', + timeout: timeout + }, function(err, res, body) { + if (err) { + logger.log('error', 'Failed to update Mojang services: %s', JSON.stringify(err)); + } else { + try { + body = JSON.parse(body); + + for (var i = 0; i < body.length; i++) { + var service = body[i]; + var name = Object.keys(service)[0]; // Because they return an array of object, we have to do this :( + + // If it's not in the lookup, we don't care about it. + if (name in serviceNameLookup) { + updateService(name, service[name]); + } + } + + logger.log('debug', 'Updated Mojang services: %s', JSON.stringify(serviceStates)); + } catch(err) { + // Catch anything weird that can happen, since things probably will. + logger.log('error', 'Failed to parse Mojang\'s response: %s', JSON.stringify(err)); + } + } + }); +}; + +exports.toMessage = function() { + // This is what we send to the clients. + return serviceStates; +}; \ No newline at end of file diff --git a/lib/ping.js b/lib/ping.js index 111bd81..5cf4981 100644 --- a/lib/ping.js +++ b/lib/ping.js @@ -5,42 +5,52 @@ var mcpc_ping = require('mc-ping-updated'); function pingMinecraftPC(host, port, timeout, callback) { var milliseconds = (new Date).getTime(); - mcpc_ping(host, port, function(err, res) { - if (err) { - callback(err, null); - } else { - // Remap our JSON into our custom structure. - callback(null, { - players: { - online: res.players.online, - max: res.players.max - }, - version: res.version.protocol, - latency: (new Date).getTime() - milliseconds - }); - } - }, timeout); + // Try catch incase the down stream module is bad at handling exceptions. + try { + mcpc_ping(host, port, function(err, res) { + if (err) { + callback(err, null); + } else { + // Remap our JSON into our custom structure. + callback(null, { + players: { + online: res.players.online, + max: res.players.max + }, + version: res.version.protocol, + latency: (new Date).getTime() - milliseconds + }); + } + }, timeout); + } catch (err) { + callback(err, null); + } } // This is a wrapper function for mcpe-ping, mainly used to convert the data structure of the result. function pingMinecraftPE(host, port, timeout, callback) { var milliseconds = (new Date).getTime(); - mcpe_ping(host, port || 19132, function(err, res) { - if (err) { - callback(err, null); - } else { - // Remap our JSON into our custom structure. - callback(err, { - players: { - online: res.currentPlayers, - max: res.maxPlayers - }, - version: res.version, - latency: (new Date).getTime() - milliseconds - }); - } - }, timeout); + // Try catch incase the down stream module is bad at handling exceptions. + try { + mcpe_ping(host, port || 19132, function(err, res) { + if (err) { + callback(err, null); + } else { + // Remap our JSON into our custom structure. + callback(err, { + players: { + online: res.currentPlayers, + max: res.maxPlayers + }, + version: res.version, + latency: (new Date).getTime() - milliseconds + }); + } + }, timeout); + } catch (err) { + callback(err, null); + } } exports.ping = function(host, port, type, timeout, callback) { diff --git a/package.json b/package.json index 43db8b2..2caff3b 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "mc-ping-updated": "0.0.6", "mcpe-ping": "0.0.3", "mime": "^1.3.4", + "request": "^2.65.0", "socket.io": "^1.3.7", "winston": "^2.0.0" },