commit
0e5859a829
@ -9,10 +9,6 @@
|
||||
"pingAll": 3000,
|
||||
"connectTimeout": 2500
|
||||
},
|
||||
"performance": {
|
||||
"skipUnfurlSrv": false,
|
||||
"unfurlSrvCacheTtl": 120000
|
||||
},
|
||||
"logToDatabase": false,
|
||||
"graphDuration": 86400000,
|
||||
"serverGraphDuration": 180000
|
||||
|
@ -1,3 +1,7 @@
|
||||
**5.4.3** *(May 14 2020)*
|
||||
- Added support for the optional field `config->skipSrvTimeout` in `config.json`. If a configured server does not return a valid response when unfurling potential SRV records, it will avoid re-unfurling SRV records for this duration in milliseconds. Use a value of `0` to disable this feature altogether.
|
||||
- Removes support for the `config->performance->skipUnfurlSrv` and `config->performance->unfurlSrvCacheTtl` fields in `config.json
|
||||
|
||||
**5.4.2** *(May 13 2020)*
|
||||
- Fixes a typo causing `_minecraft._tcp.*` SRV records to not resolve.
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
const sqlite = require('sqlite3')
|
||||
|
||||
const TimeTracker = require('./time')
|
||||
|
||||
class Database {
|
||||
constructor (app) {
|
||||
this._app = app
|
||||
@ -16,7 +18,7 @@ class Database {
|
||||
|
||||
loadGraphPoints (graphDuration, callback) {
|
||||
// Query recent pings
|
||||
const endTime = new Date().getTime()
|
||||
const endTime = TimeTracker.getEpochMillis()
|
||||
const startTime = endTime - graphDuration
|
||||
|
||||
this.getRecentPings(startTime, endTime, pingData => {
|
||||
|
75
lib/dns.js
Normal file
75
lib/dns.js
Normal file
@ -0,0 +1,75 @@
|
||||
const dns = require('dns')
|
||||
|
||||
const logger = require('./logger')
|
||||
const TimeTracker = require('./time')
|
||||
|
||||
const config = require('../config')
|
||||
|
||||
const SKIP_SRV_TIMEOUT = config.skipSrvTimeout || 60 * 60 * 1000
|
||||
|
||||
class DNSResolver {
|
||||
constructor (ip, port) {
|
||||
this._ip = ip
|
||||
this._port = port
|
||||
}
|
||||
|
||||
_skipSrv () {
|
||||
this._skipSrvUntil = TimeTracker.getEpochMillis() + SKIP_SRV_TIMEOUT
|
||||
}
|
||||
|
||||
_isSkipSrv () {
|
||||
return this._skipSrvUntil && TimeTracker.getEpochMillis() <= this._skipSrvUntil
|
||||
}
|
||||
|
||||
resolve (callback) {
|
||||
if (this._isSkipSrv()) {
|
||||
callback(this._ip, this._port, config.rates.connectTimeout)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const startTime = TimeTracker.getEpochMillis()
|
||||
|
||||
let callbackFired = false
|
||||
|
||||
const fireCallback = (ip, port) => {
|
||||
if (!callbackFired) {
|
||||
callbackFired = true
|
||||
|
||||
// Send currentTime - startTime to provide remaining connectionTime allowance
|
||||
callback(ip || this._ip, port || this._port, TimeTracker.getEpochMillis() - startTime)
|
||||
}
|
||||
}
|
||||
|
||||
const timeoutCallback = setTimeout(fireCallback, config.rates.connectTimeout)
|
||||
|
||||
dns.resolveSrv('_minecraft._tcp.' + this._ip, (err, records) => {
|
||||
// Cancel the timeout handler if not already fired
|
||||
if (!callbackFired) {
|
||||
clearTimeout(timeoutCallback)
|
||||
}
|
||||
|
||||
// Test if the error indicates a miss, or if the records returned are empty
|
||||
if ((err && (err.code === 'ENOTFOUND' || err.code === 'ENODATA')) || !records || records.length === 0) {
|
||||
// Compare config.skipSrvTimeout directly since SKIP_SRV_TIMEOUT has an or'd value
|
||||
// isValidSkipSrvTimeout == whether the config has a valid skipSrvTimeout value set
|
||||
const isValidSkipSrvTimeout = typeof config.skipSrvTimeout === 'number' && config.skipSrvTimeout > 0
|
||||
|
||||
// Only activate _skipSrv if the skipSrvTimeout value is either NaN or > 0
|
||||
// 0 represents a disabled flag
|
||||
if (!this._isSkipSrv() && isValidSkipSrvTimeout) {
|
||||
this._skipSrv()
|
||||
|
||||
logger.log('warn', 'No SRV records were resolved for %s. Minetrack will skip attempting to resolve %s SRV records for %d minutes.', this._ip, this._ip, SKIP_SRV_TIMEOUT / (60 * 1000))
|
||||
}
|
||||
|
||||
fireCallback()
|
||||
} else {
|
||||
// Only fires if !err && records.length > 0
|
||||
fireCallback(records[0].name, records[0].port)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DNSResolver
|
@ -9,7 +9,7 @@ winston.add(winston.transports.File, {
|
||||
winston.add(winston.transports.Console, {
|
||||
timestamp: () => {
|
||||
const date = new Date()
|
||||
return date.toLocaleTimeString() + ' ' + date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear().toString().substring(2, 4)
|
||||
return date.toLocaleTimeString() + ' ' + date.toLocaleDateString()
|
||||
},
|
||||
colorize: true
|
||||
})
|
||||
|
@ -9,10 +9,10 @@ const config = require('../config')
|
||||
function ping (serverRegistration, timeout, callback, version) {
|
||||
switch (serverRegistration.data.type) {
|
||||
case 'PC':
|
||||
serverRegistration.unfurlSrv((host, port) => {
|
||||
serverRegistration.dnsResolver.unfurlSrv((host, port, remainingTimeout) => {
|
||||
const server = new minecraftJavaPing.MinecraftServer(host, port || 25565)
|
||||
|
||||
server.ping(timeout, version, (err, res) => {
|
||||
server.ping(remainingTimeout, version, (err, res) => {
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
const crypto = require('crypto')
|
||||
const dns = require('dns')
|
||||
|
||||
const DNSResolver = require('./dns')
|
||||
const TimeTracker = require('./time')
|
||||
const Server = require('./server')
|
||||
|
||||
@ -18,6 +18,7 @@ class ServerRegistration {
|
||||
this.serverId = serverId
|
||||
this.data = data
|
||||
this._pingHistory = []
|
||||
this.dnsResolver = new DNSResolver(this.data.ip, this.data.port)
|
||||
}
|
||||
|
||||
handlePing (timestamp, resp, err, version) {
|
||||
@ -310,62 +311,6 @@ class ServerRegistration {
|
||||
color: this.data.color
|
||||
}
|
||||
}
|
||||
|
||||
unfurlSrv (callback) {
|
||||
// Skip unfurling SRV, instantly return pre-configured data
|
||||
if (config.performance && config.performance.skipUnfurlSrv) {
|
||||
callback(this.data.ip, this.data.port)
|
||||
return
|
||||
}
|
||||
|
||||
const timestamp = new Date().getTime()
|
||||
|
||||
// If a cached copy exists and is within its TTL, instantly return
|
||||
if (this._lastUnfurlSrv && timestamp - this._lastUnfurlSrv.timestamp <= config.performance.unfurlSrvCacheTtl) {
|
||||
callback(this._lastUnfurlSrv.ip, this._lastUnfurlSrv.port)
|
||||
return
|
||||
}
|
||||
|
||||
// Group callbacks into an array
|
||||
// Once resolved, fire callbacks sequentially
|
||||
// This avoids callbacks possibly executing out of order
|
||||
if (!this._unfurlSrvCallbackQueue) {
|
||||
this._unfurlSrvCallbackQueue = []
|
||||
}
|
||||
|
||||
this._unfurlSrvCallbackQueue.push(callback)
|
||||
|
||||
// Prevent multiple #resolveSrv calls per ServerRegistration
|
||||
if (!this._isUnfurlingSrv) {
|
||||
this._isUnfurlingSrv = true
|
||||
|
||||
dns.resolveSrv('_minecraft._tcp.' + this.data.ip, (_, records) => {
|
||||
this._lastUnfurlSrv = {
|
||||
timestamp
|
||||
}
|
||||
|
||||
if (records && records.length > 0) {
|
||||
this._lastUnfurlSrv.ip = records[0].name
|
||||
this._lastUnfurlSrv.port = records[0].port
|
||||
} else {
|
||||
// Provide fallback to pre-configured data
|
||||
this._lastUnfurlSrv.ip = this.data.ip
|
||||
this._lastUnfurlSrv.port = this.data.port
|
||||
}
|
||||
|
||||
// Fire the waiting callbacks in queue
|
||||
// Release blocking flag to allow new #resolveSrv calls
|
||||
this._isUnfurlingSrv = false
|
||||
|
||||
for (const callback of this._unfurlSrvCallbackQueue) {
|
||||
callback(this._lastUnfurlSrv.ip, this._lastUnfurlSrv.port)
|
||||
}
|
||||
|
||||
// Reset the callback queue
|
||||
this._unfurlSrvCallbackQueue = []
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ServerRegistration
|
||||
|
@ -7,7 +7,7 @@ class TimeTracker {
|
||||
}
|
||||
|
||||
newTimestamp () {
|
||||
const timestamp = new Date().getTime()
|
||||
const timestamp = TimeTracker.getEpochMillis()
|
||||
|
||||
this._points.push(timestamp)
|
||||
|
||||
@ -22,6 +22,10 @@ class TimeTracker {
|
||||
return this._points
|
||||
}
|
||||
|
||||
static getEpochMillis () {
|
||||
return new Date().getTime()
|
||||
}
|
||||
|
||||
static getMaxServerGraphDataLength () {
|
||||
return Math.ceil(config.serverGraphDuration / config.rates.pingAll)
|
||||
}
|
||||
|
9
main.js
9
main.js
@ -30,15 +30,6 @@ if (!config.serverGraphDuration) {
|
||||
config.serverGraphDuration = 3 * 60 * 10000
|
||||
}
|
||||
|
||||
if (config.performance && config.performance.skipUnfurlSrv) {
|
||||
logger.log('warn', '"performance.skipUnfurlSrv" is enabled. Any configured hosts using SRV records may not properly resolve.')
|
||||
}
|
||||
|
||||
if (!config.performance || typeof config.performance.unfurlSrvCacheTtl === 'undefined') {
|
||||
logger.log('warn', '"performance.unfurlSrvCacheTtl" is not defined in config.json - defaulting to 120 seconds!')
|
||||
config.performance.unfurlSrvCacheTtl = 2 * 60 * 1000
|
||||
}
|
||||
|
||||
if (!config.logToDatabase) {
|
||||
logger.log('warn', 'Database logging is not enabled. You can enable it by setting "logToDatabase" to true in config.json. This requires sqlite3 to be installed.')
|
||||
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minetrack",
|
||||
"version": "5.4.2",
|
||||
"version": "5.4.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "minetrack",
|
||||
"version": "5.4.2",
|
||||
"version": "5.4.3",
|
||||
"description": "A Minecraft server tracker that lets you focus on the basics.",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
|
Loading…
Reference in New Issue
Block a user