535 lines
16 KiB
JavaScript
535 lines
16 KiB
JavaScript
var graphs = [];
|
|
var lastPlayerEntries = [];
|
|
|
|
var historyPlot;
|
|
var displayedGraphData;
|
|
var hiddenGraphData = [];
|
|
|
|
var isConnected = false;
|
|
|
|
var mojangServicesUpdater;
|
|
var sortServersTask;
|
|
|
|
var currentServerHover;
|
|
|
|
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++) {
|
|
versions += '<span class="version">' + publicConfig.minecraftVersions[lastEntry.info.type][lastEntry.versions[i]] + '</span> ';
|
|
}
|
|
|
|
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 += '<span class="color-gray"> (';
|
|
|
|
var playerDifference = listing[listing.length - 1][1] - listing[0][1];
|
|
|
|
if (playerDifference >= 0) {
|
|
newStatus += '+';
|
|
}
|
|
|
|
newStatus += playerDifference + ')</span>';
|
|
}
|
|
|
|
lastPlayerEntries[info.name] = result.players.online;
|
|
|
|
div.html(newStatus);
|
|
} else {
|
|
var newStatus = '<span class="color-red">';
|
|
|
|
if (findErrorMessage(lastEntry.error)) {
|
|
newStatus += findErrorMessage(lastEntry.error);
|
|
} else {
|
|
newStatus += 'Failed to ping!';
|
|
}
|
|
|
|
div.html(newStatus + '</span>');
|
|
}
|
|
|
|
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() {
|
|
if (categoriesVisible) {
|
|
var byCategories = getServersByCategory();
|
|
|
|
var categories = Object.keys(byCategories);
|
|
|
|
for (var i = 0; i < categories.length; i++) {
|
|
var relevantPlayers = [];
|
|
|
|
for (var x = 0; x < byCategories[categories[i]].length; x++) {
|
|
var server = byCategories[categories[i]][x];
|
|
|
|
relevantPlayers[server.name] = lastPlayerEntries[server.name];
|
|
}
|
|
|
|
var keys = Object.keys(relevantPlayers);
|
|
|
|
keys.sort(function(a, b) {
|
|
return relevantPlayers[b] - relevantPlayers[a];
|
|
});
|
|
|
|
for (var x = 0; x < keys.length; x++) {
|
|
$('#' + safeName(keys[x])).appendTo('#server-container-' + categories[i]);
|
|
|
|
$('#ranking_' + safeName(keys[x])).text('#' + (x + 1));
|
|
}
|
|
}
|
|
} else {
|
|
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++) {
|
|
$('#' + safeName(serverNames[i])).appendTo('#server-container-all');
|
|
|
|
$('#ranking_' + safeName(serverNames[i])).text('#' + (i + 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
function renderPercentageBarText(server) {
|
|
var totalPlayers = getCurrentTotalPlayers();
|
|
var playerCount = lastPlayerEntries[server];
|
|
|
|
$('#perc-bar-text').html('<strong>' + server + '</strong><br />' + roundToPoint(playerCount / totalPlayers * 100, 10) + '% of ' + formatNumber(totalPlayers) + ' tracked players.');
|
|
}
|
|
|
|
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) {
|
|
$('<div/>', {
|
|
id: 'perc_bar_part_' + safeNameCopy,
|
|
class: 'perc-bar-part',
|
|
html: '',
|
|
style: 'background: ' + stringToColor(server) + ';'
|
|
}).appendTo(parent);
|
|
|
|
div = $('#perc_bar_part_' + safeNameCopy);
|
|
|
|
div.mouseover(function(e) {
|
|
renderPercentageBarText(server);
|
|
|
|
var text = $('#perc-bar-text');
|
|
|
|
text.stop(true, false);
|
|
text.slideDown(100);
|
|
|
|
currentServerHover = server;
|
|
});
|
|
|
|
div.mouseout(function(e) {
|
|
$('#perc-bar-text').slideUp(100);
|
|
currentServerHover = undefined;
|
|
});
|
|
}
|
|
|
|
// Update our position/width
|
|
var width = (playerCount / totalPlayers) * parent.width();
|
|
|
|
div.css({
|
|
width: width + 'px',
|
|
left: leftPadding + 'px',
|
|
'border-top-left-radius': (pos === 0 ? '2px' : 0),
|
|
'border-bottom-left-radius': (pos === 0 ? '2px' : 0),
|
|
'border-top-right-radius': (pos === keys.length - 1 ? '2px' : 0),
|
|
'border-bottom-right-radius': (pos === keys.length ? '2px' : 0)
|
|
});
|
|
|
|
leftPadding += width;
|
|
})(i, keys[i], keys.length);
|
|
}
|
|
|
|
if (currentServerHover) renderPercentageBarText(currentServerHover);
|
|
}
|
|
|
|
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 {
|
|
$('#tagline').attr('class', 'status-offline');
|
|
$('#tagline-text').text('Failed to update! Refresh?');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function printPort(port) {
|
|
if(port == undefined || port == 25565) {
|
|
return "";
|
|
} else {
|
|
return ":" + port;
|
|
}
|
|
}
|
|
|
|
$(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;
|
|
|
|
$('#tagline').attr('class', 'status-offline');
|
|
$('#tagline-text').text('Disconnected! Refresh?');
|
|
|
|
lastPlayerEntries = {};
|
|
graphs = {};
|
|
|
|
$('#server-container-list').html('');
|
|
|
|
createdCategories = false;
|
|
|
|
$('#big-graph').html('');
|
|
$('#big-graph-checkboxes').html('');
|
|
$('#big-graph-controls').css('display', 'none');
|
|
|
|
$("#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 = '<table><tr>';
|
|
|
|
keys.sort();
|
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var checkedString = '';
|
|
|
|
if (displayedGraphData[keys[i]]) {
|
|
checkedString = 'checked=checked';
|
|
}
|
|
|
|
html += '<td><input type="checkbox" class="graph-control" id="graph-controls" data-target-network="' + keys[i] + '" ' + checkedString + '> ' + keys[i] + '</input></td>';
|
|
|
|
if (sinceBreak >= 7) {
|
|
sinceBreak = 0;
|
|
|
|
html += '</tr><tr>';
|
|
} else {
|
|
sinceBreak++;
|
|
}
|
|
}
|
|
|
|
$('#big-graph-checkboxes').append(html + '</tr></table>');
|
|
$('#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 ? '<span class="type">' + info.type + '</span>' : '';
|
|
|
|
var safeNameCopy = safeName(info.name);
|
|
|
|
$('<div/>', {
|
|
id: safeNameCopy,
|
|
class: 'server',
|
|
html: '<div id="server-' + safeNameCopy + '" class="column" style="width: 80px;">\
|
|
<img id="favicon_' + safeNameCopy + '" title="' + info.ip + printPort(info.port) + '">\
|
|
<br />\
|
|
<p class="text-center-align rank" id="ranking_' + safeNameCopy + '"></p>\
|
|
</div>\
|
|
<div class="column" style="width: 220px;">\
|
|
<h3>' + info.name + ' ' + typeString + '</h3>\
|
|
<span id="status_' + safeNameCopy + '">Waiting</span>\
|
|
<div id="version_' + safeNameCopy + '" class="color-gray versions"><span class="version"></span></div>\
|
|
<span id="record_' + safeNameCopy + '" class="color-gray"></span>\
|
|
</div>\
|
|
<div class="column" style="float: right;">\
|
|
<div class="chart" id="chart_' + safeNameCopy + '"></div>\
|
|
</div>'
|
|
}).appendTo("#server-container-" + getServerByIp(info.ip).category);
|
|
|
|
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);
|
|
}
|
|
});
|
|
|
|
$(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];
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
});
|
|
});
|