remove graph smoothing behavior, add null playerCount support
This commit is contained in:
parent
19190f8d79
commit
ef0c41ee1d
@ -149,7 +149,7 @@ export class GraphDisplayManager {
|
|||||||
|
|
||||||
let playerCount
|
let playerCount
|
||||||
|
|
||||||
if (id >= serverGraphData.length) {
|
if (id >= serverGraphData.length || typeof serverGraphData[id] !== 'number') {
|
||||||
playerCount = '-'
|
playerCount = '-'
|
||||||
} else {
|
} else {
|
||||||
playerCount = formatNumber(serverGraphData[id])
|
playerCount = formatNumber(serverGraphData[id])
|
||||||
|
@ -38,6 +38,10 @@ export class RelativeScale {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (max === Number.MAX_VALUE) {
|
||||||
|
max = 0
|
||||||
|
}
|
||||||
|
|
||||||
return RelativeScale.scale([0, max], tickCount)
|
return RelativeScale.scale([0, max], tickCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +61,7 @@ export class RelativeScale {
|
|||||||
let max = Number.MIN_VALUE
|
let max = Number.MIN_VALUE
|
||||||
|
|
||||||
for (const point of data) {
|
for (const point of data) {
|
||||||
|
if (typeof point === 'number') {
|
||||||
if (point > max) {
|
if (point > max) {
|
||||||
max = point
|
max = point
|
||||||
}
|
}
|
||||||
@ -64,6 +69,14 @@ export class RelativeScale {
|
|||||||
min = point
|
min = point
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min === Number.MAX_VALUE) {
|
||||||
|
min = 0
|
||||||
|
}
|
||||||
|
if (max === Number.MIN_VALUE) {
|
||||||
|
max = 0
|
||||||
|
}
|
||||||
|
|
||||||
return [min, max]
|
return [min, max]
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ export class ServerRegistration {
|
|||||||
this._app = app
|
this._app = app
|
||||||
this.serverId = serverId
|
this.serverId = serverId
|
||||||
this.data = data
|
this.data = data
|
||||||
this._graphData = []
|
this._graphData = [[], []]
|
||||||
this._failedSequentialPings = 0
|
this._failedSequentialPings = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,16 @@ export class ServerRegistration {
|
|||||||
plugins: [
|
plugins: [
|
||||||
uPlotTooltipPlugin((pos, id, plot) => {
|
uPlotTooltipPlugin((pos, id, plot) => {
|
||||||
if (pos) {
|
if (pos) {
|
||||||
const text = formatNumber(plot.data[1][id]) + ' Players<br>' + formatTimestamp(plot.data[0][id] * 1000)
|
const playerCount = plot.data[1][id]
|
||||||
|
|
||||||
|
if (typeof playerCount !== 'number') {
|
||||||
|
this._app.tooltip.hide()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: update timestamp schema
|
||||||
|
const text = formatNumber(playerCount) + ' Players<br>' + formatTimestamp(plot.data[0][id] * 1000)
|
||||||
|
|
||||||
this._app.tooltip.set(pos.left, pos.top, 10, 10, text)
|
this._app.tooltip.set(pos.left, pos.top, 10, 10, text)
|
||||||
} else {
|
} else {
|
||||||
@ -147,13 +156,23 @@ export class ServerRegistration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlePing (payload, timestamp) {
|
handlePing (payload, timestamp) {
|
||||||
if (typeof payload.playerCount !== 'undefined') {
|
if (typeof payload.playerCount === 'number') {
|
||||||
this.playerCount = payload.playerCount
|
this.playerCount = payload.playerCount
|
||||||
|
|
||||||
// Only update graph for successful pings
|
// Reset failed ping counter to ensure the next connection error
|
||||||
// This intentionally pauses the server graph when pings begin to fail
|
// doesn't instantly retrigger a layout change
|
||||||
|
this._failedSequentialPings = 0
|
||||||
|
} else {
|
||||||
|
// Attempt to retain a copy of the cached playerCount for up to N failed pings
|
||||||
|
// This prevents minor connection issues from constantly reshuffling the layout
|
||||||
|
if (++this._failedSequentialPings > 5) {
|
||||||
|
this.playerCount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use payload.playerCount so nulls WILL be pushed into the graphing data
|
||||||
this._graphData[0].push(Math.floor(timestamp / 1000))
|
this._graphData[0].push(Math.floor(timestamp / 1000))
|
||||||
this._graphData[1].push(this.playerCount)
|
this._graphData[1].push(payload.playerCount)
|
||||||
|
|
||||||
// Trim graphData to within the max length by shifting out the leading elements
|
// Trim graphData to within the max length by shifting out the leading elements
|
||||||
for (const series of this._graphData) {
|
for (const series of this._graphData) {
|
||||||
@ -164,14 +183,18 @@ export class ServerRegistration {
|
|||||||
|
|
||||||
this.redraw()
|
this.redraw()
|
||||||
|
|
||||||
// Reset failed ping counter to ensure the next connection error
|
if (typeof payload.playerCount !== 'undefined') {
|
||||||
// doesn't instantly retrigger a layout change
|
this.playerCount = payload.playerCount || 0
|
||||||
this._failedSequentialPings = 0
|
|
||||||
} else {
|
// Use payload.playerCount so nulls WILL be pushed into the graphing data
|
||||||
// Attempt to retain a copy of the cached playerCount for up to N failed pings
|
this._graphData[0].push(Math.floor(timestamp / 1000))
|
||||||
// This prevents minor connection issues from constantly reshuffling the layout
|
this._graphData[1].push(payload.playerCount)
|
||||||
if (++this._failedSequentialPings > 5) {
|
|
||||||
this.playerCount = 0
|
// Trim graphData to within the max length by shifting out the leading elements
|
||||||
|
for (const series of this._graphData) {
|
||||||
|
if (series.length > this._app.publicConfig.serverGraphMaxLength) {
|
||||||
|
series.shift()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,13 +260,23 @@ export class ServerRegistration {
|
|||||||
const playerCountLabelElement = document.getElementById('player-count_' + this.serverId)
|
const playerCountLabelElement = document.getElementById('player-count_' + this.serverId)
|
||||||
const errorElement = document.getElementById('error_' + this.serverId)
|
const errorElement = document.getElementById('error_' + this.serverId)
|
||||||
|
|
||||||
if (ping.error) {
|
if (ping.error || typeof ping.playerCount !== 'number') {
|
||||||
// Hide any visible player-count and show the error element
|
// Hide any visible player-count and show the error element
|
||||||
playerCountLabelElement.style.display = 'none'
|
playerCountLabelElement.style.display = 'none'
|
||||||
errorElement.style.display = 'block'
|
errorElement.style.display = 'block'
|
||||||
|
|
||||||
errorElement.innerText = ping.error.message
|
let errorMessage
|
||||||
} else if (typeof ping.playerCount !== 'undefined') {
|
|
||||||
|
if (ping.error) {
|
||||||
|
errorMessage = ping.error.message
|
||||||
|
} else if (typeof ping.playerCount !== 'number') {
|
||||||
|
// If the frontend has freshly connection, and the server's last ping was in error, it may not contain an error object
|
||||||
|
// In this case playerCount will safely be null, so provide a generic error message instead
|
||||||
|
errorMessage = 'Failed to ping'
|
||||||
|
}
|
||||||
|
|
||||||
|
errorElement.innerText = errorMessage
|
||||||
|
} else if (typeof ping.playerCount === 'number') {
|
||||||
// Ensure the player-count element is visible and hide the error element
|
// Ensure the player-count element is visible and hide the error element
|
||||||
playerCountLabelElement.style.display = 'block'
|
playerCountLabelElement.style.display = 'block'
|
||||||
errorElement.style.display = 'none'
|
errorElement.style.display = 'none'
|
||||||
|
@ -92,8 +92,9 @@ class PingController {
|
|||||||
const result = results[serverRegistration.serverId]
|
const result = results[serverRegistration.serverId]
|
||||||
|
|
||||||
// Log to database if enabled
|
// Log to database if enabled
|
||||||
|
// Use null to represent a failed ping
|
||||||
if (config.logToDatabase) {
|
if (config.logToDatabase) {
|
||||||
const playerCount = result.resp ? result.resp.players.online : 0
|
const playerCount = result.resp ? result.resp.players.online : null
|
||||||
|
|
||||||
this._app.database.insertPing(serverRegistration.data.ip, timestamp, playerCount)
|
this._app.database.insertPing(serverRegistration.data.ip, timestamp, playerCount)
|
||||||
}
|
}
|
||||||
@ -130,6 +131,12 @@ class PingController {
|
|||||||
const version = serverRegistration.getNextProtocolVersion()
|
const version = serverRegistration.getNextProtocolVersion()
|
||||||
|
|
||||||
ping(serverRegistration, config.rates.connectTimeout, (err, resp) => {
|
ping(serverRegistration, config.rates.connectTimeout, (err, resp) => {
|
||||||
|
if (Math.random() < 0.1) {
|
||||||
|
err = {
|
||||||
|
message: 'random fail'
|
||||||
|
}
|
||||||
|
resp = undefined
|
||||||
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.log('error', 'Failed to ping %s: %s', serverRegistration.data.ip, err.message)
|
logger.log('error', 'Failed to ping %s: %s', serverRegistration.data.ip, err.message)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ class ServerRegistration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlePing (timestamp, resp, err, version) {
|
handlePing (timestamp, resp, err, version) {
|
||||||
const playerCount = resp ? resp.players.online : 0
|
// Use null to represent a failed ping
|
||||||
|
const playerCount = resp ? resp.players.online : null
|
||||||
|
|
||||||
// Store into in-memory ping data
|
// Store into in-memory ping data
|
||||||
this._pingHistory.push(playerCount)
|
this._pingHistory.push(playerCount)
|
||||||
@ -37,7 +38,7 @@ class ServerRegistration {
|
|||||||
let updateHistoryGraph = false
|
let updateHistoryGraph = false
|
||||||
|
|
||||||
if (config.logToDatabase) {
|
if (config.logToDatabase) {
|
||||||
if (this.addGraphPoint(resp !== undefined, playerCount, timestamp)) {
|
if (this.addGraphPoint(playerCount, timestamp)) {
|
||||||
updateHistoryGraph = true
|
updateHistoryGraph = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,37 +143,21 @@ class ServerRegistration {
|
|||||||
for (const point of points) {
|
for (const point of points) {
|
||||||
// 0 is the index of the timestamp
|
// 0 is the index of the timestamp
|
||||||
if (point[0] - lastTimestamp >= 60 * 1000) {
|
if (point[0] - lastTimestamp >= 60 * 1000) {
|
||||||
// This check tries to smooth out randomly dropped pings
|
|
||||||
// By default only filter pings that are online (playerCount > 0)
|
|
||||||
// This will keep looking forward until it finds a ping that is online
|
|
||||||
// If it can't find one within a reasonable timeframe, it will select a failed ping
|
|
||||||
if (point[0] - lastTimestamp >= 120 * 1000 || point[1] > 0) {
|
|
||||||
minutePoints.push(point)
|
|
||||||
lastTimestamp = point[0]
|
lastTimestamp = point[0]
|
||||||
}
|
|
||||||
|
// FIXME: update schema, remove timestamp
|
||||||
|
minutePoints.push(point)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minutePoints.length > 0) {
|
if (minutePoints.length > 0) {
|
||||||
this.graphData = minutePoints
|
this.graphData = minutePoints
|
||||||
|
|
||||||
// Select the last entry to use for lastGraphDataPush
|
|
||||||
this._lastGraphDataPush = minutePoints[minutePoints.length - 1][0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addGraphPoint (isSuccess, playerCount, timestamp) {
|
|
||||||
// If the ping failed, then to avoid destroying the graph, ignore it
|
|
||||||
// However if it's been too long since the last successful ping, push it anyways
|
|
||||||
if (this._lastGraphDataPush) {
|
|
||||||
const timeSince = timestamp - this._lastGraphDataPush
|
|
||||||
if ((isSuccess && timeSince < 60 * 1000) || (!isSuccess && timeSince < 70 * 1000)) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addGraphPoint (playerCount, timestamp) {
|
||||||
|
// FIXME: update schema, remove timestamp
|
||||||
this.graphData.push([timestamp, playerCount])
|
this.graphData.push([timestamp, playerCount])
|
||||||
this._lastGraphDataPush = timestamp
|
|
||||||
|
|
||||||
// Trim old graphPoints according to #getMaxGraphDataLength
|
// Trim old graphPoints according to #getMaxGraphDataLength
|
||||||
if (this.graphData.length > TimeTracker.getMaxGraphDataLength()) {
|
if (this.graphData.length > TimeTracker.getMaxGraphDataLength()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user