node-gamedig/protocols/battlefield.js

164 lines
6.2 KiB
JavaScript
Raw Normal View History

2019-01-07 07:52:29 +01:00
const Core = require('./core');
2014-10-29 08:02:03 +01:00
class Battlefield extends Core {
constructor() {
2017-08-09 12:32:09 +02:00
super();
this.encoding = 'latin1';
}
2019-01-07 07:52:29 +01:00
async run(state) {
await this.withTcp(async socket => {
{
const data = await this.query(socket, ['serverInfo']);
2019-01-12 11:43:36 +01:00
state.name = data.shift();
2019-01-07 07:52:29 +01:00
state.raw.numplayers = parseInt(data.shift());
state.maxplayers = parseInt(data.shift());
state.raw.gametype = data.shift();
state.map = data.shift();
state.raw.roundsplayed = parseInt(data.shift());
state.raw.roundstotal = parseInt(data.shift());
const teamCount = data.shift();
state.raw.teams = [];
for (let i = 0; i < teamCount; i++) {
const tickets = parseFloat(data.shift());
state.raw.teams.push({
tickets: tickets
});
}
state.raw.targetscore = parseInt(data.shift());
2019-01-12 11:43:36 +01:00
state.raw.status = data.shift();
// Seems like the fields end at random places beyond this point
// depending on the server version
if (data.length) state.raw.ranked = (data.shift() === 'true');
if (data.length) state.raw.punkbuster = (data.shift() === 'true');
if (data.length) state.password = (data.shift() === 'true');
if (data.length) state.raw.uptime = parseInt(data.shift());
if (data.length) state.raw.roundtime = parseInt(data.shift());
const isBadCompany2 = data[0] === 'BC2';
if (isBadCompany2) {
if (data.length) data.shift();
if (data.length) data.shift();
2019-01-07 07:52:29 +01:00
}
2019-01-12 11:43:36 +01:00
if (data.length) {
state.raw.ip = data.shift();
const split = state.raw.ip.split(':');
state.gameHost = split[0];
state.gamePort = split[1];
} else {
// best guess if the server doesn't tell us what the server port is
// these are just the default game ports for different default query ports
if (this.options.port === 48888) state.gamePort = 7673;
if (this.options.port === 22000) state.gamePort = 25200;
2019-01-07 07:52:29 +01:00
}
2019-01-12 11:43:36 +01:00
if (data.length) state.raw.punkbusterversion = data.shift();
if (data.length) state.raw.joinqueue = (data.shift() === 'true');
if (data.length) state.raw.region = data.shift();
if (data.length) state.raw.pingsite = data.shift();
if (data.length) state.raw.country = data.shift();
if (data.length) state.raw.quickmatch = (data.shift() === 'true');
2019-01-07 07:52:29 +01:00
}
2017-08-09 12:32:09 +02:00
2019-01-07 07:52:29 +01:00
{
const data = await this.query(socket, ['version']);
data.shift();
state.raw.version = data.shift();
}
{
const data = await this.query(socket, ['listPlayers', 'all']);
const fieldCount = parseInt(data.shift());
const fields = [];
for (let i = 0; i < fieldCount; i++) {
fields.push(data.shift());
}
const numplayers = data.shift();
for (let i = 0; i < numplayers; i++) {
const player = {};
for (let key of fields) {
let value = data.shift();
if (key === 'teamId') key = 'team';
else if (key === 'squadId') key = 'squad';
if (
key === 'kills'
|| key === 'deaths'
|| key === 'score'
|| key === 'rank'
|| key === 'team'
|| key === 'squad'
|| key === 'ping'
|| key === 'type'
) {
value = parseInt(value);
2017-08-09 12:32:09 +02:00
}
2019-01-07 07:52:29 +01:00
player[key] = value;
}
state.players.push(player);
}
2017-08-09 12:32:09 +02:00
}
2019-01-07 07:52:29 +01:00
});
2017-08-09 12:32:09 +02:00
}
2019-01-07 07:52:29 +01:00
async query(socket, params) {
const outPacket = this.buildPacket(params);
return await this.tcpSend(socket, outPacket, (data) => {
const decoded = this.decodePacket(data);
2019-01-07 07:52:29 +01:00
if(decoded) {
2019-01-09 12:50:30 +01:00
this.debugLog(decoded);
2019-01-07 07:52:29 +01:00
if(decoded.shift() !== 'OK') throw new Error('Missing OK');
return decoded;
}
2017-08-09 12:32:09 +02:00
});
}
2019-01-07 07:52:29 +01:00
buildPacket(params) {
const paramBuffers = [];
for (const param of params) {
paramBuffers.push(Buffer.from(param,'utf8'));
}
let totalLength = 12;
for (const paramBuffer of paramBuffers) {
totalLength += paramBuffer.length+1+4;
}
const b = Buffer.alloc(totalLength);
b.writeUInt32LE(0,0);
b.writeUInt32LE(totalLength,4);
b.writeUInt32LE(params.length,8);
let offset = 12;
for (const paramBuffer of paramBuffers) {
b.writeUInt32LE(paramBuffer.length, offset); offset += 4;
paramBuffer.copy(b, offset); offset += paramBuffer.length;
b.writeUInt8(0, offset); offset += 1;
}
return b;
}
2017-08-09 12:32:09 +02:00
decodePacket(buffer) {
if(buffer.length < 8) return false;
const reader = this.reader(buffer);
const header = reader.uint(4);
const totalLength = reader.uint(4);
2017-08-09 12:32:09 +02:00
if(buffer.length < totalLength) return false;
2019-01-12 11:43:36 +01:00
this.debugLog("Expected " + totalLength + " bytes, have " + buffer.length);
const paramCount = reader.uint(4);
const params = [];
2017-08-09 12:32:09 +02:00
for(let i = 0; i < paramCount; i++) {
const len = reader.uint(4);
2017-08-09 12:32:09 +02:00
params.push(reader.string({length:len}));
const strNull = reader.uint(1);
2017-08-09 12:32:09 +02:00
}
return params;
}
}
2014-10-29 08:02:03 +01:00
module.exports = Battlefield;