2019-01-09 12:35:11 +01:00
|
|
|
const Core = require('./core');
|
2014-10-29 08:02:03 +01:00
|
|
|
|
2019-01-07 01:52:03 +01:00
|
|
|
class Gamespy1 extends Core {
|
2017-08-09 12:32:09 +02:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.encoding = 'latin1';
|
|
|
|
this.byteorder = 'be';
|
|
|
|
}
|
2014-10-29 08:02:03 +01:00
|
|
|
|
2019-01-09 12:35:11 +01:00
|
|
|
async run(state) {
|
|
|
|
{
|
|
|
|
const data = await this.sendPacket('info');
|
|
|
|
state.raw = data;
|
|
|
|
if ('hostname' in state.raw) state.name = state.raw.hostname;
|
|
|
|
if ('mapname' in state.raw) state.map = state.raw.mapname;
|
|
|
|
if (this.trueTest(state.raw.password)) state.password = true;
|
|
|
|
if ('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers);
|
2019-01-12 12:45:09 +01:00
|
|
|
if ('hostport' in state.raw) state.gamePort = parseInt(state.raw.hostport);
|
2019-01-09 12:35:11 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
const data = await this.sendPacket('rules');
|
|
|
|
state.raw.rules = data;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const data = await this.sendPacket('players');
|
2019-01-22 07:11:39 +01:00
|
|
|
const playersById = {};
|
|
|
|
const teamNamesById = {};
|
2019-01-09 12:35:11 +01:00
|
|
|
for (const ident of Object.keys(data)) {
|
|
|
|
const split = ident.split('_');
|
|
|
|
let key = split[0];
|
|
|
|
const id = split[1];
|
|
|
|
let value = data[ident];
|
2014-10-29 08:02:03 +01:00
|
|
|
|
2019-01-09 12:35:11 +01:00
|
|
|
if (key === 'teamname') {
|
2019-01-22 07:11:39 +01:00
|
|
|
teamNamesById[id] = value;
|
2019-01-09 12:35:11 +01:00
|
|
|
} else {
|
2019-01-22 07:11:39 +01:00
|
|
|
if (!(id in playersById)) playersById[id] = {};
|
2019-01-09 12:35:11 +01:00
|
|
|
if (key === 'playername') key = 'name';
|
|
|
|
else if (key === 'team') value = parseInt(value);
|
2019-01-22 07:11:39 +01:00
|
|
|
else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills') value = parseInt(value);
|
|
|
|
playersById[id][key] = value;
|
2019-01-09 12:35:11 +01:00
|
|
|
}
|
2017-08-09 12:32:09 +02:00
|
|
|
}
|
2019-01-22 07:11:39 +01:00
|
|
|
state.raw.teams = teamNamesById;
|
2017-08-09 12:32:09 +02:00
|
|
|
|
2019-01-22 07:11:39 +01:00
|
|
|
const players = Object.values(playersById);
|
|
|
|
|
|
|
|
// Determine which team id might be for spectators
|
|
|
|
let specTeamId = null;
|
|
|
|
for (const player of players) {
|
|
|
|
if (!player.team) {
|
|
|
|
continue;
|
|
|
|
} else if (teamNamesById[player.team]) {
|
|
|
|
continue;
|
|
|
|
} else if (teamNamesById[player.team-1] && (specTeamId === null || specTeamId === player.team)) {
|
|
|
|
specTeamId = player.team;
|
|
|
|
} else {
|
|
|
|
specTeamId = null;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.logger.debug(log => {
|
|
|
|
if (specTeamId === null) {
|
|
|
|
log("Could not detect a team ID for spectators");
|
|
|
|
} else {
|
|
|
|
log("Detected that team ID " + specTeamId + " is probably for spectators");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const seenHashes = new Set();
|
|
|
|
for (const player of players) {
|
|
|
|
// Some servers (bf1942) report the same player multiple times (bug?)
|
|
|
|
// Ignore these duplicates
|
|
|
|
if (player.keyhash) {
|
|
|
|
if (seenHashes.has(player.keyhash)) {
|
|
|
|
this.logger.debug("Rejected player with hash " + player.keyhash + " (Duplicate keyhash)");
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
seenHashes.add(player.keyhash);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert player's team ID to team name if possible
|
|
|
|
if (player.team) {
|
|
|
|
if (teamNamesById[player.team]) {
|
|
|
|
player.team = teamNamesById[player.team];
|
|
|
|
} else if (player.team === specTeamId) {
|
|
|
|
player.team = "spec";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state.players.push(player);
|
2019-01-09 12:35:11 +01:00
|
|
|
}
|
|
|
|
}
|
2017-08-09 12:32:09 +02:00
|
|
|
}
|
2017-08-09 11:05:55 +02:00
|
|
|
|
2019-01-09 12:35:11 +01:00
|
|
|
async sendPacket(type) {
|
2019-01-22 07:11:39 +01:00
|
|
|
let receivedQueryId;
|
2017-08-09 11:05:55 +02:00
|
|
|
const output = {};
|
2019-01-22 07:11:39 +01:00
|
|
|
const parts = new Set();
|
|
|
|
let maxPartNum = 0;
|
|
|
|
|
2019-01-09 12:35:11 +01:00
|
|
|
return await this.udpSend('\\'+type+'\\', buffer => {
|
2017-08-09 11:05:55 +02:00
|
|
|
const reader = this.reader(buffer);
|
2019-02-05 02:58:28 +01:00
|
|
|
const str = reader.string(buffer.length);
|
2017-08-09 11:05:55 +02:00
|
|
|
const split = str.split('\\');
|
2017-08-09 12:32:09 +02:00
|
|
|
split.shift();
|
2017-08-09 11:05:55 +02:00
|
|
|
const data = {};
|
2017-08-09 12:32:09 +02:00
|
|
|
while(split.length) {
|
2017-08-09 11:05:55 +02:00
|
|
|
const key = split.shift();
|
|
|
|
const value = split.shift() || '';
|
2017-08-09 12:32:09 +02:00
|
|
|
data[key] = value;
|
|
|
|
}
|
2019-01-22 07:11:39 +01:00
|
|
|
|
|
|
|
let queryId, partNum;
|
|
|
|
const partFinal = ('final' in data);
|
|
|
|
if (data.queryid) {
|
|
|
|
const split = data.queryid.split('.');
|
|
|
|
if (split.length >= 2) {
|
|
|
|
partNum = parseInt(split[1]);
|
|
|
|
}
|
|
|
|
queryId = split[0];
|
|
|
|
}
|
|
|
|
delete data.final;
|
|
|
|
delete data.queryid;
|
|
|
|
this.logger.debug("Received part num=" + partNum + " queryId=" + queryId + " final=" + partFinal);
|
|
|
|
|
|
|
|
if (queryId) {
|
|
|
|
if (receivedQueryId && receivedQueryId !== queryId) {
|
|
|
|
this.logger.debug("Rejected packet (Wrong query ID)");
|
|
|
|
return;
|
|
|
|
} else if (!receivedQueryId) {
|
|
|
|
receivedQueryId = queryId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!partNum) {
|
|
|
|
partNum = parts.size;
|
|
|
|
this.logger.debug("No part number received (assigned #" + partNum + ")");
|
|
|
|
}
|
|
|
|
if (parts.has(partNum)) {
|
|
|
|
this.logger.debug("Rejected packet (Duplicate part)");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
parts.add(partNum);
|
|
|
|
if (partFinal) {
|
|
|
|
maxPartNum = partNum;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.debug("Received part #" + partNum + " of " + (maxPartNum ? maxPartNum : "?"));
|
|
|
|
for(const i of Object.keys(data)) {
|
|
|
|
output[i] = data[i];
|
|
|
|
}
|
|
|
|
if (maxPartNum && parts.size === maxPartNum) {
|
|
|
|
this.logger.debug("Received all parts");
|
2019-10-16 05:31:53 +02:00
|
|
|
this.logger.debug(output);
|
2019-01-09 12:35:11 +01:00
|
|
|
return output;
|
2017-08-09 12:32:09 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-08-09 11:05:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Gamespy1;
|