Backend cleanup (#146)
* Add ServerRegistration, begin refactoring to match frontend * move graphData logic into ServerRegistration * move ping updates/history into ServerRegistration * start updating main app entry methods * fix default rates.updateMojangStatus * fix record loading delays on freshly booted instances * move database loading logic to method + callback * use data in frontend for type lookup instead of ping * cleanup app.js * reorganize methods to improve flow * avoid useless mojang updates, remove legacy fields * rename legacy fields for consistency * finish restructure around App model * ensure versions are sorted by release order * filter errors sent to frontend to avoid data leaks * fix version listing behavior on frontend * 5.1.0
This commit is contained in:
202
lib/ping.js
202
lib/ping.js
@ -1,85 +1,143 @@
|
||||
/**
|
||||
* THIS IS LEGACY, UNMAINTAINED CODE
|
||||
* IT MAY (AND LIKELY DOES) CONTAIN BUGS
|
||||
* USAGE IS NOT RECOMMENDED
|
||||
*/
|
||||
var mcpe_ping = require('mcpe-ping-fixed');
|
||||
var mcpc_ping = require('mc-ping-updated');
|
||||
const dns = require('dns')
|
||||
|
||||
var logger = require('./logger');
|
||||
var util = require('./util');
|
||||
const minecraftJavaPing = require('mc-ping-updated')
|
||||
const minecraftBedrockPing = require('mcpe-ping-fixed')
|
||||
|
||||
// This is a wrapper function for mc-ping-updated, mainly used to convert the data structure of the result.
|
||||
function pingMinecraftPC(host, port, timeout, callback, version) {
|
||||
var startTime = util.getCurrentTimeMs();
|
||||
const logger = require('./logger')
|
||||
|
||||
mcpc_ping(host, port, function(err, res) {
|
||||
if (err) {
|
||||
callback(err, null);
|
||||
} else {
|
||||
// Remap our JSON into our custom structure.
|
||||
var favicon;
|
||||
const config = require('../config')
|
||||
|
||||
// Ensure the returned favicon is a data URI
|
||||
if (res.favicon && res.favicon.indexOf('data:image/') === 0) {
|
||||
favicon = res.favicon;
|
||||
}
|
||||
function ping (host, port, type, timeout, callback, version) {
|
||||
switch (type) {
|
||||
case 'PC':
|
||||
unfurlSrv(host, port, (host, port) => {
|
||||
minecraftJavaPing(host, port || 25565, (err, res) => {
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else {
|
||||
const payload = {
|
||||
players: {
|
||||
online: capPlayerCount(host, parseInt(res.players.online))
|
||||
},
|
||||
version: parseInt(res.version.protocol)
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
players: {
|
||||
online: capPlayerCount(host, parseInt(res.players.online)),
|
||||
max: parseInt(res.players.max)
|
||||
},
|
||||
version: parseInt(res.version.protocol),
|
||||
latency: util.getCurrentTimeMs() - startTime,
|
||||
favicon: favicon
|
||||
});
|
||||
}
|
||||
}, timeout, version);
|
||||
// Ensure the returned favicon is a data URI
|
||||
if (res.favicon && res.favicon.startsWith('data:image/')) {
|
||||
payload.favicon = res.favicon
|
||||
}
|
||||
|
||||
callback(null, payload)
|
||||
}
|
||||
}, timeout, version)
|
||||
})
|
||||
break
|
||||
|
||||
case 'PE':
|
||||
minecraftBedrockPing(host, port || 19132, (err, res) => {
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else {
|
||||
callback(null, {
|
||||
players: {
|
||||
online: capPlayerCount(host, parseInt(res.currentPlayers))
|
||||
}
|
||||
})
|
||||
}
|
||||
}, timeout)
|
||||
break
|
||||
|
||||
default:
|
||||
throw new Error('Unsupported type: ' + type)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 startTime = util.getCurrentTimeMs();
|
||||
|
||||
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: capPlayerCount(host, parseInt(res.currentPlayers)),
|
||||
max: parseInt(res.maxPlayers)
|
||||
},
|
||||
latency: util.getCurrentTimeMs() - startTime
|
||||
});
|
||||
}
|
||||
}, timeout);
|
||||
function unfurlSrv (hostname, port, callback) {
|
||||
dns.resolveSrv('_minecraft._tcp.' + hostname, (_, records) => {
|
||||
if (!records || records.length < 1) {
|
||||
callback(hostname, port)
|
||||
} else {
|
||||
callback(records[0].name, records[0].port)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// player count can be up to 1^32-1, which is a massive scale and destroys browser performance when rendering graphs
|
||||
// Artificially cap and warn to prevent propogating garbage
|
||||
function capPlayerCount(host, playerCount) {
|
||||
const maxPlayerCount = 250000;
|
||||
if (playerCount !== Math.min(playerCount, maxPlayerCount)) {
|
||||
logger.log('warn', '%s returned a player count of %d, Minetrack has capped it to %d to prevent browser performance issues with graph rendering. If this is in error, please edit maxPlayerCount in ping.js!', host, playerCount, maxPlayerCount);
|
||||
return maxPlayerCount;
|
||||
} else if (playerCount !== Math.max(playerCount, 0)) {
|
||||
logger.log('warn', '%s returned an invalid player count of %d, setting to 0.', host, playerCount);
|
||||
return 0;
|
||||
}
|
||||
return playerCount;
|
||||
function capPlayerCount (host, playerCount) {
|
||||
const maxPlayerCount = 250000
|
||||
|
||||
if (playerCount !== Math.min(playerCount, maxPlayerCount)) {
|
||||
logger.log('warn', '%s returned a player count of %d, Minetrack has capped it to %d to prevent browser performance issues with graph rendering. If this is in error, please edit maxPlayerCount in ping.js!', host, playerCount, maxPlayerCount)
|
||||
|
||||
return maxPlayerCount
|
||||
} else if (playerCount !== Math.max(playerCount, 0)) {
|
||||
logger.log('warn', '%s returned an invalid player count of %d, setting to 0.', host, playerCount)
|
||||
|
||||
return 0
|
||||
}
|
||||
return playerCount
|
||||
}
|
||||
|
||||
exports.ping = function(host, port, type, timeout, callback, version) {
|
||||
if (type === 'PC') {
|
||||
util.unfurlSRV(host, port, function(host, port){
|
||||
pingMinecraftPC(host, port || 25565, timeout, callback, version);
|
||||
})
|
||||
} else if (type === 'PE') {
|
||||
pingMinecraftPE(host, port || 19132, timeout, callback);
|
||||
} else {
|
||||
throw new Error('Unsupported type: ' + type);
|
||||
}
|
||||
};
|
||||
class PingController {
|
||||
constructor (app) {
|
||||
this._app = app
|
||||
}
|
||||
|
||||
schedule () {
|
||||
setInterval(this.pingAll, config.rates.pingAll)
|
||||
|
||||
this.pingAll()
|
||||
}
|
||||
|
||||
pingAll = () => {
|
||||
for (const serverRegistration of this._app.serverRegistrations) {
|
||||
const version = serverRegistration.getNextProtocolVersion()
|
||||
|
||||
ping(serverRegistration.data.ip, serverRegistration.data.port, serverRegistration.data.type, config.rates.connectTimeout, (err, resp) => {
|
||||
if (err) {
|
||||
logger.log('error', 'Failed to ping %s: %s', serverRegistration.data.ip, err.message)
|
||||
}
|
||||
|
||||
this.handlePing(serverRegistration, resp, err, version)
|
||||
}, version.protocolId)
|
||||
}
|
||||
}
|
||||
|
||||
handlePing (serverRegistration, resp, err, version) {
|
||||
const timestamp = new Date().getTime()
|
||||
|
||||
this._app.server.broadcast('update', serverRegistration.getUpdate(timestamp, resp, err, version))
|
||||
|
||||
serverRegistration.addPing(timestamp, resp)
|
||||
|
||||
if (config.logToDatabase) {
|
||||
const playerCount = resp ? resp.players.online : 0
|
||||
|
||||
// Log to database
|
||||
this._app.database.insertPing(serverRegistration.data.ip, timestamp, playerCount)
|
||||
|
||||
if (serverRegistration.addGraphPoint(resp !== undefined, playerCount, timestamp)) {
|
||||
this._app.server.broadcast('updateHistoryGraph', {
|
||||
name: serverRegistration.data.name,
|
||||
playerCount: playerCount,
|
||||
timestamp: timestamp
|
||||
})
|
||||
}
|
||||
|
||||
// Update calculated graph peak regardless if the graph is being updated
|
||||
// This can cause a (harmless) desync between live and stored data, but it allows it to be more accurate for long surviving processes
|
||||
if (serverRegistration.findNewGraphPeak()) {
|
||||
const graphPeak = serverRegistration.getGraphPeak()
|
||||
|
||||
this._app.server.broadcast('updatePeak', {
|
||||
name: serverRegistration.data.name,
|
||||
playerCount: graphPeak.playerCount,
|
||||
timestamp: graphPeak.timestamp
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PingController
|
||||
|
Reference in New Issue
Block a user