Begin work on frontend!
This commit is contained in:
parent
e213561436
commit
d64252d35d
32
app.js
32
app.js
@ -2,6 +2,7 @@ var server = require('./lib/server');
|
||||
var ping = require('./lib/ping');
|
||||
var logger = require('./lib/logger');
|
||||
var mojang = require('./lib/mojang_services');
|
||||
var util = require('./lib/util');
|
||||
|
||||
var config = require('./config.json');
|
||||
|
||||
@ -20,7 +21,14 @@ function pingAll() {
|
||||
logger.log('error', 'Failed to ping ' + network.ip + ': ' + JSON.stringify(err));
|
||||
}
|
||||
|
||||
server.io.sockets.emit('update', res);
|
||||
server.io.sockets.emit('update', {
|
||||
result: res,
|
||||
error: err,
|
||||
info: {
|
||||
name: network.name,
|
||||
timestamp: util.getCurrentTimeMs()
|
||||
}
|
||||
});
|
||||
|
||||
// Log our response.
|
||||
if (!networkHistory[network.ip]) {
|
||||
@ -29,18 +37,25 @@ function pingAll() {
|
||||
|
||||
var _networkHistory = networkHistory[network.ip];
|
||||
|
||||
// Remove our previous entrie's favicons, we don't need them, just the latest one.
|
||||
// Remove our previous data that we don't need anymore.
|
||||
for (var i = 0; i < _networkHistory.length; i++) {
|
||||
delete _networkHistory[i].favicon;
|
||||
delete _networkHistory[i].info;
|
||||
}
|
||||
|
||||
_networkHistory.push({
|
||||
error: err,
|
||||
result: res
|
||||
result: res,
|
||||
timestamp: util.getCurrentTimeMs(),
|
||||
info: {
|
||||
ip: network.ip,
|
||||
port: network.port,
|
||||
type: network.type,
|
||||
name: network.name
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure we never log too much.
|
||||
if (_networkHistory.length > 300) {
|
||||
if (_networkHistory.length > 72) { // 60/2.5 = 24, so 24 is one minute
|
||||
_networkHistory.shift();
|
||||
}
|
||||
});
|
||||
@ -68,6 +83,13 @@ function startMainLoop() {
|
||||
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) < config.servers.length) {
|
||||
client.disconnect();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We're good to connect them!
|
||||
connectedClients += 1;
|
||||
|
||||
|
@ -1,4 +1,124 @@
|
||||
@import url(https://fonts.googleapis.com/css?family=Open+Sans:700,300,400);
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #3B3738;
|
||||
color: #FFF;
|
||||
font-family: "Open Sans", sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 300 !important;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
#header {
|
||||
background: #EBEBEB;
|
||||
color: #3B3738;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
border-top: 1px solid #DED3D6;
|
||||
width: 840px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#header a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
border-bottom: 1px dashed #3B3738;
|
||||
}
|
||||
|
||||
#header a:hover {
|
||||
border-bottom: 1px dashed transparent;
|
||||
}
|
||||
|
||||
/* Tagline */
|
||||
#tagline {
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
width: 840px;
|
||||
margin: 0 auto;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* Colors used by the Mojang service's status bar */
|
||||
.status-online {
|
||||
background: #87D37C;
|
||||
color: #3B3738;
|
||||
}
|
||||
|
||||
.status-unstable {
|
||||
background: #E9E581;
|
||||
color: #3B3738;
|
||||
}
|
||||
|
||||
.status-offline {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
.status-connecting {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
/* Server listing */
|
||||
#server-container {
|
||||
width: 800px;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
#server-container .server:nth-child(2n) {
|
||||
background: #4E4E4E;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.server {
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.server > .column > img {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.server > .column {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Charts */
|
||||
.chart {
|
||||
height: 100px;
|
||||
width: 400px;
|
||||
margin-right: -3px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#tooltip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* Existing elements */
|
||||
h3 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Basic classes used randomly */
|
||||
.color-gray {
|
||||
color: #C4C4C4;
|
||||
}
|
||||
|
||||
.color-red {
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
.text-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
@ -12,9 +12,34 @@
|
||||
|
||||
<body>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.7/socket.io.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<div id="tooltip"></div>
|
||||
|
||||
<div id="header">
|
||||
|
||||
<h1 class="text-uppercase">Minetrack</h1>
|
||||
|
||||
<p class="text-uppercase">All your favorite Minecraft servers, right now.</p>
|
||||
<p class="text-uppercase">Made by <a href="http://cryptkpr.me">Cryptkeeper</a> | Source code on <a href="https://github.com/Cryptkeeper/Minetrack">Github</a></p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="tagline" class="status-connecting">
|
||||
|
||||
<span id="tagline-text" class="text-uppercase">Connecting...</span>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="server-container" class="container"></div>
|
||||
|
||||
<!-- 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/socket.io/1.3.7/socket.io.min.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.min.js"></script>
|
||||
|
||||
<!-- Internal JS assets -->
|
||||
<script src="js/util.js"></script>
|
||||
<script src="js/site.js"></script>
|
||||
|
||||
</body>
|
||||
|
@ -1,15 +1,229 @@
|
||||
var lastMojangServiceUpdate;
|
||||
|
||||
var smallChartOptions = {
|
||||
series: {
|
||||
shadowSize: 0
|
||||
},
|
||||
xaxis: {
|
||||
font: {
|
||||
color: "#E3E3E3"
|
||||
},
|
||||
show: false
|
||||
},
|
||||
yaxis: {
|
||||
minTickSize: 100,
|
||||
tickDecimals: 0,
|
||||
show: true,
|
||||
tickLength: 10,
|
||||
tickFormatter: function(value) {
|
||||
return formatNumber(value);
|
||||
},
|
||||
font: {
|
||||
color: "#E3E3E3"
|
||||
},
|
||||
labelWidth: -10
|
||||
},
|
||||
grid: {
|
||||
hoverable: true,
|
||||
color: "#C4C4C4"
|
||||
},
|
||||
colors: [
|
||||
"#E9E581"
|
||||
]
|
||||
};
|
||||
|
||||
var graphs = {};
|
||||
var lastLatencyEntries = {};
|
||||
var lastPlayerEntries = {};
|
||||
|
||||
// Generate (and set) the HTML that displays Mojang status.
|
||||
function updateMojangServices() {
|
||||
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 updateServerStatus(lastEntry) {
|
||||
var info = lastEntry.info;
|
||||
var div = $('#status_' + safeName(info.name));
|
||||
|
||||
if (lastEntry.result) {
|
||||
var result = lastEntry.result;
|
||||
var newStatus = formatNumber(result.players.online) + '/' + formatNumber(result.players.max);
|
||||
|
||||
if (lastPlayerEntries[info.name]) {
|
||||
newStatus += '<span class="color-gray"> (';
|
||||
|
||||
var playerDifference = lastPlayerEntries[info.name] - result.players.online;
|
||||
|
||||
if (playerDifference >= 0) {
|
||||
newStatus += '+';
|
||||
}
|
||||
|
||||
newStatus += playerDifference + ')</span>';
|
||||
}
|
||||
|
||||
if (lastLatencyEntries[info.name]) {
|
||||
newStatus += '<br />';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
function safeName(name) {
|
||||
return name.replace(' ', '');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var socket = io.connect();
|
||||
var mojangServicesUpdater;
|
||||
|
||||
socket.on('connect', function() {
|
||||
|
||||
$('#tagline-text').text('Loading...');
|
||||
});
|
||||
|
||||
socket.on('disconnect', function() {
|
||||
if (mojangServicesUpdater) {
|
||||
clearInterval(mojangServicesUpdater);
|
||||
}
|
||||
|
||||
$('#tagline').attr('class', 'status-connecting');
|
||||
$('#tagline-text').text('Attempting reconnnect...');
|
||||
|
||||
lastPlayerEntries = {};
|
||||
lastLatencyEntries = {};
|
||||
graphs = {};
|
||||
|
||||
$('#server-container').html('');
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
$('<div/>', {
|
||||
id: safeName(info.name),
|
||||
class: 'server',
|
||||
html: '<div class="column" style="width: 80px;">\
|
||||
<img style="padding-top: 5px;" id="favicon_' + safeName(info.name) + '">\
|
||||
</div>\
|
||||
<div class="column" style="width: 280px;"><h3>' + info.name + '</h3>\
|
||||
<span class="color-gray">' + info.ip + '</span>\
|
||||
<br />\
|
||||
<span id="status_' + safeName(info.name) + '">Waiting</span>\
|
||||
</div>\
|
||||
<div class="column" style="float: right;">\
|
||||
<div class="chart" id="chart_' + safeName(info.name) + '"></div>\
|
||||
</div>'
|
||||
}).appendTo("#server-container");
|
||||
|
||||
if (lastEntry.result && lastEntry.result.favicon) {
|
||||
$('#favicon_' + safeName(info.name)).attr('src', lastEntry.result.favicon);
|
||||
}
|
||||
|
||||
updateServerStatus(lastEntry);
|
||||
|
||||
graphs[lastEntry.info.name] = {
|
||||
listing: listing,
|
||||
plot: $.plot('#chart_' + safeName(info.name), [listing], smallChartOptions)
|
||||
};
|
||||
|
||||
$('#chart_' + safeName(info.name)).bind('plothover', function(event, pos, item) {
|
||||
if (item) {
|
||||
renderTooltip(item.pageX + 5, item.pageY + 5, getTimestamp(item.datapoint[0] / 1000) + '\
|
||||
<br />\
|
||||
' + formatNumber(item.datapoint[1]) + ' Players');
|
||||
} else {
|
||||
hideTooltip();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('update', function(server) {
|
||||
socket.on('update', function(update) {
|
||||
var graph = graphs[update.info.name];
|
||||
|
||||
updateServerStatus(update);
|
||||
|
||||
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) {
|
||||
// Store the update and force an update.
|
||||
lastMojangServiceUpdate = data;
|
||||
|
||||
updateMojangServices();
|
||||
});
|
||||
|
||||
// Start any special updating tasks.
|
||||
mojangServicesUpdater = setInterval(function() {
|
||||
updateMojangServices();
|
||||
}, 1000);
|
||||
});
|
49
assets/js/util.js
Normal file
49
assets/js/util.js
Normal file
@ -0,0 +1,49 @@
|
||||
var tooltip = $('#tooltip');
|
||||
|
||||
function getTimestamp(ms, timeOnly) {
|
||||
var date = new Date(0);
|
||||
|
||||
date.setUTCSeconds(ms);
|
||||
|
||||
return date.toLocaleTimeString();
|
||||
}
|
||||
|
||||
function renderTooltip(x, y, html) {
|
||||
tooltip.html(html).css({
|
||||
top: y,
|
||||
left: x
|
||||
}).fadeIn(0);
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
tooltip.hide();
|
||||
}
|
||||
|
||||
function formatNumber(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
|
||||
function msToTime(timer) {
|
||||
var milliseconds = timer % 1000;
|
||||
timer = (timer - milliseconds) / 1000;
|
||||
|
||||
var seconds = timer % 60;
|
||||
timer = (timer - seconds) / 60;
|
||||
|
||||
var minutes = timer % 60;
|
||||
var hours = (timer - minutes) / 60;
|
||||
|
||||
var string = '';
|
||||
|
||||
if (hours > 0) {
|
||||
string += hours + 'h';
|
||||
}
|
||||
if (minutes > 0) {
|
||||
string += minutes + 'm';
|
||||
}
|
||||
if (seconds > 0) {
|
||||
string += seconds + 's';
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
58
config.json
58
config.json
@ -6,8 +6,13 @@
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Mineplex",
|
||||
"ip": "us.mineplex.com",
|
||||
"name": "HiveMC",
|
||||
"ip": "play.hivemc.com",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "CCGN",
|
||||
"ip": "play.cubecraftgames.net",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
@ -16,15 +21,56 @@
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Hypixel PE",
|
||||
"ip": "pe.hypixel.net",
|
||||
"type": "PE"
|
||||
"name": "Shotbow",
|
||||
"ip": "us.shotbow.net",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Badlion",
|
||||
"ip": "na.badlion.net",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "MCGamer",
|
||||
"ip": "play.mcgamer.net",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Olimpocraft",
|
||||
"ip": "olimpo.me",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Minecade",
|
||||
"ip": "mineca.de",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "The Nexus",
|
||||
"ip": "hub.thenexusmc.com",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Kohi",
|
||||
"ip": "kohi.us",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Wynncraft",
|
||||
"ip": "play.wynncraft.com",
|
||||
"type": "PC"
|
||||
},
|
||||
{
|
||||
"name": "Mineplex",
|
||||
"ip": "us.mineplex.com",
|
||||
"type": "PC"
|
||||
}
|
||||
],
|
||||
"routes": {
|
||||
"/": "assets/html/index.html",
|
||||
"/images/compass.png": "assets/images/compass.png",
|
||||
"/js/site.js": "assets/js/site.js",
|
||||
"/js/util.js": "assets/js/util.js",
|
||||
"/css/main.css": "assets/css/main.css"
|
||||
},
|
||||
"site": {
|
||||
@ -35,6 +81,6 @@
|
||||
"upateMojangStatus": 5000,
|
||||
"mojangStatusTimeout": 3500,
|
||||
"pingAll": 2500,
|
||||
"connectTimeout": 2500
|
||||
"connectTimeout": 2000
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
var request = require('request');
|
||||
|
||||
var logger = require('./logger');
|
||||
var profiler = require('./profiler');
|
||||
var util = require('./util');
|
||||
|
||||
var serviceNameLookup = {
|
||||
'minecraft.net': 'Website',
|
||||
'sessionserver.mojang.com': 'Sessions',
|
||||
'authserver.mojang.com': 'Auth',
|
||||
'textures.minecraft.net': 'Skins',
|
||||
@ -25,7 +24,7 @@ function updateService(name, status) {
|
||||
|
||||
// If it's an outage, track when it started.
|
||||
if (status === 'yellow'|| status === 'red') {
|
||||
newEntry.startTime = profiler.getCurrentTimeMs();
|
||||
newEntry.startTime = util.getCurrentTimeMs();
|
||||
}
|
||||
|
||||
// Generate a nice title from the color.
|
||||
|
15
lib/ping.js
15
lib/ping.js
@ -1,11 +1,11 @@
|
||||
var mcpe_ping = require('mcpe-ping');
|
||||
var mcpc_ping = require('mc-ping-updated');
|
||||
|
||||
var profiler = require('./profiler');
|
||||
var util = require('./util');
|
||||
|
||||
// This is a wrapper function for mc-ping-updated, mainly used to convert the data structure of the result.
|
||||
function pingMinecraftPC(host, port, timeout, callback) {
|
||||
profiler.track(host);
|
||||
var startTime = util.getCurrentTimeMs();
|
||||
|
||||
// Try catch incase the down stream module is bad at handling exceptions.
|
||||
try {
|
||||
@ -20,7 +20,8 @@ function pingMinecraftPC(host, port, timeout, callback) {
|
||||
max: res.players.max
|
||||
},
|
||||
version: res.version.protocol,
|
||||
latency: profiler.untrack(host)
|
||||
latency: util.getCurrentTimeMs() - startTime,
|
||||
favicon: res.favicon
|
||||
});
|
||||
}
|
||||
}, timeout);
|
||||
@ -31,7 +32,7 @@ function pingMinecraftPC(host, port, timeout, callback) {
|
||||
|
||||
// This is a wrapper function for mcpe-ping, mainly used to convert the data structure of the result.
|
||||
function pingMinecraftPE(host, port, timeout, callback) {
|
||||
profiler.track(host);
|
||||
var startTime = util.getCurrentTimeMs();
|
||||
|
||||
// Try catch incase the down stream module is bad at handling exceptions.
|
||||
try {
|
||||
@ -42,11 +43,11 @@ function pingMinecraftPE(host, port, timeout, callback) {
|
||||
// Remap our JSON into our custom structure.
|
||||
callback(err, {
|
||||
players: {
|
||||
online: res.currentPlayers,
|
||||
max: res.maxPlayers
|
||||
online: parseInt(res.currentPlayers),
|
||||
max: parseInt(res.maxPlayers)
|
||||
},
|
||||
version: res.version,
|
||||
latency: profiler.untrack(host)
|
||||
latency: util.getCurrentTimeMs() - startTime
|
||||
});
|
||||
}
|
||||
}, timeout);
|
||||
|
@ -1,31 +0,0 @@
|
||||
var logger = require('./logger');
|
||||
|
||||
var timestamps = {};
|
||||
|
||||
function getCurrentTimeMs() {
|
||||
return (new Date).getTime();
|
||||
};
|
||||
|
||||
exports.track = function(name) {
|
||||
if (timestamps[name]) {
|
||||
throw new Error(name + ' is already being profiled!');
|
||||
}
|
||||
|
||||
timestamps[name] = getCurrentTimeMs();
|
||||
};
|
||||
|
||||
exports.untrack = function(name) {
|
||||
if (!timestamps[name]) {
|
||||
throw new Error(name + ' isn\'t being profiled!');
|
||||
}
|
||||
|
||||
var timestamp = getCurrentTimeMs() - timestamps[name];
|
||||
|
||||
delete timestamps[name];
|
||||
|
||||
logger.log('debug', name + ' took ' + timestamp + 'ms');
|
||||
|
||||
return timestamp;
|
||||
};
|
||||
|
||||
exports.getCurrentTimeMs = getCurrentTimeMs;
|
3
lib/util.js
Normal file
3
lib/util.js
Normal file
@ -0,0 +1,3 @@
|
||||
exports.getCurrentTimeMs = function() {
|
||||
return new Date().getTime();
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
"description": "A Minecraft server tracker that lets you focus on the basics.",
|
||||
"main": "app.js",
|
||||
"dependencies": {
|
||||
"mc-ping-updated": "0.0.6",
|
||||
"mc-ping-updated": "0.0.7",
|
||||
"mcpe-ping": "0.0.3",
|
||||
"mime": "^1.3.4",
|
||||
"request": "^2.65.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user