Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Jazz Jackrabbit 2 ListServer written for Node.JS
- Created by djazz (@daniel_hede)
- http://djazz.mine.nu/
- Fully compatible with JJ2
- */
- 'use strict';
- // Modules
- var net = require('net');
- var fs = require('fs');
- var path = require('path');
- var dns = require('dns');
- // Settings
- // Listserver creation/modification date and time, change only if you are the author
- var mtime = new Date("6 June 2012 19:42:56 GMT+0200");
- // ServerNet settings
- var serverNetTimeoutTime = 30; // If no connection within 30 secs, abort attempt
- var serverNetReconnectTime = 60; // Wait one minute before reconnecting
- var serversAutoCheckInterval = 10*60*1000; // Wait 10 minutes before automatically updating from servers.txt
- // Global variables
- var serverlist = [];
- var clients = [];
- var listservers = {};
- var listclients = {};
- var specifiedservers = [];
- var motd = "";
- var banlist = [];
- var gametypelog = [0, 0, 0, 0];
- var totalgames = 0;
- // Global functions
- function Server (config) {
- if(!config) config = {};
- this.address = config.address;
- this.port = parseInt(config.port, 10) || 0;
- this.location = config.location || 'local';
- this['private'] = !!(config['private'] || false);
- this.gametype = parseInt(config.gametype, 10) || 0;
- this.version = new Buffer(4);
- if(Buffer.isBuffer(config.version)) {
- config.version.copy(this.version, 0, 0, 4);
- }
- else if(typeof config.version === 'string') {
- this.version.write(config.version, 0, 4, 'binary');
- }
- this.starttime = new Date(Date.now() - (config.uptime || 0)*1000);
- if(!config.capacity) config.capacity = [0, 0];
- this.capacity = [+config.capacity[0] || 0, +config.capacity[1] || 0];
- this.servername = config.servername || '';
- };
- Server.prototype.getUptime = function () {
- return Math.round((Date.now() - this.starttime.getTime())/1000);
- };
- Server.prototype.getASCII = function () {
- var gt = 'unknown';
- switch(this.gametype) {
- case 1: gt = 'battle'; break;
- case 2: gt = 'treasure'; break;
- case 3: gt = 'ctf'; break;
- }
- return this.address+':'+this.port+' '+this.location+' '+(this['private']?'private':'public')+' '+gt+' 1.'+this.version.toString('binary')+' '+this.getUptime()+' ['+this.capacity.join('/')+'] '+this.servername;
- };
- Server.prototype.getBinaryPacket = function () {
- var address = new Buffer(this.address.split(".").reverse());
- var port = new Buffer(2);
- port[1] = parseInt(this.port / 256, 10);
- port[0] = parseInt(this.port - port[1]*256, 10);
- var packet = new Buffer("l"+"addr"+"po"+this.servername, 'binary');
- address.copy(packet, 1);
- port.copy(packet, 1+4);
- packet[0] = packet.length;
- return packet;
- };
- Server.prototype.getAddPacket = function () {
- var packet = new Buffer("lt"+"addr"+"po"+"f"+"vers"+"upti"+"ca"+this.servername+"\x00", 'binary');
- packet[0] = packet.length-2;
- packet[1] = 0x00; // Add server packet
- new Buffer(this.address.split('.').reverse()).copy(packet, 2); // IP address
- packet.writeUInt16LE(this.port, 6);
- packet[8] = this.gametype | (this['private'] & 1); // Flags
- this.version.copy(packet, 9);
- packet.writeUInt32LE(this.getUptime(), 13);
- packet[17] = this.capacity[0];
- packet[18] = this.capacity[1];
- // Servername already added
- return packet;
- };
- Server.prototype.getRemovePacket = function () {
- var packet = new Buffer("lt"+"addr"+"po", 'binary');
- packet[0] = packet.length-2;
- packet[1] = 0x01; // Remove server packet
- new Buffer(this.address.split('.').reverse()).copy(packet, 2); // IP address
- packet.writeUInt16LE(this.port, 6);
- return packet;
- };
- Server.prototype.getUpdatePlayersPacket = function () {
- var packet = new Buffer("lt"+"addr"+"po"+"p", 'binary');
- packet[0] = packet.length-2;
- packet[1] = 0x02; // Update player count packet
- new Buffer(this.address.split('.').reverse()).copy(packet, 2); // IP address
- packet.writeUInt16LE(this.port, 6);
- packet[8] = this.capacity[0];
- return packet;
- };
- function loadMOTD (callback) {
- fs.readFile(path.join(__dirname, "motd.txt"), "utf8", function (err, data) {
- if(err) {console.log("MOTD loading error: "+err); return;}
- motd = new Buffer(data.substr(0, data.length-1), "binary");
- if(callback) {callback();}
- });
- };
- function loadBanlist (callback) {
- fs.readFile(path.join(__dirname, "banlist.txt"), function (err, data) {
- if(err) {console.log("Banlist loading error: "+err); return;}
- var bans = data.toString("utf8").split("\n"), i;
- banlist = [];
- for(i=0; i < bans.length; i++) {
- if(bans[i]==="" || bans[i].substr(0, 1)===";") {continue;}
- banlist.push(bans[i].trim());
- }
- for(var i=0; i < serverlist.length; i++) {
- if(isBanned(serverlist[i].address)) {
- serverNetBroadcast(serverlist[i].getRemovePacket());
- console.log(timestamp(), clients[i].remoteAddress+" was banned and delisted");
- serverlist.splice(i, 1);
- clients.splice(i, 1);
- i--;
- }
- }
- if(callback) {callback();}
- });
- };
- function isBanned (ip) {
- var adr = ip.split("."), line, banadr, found, i, j;
- for(i=0; i < banlist.length; i+=1) {
- line = banlist[i];
- banadr = line.split(".");
- found = 0;
- for(j=0; j < 4; j+=1) {
- if(banadr[j]==='*') {found+=1;}
- else if(banadr[j]===adr[j]) {found+=1;}
- }
- if(found===4) {return true;}
- }
- return false;
- };
- function sortListFunction (a, b) {
- var players = b.capacity[0]-a.capacity[0];
- if(players !== 0) {
- return players;
- }
- else {
- return b.starttime-a.starttime;
- }
- };
- function timestamp () {
- var now = new Date();
- return now.toString()+" ";
- };
- function buildServerList () {
- var list = [];
- for(var i=0; i < serverlist.length; i++) {
- list.push(serverlist[i]);
- }
- for(var i in listservers) {
- for(var j in listservers[i].serverlist) {
- list.push(listservers[i].serverlist[j]);
- }
- }
- for(var i in listclients) {
- for(var j in listclients[i].serverlist) {
- list.push(listclients[i].serverlist[j]);
- }
- }
- var outputlist = [];
- var addresses = [];
- for(var i=0; i < list.length; i++) {
- if(isBanned(list[i].address)) continue;
- var addr = list[i].address+':'+list[i].port;
- var index = addresses.indexOf(addr);
- if(index === -1) {
- addresses.push(addr);
- outputlist.push(list[i]);
- }
- }
- outputlist.sort(sortListFunction);
- return outputlist;
- };
- function loadServers (callback) {
- fs.readFile(path.join(__dirname, "servers.txt"), "utf8", function (err, data) {
- if(err) {console.log(timestamp(), "servers.txt loading error: "+err); return;}
- var lines = data.toString("utf8").split("\n");
- var list = [];
- var counter = 0;
- var total = 0;
- function find_ip (ip) {
- for(var i=0; i < list.length; i++) {
- if(list[i].ip === ip) return true;
- }
- return false;
- };
- function allLoaded () {
- for(var i=0; i < list.length; i++) {
- if(!listservers.hasOwnProperty(list[i].ip)) {
- listservers[list[i].ip] = {serverlist: {}, host: list[i].host, starttime: Date.now()};
- startServerNet(list[i].ip);
- console.log(timestamp(), 'Added '+list[i].host+' ('+list[i].ip+') to synclist');
- }
- }
- for(var i in listservers) {
- if(!find_ip(i)) {
- console.log(timestamp(), 'Removed '+listservers[i].host+' ('+i+') from synclist');
- listservers[i].close();
- }
- }
- if(callback) {
- callback();
- }
- };
- specifiedservers = [];
- for(var i=0; i < lines.length; i++) {
- if(lines[i] === "" || lines[i].substr(0, 1) === ";") {continue;}
- lines[i] = lines[i].trim();
- if(lines[i].length === 0) continue;
- total++;
- (function (hostname) {
- var index = specifiedservers.length;
- specifiedservers[index] = {host: hostname};
- dns.lookup(hostname, 4, function (err, address, family) {
- if(err) {
- console.log(timestamp(), hostname+': DNS '+err);
- }
- else {
- if(specifiedservers[index].host === hostname) {
- specifiedservers[index].ip = address;
- }
- list.push({
- ip: address,
- host: hostname
- });
- }
- counter++;
- if(counter === total) {
- allLoaded();
- }
- });
- }(lines[i]));
- }
- if(total === 0) {
- allLoaded();
- }
- });
- };
- function startServerNet (address) {
- function closeServerNet () {
- clearTimeout(timeout);
- clearInterval(delay);
- servernet && servernet.destroy();
- delete listservers[address];
- };
- var listserver = listservers[address];
- listserver.connected = false;
- var servernet = net.connect(10056, listserver.host);
- servernet.setTimeout(serverNetTimeoutTime*1000, function () {
- //console.log('timed out');
- servernet.destroy();
- });
- listserver.socket = servernet;
- var databuffer = [];
- var delay;
- var pinger;
- var timeout = setTimeout(function () {
- servernet.destroy();
- }, serverNetTimeoutTime*1000);
- servernet.on('error', function (err) {
- console.log(timestamp(), 'ServerNet '+listserver.host+' - '+err);
- });
- servernet.on('connect', function () {
- //console.log(timestamp(), 'ServerNet '+listserver.host+' - Connected', address);
- clearTimeout(timeout);
- pinger = setInterval(function () {
- servernet.write(new Buffer([0x00, 0x80])); // Ping
- }, 20*1000);
- listserver.connected = true;
- });
- servernet.on('data', function (data) {
- clearTimeout(timeout);
- timeout = setTimeout(function () {
- servernet.destroy();
- }, serverNetTimeoutTime*1000);
- Array.prototype.push.apply(
- databuffer,
- Array.prototype.slice.apply(data) // Convert from buffer to array
- ); // Append data to array
- while(databuffer.length >= databuffer[0]+2) {
- var packet = new Buffer(databuffer.splice(0, databuffer[0]+2));
- // Note: packetLength = packet.length + 2;
- parseServerNetPacket(packet, listserver);
- }
- });
- servernet.on('close', function () {
- //console.log(timestamp(), 'ServerNet '+listserver.host+' - Disconnected', address);
- clearTimeout(timeout);
- clearInterval(pinger);
- listserver.connected = false;
- for(var i in listserver.serverlist) {
- delete listserver.serverlist[i];
- }
- databuffer = [];
- delay = setInterval(function () {
- if(listclients[address]) return;
- clearInterval(delay);
- servernet.connect(10056, address);
- timeout = setTimeout(function () {
- servernet.destroy();
- }, serverNetTimeoutTime*1000);
- }, serverNetReconnectTime*1000);
- });
- listserver.close = closeServerNet;
- };
- function parseServerNetPacket (packet, listserver) {
- var packetType = packet[1];
- switch(packetType) {
- case 0: // Add
- //console.log(packet, packet.toString('binary'));
- var ip = Array.prototype.slice.apply(packet.slice(2, 2+4)).reverse().join(".");
- var port = packet.readUInt16LE(6);
- var flags = packet[8];
- var version = packet.slice(9, 9+4);
- var uptime = packet.readUInt32LE(13, 13+4);
- var capacity = [packet[17], packet[18]];
- var servername = packet.slice(19, packet.length-1).toString('binary');
- //console.log('ServerNet', 'add', ip+':'+port, servername);
- listserver.serverlist[ip+':'+port] = new Server({
- address: ip,
- port: port,
- location: 'mirror',
- 'private': flags & 1,
- gametype: ((flags >> 1) & 3),
- version: version,
- uptime: uptime,
- capacity: capacity,
- servername: servername
- });
- break;
- case 1: // Remove
- var ip = Array.prototype.slice.apply(packet.slice(2, 2+4)).reverse().join(".");
- var port = packet.readUInt16LE(6);
- //console.log('ServerNet', 'remove', ip+':'+port);
- delete listserver.serverlist[ip+':'+port];
- break;
- case 2: // Playercount update
- var ip = Array.prototype.slice.apply(packet.slice(2, 2+4)).reverse().join(".");
- var port = packet.readUInt16LE(6);
- var playerCount = packet[8];
- //console.log('ServerNet', 'update', ip+':'+port, playerCount);
- if(listserver.serverlist[ip+':'+port]) {
- listserver.serverlist[ip+':'+port].capacity[0] = playerCount;
- }
- break;
- case 0x80: // Ping
- // ??
- //console.log('ping');
- break;
- default:
- console.log(timestamp(), "ServerNet - Unknown packet (0x"+packetType.toString(16)+")");
- break;
- }
- };
- function serverNetBroadcast (packet) {
- for(var i in listservers) {
- listservers[i].socket.write(packet);
- }
- for(var i in listclients) {
- listclients[i].socket.write(packet);
- }
- };
- // Load the files for the first time
- // Then start the main code
- loadMOTD(function () {
- loadBanlist(function () {
- main();
- });
- });
- function zf (n) {return (n>9?n:"0"+n);}; // For minutes and seconds (zerofill)
- function sl_conv(time) { return Math.floor(time / 86400)+" Days, "+Math.floor((time % 86400)/3600)+":"+zf(Math.floor((time % 3600)/60))+":"+zf((time % 3600)%60);} // Originally from the GIP (sl) but modified to fit the ListServer
- function main () { // Main code
- function autoCheck () {
- loadServers(function () {
- setTimeout(autoCheck, serversAutoCheckInterval);
- });
- };
- autoCheck();
- var serverStartTime = Date.now();
- // Watch files for changes
- fs.watch(path.join(__dirname, "servers.txt"), function () {
- console.log(timestamp(), "List of synced servers updated");
- loadServers();
- });
- fs.watch(path.join(__dirname, "motd.txt"), function () {
- console.log(timestamp(), "Message of the day updated");
- loadMOTD();
- });
- fs.watch(path.join(__dirname, "banlist.txt"), function () {
- console.log(timestamp(), "Banlist updated");
- loadBanlist();
- });
- // Binary serverlist
- var binaryList = net.createServer(function (socket) {
- socket.write("\x07"+"LIST"+"\x01\x01");
- var outputlist = buildServerList();
- for(var i=0; i < outputlist.length; i++) {
- socket.write(outputlist[i].getBinaryPacket());
- }
- socket.end();
- }).listen(10053);
- // Add/update server
- var serverAddition = net.createServer(function (socket) {
- var address = socket.remoteAddress;
- if(isBanned(socket.remoteAddress)) {
- socket.end("You have been banned from the ListServer");
- console.log(timestamp(), "Denied "+socket.remoteAddress+" from listing a server (banned IP)");
- }
- else {
- socket.on('data', function (data) {
- var index = clients.indexOf(socket);
- if(data.length === 42) { // List a new server
- if(index === -1) { // New client, add it
- clients.push(socket);
- serverlist.push(new Server());
- index = clients.indexOf(socket);
- }
- var server = serverlist[index];
- server.address = socket.remoteAddress;
- server.port = data.readUInt16LE(0);
- var i = 2;
- var name = "";
- while(data[i] > 0 && i+2 < 33) {
- name += String.fromCharCode(data[i++]);
- }
- server.servername = name;
- i = 2+33;
- server.capacity[0] = data[i++];
- server.capacity[1] = data[i++];
- var flags = data[i++];
- server['private'] = flags & 1;
- server.gametype = (flags >> 1) & 3;
- data.copy(server.version, 0, i, i+4);
- console.log(timestamp(), socket.remoteAddress+' listed server "'+server.servername+'"');
- var gm = server.gametype;
- if(gm > 3) gm = 0;
- gametypelog[gm]++;
- totalgames++;
- serverNetBroadcast(server.getAddPacket());
- }
- else if(data.length === 2 && index !== -1) {
- var server = serverlist[index];
- switch(data[0]) {
- case 0x00: // Player count update
- server.capacity[0] = data[1];
- serverNetBroadcast(server.getUpdatePlayersPacket());
- break;
- }
- }
- else {
- //console.log(timestamp(), socket.remoteAddress+" tried to list from a browser");
- socket.end("Welcome to the ELSE-statement, program.", 'utf8');
- }
- });
- socket.on('close', function () {
- var index = clients.indexOf(socket);
- if(index !== -1) {
- console.log(timestamp(), address+" delisted");
- serverNetBroadcast(serverlist[index].getRemovePacket());
- serverlist.splice(index, 1);
- clients.splice(index, 1);
- }
- });
- }
- }).listen(10054);
- // Status page for the listserver
- var serverListStatus = net.createServer(function (socket) {
- var list = buildServerList();
- var ownServers = 0;
- var mirroredServers = 0;
- for(var i=0; i < list.length; i++) {
- if(list[i].location === 'mirror') {
- mirroredServers++;
- }
- else {
- ownServers++;
- }
- }
- var lines = [
- "Jazz Jackrabbit 2 List Server v1.00 of "+mtime.toUTCString(),
- "Created by djazz - Powered by node.js",
- "",
- "Uptime : "+sl_conv(Math.round((Date.now() - serverStartTime)/1000)),
- "",
- "Servers in list : "+ownServers,
- "Mirrored servers : "+mirroredServers,
- "",
- "Number of Unknown games : "+gametypelog[0]+" ("+(Math.round(gametypelog[0]/totalgames*100) || 0)+"%)",
- "Number of Battle games : "+gametypelog[1]+" ("+(Math.round(gametypelog[1]/totalgames*100) || 0)+"%)",
- "Number of Treasure games : "+gametypelog[2]+" ("+(Math.round(gametypelog[2]/totalgames*100) || 0)+"%)",
- "Number of Capture the flag games : "+gametypelog[3]+" ("+(Math.round(gametypelog[3]/totalgames*100) || 0)+"%)",
- "Total number of games : "+totalgames,
- "",
- "Memory usage : "+Math.round((process.memoryUsage().rss/1024/1024)*1000)/1000+" MB",
- ""
- ];
- var listServerCount = specifiedservers.length;
- var numConnectedTo = 0;
- var listservers_str = [];
- var index = 1;
- for(var i=0; i < listServerCount; i++) {
- var listserver_info = "";
- if(specifiedservers[i].ip && ((listservers[specifiedservers[i].ip] && listservers[specifiedservers[i].ip].connected) || listclients[specifiedservers[i].ip])) {
- numConnectedTo++;
- var isServer = listservers[specifiedservers[i].ip] && listservers[specifiedservers[i].ip].connected;
- var isClient = !!listclients[specifiedservers[i].ip];
- var totalGames = (isServer && Object.keys(listservers[specifiedservers[i].ip].serverlist).length) || (isClient && Object.keys(listclients[specifiedservers[i]].serverlist).length) || 0;
- var addS = totalGames === 1 ? '' : 's';
- var connection = "C <-> S";
- if(isServer && !isClient) connection = "C <- S";
- else if(!isServer && isClient) connection = "C -> S";
- listserver_info = " ("+sl_conv(Math.round((Date.now() - (isServer? listservers[specifiedservers[i].ip].starttime : listclients[specifiedservers[i].ip].starttime))/1000))+", "+connection+", "+totalGames+" server"+addS+")";
- }
- listservers_str.push(" "+(index++)+". "+specifiedservers[i].host+listserver_info);
- }
- lines.push("Connections to other list servers ["+numConnectedTo+"/"+listServerCount+"]");
- lines.push(listservers_str.join("\r\n"));
- lines.push("");
- lines.push("Connected guests (C -> S)");
- var index = 1;
- for(var i in listclients) {
- if(!listservers[i]) {
- lines.push(" "+(index++)+". "+(listclients[i].hosts[0]? listclients[i].hosts[0]+' ('+i+')': i)+" ("+sl_conv(Math.round((Date.now() - listclients[i].starttime)/1000))+") ");
- }
- }
- socket.end(lines.join("\r\n"), 'binary');
- }).listen(10055);
- // ServerNet server
- var serverNet = net.createServer(function (socket) {
- var address = socket.remoteAddress;
- socket.setTimeout(serverNetTimeoutTime*1000, function () {
- //console.log('client timeout');
- socket.destroy();
- });
- if(listclients[address]) {
- console.log(timestamp(), address+': Double connection, closing old connection..');
- clearInterval(listclients[address].pinger);
- listclients[address].socket.destroy();
- for(var i in listclients[address].serverlist) {
- delete listclients[address].serverlist[i];
- }
- delete listclients[address];
- }
- if(listservers[address] && listservers[address].connected) {
- socket.destroy();
- console.log(timestamp(), address+" is already connected as listserver");
- return;
- }
- //console.log('servernet request from '+address);
- // Send my servers
- for(var i=0; i < serverlist.length; i++) {
- socket.write(serverlist[i].getAddPacket());
- }
- listclients[address] = {serverlist: {}, hosts: [address], starttime: Date.now()};
- var listclient = listclients[address];
- dns.reverse(address, function (err, domains) {
- if(err) {
- console.error(timestamp(), address+" Reverse DNS "+err);
- return;
- }
- if(listclient) listclient.hosts = domains;
- });
- listclient.socket = socket;
- var pinger = setInterval(function () {
- socket.write(new Buffer([0x00, 0x80])); // Ping
- }, 20*1000);
- listclient.pinger = pinger;
- var databuffer = [];
- if(listservers[address]) { // Only for trusted listservers
- socket.on('data', function (data) {
- Array.prototype.push.apply(
- databuffer,
- Array.prototype.slice.apply(data) // Convert from buffer to array
- ); // Append data to array
- while(databuffer.length >= databuffer[0]+2) {
- var packet = new Buffer(databuffer.splice(0, databuffer[0]+2));
- // Note: packetLength = packet.length + 2;
- parseServerNetPacket(packet, listclient);
- }
- });
- }
- socket.on('error', function (err) {
- console.log('servernet client error: '+err);
- });
- socket.on('close', function () {
- //console.log('servernet client closed: '+address);
- clearInterval(pinger);
- if(listclients[address] && listclients[address].socket === socket) {
- delete listclients[address];
- }
- });
- }).listen(10056);
- // The main serverlist
- var serverList = net.createServer(function (socket) {
- var outputlist = buildServerList();
- for(var i=0; i < outputlist.length; i++) {
- outputlist[i] = outputlist[i].getASCII();
- }
- socket.end(outputlist.join('\r\n')+'\r\n', 'binary');
- }).listen(10057);
- var motdServer = net.createServer(function (socket) {
- socket.end(motd, "utf8");
- //console.log(timestamp(), socket.remoteAddress+" requested the MOTD");
- }).listen(10058);
- console.log(timestamp(), 'All servers started');
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement