diff --git a/assets/js/graph.js b/assets/js/graph.js
index 72a99ac..b3f48c6 100644
--- a/assets/js/graph.js
+++ b/assets/js/graph.js
@@ -2,7 +2,7 @@ import uPlot from '../lib/uPlot.esm'
import { RelativeScale } from './scale'
-import { formatNumber, formatTimestamp, isMobileBrowser } from './util'
+import { formatNumber, formatTimestampSeconds, isMobileBrowser } from './util'
import { uPlotTooltipPlugin } from './tooltip'
import { FAVORITE_SERVERS_STORAGE_KEY } from './favorites'
@@ -23,7 +23,7 @@ export class GraphDisplayManager {
this._showOnlyFavorites = false
}
- addGraphPoint (serverId, timestamp, playerCount) {
+ addGraphPoint (timestamp, playerCounts) {
if (!this._hasLoadedSettings) {
// _hasLoadedSettings is controlled by #setGraphData
// It will only be true once the context has been loaded and initial payload received
@@ -32,7 +32,11 @@ export class GraphDisplayManager {
return
}
- // FIXME
+ this._graphTimestamps.push(timestamp)
+
+ for (let i = 0; i < playerCounts.length; i++) {
+ this._graphData[i].push(playerCounts[i])
+ }
}
loadLocalStorage () {
@@ -106,7 +110,7 @@ export class GraphDisplayManager {
}
}
- buildPlotInstance (graphData) {
+ buildPlotInstance (timestamps, data) {
// Lazy load settings from localStorage, if any and if enabled
if (!this._hasLoadedSettings) {
this._hasLoadedSettings = true
@@ -114,14 +118,8 @@ export class GraphDisplayManager {
this.loadLocalStorage()
}
- // FIXME: timestamps are not shared!
- this._graphTimestamps = graphData[0].map(val => Math.floor(val[0] / 1000))
- this._graphData = Object.values(graphData).map(val => {
- return val.map(element => {
- // Safely handle null data points, they represent gaps in the graph
- return element === null ? null : element[1]
- })
- })
+ this._graphTimestamps = timestamps
+ this._graphData = data
const series = this._app.serverRegistry.getServerRegistrations().map(serverRegistration => {
return {
@@ -141,7 +139,7 @@ export class GraphDisplayManager {
uPlotTooltipPlugin((pos, id, plot) => {
if (pos) {
// FIXME
- let text = '' + formatTimestamp(this._graphTimestamps[id] * 1000) + '
'
+ let text = '' + formatTimestampSeconds(this._graphTimestamps[id]) + '
'
for (let i = 1; i < plot.series.length; i++) {
const serverRegistration = this._app.serverRegistry.getServerRegistration(i - 1)
diff --git a/assets/js/servers.js b/assets/js/servers.js
index 68113f7..bca0d40 100644
--- a/assets/js/servers.js
+++ b/assets/js/servers.js
@@ -2,7 +2,7 @@ import uPlot from '../lib/uPlot.esm'
import { RelativeScale } from './scale'
-import { formatNumber, formatTimestamp, formatDate, formatMinecraftServerAddress, formatMinecraftVersions } from './util'
+import { formatNumber, formatTimestampSeconds, formatDate, formatMinecraftServerAddress, formatMinecraftVersions } from './util'
import { uPlotTooltipPlugin } from './tooltip'
import MISSING_FAVICON from '../images/missing_favicon.svg'
@@ -69,7 +69,7 @@ export class ServerRegistration {
addGraphPoints (points, timestampPoints) {
this._graphData = [
- timestampPoints.map(val => Math.floor(val / 1000)),
+ timestampPoints.slice(),
points
]
}
@@ -90,8 +90,7 @@ export class ServerRegistration {
return
}
- // FIXME: update timestamp schema
- const text = formatNumber(playerCount) + ' Players
' + formatTimestamp(plot.data[0][id] * 1000)
+ const text = formatNumber(playerCount) + ' Players
' + formatTimestampSeconds(plot.data[0][id])
this._app.tooltip.set(pos.left, pos.top, 10, 10, text)
} else {
@@ -171,7 +170,7 @@ export class ServerRegistration {
}
// Use payload.playerCount so nulls WILL be pushed into the graphing data
- this._graphData[0].push(Math.floor(timestamp / 1000))
+ this._graphData[0].push(timestamp)
this._graphData[1].push(payload.playerCount)
// Trim graphData to within the max length by shifting out the leading elements
@@ -182,21 +181,6 @@ export class ServerRegistration {
}
this.redraw()
-
- if (typeof payload.playerCount !== 'undefined') {
- this.playerCount = payload.playerCount || 0
-
- // Use payload.playerCount so nulls WILL be pushed into the graphing data
- this._graphData[0].push(Math.floor(timestamp / 1000))
- this._graphData[1].push(payload.playerCount)
-
- // 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()
- }
- }
- }
}
redraw () {
@@ -219,7 +203,7 @@ export class ServerRegistration {
const peakValueElement = document.getElementById('peak-value_' + this.serverId)
peakValueElement.innerText = formatNumber(data.playerCount)
- peakLabelElement.title = 'At ' + formatTimestamp(data.timestamp)
+ peakLabelElement.title = 'At ' + formatTimestampSeconds(data.timestamp)
this.lastPeakData = data
}
@@ -245,7 +229,7 @@ export class ServerRegistration {
// Safely handle legacy recordData that may not include the timestamp payload
if (recordData.timestamp > 0) {
recordValueElement.innerHTML = formatNumber(recordData.playerCount) + ' (' + formatDate(recordData.timestamp) + ')'
- recordLabelElement.title = 'At ' + formatDate(recordData.timestamp) + ' ' + formatTimestamp(recordData.timestamp)
+ recordLabelElement.title = 'At ' + formatDate(recordData.timestamp) + ' ' + formatTimestampSeconds(recordData.timestamp)
} else {
recordValueElement.innerText = formatNumber(recordData.playerCount)
}
diff --git a/assets/js/socket.js b/assets/js/socket.js
index b60645d..24e1049 100644
--- a/assets/js/socket.js
+++ b/assets/js/socket.js
@@ -81,8 +81,6 @@ export class SocketManager {
break
case 'updateServers': {
- let requestGraphRedraw = false
-
for (let serverId = 0; serverId < payload.updates.length; serverId++) {
// The backend may send "update" events prior to receiving all "add" events
// A server has only been added once it's ServerRegistration is defined
@@ -92,28 +90,18 @@ export class SocketManager {
if (serverRegistration) {
serverRegistration.handlePing(serverUpdate, payload.timestamp)
-
serverRegistration.updateServerStatus(serverUpdate, this._app.publicConfig.minecraftVersions)
}
-
- // Use update payloads to conditionally append data to graph
- // Skip any incoming updates if the graph is disabled
- if (serverUpdate.updateHistoryGraph && this._app.graphDisplayManager.isVisible) {
- // Update may not be successful, safely append 0 points
- const playerCount = serverUpdate.playerCount || 0
-
- this._app.graphDisplayManager.addGraphPoint(serverRegistration.serverId, payload.timestamp, playerCount)
-
- // Only redraw the graph if not mutating hidden data
- if (serverRegistration.isVisible) {
- requestGraphRedraw = true
- }
- }
}
- // Run redraw tasks after handling bulk updates
- if (requestGraphRedraw) {
- this._app.graphDisplayManager.redraw()
+ // Bulk add playerCounts into graph during #updateHistoryGraph
+ if (payload.updateHistoryGraph) {
+ this._app.graphDisplayManager.addGraphPoint(payload.timestamp, Object.values(payload.updates).map(update => update.playerCount))
+
+ // Run redraw tasks after handling bulk updates
+ if (this._app.graphDisplayManager.isVisible) {
+ this._app.graphDisplayManager.redraw()
+ }
}
this._app.percentageBar.redraw()
@@ -132,7 +120,7 @@ export class SocketManager {
// This is used for the manual graph load request behavior
this._app.graphDisplayManager.isVisible = true
- this._app.graphDisplayManager.buildPlotInstance(payload.graphData)
+ this._app.graphDisplayManager.buildPlotInstance(payload.timestamps, payload.graphData)
// Build checkbox elements for graph controls
let lastRowCounter = 0
diff --git a/assets/js/util.js b/assets/js/util.js
index 6e55e42..31f89b3 100644
--- a/assets/js/util.js
+++ b/assets/js/util.js
@@ -99,15 +99,15 @@ export function formatMinecraftVersions (versions, knownVersions) {
}).join(', ')
}
-export function formatTimestamp (millis) {
+export function formatTimestampSeconds (secs) {
const date = new Date(0)
- date.setUTCSeconds(millis / 1000)
+ date.setUTCSeconds(secs)
return date.toLocaleTimeString()
}
-export function formatDate (millis) {
+export function formatDate (secs) {
const date = new Date(0)
- date.setUTCSeconds(millis / 1000)
+ date.setUTCSeconds(secs)
return date.toLocaleDateString()
}
diff --git a/lib/app.js b/lib/app.js
index 36fdc90..9ca1ce4 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -41,18 +41,19 @@ class App {
if (config.logToDatabase) {
client.on('message', (message) => {
if (message === 'requestHistoryGraph') {
- // Send historical graphData built from all serverRegistrations
- const graphData = {}
+ // FIXME: update schema, remove timestamp
+ // Since all history graph updates share timestamps, pull the timestamps from a single ServerRegistration
+ const timestamps = this.serverRegistrations[0].graphData.map(point => Math.floor(point[0] / 1000))
- this.serverRegistrations.forEach((serverRegistration) => {
- graphData[serverRegistration.serverId] = serverRegistration.graphData
- })
+ // Send historical graphData built from all serverRegistrations
+ const graphData = this.serverRegistrations.map(serverRegistration => serverRegistration.graphData.map(point => point[1]))
// Send graphData in object wrapper to avoid needing to explicity filter
// any header data being appended by #MessageOf since the graph data is fed
// directly into the graphing system
client.send(MessageOf('historyGraph', {
- graphData: graphData
+ timestamps,
+ graphData
}))
}
})
@@ -77,7 +78,7 @@ class App {
}
})(),
mojangServices: this.mojangUpdater.getLastUpdate(),
- timestampPoints: this.timeTracker.getPoints(),
+ timestampPoints: this.timeTracker.getPointsSeconds(),
servers: this.serverRegistrations.map(serverRegistration => serverRegistration.getPingHistory())
}
diff --git a/lib/database.js b/lib/database.js
index 60f795a..c27e30d 100644
--- a/lib/database.js
+++ b/lib/database.js
@@ -63,8 +63,8 @@ class Database {
// When complete increment completeTasks to know when complete
this.getRecord(serverRegistration.data.ip, (playerCount, timestamp) => {
serverRegistration.recordData = {
- playerCount: playerCount,
- timestamp: timestamp
+ playerCount,
+ timestamp: Math.floor(timestamp / 1000)
}
// Check if completedTasks hit the finish value
diff --git a/lib/ping.js b/lib/ping.js
index 58ef9c8..dcdd549 100644
--- a/lib/ping.js
+++ b/lib/ping.js
@@ -85,6 +85,14 @@ class PingController {
pingAll = () => {
const timestamp = this._app.timeTracker.newTimestamp()
+ // Flag each group as history graph additions each minute
+ // This is sent to the frontend for graph updates
+ const updateHistoryGraph = config.logToDatabase && (!this._lastHistoryGraphUpdate || timestamp - this._lastHistoryGraphUpdate >= 60 * 1000)
+
+ if (updateHistoryGraph) {
+ this._lastHistoryGraphUpdate = timestamp
+ }
+
this.startPingTasks(results => {
const updates = []
@@ -102,7 +110,7 @@ class PingController {
// Generate a combined update payload
// This includes any modified fields and flags used by the frontend
// This will not be cached and can contain live metadata
- const update = serverRegistration.handlePing(timestamp, result.resp, result.err, result.version)
+ const update = serverRegistration.handlePing(timestamp, result.resp, result.err, result.version, updateHistoryGraph)
updates[serverRegistration.serverId] = update
}
@@ -110,7 +118,8 @@ class PingController {
// Send object since updates uses serverIds as keys
// Send a single timestamp entry since it is shared
this._app.server.broadcast(MessageOf('updateServers', {
- timestamp,
+ timestamp: Math.floor(timestamp / 1000),
+ updateHistoryGraph,
updates
}))
})
diff --git a/lib/servers.js b/lib/servers.js
index a0ee646..6ef2839 100644
--- a/lib/servers.js
+++ b/lib/servers.js
@@ -20,7 +20,7 @@ class ServerRegistration {
this._pingHistory = []
}
- handlePing (timestamp, resp, err, version) {
+ handlePing (timestamp, resp, err, version, updateHistoryGraph) {
// Use null to represent a failed ping
const playerCount = resp ? resp.players.online : null
@@ -35,19 +35,21 @@ class ServerRegistration {
// Only notify the frontend to append to the historical graph
// if both the graphing behavior is enabled and the backend agrees
// that the ping is eligible for addition
- let updateHistoryGraph = false
+ if (updateHistoryGraph) {
+ // FIXME: update schema, remove timestamp
+ this.graphData.push([timestamp, playerCount])
- if (config.logToDatabase) {
- if (this.addGraphPoint(playerCount, timestamp)) {
- updateHistoryGraph = true
+ // Trim old graphPoints according to #getMaxGraphDataLength
+ if (this.graphData.length > TimeTracker.getMaxGraphDataLength()) {
+ this.graphData.shift()
}
}
// Delegate out update payload generation
- return this.getUpdate(timestamp, resp, err, version, updateHistoryGraph)
+ return this.getUpdate(timestamp, resp, err, version)
}
- getUpdate (timestamp, resp, err, version, updateHistoryGraph) {
+ getUpdate (timestamp, resp, err, version) {
const update = {}
if (resp) {
@@ -59,7 +61,7 @@ class ServerRegistration {
if (config.logToDatabase && (!this.recordData || resp.players.online > this.recordData.playerCount)) {
this.recordData = {
playerCount: resp.players.online,
- timestamp: timestamp
+ timestamp: Math.floor(timestamp / 1000)
}
// Append an updated recordData
@@ -80,12 +82,6 @@ class ServerRegistration {
if (this.findNewGraphPeak()) {
update.graphPeakData = this.getGraphPeak()
}
-
- // Handled inside logToDatabase to validate logic from #getUpdate call
- // Only append when true since an undefined value == false
- if (updateHistoryGraph) {
- update.updateHistoryGraph = true
- }
}
} else if (err) {
// Append a filtered copy of err
@@ -155,18 +151,6 @@ class ServerRegistration {
}
}
- addGraphPoint (playerCount, timestamp) {
- // FIXME: update schema, remove timestamp
- this.graphData.push([timestamp, playerCount])
-
- // Trim old graphPoints according to #getMaxGraphDataLength
- if (this.graphData.length > TimeTracker.getMaxGraphDataLength()) {
- this.graphData.shift()
- }
-
- return true
- }
-
findNewGraphPeak () {
let index = -1
for (let i = 0; i < this.graphData.length; i++) {
@@ -192,7 +176,7 @@ class ServerRegistration {
const graphPeak = this.graphData[this._graphPeakIndex]
return {
playerCount: graphPeak[1],
- timestamp: graphPeak[0]
+ timestamp: Math.floor(graphPeak[0] / 1000)
}
}
diff --git a/lib/time.js b/lib/time.js
index 966272a..3556dfe 100644
--- a/lib/time.js
+++ b/lib/time.js
@@ -18,8 +18,8 @@ class TimeTracker {
return timestamp
}
- getPoints () {
- return this._points
+ getPointsSeconds () {
+ return this._points.map(value => Math.floor(value / 1000))
}
static getMaxServerGraphDataLength () {