mirror of
https://github.com/bluxmit/alnoda-workspaces.git
synced 2024-09-16 01:37:08 +12:00
180 lines
5.2 KiB
JavaScript
Executable file
180 lines
5.2 KiB
JavaScript
Executable file
// Cronicle Server Discovery Layer
|
|
// Copyright (c) 2015 Joseph Huckaby
|
|
// Released under the MIT License
|
|
|
|
var dgram = require("dgram");
|
|
var os = require('os');
|
|
var Netmask = require('netmask').Netmask;
|
|
|
|
var Class = require("pixl-class");
|
|
var Tools = require("pixl-tools");
|
|
|
|
module.exports = Class.create({
|
|
|
|
nearbyServers: null,
|
|
lastDiscoveryBroadcast: 0,
|
|
|
|
setupDiscovery: function(callback) {
|
|
// setup auto-discovery system
|
|
// listen for UDP pings, and broadcast our own ping
|
|
var self = this;
|
|
|
|
this.nearbyServers = {};
|
|
this.lastDiscoveryBroadcast = 0;
|
|
|
|
// disable if port is unset
|
|
if (!this.server.config.get('udp_broadcast_port')) {
|
|
if (callback) callback();
|
|
return;
|
|
}
|
|
|
|
// guess best broadcast IP
|
|
this.broadcastIP = this.server.config.get('broadcast_ip') || this.calcBroadcastIP();
|
|
this.logDebug(4, "Using broadcast IP: " + this.broadcastIP );
|
|
|
|
// start UDP socket listener
|
|
this.logDebug(4, "Starting UDP server on port: " + this.server.config.get('udp_broadcast_port'));
|
|
var listener = this.discoveryListener = dgram.createSocket("udp4");
|
|
|
|
listener.on("message", function (msg, rinfo) {
|
|
self.discoveryReceive( msg, rinfo );
|
|
} );
|
|
|
|
listener.on("error", function (err) {
|
|
self.logError('udp', "UDP socket listener error: " + err);
|
|
self.discoveryListener = null;
|
|
} );
|
|
|
|
listener.bind( this.server.config.get('udp_broadcast_port'), function() {
|
|
if (callback) callback();
|
|
} );
|
|
},
|
|
|
|
discoveryTick: function() {
|
|
// broadcast pings every N
|
|
if (!this.discoveryListener) return;
|
|
var now = Tools.timeNow(true);
|
|
|
|
if (now - this.lastDiscoveryBroadcast >= this.server.config.get('master_ping_freq')) {
|
|
this.lastDiscoveryBroadcast = now;
|
|
|
|
// only broadcast if not part of a cluster
|
|
if (!this.multi.cluster) {
|
|
this.discoveryBroadcast( 'heartbeat', {
|
|
hostname: this.server.hostname,
|
|
ip: this.server.ip
|
|
} );
|
|
}
|
|
|
|
// prune servers who have stopped broadcasting
|
|
for (var hostname in this.nearbyServers) {
|
|
var server = this.nearbyServers[hostname];
|
|
if (now - server.now >= this.server.config.get('master_ping_timeout')) {
|
|
delete this.nearbyServers[hostname];
|
|
if (this.multi.master) {
|
|
this.authSocketEmit( 'update', { nearby: this.nearbyServers } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
discoveryBroadcast: function(type, message, callback) {
|
|
// broadcast message via UDP
|
|
var self = this;
|
|
|
|
message.action = type;
|
|
this.logDebug(10, "Broadcasting message: " + type, message);
|
|
|
|
var client = dgram.createSocket('udp4');
|
|
var message = Buffer.from( JSON.stringify(message) + "\n" );
|
|
client.bind( 0, function() {
|
|
client.setBroadcast( true );
|
|
client.send(message, 0, message.length, self.server.config.get('udp_broadcast_port'), self.broadcastIP, function(err) {
|
|
if (err) self.logDebug(9, "UDP broadcast failed: " + err);
|
|
client.close();
|
|
if (callback) callback();
|
|
} );
|
|
} );
|
|
},
|
|
|
|
discoveryReceive: function(msg, rinfo) {
|
|
// receive UDP message from another server
|
|
this.logDebug(10, "Received UDP message: " + msg + " from " + rinfo.address + ":" + rinfo.port);
|
|
|
|
var text = msg.toString();
|
|
if (text.match(/^\{/)) {
|
|
// appears to be JSON
|
|
var json = null;
|
|
try { json = JSON.parse(text); }
|
|
catch (e) {
|
|
this.logError(9, "Failed to parse UDP JSON message: " + e);
|
|
}
|
|
if (json && json.action) {
|
|
switch (json.action) {
|
|
|
|
case 'heartbeat':
|
|
if (json.hostname && (json.hostname != this.server.hostname)) {
|
|
json.now = Tools.timeNow();
|
|
delete json.action;
|
|
|
|
if (!this.nearbyServers[ json.hostname ]) {
|
|
// first time we've seen this server
|
|
this.nearbyServers[ json.hostname ] = json;
|
|
if (this.multi.master) {
|
|
this.logDebug(6, "Discovered nearby server: " + json.hostname, json);
|
|
this.authSocketEmit( 'update', { nearby: this.nearbyServers } );
|
|
}
|
|
}
|
|
else {
|
|
// update from existing server
|
|
this.nearbyServers[ json.hostname ] = json;
|
|
}
|
|
this.logDebug(10, "Received heartbeat from: " + json.hostname, json);
|
|
}
|
|
break;
|
|
|
|
} // switch action
|
|
} // got json
|
|
} // appears to be json
|
|
},
|
|
|
|
calcBroadcastIP: function() {
|
|
// Attempt to determine server's Broadcast IP, using the first LAN IP and Netmask
|
|
// https://en.wikipedia.org/wiki/Broadcast_address
|
|
var ifaces = os.networkInterfaces();
|
|
var addrs = [];
|
|
for (var key in ifaces) {
|
|
if (ifaces[key] && ifaces[key].length) {
|
|
Array.from(ifaces[key]).forEach( function(item) { addrs.push(item); } );
|
|
}
|
|
}
|
|
var addr = Tools.findObject( addrs, { family: 'IPv4', internal: false } );
|
|
if (addr && addr.address && addr.address.match(/^\d+\.\d+\.\d+\.\d+$/) && addr.netmask && addr.netmask.match(/^\d+\.\d+\.\d+\.\d+$/)) {
|
|
// well that was easy
|
|
var ip = addr.address;
|
|
var mask = addr.netmask;
|
|
|
|
var block = null;
|
|
try { block = new Netmask( ip + '/' + mask ); }
|
|
catch (err) {;}
|
|
|
|
if (block && block.broadcast && block.broadcast.match(/^\d+\.\d+\.\d+\.\d+$/)) {
|
|
return block.broadcast;
|
|
}
|
|
}
|
|
return '255.255.255.255';
|
|
},
|
|
|
|
shutdownDiscovery: function() {
|
|
// shutdown
|
|
var self = this;
|
|
|
|
// shutdown UDP listener
|
|
if (this.discoveryListener) {
|
|
this.logDebug(3, "Shutting down UDP server");
|
|
this.discoveryListener.close();
|
|
}
|
|
}
|
|
|
|
});
|