<commit_before>// dmaster: A web-based Doom server browser and REST API.
// Copyright (C) 2013  <NAME>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

var config = require('config');
var dgram = require('dgram');
var printf = require('printf');
var q = require('q');

var db = require('./db.js');
var huffman = require('./huffman.js');
var packet = require('./packet.js');
var webapp = require('./webapp.js');
var zan = require('./zandronum.js');

var socket = dgram.createSocket('udp4');
var huf = new huffman.Huffman(zan.huffmanFreqs);

function send_query(socket) {
	var flags = zan.SQF_MAXPLAYERS | zan.SQF_MAXCLIENTS | zan.SQF_FORCEPASSWORD |
				zan.SQF_IWAD | zan.SQF_MAPNAME | zan.SQF_GAMETYPE | zan.SQF_NAME |
				zan.SQF_URL | zan.SQF_EMAIL | zan.SQF_PLAYERDATA | zan.SQF_PWADS;
	var queryFlags = new Buffer(4);
	queryFlags.writeUInt32LE(flags, 0);

	const timestamp = new Buffer([0, 0, 0, 0]);
	const challenge = huf.encode(Buffer.concat([zan.LAUNCHER_SERVER_CHALLENGE, queryFlags, timestamp]));

	db.each('SELECT id, address, port FROM servers;', function(err, row) {
		socket.send(challenge, 0, challenge.length, row.port, row.address, function(error, length) {
			if (error) {
				console.log('server query error: ' + error + '.');
			}
		});
	});
}

function send_challenge(socket) {
	const masters = config.masters;
	const challenge = huf.encode(Buffer.concat([zan.LAUNCHER_MASTER_CHALLENGE, zan.MASTER_SERVER_VERSION]));

	for (var i = 0;i < masters.length;i++) {
		(function(i) {
			socket.send(challenge, 0, challenge.length, masters[i].port, masters[i].address, function(error, length) {
				if (error) {
					console.log('mater challenge error: ' + error + '.');
				}
			});
		})(i);
	}
}

socket.on('message', function(msg, rinfo) {
	var data = huf.decode(msg);
	var flag = data.readUInt32LE(0);

	switch (flag) {
	case zan.MSC_IPISBANNED:
		console.log('master query ignored, banned from the master server.');
		break;
	case zan.MSC_REQUESTIGNORED:
		// console.log('master query ignored, please throttle your requests.');
		break;
	case zan.MSC_WRONGVERSION:
		console.log('master query ignored, protocol version out of date.');
		break;
	case zan.MSC_BEGINSERVERLISTPART:
		var serverList = packet.unmarshallServerList(data.slice(4));
		var servers = serverList.servers;
		var stmt = 'INSERT INTO servers (address, port) VALUES (?, ?);';

		for (var i = 0;i < servers.length;i++) {
			var address = servers[i].address;
			for (var j = 0;j < servers[i].ports.length;j++) {
				var port = servers[i].ports[j];
				db.run(stmt, address, port, function(error) {
					if (error) {
						throw new Error(error);
					}
				});
			}
		}
		break;
	case zan.SERVER_LAUNCHER_CHALLENGE:
		// Look up the id associated with the given address and port.  Stores
		// the value in a promise so we can unmarshall packets while we wait
		// on the id to be returned.
		var serverId = q.defer();
		db.get(
			'SELECT id FROM servers WHERE address = ? AND port = ?;',
			rinfo.address, rinfo.port,
			function(err, row) {
				if (err) {
					serverId.reject(err);
				} else if (!('id' in row)) {
					serverId.reject(rinfo.address + ':' + rinfo.port + ' was not found in the server database.');
				} else {
					serverId.resolve(row.id);
				}
			}
		);

		var serverInfo = packet.unmarshallServerInfo(data.slice(4));

		// Update basic server information.
		const validServerColumns = [
			'name', 'url', 'email', 'map', 'maxclients', 'maxplayers',
			'gametype', 'iwad', 'password'
		];
		var sets = [];
		var params = [];

		for (var i = 0;i < validServerColumns.length;i++) {
			if (validServerColumns[i] in serverInfo) {
				sets.push(validServerColumns[i] + '=?');
				params.push(serverInfo[validServerColumns[i]]);
			}
		}

		if (params.length > 0) {
			serverId.promise.then(function(value) {
				params.push(value);
				var stmt =
					'UPDATE servers SET ' + sets.join(',') +
					',updated=datetime(\'now\') WHERE id = ?';
				db.run(stmt, params, function(error) {
					if (error) {
						throw new Error(error);
					}
				});
			}, function(reason) {
				console.log(reason);
			});
		}

		if ('players' in serverInfo && serverInfo.players.length > 0) {
			serverId.promise.then(function(value) {
				db.run('DELETE FROM players WHERE server_id = ?', value, function(error) {
					if (error) {
						throw error;
					} else {
						for (var i = 0;i < serverInfo.players.length;i++) {
							var player = serverInfo.players[i];
							db.run(
								'INSERT INTO players ' +
								'(server_id, ping, score, team, spectator, name) ' +
								'VALUES (?, ?, ?, ?, ?, ?);',
								value, player.ping, player.score, player.team, player.spectator, player.name,
								function(error) {
									if (error) {<commit_msg>Empty servers now have their players cleared.<commit_after>// dmaster: A web-based Doom server browser and REST API.
// Copyright (C) 2013  <NAME>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

var config = require('config');
var dgram = require('dgram');
var printf = require('printf');
var q = require('q');

var db = require('./db.js');
var huffman = require('./huffman.js');
var packet = require('./packet.js');
var webapp = require('./webapp.js');
var zan = require('./zandronum.js');

var socket = dgram.createSocket('udp4');
var huf = new huffman.Huffman(zan.huffmanFreqs);

function send_query(socket) {
	var flags = zan.SQF_MAXPLAYERS | zan.SQF_MAXCLIENTS | zan.SQF_FORCEPASSWORD |
				zan.SQF_IWAD | zan.SQF_MAPNAME | zan.SQF_GAMETYPE | zan.SQF_NAME |
				zan.SQF_URL | zan.SQF_EMAIL | zan.SQF_PLAYERDATA | zan.SQF_PWADS;
	var queryFlags = new Buffer(4);
	queryFlags.writeUInt32LE(flags, 0);

	const timestamp = new Buffer([0, 0, 0, 0]);
	const challenge = huf.encode(Buffer.concat([zan.LAUNCHER_SERVER_CHALLENGE, queryFlags, timestamp]));

	db.each('SELECT id, address, port FROM servers;', function(err, row) {
		socket.send(challenge, 0, challenge.length, row.port, row.address, function(error, length) {
			if (error) {
				console.log('server query error: ' + error + '.');
			}
		});
	});
}

function send_challenge(socket) {
	const masters = config.masters;
	const challenge = huf.encode(Buffer.concat([zan.LAUNCHER_MASTER_CHALLENGE, zan.MASTER_SERVER_VERSION]));

	for (var i = 0;i < masters.length;i++) {
		(function(i) {
			socket.send(challenge, 0, challenge.length, masters[i].port, masters[i].address, function(error, length) {
				if (error) {
					console.log('mater challenge error: ' + error + '.');
				}
			});
		})(i);
	}
}

socket.on('message', function(msg, rinfo) {
	var data = huf.decode(msg);
	var flag = data.readUInt32LE(0);

	switch (flag) {
	case zan.MSC_IPISBANNED:
		console.log('master query ignored, banned from the master server.');
		break;
	case zan.MSC_REQUESTIGNORED:
		// console.log('master query ignored, please throttle your requests.');
		break;
	case zan.MSC_WRONGVERSION:
		console.log('master query ignored, protocol version out of date.');
		break;
	case zan.MSC_BEGINSERVERLISTPART:
		var serverList = packet.unmarshallServerList(data.slice(4));
		var servers = serverList.servers;
		var stmt = 'INSERT INTO servers (address, port) VALUES (?, ?);';

		for (var i = 0;i < servers.length;i++) {
			var address = servers[i].address;
			for (var j = 0;j < servers[i].ports.length;j++) {
				var port = servers[i].ports[j];
				db.run(stmt, address, port, function(error) {
					if (error) {
						throw new Error(error);
					}
				});
			}
		}
		break;
	case zan.SERVER_LAUNCHER_CHALLENGE:
		// Look up the id associated with the given address and port.  Stores
		// the value in a promise so we can unmarshall packets while we wait
		// on the id to be returned.
		var serverId = q.defer();
		db.get(
			'SELECT id FROM servers WHERE address = ? AND port = ?;',
			rinfo.address, rinfo.port,
			function(err, row) {
				if (err) {
					serverId.reject(err);
				} else if (!('id' in row)) {
					serverId.reject(rinfo.address + ':' + rinfo.port + ' was not found in the server database.');
				} else {
					serverId.resolve(row.id);
				}
			}
		);

		var serverInfo = packet.unmarshallServerInfo(data.slice(4));

		// Update basic server information.
		const validServerColumns = [
			'name', 'url', 'email', 'map', 'maxclients', 'maxplayers',
			'gametype', 'iwad', 'password'
		];
		var sets = [];
		var params = [];

		for (var i = 0;i < validServerColumns.length;i++) {
			if (validServerColumns[i] in serverInfo) {
				sets.push(validServerColumns[i] + '=?');
				params.push(serverInfo[validServerColumns[i]]);
			}
		}

		if (params.length > 0) {
			serverId.promise.then(function(value) {
				params.push(value);
				var stmt =
					'UPDATE servers SET ' + sets.join(',') +
					',updated=datetime(\'now\') WHERE id = ?';
				db.run(stmt, params, function(error) {
					if (error) {
						throw new Error(error);
					}
				});
			}, function(reason) {
				console.log(reason);
			});
		}

		if ('players' in serverInfo) {
			serverId.promise.then(function(value) {
				db.run('DELETE FROM players WHERE server_id = ?', value, function(error) {
					if (error) {
						throw error;
					} else {
						for (var i = 0;i < serverInfo.players.length;i++) {
							var player = serverInfo.players[i];
							db.run(
								'INSERT INTO players ' +
								'(server_id, ping, score, team, spectator, name) ' +
								'VALUES (?, ?, ?, ?, ?, ?);',
								value, player.ping, player.score, player.team, player.spectator, player.name,
								function(error) {
									if (error) {
{'fraction_non_alphanumeric': 0.14755472752678156, 'fraction_numerical': 0.005123428039124359, 'mean_word_length': 3.3856209150326797, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2786351', 'n_tokens_mistral': 4214, 'n_tokens_neox': 3588, 'n_words': 984}