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.
npm run import -- "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
}
/**
* 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:
_parseInfoString(str)
:
\
) and extracts key-value pairs._parseServers(str)
:
addr
(IP address) and port
properties._parsePlayers(str)
:
frags
, ping
, and name
properties.Response Parsing Functions:
_parseStatusResponse(data)
:
_parseInfoString
._parsePlayers
._parseInfoResponse(data)
:
_parseInfoString
.