commit
790227764a
18
CHANGELOG.md
18
CHANGELOG.md
@ -3,3 +3,21 @@
|
|||||||
- Servers now display their name on hover instead of their IP.
|
- Servers now display their name on hover instead of their IP.
|
||||||
- Graph controls are now saved and loaded automatically.
|
- Graph controls are now saved and loaded automatically.
|
||||||
- Moved server configuration into servers.json from config.json.
|
- Moved server configuration into servers.json from config.json.
|
||||||
|
|
||||||
|
**2.1.0** *(Feb 23rd 2016)*
|
||||||
|
- You can now categorize servers. Add a "category tag" to their entry in ```servers.json```.
|
||||||
|
- Define the tags in ```config.json```, such as below:
|
||||||
|
|
||||||
|
```
|
||||||
|
"serverCategories": {
|
||||||
|
"major": "Major Networks",
|
||||||
|
"midsized": "Midsized Networks",
|
||||||
|
"small": "Small Networks"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- If you have no categories, it will create a (hidden) category named "default".
|
||||||
|
- You can control whether or not categories are visible by default using the "categoriesVisible" tag in ```config.json```.
|
||||||
|
- If true and there's >1 category, the browser will have an option to hide/show the categories. Otherwise the controls are always hidden.
|
||||||
|
- New endpoint (```publicConfig.json```) allows the browser to know system details before the socket connection is established.
|
||||||
|
- New header design to make it less annoying.
|
||||||
|
- Various bug fixes.
|
||||||
|
53
app.js
53
app.js
@ -142,22 +142,9 @@ function startServices() {
|
|||||||
|
|
||||||
logger.log('info', '%s connected, total clients: %d', client.request.connection.remoteAddress, connectedClients);
|
logger.log('info', '%s connected, total clients: %d', client.request.connection.remoteAddress, connectedClients);
|
||||||
|
|
||||||
setTimeout(function() {
|
// We send the boot time (also sent in publicConfig.json) to the frontend to validate they have the same config.
|
||||||
client.emit('setGraphDuration', config.graphDuration);
|
// If so, they'll send back "requestListing" event, otherwise they will pull the new config and retry.
|
||||||
|
client.emit('bootTime', util.getBootTime());
|
||||||
// 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]]]);
|
|
||||||
}
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
// Attach our listeners.
|
// Attach our listeners.
|
||||||
client.on('disconnect', function() {
|
client.on('disconnect', function() {
|
||||||
@ -172,6 +159,40 @@ function startServices() {
|
|||||||
client.emit('historyGraph', graphData);
|
client.emit('historyGraph', graphData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
client.on('requestListing', 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 < servers.length; i++) {
|
||||||
|
var server = servers[i];
|
||||||
|
|
||||||
|
if (!(server.name in networkHistory) || networkHistory[server.name].length < 1) {
|
||||||
|
// This server hasn't been ping'd yet. Send a hacky placeholder.
|
||||||
|
client.emit('add', [[{
|
||||||
|
error: {
|
||||||
|
description: 'Waiting'
|
||||||
|
},
|
||||||
|
result: null,
|
||||||
|
timestamp: util.getCurrentTimeMs(),
|
||||||
|
info: {
|
||||||
|
ip: server.ip,
|
||||||
|
port: server.port,
|
||||||
|
type: server.type,
|
||||||
|
name: server.name
|
||||||
|
}
|
||||||
|
}]]);
|
||||||
|
} else {
|
||||||
|
client.emit('add', [networkHistory[networkHistoryKeys[i]]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
startMainLoop();
|
startMainLoop();
|
||||||
|
@ -18,19 +18,34 @@ html, body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
#push {
|
#push {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header, footer */
|
/* Header */
|
||||||
#header, #footer {
|
#header, #tagline {
|
||||||
|
width: 1540px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
background: #EBEBEB;
|
background: #EBEBEB;
|
||||||
color: #3B3738;
|
color: #3B3738;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
text-align: center;
|
overflow: auto;
|
||||||
width: 1540px;
|
}
|
||||||
margin: 0 auto;
|
|
||||||
|
#header .slogan {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header .subslogan {
|
||||||
|
font-size: 19px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header a {
|
#header a {
|
||||||
@ -43,34 +58,46 @@ html, body {
|
|||||||
border-bottom: 1px dashed transparent;
|
border-bottom: 1px dashed transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header > .slogan {
|
|
||||||
letter-spacing: 2px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header > .info {
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header > h1 {
|
#header > h1 {
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header > #column-center {
|
||||||
|
width: 1480px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header > #column-center > .column {
|
||||||
|
width: 740px;
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category controls */
|
||||||
|
#category-controller {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
#footer {
|
#footer {
|
||||||
border-top-right-radius: 2px;
|
width: 1540px;
|
||||||
border-top-left-radius: 2px;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
background: #EBEBEB;
|
||||||
|
color: #3B3738;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
border-top-left-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tagline */
|
/* Tagline */
|
||||||
#tagline {
|
#tagline {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 1540px;
|
|
||||||
margin: 0 auto;
|
|
||||||
border-bottom-left-radius: 2px;
|
|
||||||
border-bottom-right-radius: 2px;
|
border-bottom-right-radius: 2px;
|
||||||
|
border-bottom-left-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Colors used by the Mojang service's status bar */
|
/* Colors used by the Mojang service's status bar */
|
||||||
@ -93,7 +120,7 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Server listing */
|
/* Server listing */
|
||||||
#server-container {
|
.server-container {
|
||||||
width: 1540px;
|
width: 1540px;
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -130,6 +157,12 @@ html, body {
|
|||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: 15px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
/* Charts */
|
/* Charts */
|
||||||
.chart {
|
.chart {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
@ -169,30 +202,6 @@ h3 {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Icon Quick jump */
|
|
||||||
#quick-jump-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 10px;
|
|
||||||
left: 10px;
|
|
||||||
overflow: auto;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-jump-icon {
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quick-jump-icon:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
height: 42px;
|
|
||||||
width: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The big graph */
|
/* The big graph */
|
||||||
#big-graph, #big-graph-controls, #big-graph-checkboxes {
|
#big-graph, #big-graph-controls, #big-graph-checkboxes {
|
||||||
width: 1540px;
|
width: 1540px;
|
||||||
@ -221,7 +230,6 @@ h3 {
|
|||||||
|
|
||||||
#big-graph-controls a:hover {
|
#big-graph-controls a:hover {
|
||||||
border-bottom: 1px dashed transparent;
|
border-bottom: 1px dashed transparent;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Basic elements */
|
/* Basic elements */
|
||||||
@ -238,5 +246,4 @@ h3 {
|
|||||||
.button:hover {
|
.button:hover {
|
||||||
background: #ecf0f1;
|
background: #ecf0f1;
|
||||||
color: #3498db;
|
color: #3498db;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
@ -18,52 +18,71 @@
|
|||||||
|
|
||||||
<div id="push">
|
<div id="push">
|
||||||
|
|
||||||
<div id="header">
|
<div id="header">
|
||||||
|
|
||||||
<h1 class="text-uppercase">Minetrack</h1>
|
<div id="column-center">
|
||||||
<p class="slogan text-uppercase">All your favorite Minecraft servers, right now.</p>
|
|
||||||
<p class="info text-uppercase">Made with <span style="color: #e74c3c;">♥</span> by <a href="http://cryptkpr.me">Cryptkeeper</a> · Open source'd on <a href="https://github.com/Cryptkeeper/Minetrack">Github</a></p>
|
|
||||||
|
|
||||||
<br />
|
<div class="column">
|
||||||
|
|
||||||
<p class="info text-uppercase">Watching <span id="stat_totalPlayers">0</span> players on <span id="stat_networks">0</span> networks.</p>
|
<h1 class="text-uppercase">Minetrack</h1>
|
||||||
|
<p class="slogan text-uppercase">All your favorite Minecraft servers, right now.</p>
|
||||||
|
<p class="subslogan text-uppercase">Watching <span id="stat_totalPlayers">0</span> players on <span id="stat_networks">0</span> networks.</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="tagline" class="status-connecting">
|
<div class="column">
|
||||||
|
|
||||||
<span id="tagline-text" class="text-uppercase">Connecting...</span>
|
|
||||||
|
|
||||||
</div>
|
<p class="text-uppercase">Source code available on <a href="https://github.com/Cryptkeeper/Minetrack">Github</a></p>
|
||||||
|
<p class="text-uppercase">Made with <span style="color: #e74c3c;">♥</span> by <a href="http://cryptkpr.me">Cryptkeeper</a></p>
|
||||||
|
|
||||||
<div id="big-graph"></div>
|
<br />
|
||||||
|
|
||||||
<div id="big-graph-controls" style="display: none;">
|
<div id="category-controller">
|
||||||
|
|
||||||
<span style="text-align: center; display: block;">
|
<a class="text-uppercase" onclick="setCategoriesVisible(true);">Show Categories</a> // <a class="text-uppercase" onclick="setCategoriesVisible(false);">Hide Categories</a>
|
||||||
|
|
||||||
<a onclick="toggleControlsDrawer();">Click to toggle graph controls</a>
|
</div>
|
||||||
|
|
||||||
</span>
|
</div>
|
||||||
|
|
||||||
<div id="big-graph-controls-drawer" style="display: none;">
|
|
||||||
|
|
||||||
<div id="big-graph-checkboxes"></div>
|
</div>
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<span style="text-align: center; display: block; margin-bottom: 15px;">
|
|
||||||
|
|
||||||
<span onclick="setAllGraphVisibility(true);" class="button">Show All</span>
|
|
||||||
<span onclick="setAllGraphVisibility(false);" class="button">Hide All</span>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<div id="tagline" class="status-connecting">
|
||||||
|
|
||||||
|
<span id="tagline-text" class="text-uppercase">Connecting...</span>
|
||||||
|
|
||||||
<div id="server-container" class="container"></div>
|
</div>
|
||||||
|
|
||||||
|
<div id="big-graph"></div>
|
||||||
|
|
||||||
|
<div id="big-graph-controls" style="display: none;">
|
||||||
|
|
||||||
|
<span style="text-align: center; display: block;">
|
||||||
|
|
||||||
|
<a onclick="toggleControlsDrawer();">Click to toggle graph controls</a>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div id="big-graph-controls-drawer" style="display: none;">
|
||||||
|
|
||||||
|
<div id="big-graph-checkboxes"></div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<span style="text-align: center; display: block; margin-bottom: 15px;">
|
||||||
|
|
||||||
|
<span onclick="setAllGraphVisibility(true);" class="button">Show All</span>
|
||||||
|
<span onclick="setAllGraphVisibility(false);" class="button">Hide All</span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="server-container-list"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -73,8 +92,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="quick-jump-container"></div>
|
|
||||||
|
|
||||||
<!-- External JS assets -->
|
<!-- External JS assets -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||||
|
|
||||||
@ -84,8 +101,11 @@
|
|||||||
|
|
||||||
<!-- Internal JS assets -->
|
<!-- Internal JS assets -->
|
||||||
<script src="js/util.js"></script>
|
<script src="js/util.js"></script>
|
||||||
|
<script src="js/graph.js"></script>
|
||||||
<script src="js/site.js"></script>
|
<script src="js/site.js"></script>
|
||||||
|
|
||||||
|
<script src="publicConfig.json"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 585 B After Width: | Height: | Size: 20 KiB |
BIN
assets/images/logo_2014.png
Normal file
BIN
assets/images/logo_2014.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
130
assets/js/graph.js
Normal file
130
assets/js/graph.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// Used by the individual server entries
|
||||||
|
var smallChartOptions = {
|
||||||
|
series: {
|
||||||
|
shadowSize: 0
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
font: {
|
||||||
|
color: "#E3E3E3"
|
||||||
|
},
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
minTickSize: 75,
|
||||||
|
tickDecimals: 0,
|
||||||
|
show: true,
|
||||||
|
tickLength: 10,
|
||||||
|
tickFormatter: function(value) {
|
||||||
|
return formatNumber(value);
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
color: "#E3E3E3"
|
||||||
|
},
|
||||||
|
labelWidth: -10
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
hoverable: true,
|
||||||
|
color: "#696969"
|
||||||
|
},
|
||||||
|
colors: [
|
||||||
|
"#E9E581"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used by the one chart to rule them all
|
||||||
|
var bigChartOptions = {
|
||||||
|
series: {
|
||||||
|
shadowSize: 0
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
font: {
|
||||||
|
color: "#E3E3E3"
|
||||||
|
},
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
show: true,
|
||||||
|
tickSize: 2000,
|
||||||
|
tickLength: 10,
|
||||||
|
tickFormatter: function(value) {
|
||||||
|
return formatNumber(value);
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
color: "#E3E3E3"
|
||||||
|
},
|
||||||
|
labelWidth: -5,
|
||||||
|
min: 0
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
hoverable: true,
|
||||||
|
color: "#696969"
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function toggleControlsDrawer() {
|
||||||
|
var div = $('#big-graph-controls-drawer');
|
||||||
|
|
||||||
|
div.css('display', div.css('display') !== 'none' ? 'none' : 'block');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveGraphControls(displayedServers) {
|
||||||
|
if (typeof(localStorage)) {
|
||||||
|
var json = JSON.stringify(displayedServers);
|
||||||
|
|
||||||
|
localStorage.setItem('displayedServers', json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadGraphControls() {
|
||||||
|
if (typeof(localStorage)) {
|
||||||
|
var item = localStorage.getItem('displayedServers');
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
return JSON.parse(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetGraphControls() {
|
||||||
|
if (typeof(localStorage)) {
|
||||||
|
localStorage.removeItem('displayedServers');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by flot.js when they hover over a data point.
|
||||||
|
function handlePlotHover(event, pos, item) {
|
||||||
|
if (item) {
|
||||||
|
var text = getTimestamp(item.datapoint[0] / 1000) + '\
|
||||||
|
<br />\
|
||||||
|
' + formatNumber(item.datapoint[1]) + ' Players';
|
||||||
|
|
||||||
|
if (item.series && item.series.label) {
|
||||||
|
text = item.series.label + '<br />' + text;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTooltip(item.pageX + 5, item.pageY + 5, text);
|
||||||
|
} else {
|
||||||
|
hideTooltip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the backend data into the schema used by flot.js
|
||||||
|
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;
|
||||||
|
}
|
@ -1,126 +1,14 @@
|
|||||||
var smallChartOptions = {
|
var graphs = [];
|
||||||
series: {
|
var lastPlayerEntries = [];
|
||||||
shadowSize: 0
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
font: {
|
|
||||||
color: "#E3E3E3"
|
|
||||||
},
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
minTickSize: 75,
|
|
||||||
tickDecimals: 0,
|
|
||||||
show: true,
|
|
||||||
tickLength: 10,
|
|
||||||
tickFormatter: function(value) {
|
|
||||||
return formatNumber(value);
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
color: "#E3E3E3"
|
|
||||||
},
|
|
||||||
labelWidth: -10
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
hoverable: true,
|
|
||||||
color: "#696969"
|
|
||||||
},
|
|
||||||
colors: [
|
|
||||||
"#E9E581"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var bigChartOptions = {
|
|
||||||
series: {
|
|
||||||
shadowSize: 0
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
font: {
|
|
||||||
color: "#E3E3E3"
|
|
||||||
},
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
show: true,
|
|
||||||
tickSize: 2000,
|
|
||||||
tickLength: 10,
|
|
||||||
tickFormatter: function(value) {
|
|
||||||
return formatNumber(value);
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
color: "#E3E3E3"
|
|
||||||
},
|
|
||||||
labelWidth: -5,
|
|
||||||
min: 0
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
hoverable: true,
|
|
||||||
color: "#696969"
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var lastMojangServiceUpdate;
|
|
||||||
|
|
||||||
var graphs = {};
|
|
||||||
var lastPlayerEntries = {};
|
|
||||||
|
|
||||||
var historyPlot;
|
var historyPlot;
|
||||||
var displayedGraphData;
|
var displayedGraphData;
|
||||||
var hiddenGraphData = [];
|
var hiddenGraphData = [];
|
||||||
|
|
||||||
// Generate (and set) the HTML that displays Mojang status.
|
var isConnected = false;
|
||||||
function updateMojangServices() {
|
|
||||||
if (!lastMojangServiceUpdate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = Object.keys(lastMojangServiceUpdate);
|
var mojangServicesUpdater;
|
||||||
var newStatus = 'Mojang Services: ';
|
var sortServersTask;
|
||||||
var serviceCountByType = {
|
|
||||||
Online: 0,
|
|
||||||
Unstable: 0,
|
|
||||||
Offline: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var entry = lastMojangServiceUpdate[keys[i]];
|
|
||||||
|
|
||||||
serviceCountByType[entry.title] += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serviceCountByType['Online'] === keys.length) {
|
|
||||||
$('#tagline').attr('class', 'status-online');
|
|
||||||
|
|
||||||
newStatus += 'All systems operational.';
|
|
||||||
} else {
|
|
||||||
if (serviceCountByType['Unstable'] > serviceCountByType['Offline']) {
|
|
||||||
$('#tagline').attr('class', 'status-unstable');
|
|
||||||
} else {
|
|
||||||
$('#tagline').attr('class', 'status-offline');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var entry = lastMojangServiceUpdate[keys[i]];
|
|
||||||
|
|
||||||
if (entry.startTime) {
|
|
||||||
newStatus += entry.name + ' ' + entry.title.toLowerCase() + ' for ' + msToTime((new Date()).getTime() - entry.startTime + ' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#tagline-text').text(newStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
function findErrorMessage(error) {
|
|
||||||
if (error.description) {
|
|
||||||
return error.description;
|
|
||||||
} else if (error.errno) {
|
|
||||||
return error.errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateServerStatus(lastEntry) {
|
function updateServerStatus(lastEntry) {
|
||||||
var info = lastEntry.info;
|
var info = lastEntry.info;
|
||||||
@ -171,19 +59,50 @@ function updateServerStatus(lastEntry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sortServers() {
|
function sortServers() {
|
||||||
var keys = Object.keys(lastPlayerEntries);
|
if (categoriesVisible) {
|
||||||
var nameList = [];
|
var byCategories = getServersByCategory();
|
||||||
|
|
||||||
keys.sort(function(a, b) {
|
var categories = Object.keys(byCategories);
|
||||||
return lastPlayerEntries[b] - lastPlayerEntries[a];
|
|
||||||
});
|
|
||||||
|
|
||||||
keys.reverse();
|
for (var i = 0; i < categories.length; i++) {
|
||||||
|
var relevantPlayers = [];
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (var x = 0; x < byCategories[categories[i]].length; x++) {
|
||||||
$('#' + safeName(keys[i])).prependTo('#server-container');
|
var server = byCategories[categories[i]][x];
|
||||||
|
|
||||||
$('#ranking_' + safeName(keys[i])).text('#' + (keys.length - i));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,10 +142,35 @@ function setAllGraphVisibility(visible) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleControlsDrawer() {
|
function validateBootTime(bootTime, socket) {
|
||||||
var div = $('#big-graph-controls-drawer');
|
$('#tagline-text').text('Validating...');
|
||||||
|
|
||||||
div.css('display', div.css('display') !== 'none' ? 'none' : 'block');
|
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?');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
@ -236,27 +180,13 @@ $(document).ready(function() {
|
|||||||
reconnectionAttempts: 10
|
reconnectionAttempts: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
var mojangServicesUpdater;
|
socket.on('bootTime', function(bootTime) {
|
||||||
var sortServersTask;
|
validateBootTime(bootTime, socket);
|
||||||
|
});
|
||||||
var graphDuration;
|
|
||||||
|
|
||||||
socket.on('connect', function() {
|
|
||||||
$('#tagline-text').text('Loading...');
|
|
||||||
|
|
||||||
if (!isMobileBrowser()) {
|
|
||||||
socket.emit('requestHistoryGraph');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('disconnect', function() {
|
socket.on('disconnect', function() {
|
||||||
if (mojangServicesUpdater) {
|
if (mojangServicesUpdater) clearInterval(mojangServicesUpdater);
|
||||||
clearInterval(mojangServicesUpdater);
|
if (sortServersTask) clearInterval(sortServersTask);
|
||||||
}
|
|
||||||
|
|
||||||
if (sortServersTask) {
|
|
||||||
clearInterval(sortServersTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#tagline').attr('class', 'status-offline');
|
$('#tagline').attr('class', 'status-offline');
|
||||||
$('#tagline-text').text('Disconnected! Refresh?');
|
$('#tagline-text').text('Disconnected! Refresh?');
|
||||||
@ -264,16 +194,20 @@ $(document).ready(function() {
|
|||||||
lastPlayerEntries = {};
|
lastPlayerEntries = {};
|
||||||
graphs = {};
|
graphs = {};
|
||||||
|
|
||||||
$('#server-container').html('');
|
$('#server-container-list').html('');
|
||||||
$('#quick-jump-container').html('');
|
|
||||||
|
createdCategories = false;
|
||||||
|
|
||||||
$('#big-graph').html('');
|
$('#big-graph').html('');
|
||||||
$('#big-graph-checkboxes').html('');
|
$('#big-graph-checkboxes').html('');
|
||||||
$('#big-graph-controls').css('display', 'none');
|
$('#big-graph-controls').css('display', 'none');
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('setGraphDuration', function(value) {
|
$('#category-controller').css('display', 'none');
|
||||||
graphDuration = value;
|
|
||||||
|
$("#stat_totalPlayers").text(0);
|
||||||
|
$("#stat_networks").text(0);
|
||||||
|
|
||||||
|
isConnected = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('historyGraph', function(rawData) {
|
socket.on('historyGraph', function(rawData) {
|
||||||
@ -335,14 +269,14 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
socket.on('updateHistoryGraph', function(rawData) {
|
socket.on('updateHistoryGraph', function(rawData) {
|
||||||
// Prevent race conditions.
|
// Prevent race conditions.
|
||||||
if (!graphDuration || !displayedGraphData || !hiddenGraphData) {
|
if (!displayedGraphData || !hiddenGraphData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not in our display group, use the hidden group instead.
|
// If it's not in our display group, use the hidden group instead.
|
||||||
var targetGraphData = displayedGraphData[rawData.name] ? displayedGraphData : hiddenGraphData;
|
var targetGraphData = displayedGraphData[rawData.name] ? displayedGraphData : hiddenGraphData;
|
||||||
|
|
||||||
trimOldPings(targetGraphData, graphDuration);
|
trimOldPings(targetGraphData, publicConfig.graphDuration);
|
||||||
|
|
||||||
targetGraphData[rawData.name].push([rawData.timestamp, rawData.players]);
|
targetGraphData[rawData.name].push([rawData.timestamp, rawData.players]);
|
||||||
|
|
||||||
@ -356,6 +290,10 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('add', function(servers) {
|
socket.on('add', function(servers) {
|
||||||
|
if (Object.keys(publicConfig.categories).length > 1) {
|
||||||
|
$('#category-controller').css('display', 'block');
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < servers.length; i++) {
|
for (var i = 0; i < servers.length; i++) {
|
||||||
var history = servers[i];
|
var history = servers[i];
|
||||||
var listing = [];
|
var listing = [];
|
||||||
@ -396,7 +334,7 @@ $(document).ready(function() {
|
|||||||
<div class="column" style="float: right;">\
|
<div class="column" style="float: right;">\
|
||||||
<div class="chart" id="chart_' + safeName(info.name) + '"></div>\
|
<div class="chart" id="chart_' + safeName(info.name) + '"></div>\
|
||||||
</div>'
|
</div>'
|
||||||
}).appendTo("#server-container");
|
}).appendTo("#server-container-" + getServerByIp(info.ip).category);
|
||||||
|
|
||||||
var favicon = MISSING_FAVICON_BASE64;
|
var favicon = MISSING_FAVICON_BASE64;
|
||||||
|
|
||||||
@ -406,8 +344,6 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
$('#favicon_' + safeName(info.name)).attr('src', favicon);
|
$('#favicon_' + safeName(info.name)).attr('src', favicon);
|
||||||
|
|
||||||
$('#quick-jump-container').append('<img id="quick-jump-' + safeName(info.name) + '" data-target-network="' + safeName(info.name) + '" title="' + info.name + '" alt="' + info.name + '" class="quick-jump-icon" src="' + favicon + '">');
|
|
||||||
|
|
||||||
graphs[lastEntry.info.name] = {
|
graphs[lastEntry.info.name] = {
|
||||||
listing: listing,
|
listing: listing,
|
||||||
plot: $.plot('#chart_' + safeName(info.name), [listing], smallChartOptions)
|
plot: $.plot('#chart_' + safeName(info.name), [listing], smallChartOptions)
|
||||||
@ -430,7 +366,6 @@ $(document).ready(function() {
|
|||||||
// We have a new favicon, update the old one.
|
// We have a new favicon, update the old one.
|
||||||
if (update.result && update.result.favicon) {
|
if (update.result && update.result.favicon) {
|
||||||
$('#favicon_' + safeName(update.info.name)).attr('src', update.result.favicon);
|
$('#favicon_' + safeName(update.info.name)).attr('src', update.result.favicon);
|
||||||
$('#quick-jump-' + safeName(update.info.name)).attr('src', update.result.favicon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var graph = graphs[update.info.name];
|
var graph = graphs[update.info.name];
|
||||||
@ -452,29 +387,9 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('updateMojangServices', function(data) {
|
socket.on('updateMojangServices', function(data) {
|
||||||
// Store the update and force an update.
|
if (isConnected) {
|
||||||
lastMojangServiceUpdate = data;
|
updateMojangServices(data);
|
||||||
|
}
|
||||||
updateMojangServices();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start any special updating tasks.
|
|
||||||
mojangServicesUpdater = setInterval(function() {
|
|
||||||
updateMojangServices();
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
sortServersTask = setInterval(function() {
|
|
||||||
sortServers();
|
|
||||||
}, 10 * 1000);
|
|
||||||
|
|
||||||
// Our super fancy scrolly thing!
|
|
||||||
$(document).on('click', '.quick-jump-icon', function(e) {
|
|
||||||
var serverName = $(this).attr('data-target-network');
|
|
||||||
var target = $('#server-' + serverName);
|
|
||||||
|
|
||||||
$('html, body').animate({
|
|
||||||
scrollTop: target.offset().top
|
|
||||||
}, 100);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.graph-control', function(e) {
|
$(document).on('click', '.graph-control', function(e) {
|
||||||
|
@ -2,28 +2,130 @@ var MISSING_FAVICON_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAA
|
|||||||
|
|
||||||
var tooltip = $('#tooltip');
|
var tooltip = $('#tooltip');
|
||||||
|
|
||||||
function saveGraphControls(displayedServers) {
|
var lastMojangServiceUpdate;
|
||||||
if (typeof(localStorage) !== undefined) {
|
var publicConfig;
|
||||||
var json = JSON.stringify(displayedServers);
|
|
||||||
|
|
||||||
localStorage.setItem('displayedServers', json);
|
var createdCategories = false;
|
||||||
|
var categoriesVisible;
|
||||||
|
|
||||||
|
function setPublicConfig(json) {
|
||||||
|
publicConfig = json;
|
||||||
|
|
||||||
|
$('#server-container-list').html('');
|
||||||
|
|
||||||
|
createdCategories = false;
|
||||||
|
|
||||||
|
createCategories();
|
||||||
|
setCategoriesVisible(publicConfig.categoriesVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCategoriesVisible(newCategoriesVisible) {
|
||||||
|
categoriesVisible = newCategoriesVisible;
|
||||||
|
|
||||||
|
$('.category-header').css('display', (categoriesVisible ? 'block' : 'none'));
|
||||||
|
$('.server-container').css('margin', (categoriesVisible ? '10px auto' : '0 auto'));
|
||||||
|
|
||||||
|
sortServers();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCategories() {
|
||||||
|
if (!createdCategories) {
|
||||||
|
createdCategories = true;
|
||||||
|
|
||||||
|
var keys = Object.keys(publicConfig.categories);
|
||||||
|
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var title = publicConfig.categories[keys[i]];
|
||||||
|
|
||||||
|
$('#server-container-list').append('<div id="server-container-' + keys[i] + '" class="container server-container"><h3 class="category-header">' + title + '</h3></div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#server-container-list').append('<div id="server-container-all" class="container server-container"></div>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadGraphControls() {
|
function getServersByCategory() {
|
||||||
if (typeof(localStorage) !== undefined) {
|
var byCategory = {};
|
||||||
var item = localStorage.getItem('displayedServers');
|
|
||||||
|
|
||||||
if (item) {
|
for (var i = 0; i < publicConfig.servers.length; i++) {
|
||||||
return JSON.parse(item);
|
var entry = publicConfig.servers[i];
|
||||||
|
|
||||||
|
if (!byCategory[entry.category]) {
|
||||||
|
byCategory[entry.category] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
byCategory[entry.category].push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return byCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServerByIp(ip) {
|
||||||
|
for (var i = 0; i < publicConfig.servers.length; i++) {
|
||||||
|
var entry = publicConfig.servers[i];
|
||||||
|
|
||||||
|
if (entry.ip === ip) {
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetGraphControls() {
|
// Generate (and set) the HTML that displays Mojang status.
|
||||||
if (typeof(localStorage) !== undefined) {
|
// If nothing is passed, re-render the last update.
|
||||||
localStorage.removeItem('displayedServers');
|
// If something is passed, update and then re-render.
|
||||||
}
|
function updateMojangServices(currentUpdate) {
|
||||||
|
if (currentUpdate) {
|
||||||
|
lastMojangServiceUpdate = currentUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lastMojangServiceUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = Object.keys(lastMojangServiceUpdate);
|
||||||
|
var newStatus = 'Mojang Services: ';
|
||||||
|
|
||||||
|
var serviceCountByType = {
|
||||||
|
Online: 0,
|
||||||
|
Unstable: 0,
|
||||||
|
Offline: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var entry = lastMojangServiceUpdate[keys[i]];
|
||||||
|
|
||||||
|
serviceCountByType[entry.title] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceCountByType['Online'] === keys.length) {
|
||||||
|
$('#tagline').attr('class', 'status-online');
|
||||||
|
|
||||||
|
newStatus += 'All systems operational.';
|
||||||
|
} else {
|
||||||
|
if (serviceCountByType['Unstable'] > serviceCountByType['Offline']) {
|
||||||
|
$('#tagline').attr('class', 'status-unstable');
|
||||||
|
} else {
|
||||||
|
$('#tagline').attr('class', 'status-offline');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var entry = lastMojangServiceUpdate[keys[i]];
|
||||||
|
|
||||||
|
if (entry.startTime) {
|
||||||
|
newStatus += entry.name + ' ' + entry.title.toLowerCase() + ' for ' + msToTime((new Date()).getTime() - entry.startTime + ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#tagline-text').text(newStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findErrorMessage(error) {
|
||||||
|
if (error.description) {
|
||||||
|
return error.description;
|
||||||
|
} else if (error.errno) {
|
||||||
|
return error.errno;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTimestamp(ms, timeOnly) {
|
function getTimestamp(ms, timeOnly) {
|
||||||
@ -84,39 +186,6 @@ function trimOldPings(data, graphDuration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePlotHover(event, pos, item) {
|
|
||||||
if (item) {
|
|
||||||
var text = getTimestamp(item.datapoint[0] / 1000) + '\
|
|
||||||
<br />\
|
|
||||||
' + formatNumber(item.datapoint[1]) + ' Players';
|
|
||||||
|
|
||||||
if (item.series && item.series.label) {
|
|
||||||
text = item.series.label + '<br />' + 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) {
|
function stringToColor(base) {
|
||||||
var hash;
|
var hash;
|
||||||
|
|
||||||
|
11
config.json
11
config.json
@ -4,6 +4,7 @@
|
|||||||
"/images/compass.png": "assets/images/compass.png",
|
"/images/compass.png": "assets/images/compass.png",
|
||||||
"/js/site.js": "assets/js/site.js",
|
"/js/site.js": "assets/js/site.js",
|
||||||
"/js/util.js": "assets/js/util.js",
|
"/js/util.js": "assets/js/util.js",
|
||||||
|
"/js/graph.js": "assets/js/graph.js",
|
||||||
"/css/main.css": "assets/css/main.css",
|
"/css/main.css": "assets/css/main.css",
|
||||||
"/favicons/hypixelpe.png": "assets/images/favicons/hypixelpe.png",
|
"/favicons/hypixelpe.png": "assets/images/favicons/hypixelpe.png",
|
||||||
"/favicons/mineplex.png": "assets/images/favicons/mineplex.png",
|
"/favicons/mineplex.png": "assets/images/favicons/mineplex.png",
|
||||||
@ -15,7 +16,7 @@
|
|||||||
"Mobcrush Land": "/favicons/mobcrush.png"
|
"Mobcrush Land": "/favicons/mobcrush.png"
|
||||||
},
|
},
|
||||||
"site": {
|
"site": {
|
||||||
"port": 80,
|
"port": 8080,
|
||||||
"ip": "0.0.0.0"
|
"ip": "0.0.0.0"
|
||||||
},
|
},
|
||||||
"rates": {
|
"rates": {
|
||||||
@ -25,5 +26,11 @@
|
|||||||
"connectTimeout": 2500
|
"connectTimeout": 2500
|
||||||
},
|
},
|
||||||
"logToDatabase": false,
|
"logToDatabase": false,
|
||||||
"graphDuration": 86400000
|
"graphDuration": 86400000,
|
||||||
|
"serverCategories": {
|
||||||
|
"major": "Major Networks",
|
||||||
|
"midsized": "Midsized Networks",
|
||||||
|
"small": "Small Networks"
|
||||||
|
},
|
||||||
|
"categoriesVisible": true
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,11 @@ var url = require('url');
|
|||||||
var mime = require('mime');
|
var mime = require('mime');
|
||||||
var io = require('socket.io');
|
var io = require('socket.io');
|
||||||
|
|
||||||
|
var util = require('./util');
|
||||||
var logger = require('./logger');
|
var logger = require('./logger');
|
||||||
|
|
||||||
var config = require('../config.json');
|
var config = require('../config.json');
|
||||||
|
var servers = require('../servers.json');
|
||||||
|
|
||||||
var urlMapping = [];
|
var urlMapping = [];
|
||||||
|
|
||||||
@ -34,6 +36,43 @@ function handleRequest(req, res) {
|
|||||||
message: 'API deprecated.'
|
message: 'API deprecated.'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
res.end();
|
||||||
|
} else if (requestUrl === '/publicConfig.json') {
|
||||||
|
res.setHeader('Content-Type', 'application/javascript');
|
||||||
|
|
||||||
|
var categories = config.serverCategories;
|
||||||
|
|
||||||
|
// Legacy support for people without categories configured.
|
||||||
|
if (!categories) {
|
||||||
|
categories = {
|
||||||
|
'default': 'All Networks'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < servers.length; i++) {
|
||||||
|
var entry = servers[i];
|
||||||
|
|
||||||
|
if (!entry.category) {
|
||||||
|
entry.category = 'default';
|
||||||
|
|
||||||
|
logger.warn('%s has no category, defaulting!', entry.name);
|
||||||
|
} else if (!categories[entry.category]) {
|
||||||
|
logger.warn('%s has an unknown category (%s), defaulting!', entry.name, entry.category);
|
||||||
|
|
||||||
|
entry.category = 'default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var publicConfig = {
|
||||||
|
categories: categories,
|
||||||
|
graphDuration: config.graphDuration,
|
||||||
|
servers: servers,
|
||||||
|
bootTime: util.getBootTime(),
|
||||||
|
categoriesVisible: config.categoriesVisible || false
|
||||||
|
};
|
||||||
|
|
||||||
|
res.write('setPublicConfig(' + JSON.stringify(publicConfig) + ');');
|
||||||
|
|
||||||
res.end();
|
res.end();
|
||||||
} else if (requestUrl in urlMapping) {
|
} else if (requestUrl in urlMapping) {
|
||||||
var file = urlMapping[requestUrl];
|
var file = urlMapping[requestUrl];
|
||||||
|
14
lib/util.js
14
lib/util.js
@ -3,7 +3,9 @@ var logger = require('./logger');
|
|||||||
var config = require('../config.json');
|
var config = require('../config.json');
|
||||||
var servers = require('../servers.json');
|
var servers = require('../servers.json');
|
||||||
|
|
||||||
var serverNameLookup = {};
|
var serverNameLookup = [];
|
||||||
|
|
||||||
|
var bootTime;
|
||||||
|
|
||||||
// Finds a server in servers.json with a matching IP.
|
// Finds a server in servers.json with a matching IP.
|
||||||
// If it finds one, it caches the result for faster future lookups.
|
// If it finds one, it caches the result for faster future lookups.
|
||||||
@ -136,4 +138,14 @@ exports.convertPingsToGraph = function(sqlData) {
|
|||||||
logger.info('Converted data structure in ' + (exports.getCurrentTimeMs() - startTime) + 'ms');
|
logger.info('Converted data structure in ' + (exports.getCurrentTimeMs() - startTime) + 'ms');
|
||||||
|
|
||||||
return graphData;
|
return graphData;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getBootTime = function() {
|
||||||
|
if (!bootTime) {
|
||||||
|
bootTime = exports.getCurrentTimeMs();
|
||||||
|
|
||||||
|
logger.info('Selected %d as boot time.', bootTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bootTime;
|
||||||
};
|
};
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "minetrack",
|
"name": "minetrack",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"description": "A Minecraft server tracker that lets you focus on the basics.",
|
"description": "A Minecraft server tracker that lets you focus on the basics.",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
96
servers.json
96
servers.json
@ -2,161 +2,193 @@
|
|||||||
{
|
{
|
||||||
"name": "Hypixel",
|
"name": "Hypixel",
|
||||||
"ip": "mc.hypixel.net",
|
"ip": "mc.hypixel.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "HiveMC",
|
"name": "HiveMC",
|
||||||
"ip": "play.hivemc.com",
|
"ip": "play.hivemc.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "CubeCraft",
|
"name": "CubeCraft",
|
||||||
"ip": "play.cubecraft.net",
|
"ip": "play.cubecraft.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Overcast",
|
"name": "Overcast",
|
||||||
"ip": "us.oc.tc",
|
"ip": "us.oc.tc",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Shotbow",
|
"name": "Shotbow",
|
||||||
"ip": "us.shotbow.net",
|
"ip": "us.shotbow.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Badlion",
|
"name": "Badlion",
|
||||||
"ip": "na.badlion.net",
|
"ip": "na.badlion.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MCGamer",
|
"name": "MCGamer",
|
||||||
"ip": "play.mcgamer.net",
|
"ip": "play.mcgamer.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Olimpocraft",
|
"name": "Olimpocraft",
|
||||||
"ip": "olimpo.me",
|
"ip": "olimpo.me",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Minecade",
|
"name": "Minecade",
|
||||||
"ip": "mineca.de",
|
"ip": "mineca.de",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "The Nexus",
|
"name": "The Nexus",
|
||||||
"ip": "hub.thenexusmc.com",
|
"ip": "hub.thenexusmc.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Kohi",
|
"name": "Kohi",
|
||||||
"ip": "kohi.us",
|
"ip": "kohi.us",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Wynncraft",
|
"name": "Wynncraft",
|
||||||
"ip": "play.wynncraft.com",
|
"ip": "play.wynncraft.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Mineplex",
|
"name": "Mineplex",
|
||||||
"ip": "us.mineplex.com",
|
"ip": "us.mineplex.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Hypixel PE",
|
"name": "Hypixel PE",
|
||||||
"ip": "pe.hypixel.net",
|
"ip": "pe.hypixel.net",
|
||||||
"type": "PE"
|
"type": "PE",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Cosmic PVP",
|
"name": "Cosmic PVP",
|
||||||
"ip": "cosmicpvp.me",
|
"ip": "cosmicpvp.me",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PartyZone",
|
"name": "PartyZone",
|
||||||
"ip": "play.mcpz.net",
|
"ip": "play.mcpz.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Brawl",
|
"name": "Brawl",
|
||||||
"ip": "brawl.com",
|
"ip": "brawl.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SaicoPvP",
|
"name": "SaicoPvP",
|
||||||
"ip": "saicopvp.com",
|
"ip": "saicopvp.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "GotPvP",
|
"name": "GotPvP",
|
||||||
"ip": "play.gotpvp.com",
|
"ip": "play.gotpvp.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Rewinside",
|
"name": "Rewinside",
|
||||||
"ip": "mc.rewinside.tv",
|
"ip": "mc.rewinside.tv",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Woodycraft",
|
"name": "Woodycraft",
|
||||||
"ip": "woodycraft.net",
|
"ip": "woodycraft.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Battlemine",
|
"name": "Battlemine",
|
||||||
"ip": "mc.battlemine.net",
|
"ip": "mc.battlemine.net",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Arkham",
|
"name": "Arkham",
|
||||||
"ip": "mc.arkhamnetwork.org",
|
"ip": "mc.arkhamnetwork.org",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MCMagic",
|
"name": "MCMagic",
|
||||||
"ip": "mcmagic.us",
|
"ip": "mcmagic.us",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Island Clash",
|
"name": "Island Clash",
|
||||||
"ip": "play.islandclash.com",
|
"ip": "play.islandclash.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Playminity",
|
"name": "Playminity",
|
||||||
"ip": "mc.playminity.com",
|
"ip": "mc.playminity.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Mobcrush Land",
|
"name": "Mobcrush Land",
|
||||||
"ip": "play.mobcrush.com",
|
"ip": "play.mobcrush.com",
|
||||||
"type": "PE"
|
"type": "PE",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Epicube",
|
"name": "Epicube",
|
||||||
"ip": "play.epicube.fr",
|
"ip": "play.epicube.fr",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "major"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "BeanBlockz",
|
"name": "BeanBlockz",
|
||||||
"ip": "mc.beanblockz.com",
|
"ip": "mc.beanblockz.com",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "GuildCraft",
|
"name": "GuildCraft",
|
||||||
"ip": "play.guildcraft.org",
|
"ip": "play.guildcraft.org",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Empire MC",
|
"name": "Empire MC",
|
||||||
"ip": "play.emc.gs",
|
"ip": "play.emc.gs",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Timolia",
|
"name": "Timolia",
|
||||||
"ip": "play.timolia.de",
|
"ip": "play.timolia.de",
|
||||||
"type": "PC"
|
"type": "PC",
|
||||||
|
"category": "midsized"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user