quakejs | quakejs connection | quakejs utilities | Search

This code provides functions to parse various types of responses from a Quake 3 server, extracting information like server details, player lists, and game state.

Run example

npm run import -- "quakejs parsing"

quakejs parsing


function _parseInfoString(str) {
	var data = {};

	if (str) {
		// trim the incoming string
		str = str.replace(/^\\+|\\+$/g, '');

		var split = str.split('\\');

		for (var i = 0; i < split.length - 1; i += 2) {
			var key = split[i];
			var value = split[i+1];
			data[key] = value;
		}
	}

	return data;
};

function _parseServers(str) {
	var servers = [];

	str = str.replace(/\\EOT$|^\\/g, '');

	if (str) {
		var split = str.split('\\');

		for (var i = 0; i < split.length; i++) {
			var info = split[i];
			var addr = (info.charCodeAt(0) & 0xff).toString() + '.' + (info.charCodeAt(1) & 0xff).toString() + '.' +
				(info.charCodeAt(2) & 0xff).toString() + '.' + (info.charCodeAt(3) & 0xff).toString();
			var port = ((info.charCodeAt(4) & 0xff) << 8) | (info.charCodeAt(5) & 0xff);

			servers.push({ addr: addr, port: port });
		}
	}

	return servers;
};

function _parsePlayers(str) {
	var players = [];

	if (str) {
		// trim the incoming string
		str = str.replace(/^\n+|\n+$/g, '');

		var split = str.split('\n');

		for (var i = 0; i < split.length; i++) {
			var pinfo = split[i];
			var psplit = pinfo.match(/\S+|"[^"]+"/g);  // split on space, combining quoted items

			players.push({
				frags: parseInt(psplit[0], 10),
				ping: parseInt(psplit[1], 10),
				name: psplit[2]
			});
		}
	}

	return players;
};

function _parseStatusResponse(data) {
	var self = this;
	if (data.indexOf('statusResponse\n') !== 0) {
		throw new Error('Invalid getstatus response: ' + data);
	}
	data = data.substr(15);

	var idx = data.indexOf('\n');
	var variableData = idx !== -1 ? data.substr(0, idx) : data;
	var playerData = idx !== -1 ? data.substr(idx) : null;

	var info = _parseInfoString(variableData);
	info.players = _parsePlayers(playerData);
	return info;
};

function _parseInfoResponse(data) {
	var self = this;
	if (data.indexOf('infoResponse\n') !== 0) {
		throw new Error('Invalid getinfo response: ' + data);
	}
	data = data.substr(13);
	var info = _parseInfoString(data);

	// Compute the number of bots for the template
	info.g_botplayers = info.clients - info.g_humanplayers;

	return info;
};

module.exports = {
    _parseServers,
    _parsePlayers,
    _parseStatusResponse,
    _parseInfoResponse
}

What the code could have been:

/**
 * Parse a string into an object, separated by '\\' characters.
 * 
 * @param {string} str The string to parse.
 * @returns {object} An object containing key-value pairs.
 */
function _parseInfoString(str) {
    if (!str) return {};

    // Remove leading and trailing '\\'
    str = str.replace(/^\\+|\\+$/g, '');

    const data = {};

    // Split string into key-value pairs
    for (let i = 0; i < str.split('\\').length - 1; i += 2) {
        const [key, value] = [str.split('\\')[i], str.split('\\')[i + 1]];
        data[key] = value;
    }

    return data;
}

/**
 * Parse a string containing server information.
 * 
 * @param {string} str The string to parse.
 * @returns {array} An array of objects containing server information.
 */
function _parseServers(str) {
    if (!str) return [];

    // Remove leading '\\EOT' and trailing '\\'
    str = str.replace(/\\EOT$|^\\/g, '');

    const servers = [];

    // Split string into server addresses
    for (const info of str.split('\\')) {
        const addr = [
            (info.charCodeAt(0) & 0xff).toString(),
            (info.charCodeAt(1) & 0xff).toString(),
            (info.charCodeAt(2) & 0xff).toString(),
            (info.charCodeAt(3) & 0xff).toString()
        ].join('.');

        const port = ((info.charCodeAt(4) & 0xff) << 8) | (info.charCodeAt(5) & 0xff);

        servers.push({ addr, port });
    }

    return servers;
}

/**
 * Parse a string containing player information.
 * 
 * @param {string} str The string to parse.
 * @returns {array} An array of objects containing player information.
 */
function _parsePlayers(str) {
    if (!str) return [];

    // Remove leading and trailing newline characters
    str = str.replace(/^\n+|\n+$/g, '');

    const players = [];

    // Split string into player information
    for (const pinfo of str.split('\n')) {
        const psplit = pinfo.match(/\S+|"[^"]+"/g);

        if (psplit.length === 3) {
            const [frags, ping, name] = [
                parseInt(psplit[0], 10),
                parseInt(psplit[1], 10),
                psplit[2]
            ];

            players.push({ frags, ping, name });
        }
    }

    return players;
}

/**
 * Parse a status response string.
 * 
 * @param {string} data The response string.
 * @returns {object} An object containing the parsed information.
 */
function _parseStatusResponse(data) {
    // Check if the response is valid
    if (data.indexOf('statusResponse\n')!== 0) {
        throw new Error('Invalid getstatus response:'+ data);
    }

    // Extract the variable and player data
    const variableData = data.substr(15);
    let idx = variableData.indexOf('\n');
    const playerData = idx!== -1? variableData.substr(idx) : null;

    const info = _parseInfoString(variableData);
    info.players = _parsePlayers(playerData);

    return info;
}

/**
 * Parse an info response string.
 * 
 * @param {string} data The response string.
 * @returns {object} An object containing the parsed information.
 */
function _parseInfoResponse(data) {
    // Check if the response is valid
    if (data.indexOf('infoResponse\n')!== 0) {
        throw new Error('Invalid getinfo response:'+ data);
    }

    // Remove the response prefix
    const info = _parseInfoString(data.substr(13));

    // Calculate the number of bots
    info.g_botplayers = info.clients - info.g_humanplayers;

    return info;
}

module.exports = {
    _parseServers,
    _parsePlayers,
    _parseStatusResponse,
    _parseInfoResponse
};

This code defines several functions for parsing responses from a Quake 3 server.

Here's a breakdown:

Parsing Functions:

Response Parsing Functions: