From 649d8abd33a39eff826d6050cd2f8d579dac74a9 Mon Sep 17 00:00:00 2001 From: Cryptkeeper Date: Fri, 18 Dec 2015 18:10:58 -0600 Subject: [PATCH] Fix race condition, add graph toggles --- app.js | 94 +++++++++++++++++++------------------ assets/css/main.css | 8 ++-- assets/html/index.html | 1 + assets/js/site.js | 102 +++++++++++++++++------------------------ assets/js/util.js | 49 ++++++++++++++++++++ 5 files changed, 147 insertions(+), 107 deletions(-) diff --git a/app.js b/app.js index 46af534..c8f0665 100644 --- a/app.js +++ b/app.js @@ -122,6 +122,52 @@ function startMainLoop() { }, config.rates.upateMojangStatus); } +function startServices() { + server.start(function() { + // Track how many people are currently connected. + server.io.on('connect', function(client) { + // If we haven't sent out at least one round of pings, disconnect them for now. + if (Object.keys(networkHistory).length < config.servers.length) { + client.disconnect(); + + return; + } + + // We're good to connect them! + connectedClients += 1; + + logger.log('info', 'Accepted connection: %s, total clients: %d', client.request.connection.remoteAddress, connectedClients); + + setTimeout(function() { + // Send them our previous data, so they have somewhere to start. + client.emit('updateMojangServices', mojang.toMessage()); + + // Remap our associative array into just an array. + var networkHistoryKeys = Object.keys(networkHistory); + + networkHistoryKeys.sort(); + + // Send each individually, this should look cleaner than waiting for one big array to transfer. + for (var i = 0; i < networkHistoryKeys.length; i++) { + client.emit('add', [networkHistory[networkHistoryKeys[i]]]); + } + + // Send them the big 24h graph. + client.emit('historyGraph', graphData); + }, 1); + + // Attach our listeners. + client.on('disconnect', function(client) { + connectedClients -= 1; + + logger.log('info', 'Client disconnected, total clients: %d', connectedClients); + }); + }); + + startMainLoop(); + }); +} + if (config.logToDatabase) { // Setup our database. db.setup(); @@ -132,51 +178,11 @@ if (config.logToDatabase) { graphData = util.convertPingsToGraph(data); logger.log('info', 'Queried and parsed ping history in %sms', util.getCurrentTimeMs() - timestamp); + + startServices(); }); } else { logger.warn('Database logging is not enabled. You can enable it by setting "logToDatabase" to true in config.json. This requires sqlite3 to be installed.'); -} -server.start(function() { - // Track how many people are currently connected. - server.io.on('connect', function(client) { - // If we haven't sent out at least one round of pings, disconnect them for now. - if (Object.keys(networkHistory).length < config.servers.length) { - client.disconnect(); - - return; - } - - // We're good to connect them! - connectedClients += 1; - - logger.log('info', 'Accepted connection: %s, total clients: %d', client.request.connection.remoteAddress, connectedClients); - - setTimeout(function() { - // Send them our previous data, so they have somewhere to start. - client.emit('updateMojangServices', mojang.toMessage()); - - // Remap our associative array into just an array. - var networkHistoryKeys = Object.keys(networkHistory); - - networkHistoryKeys.sort(); - - // Send each individually, this should look cleaner than waiting for one big array to transfer. - for (var i = 0; i < networkHistoryKeys.length; i++) { - client.emit('add', [networkHistory[networkHistoryKeys[i]]]); - } - - // Send them the big 24h graph. - client.emit('historyGraph', graphData); - }, 1); - - // Attach our listeners. - client.on('disconnect', function(client) { - connectedClients -= 1; - - logger.log('info', 'Client disconnected, total clients: %d', connectedClients); - }); - }); - - startMainLoop(); -}); \ No newline at end of file + startServices(); +} \ No newline at end of file diff --git a/assets/css/main.css b/assets/css/main.css index 627a599..3b14f66 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -179,9 +179,11 @@ h3 { } /* The big graph */ +#big-graph, #big-graph-controls { + width: 1000px; + margin: 15px auto 0 auto; +} + #big-graph { height: 500px; - width: 800px; - margin: 15px auto 0 auto; - padding-left: 500px; } \ No newline at end of file diff --git a/assets/html/index.html b/assets/html/index.html index 6a20306..cb16c3b 100644 --- a/assets/html/index.html +++ b/assets/html/index.html @@ -35,6 +35,7 @@
+
diff --git a/assets/js/site.js b/assets/js/site.js index 046bd5f..3445ee7 100644 --- a/assets/js/site.js +++ b/assets/js/site.js @@ -42,7 +42,7 @@ var bigChartOptions = { }, yaxis: { show: true, - tickSize: 1000, + tickSize: 2000, tickLength: 10, tickFormatter: function(value) { return formatNumber(value); @@ -64,7 +64,6 @@ var bigChartOptions = { var lastMojangServiceUpdate; var graphs = {}; -var lastLatencyEntries = {}; var lastPlayerEntries = {}; // Generate (and set) the HTML that displays Mojang status. @@ -140,20 +139,7 @@ function updateServerStatus(lastEntry) { newStatus += playerDifference + ')'; } - /*if (lastLatencyEntries[info.name]) { - newStatus += '
'; - - var latencyDifference = lastLatencyEntries[info.name] - result.latency; - - if (latencyDifference >= 0) { - newStatus += '+'; - } - - newStatus += latencyDifference + 'ms'; - }*/ - lastPlayerEntries[info.name] = result.players.online; - lastLatencyEntries[info.name] = result.latency; div.html(newStatus); } else { @@ -196,42 +182,6 @@ function sortServers() { } } -function safeName(name) { - return name.replace(/ /g, ''); -} - -function handlePlotHover(event, pos, item) { - if (item) { - var text = getTimestamp(item.datapoint[0] / 1000) + '\ -
\ - ' + formatNumber(item.datapoint[1]) + ' Players'; - - if (item.series && item.series.label) { - text = item.series.label + '
' + text; - } - - renderTooltip(item.pageX + 5, item.pageY + 5, text); - } else { - hideTooltip(); - } -} - -function convertGraphData(rawData) { - var data = []; - - var keys = Object.keys(rawData); - - for (var i = 0; i < keys.length; i++) { - data.push({ - data: rawData[keys[i]], - yaxis: 1, - label: keys[i] - }); - } - - return data; -} - $(document).ready(function() { var socket = io.connect({ reconnect: true, @@ -243,7 +193,8 @@ $(document).ready(function() { var sortServersTask; var historyPlot; - var historyData; + var displayedGraphData; + var hiddenGraphData = []; socket.on('connect', function() { $('#tagline-text').text('Loading...'); @@ -262,7 +213,6 @@ $(document).ready(function() { $('#tagline-text').text('Disconnected! Refresh?'); lastPlayerEntries = {}; - lastLatencyEntries = {}; graphs = {}; $('#server-container').html(''); @@ -271,21 +221,34 @@ $(document).ready(function() { }); socket.on('historyGraph', function(rawData) { - historyData = rawData; + displayedGraphData = rawData; historyPlot = $.plot('#big-graph', convertGraphData(rawData), bigChartOptions); $('#big-graph').bind('plothover', handlePlotHover); + + var keys = Object.keys(rawData); + + for (var i = 0; i < keys.length; i++) { + $('#big-graph-controls').append(' ' + keys[i] + ' '); + } }); socket.on('updateHistoryGraph', function(rawData) { - if (historyData[rawData.ip].length > 24 * 60) { - historyData[rawData.ip].shift(); + var targetGraphData = displayedGraphData[rawData.ip]; + + // If it's not in our display group, push it to the hidden group instead so it can be restored and still be up to date. + if (!targetGraphData) { + targetGraphData = hiddenGraphData[rawData.ip]; } - historyData[rawData.ip].push([rawData.timestamp, rawData.players]); + if (targetGraphData.length > 24 * 60) { + targetGraphData.shift(); + } - historyPlot.setData(convertGraphData(historyData)); + targetGraphData.push([rawData.timestamp, rawData.players]); + + historyPlot.setData(convertGraphData(displayedGraphData)); historyPlot.setupGrid(); historyPlot.draw(); @@ -311,10 +274,8 @@ $(document).ready(function() { if (lastEntry.error) { lastPlayerEntries[info.name] = 0; - lastLatencyEntries[info.name] = 0; } else if (lastEntry.result) { lastPlayerEntries[info.name] = lastEntry.result.players.online; - lastLatencyEntries[info.name] = lastEntry.result.latency; } $('
', { @@ -414,4 +375,25 @@ $(document).ready(function() { scrollTop: target.offset().top }, 100); }); + + $(document).on('click', '.graph-control', function(e) { + var serverIp = $(this).attr('data-target-network'); + var checked = $(this).attr('checked'); + + // Restore it, or delete it - either works. + if (!this.checked) { + hiddenGraphData[serverIp] = displayedGraphData[serverIp]; + + delete displayedGraphData[serverIp]; + } else { + displayedGraphData[serverIp] = hiddenGraphData[serverIp]; + + delete hiddenGraphData[serverIp]; + } + + historyPlot.setData(convertGraphData(displayedGraphData)); + historyPlot.setupGrid(); + + historyPlot.draw(); + }); }); diff --git a/assets/js/util.js b/assets/js/util.js index 35875e5..8290d6a 100644 --- a/assets/js/util.js +++ b/assets/js/util.js @@ -10,6 +10,10 @@ function getTimestamp(ms, timeOnly) { return date.toLocaleTimeString(); } +function safeName(name) { + return name.replace(/ /g, ''); +} + function renderTooltip(x, y, html) { tooltip.html(html).css({ top: y, @@ -25,6 +29,51 @@ function formatNumber(x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } +function handlePlotHover(event, pos, item) { + if (item) { + var text = getTimestamp(item.datapoint[0] / 1000) + '\ +
\ + ' + formatNumber(item.datapoint[1]) + ' Players'; + + if (item.series && item.series.label) { + text = item.series.label + '
' + text; + } + + renderTooltip(item.pageX + 5, item.pageY + 5, text); + } else { + hideTooltip(); + } +} + +function convertGraphData(rawData) { + var data = []; + + var keys = Object.keys(rawData); + + for (var i = 0; i < keys.length; i++) { + data.push({ + data: rawData[keys[i]], + yaxis: 1, + label: keys[i], + color: stringToColor(keys[i]) + }); + } + + return data; +} + +function stringToColor(base) { + var hash; + + for (var i = 0, hash = 0; i < base.length; i++) { + hash = base.charCodeAt(i) + ((hash << 5) - hash); + } + + color = Math.floor(Math.abs((Math.sin(hash) * 10000) % 1 * 16777216)).toString(16); + + return '#' + Array(6 - color.length + 1).join('0') + color; +} + function msToTime(timer) { var milliseconds = timer % 1000; timer = (timer - milliseconds) / 1000;