var graphs = []; var lastPlayerEntries = []; var historyPlot; var displayedGraphData; var hiddenGraphData = []; var isConnected = false; var mojangServicesUpdater; var sortServersTask; var currentServerHover; var faviconSize = 64; function updateServerStatus(lastEntry) { var info = lastEntry.info; var div = $('#status_' + safeName(info.name)); var versionDiv = $('#version_' + safeName(info.name)); if (lastEntry.versions) { var versions = ''; for (var i = 0; i < lastEntry.versions.length; i++) { if (!lastEntry.versions[i]) continue; versions += '' + publicConfig.minecraftVersions[lastEntry.info.type][lastEntry.versions[i]] + ' '; } versionDiv.html(versions); } else { versionDiv.html(''); } if (lastEntry.result) { var result = lastEntry.result; var newStatus = 'Players: ' + formatNumber(result.players.online) + ''; var listing = graphs[lastEntry.info.name].listing; if (listing.length > 0) { newStatus += ' ('; var playerDifference = listing[listing.length - 1][1] - listing[0][1]; if (playerDifference >= 0) { newStatus += '+'; } newStatus += playerDifference + ')'; } lastPlayerEntries[info.name] = result.players.online; div.html(newStatus); } else { var newStatus = ''; if (findErrorMessage(lastEntry.error)) { newStatus += findErrorMessage(lastEntry.error); } else { newStatus += 'Failed to ping!'; } div.html(newStatus + ''); } var keys = Object.keys(lastPlayerEntries); var totalPlayers = 0; for (var i = 0; i < keys.length; i++) { totalPlayers += lastPlayerEntries[keys[i]]; } $("#stat_totalPlayers").text(formatNumber(totalPlayers)); $("#stat_networks").text(formatNumber(keys.length)); if (lastEntry.record) { $('#record_' + safeName(info.name)).html('Record: ' + formatNumber(lastEntry.record)); } updatePercentageBar(); } function sortServers() { var serverNames = []; var keys = Object.keys(lastPlayerEntries); for (var i = 0; i < keys.length; i++) { serverNames.push(keys[i]); } serverNames.sort(function(a, b) { return (lastPlayerEntries[b] || 0) - (lastPlayerEntries[a] || 0); }); for (var i = 0; i < serverNames.length; i++) { $('#container_' + safeName(serverNames[i])).appendTo('#server-container-list'); $('#ranking_' + safeName(serverNames[i])).text('#' + (i + 1)); } } function updatePercentageBar() { var keys = Object.keys(lastPlayerEntries); keys.sort(function(a, b) { return lastPlayerEntries[a] - lastPlayerEntries[b]; }); var totalPlayers = getCurrentTotalPlayers(); var parent = $('#perc-bar'); var leftPadding = 0; for (var i = 0; i < keys.length; i++) { (function(pos, server, length) { var safeNameCopy = safeName(server); var playerCount = lastPlayerEntries[server]; var div = $('#perc_bar_part_' + safeNameCopy); // Setup the base if (!div.length) { $('
', { id: 'perc_bar_part_' + safeNameCopy, class: 'perc-bar-part', html: '', style: 'background: ' + getServerColor(server) + ';' }).appendTo(parent); div = $('#perc_bar_part_' + safeNameCopy); div.mouseover(function(e) { currentServerHover = server; }); div.mouseout(function(e) { hideTooltip(); currentServerHover = undefined; }); } // Update our position/width var width = (playerCount / totalPlayers) * parent.width(); div.css({ width: width + 'px', left: leftPadding + 'px' }); leftPadding += width; })(i, keys[i], keys.length); } } function getCurrentTotalPlayers() { var totalPlayers = 0; var keys = Object.keys(lastPlayerEntries); for (var i = 0; i < keys.length; i++) totalPlayers += lastPlayerEntries[keys[i]] return totalPlayers; } function setAllGraphVisibility(visible) { if (visible) { var keys = Object.keys(hiddenGraphData); for (var i = 0; i < keys.length; i++) { displayedGraphData[keys[i]] = hiddenGraphData[keys[i]]; } hiddenGraphData = []; } else { var keys = Object.keys(displayedGraphData); for (var i = 0; i < keys.length; i++) { hiddenGraphData[keys[i]] = displayedGraphData[keys[i]]; } displayedGraphData = []; } $('.graph-control').each(function(index, item) { item.checked = visible; }); historyPlot.setData(convertGraphData(displayedGraphData)); historyPlot.setupGrid(); historyPlot.draw(); // Update our localStorage if (visible) { resetGraphControls(); } else { saveGraphControls(Object.keys(displayedGraphData)); } } function validateBootTime(bootTime, socket) { $('#tagline-text').text('Validating...'); console.log('Remote bootTime is ' + bootTime + ', local is ' + publicConfig.bootTime); if (bootTime === publicConfig.bootTime) { $('#tagline-text').text('Loading...'); socket.emit('requestListing'); if (!isMobileBrowser()) socket.emit('requestHistoryGraph'); isConnected = true; // Start any special updating tasks. mojangServicesUpdater = setInterval(updateMojangServices, 1000); sortServersTask = setInterval(sortServers, 10000); } else { $('#tagline-text').text('Updating...'); $.getScript('/publicConfig.json', function(data, textStatus, xhr) { if (xhr.status === 200) { validateBootTime(publicConfig.bootTime, socket); } else { showCaption('Failed to update! Refresh?'); } }); } } function printPort(port) { if(port == undefined || port == 25565) { return ""; } else { return ":" + port; } } function updateServerPeak(name, time, playerCount) { var safeNameCopy = safeName(name); // hack: strip the AM/PM suffix, this could just use a date format instead var timestamp = getTimestamp(time / 1000).split(':'); var end = timestamp.pop().split(' ')[1]; timestamp = timestamp.join(':') + ' ' + end; $('#peak_' + safeNameCopy).html('24h Peak: ' + formatNumber(playerCount) + ' @ ' + timestamp); } $(document).ready(function() { var socket = io.connect({ reconnect: true, reconnectDelay: 1000, reconnectionAttempts: 10 }); socket.on('bootTime', function(bootTime) { validateBootTime(bootTime, socket); }); socket.on('disconnect', function() { if (mojangServicesUpdater) clearInterval(mojangServicesUpdater); if (sortServersTask) clearInterval(sortServersTask); lastMojangServiceUpdate = undefined; showCaption('Disconnected! Refresh?'); lastPlayerEntries = {}; graphs = {}; $('#server-container-list').html(''); $('#big-graph').html(''); $('#big-graph-checkboxes').html(''); $('#big-graph-controls').css('display', 'none'); $('#perc-bar').html(''); $('.mojang-status').css('background', 'transparent'); $('.mojang-status-text').text('...'); $("#stat_totalPlayers").text(0); $("#stat_networks").text(0); isConnected = false; }); socket.on('historyGraph', function(rawData) { var shownServers = loadGraphControls(); if (shownServers) { var keys = Object.keys(rawData); hiddenGraphData = []; displayedGraphData = []; for (var i = 0; i < keys.length; i++) { var name = keys[i]; if (shownServers.indexOf(name) !== -1) { displayedGraphData[name] = rawData[name]; } else { hiddenGraphData[name] = rawData[name]; } } } else { displayedGraphData = rawData; } $('#big-graph').css('height', '400px'); historyPlot = $.plot('#big-graph', convertGraphData(displayedGraphData), bigChartOptions); $('#big-graph').bind('plothover', handlePlotHover); var keys = Object.keys(rawData); var sinceBreak = 0; var html = ''; keys.sort(); for (var i = 0; i < keys.length; i++) { var checkedString = ''; if (displayedGraphData[keys[i]]) { checkedString = 'checked=checked'; } html += ''; if (sinceBreak >= 7) { sinceBreak = 0; html += ''; } else { sinceBreak++; } } $('#big-graph-checkboxes').append(html + '
' + keys[i] + '
'); $('#big-graph-controls').css('display', 'block'); }); socket.on('updateHistoryGraph', function(rawData) { // Prevent race conditions. if (!displayedGraphData || !hiddenGraphData) { return; } // If it's not in our display group, use the hidden group instead. var targetGraphData = displayedGraphData[rawData.name] ? displayedGraphData : hiddenGraphData; trimOldPings(targetGraphData, publicConfig.graphDuration); targetGraphData[rawData.name].push([rawData.timestamp, rawData.players]); // Redraw if we need to. if (displayedGraphData[rawData.name]) { historyPlot.setData(convertGraphData(displayedGraphData)); historyPlot.setupGrid(); historyPlot.draw(); } }); socket.on('add', function(servers) { for (var i = 0; i < servers.length; i++) { var history = servers[i]; var listing = []; for (var x = 0; x < history.length; x++) { var point = history[x]; if (point.result) { listing.push([point.timestamp, point.result.players.online]); } else if (point.error) { listing.push([point.timestamp, 0]); } } var lastEntry = history[history.length - 1]; var info = lastEntry.info; if (lastEntry.error) { lastPlayerEntries[info.name] = 0; } else if (lastEntry.result) { lastPlayerEntries[info.name] = lastEntry.result.players.online; } var typeString = publicConfig.serverTypesVisible ? '' + info.type + '' : ''; var safeNameCopy = safeName(info.name); $('
', { id: 'container_' + safeNameCopy, class: 'server', 'server-id': safeNameCopy, html: '
\ \
\

\
\
\

' + info.name + ' ' + typeString + '

\ Waiting\
\ \
\
\
\
\
' }).appendTo("#server-container-list"); var favicon = MISSING_FAVICON_BASE64; if (lastEntry.result && lastEntry.result.favicon) { favicon = lastEntry.result.favicon; } $('#favicon_' + safeName(info.name)).attr('src', favicon); graphs[lastEntry.info.name] = { listing: listing, plot: $.plot('#chart_' + safeNameCopy, [listing], smallChartOptions) }; updateServerStatus(lastEntry); $('#chart_' + safeNameCopy).bind('plothover', handlePlotHover); } sortServers(); updatePercentageBar(); }); socket.on('update', function(update) { // Prevent weird race conditions. if (!graphs[update.info.name]) { return; } // We have a new favicon, update the old one. if (update.result && update.result.favicon) { $('#favicon_' + safeName(update.info.name)).attr('src', update.result.favicon); } var graph = graphs[update.info.name]; updateServerStatus(update); if (update.result) { graph.listing.push([update.info.timestamp, update.result ? update.result.players.online : 0]); if (graph.listing.length > 72) { graph.listing.shift(); } graph.plot.setData([graph.listing]); graph.plot.setupGrid(); graph.plot.draw(); } }); socket.on('updateMojangServices', function(data) { if (isConnected) { updateMojangServices(data); } }); socket.on('syncComplete', function() { hideCaption(); }); socket.on('updatePeak', function(data) { updateServerPeak(data.name, data.timestamp, data.players); }); socket.on('peaks', function(data) { var keys = Object.keys(data); for (var i = 0; i < keys.length; i++) { var val = data[keys[i]]; updateServerPeak(keys[i], val[0], val[1]); } }); $(document).on('click', '.graph-control', function(e) { var serverIp = $(this).attr('data-target-network'); // 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]; } // Redraw the graph historyPlot.setData(convertGraphData(displayedGraphData)); historyPlot.setupGrid(); historyPlot.draw(); // Update our localStorage if (Object.keys(hiddenGraphData).length === 0) { resetGraphControls(); } else { saveGraphControls(Object.keys(displayedGraphData)); } }); $(document).on('mousemove', function(e) { if (currentServerHover) { var totalPlayers = getCurrentTotalPlayers(); var playerCount = lastPlayerEntries[currentServerHover]; renderTooltip(e.pageX + 10, e.pageY + 10, '' + currentServerHover + ': ' + roundToPoint(playerCount / totalPlayers * 100, 10) + '% of ' + formatNumber(totalPlayers) + ' tracked players.
(' + formatNumber(playerCount) + ' online.)'); } }); $(window).on('resize', function() { updatePercentageBar(); if (historyPlot) { historyPlot.resize(); historyPlot.setupGrid(); historyPlot.draw(); } }); });