node-gamedig/protocols/unreal2.js

145 lines
4.8 KiB
JavaScript
Raw Normal View History

2019-01-10 13:03:07 +01:00
const Core = require('./core');
2014-10-29 08:02:03 +01:00
class Unreal2 extends Core {
2017-08-09 12:32:09 +02:00
constructor() {
super();
this.encoding = 'latin1';
}
2019-01-10 13:03:07 +01:00
async run(state) {
{
const b = await this.sendPacket(0, true);
const reader = this.reader(b);
state.raw.serverid = reader.uint(4);
state.raw.ip = this.readUnrealString(reader);
2019-01-12 12:45:09 +01:00
state.gamePort = reader.uint(4);
2019-01-10 13:03:07 +01:00
state.raw.queryport = reader.uint(4);
state.name = this.readUnrealString(reader, true);
state.map = this.readUnrealString(reader, true);
state.raw.gametype = this.readUnrealString(reader, true);
state.raw.numplayers = reader.uint(4);
state.maxplayers = reader.uint(4);
this.readExtraInfo(reader, state);
}
2014-10-29 08:02:03 +01:00
2019-01-10 13:03:07 +01:00
{
const b = await this.sendPacket(1,true);
const reader = this.reader(b);
state.raw.mutators = [];
state.raw.rules = {};
while(!reader.done()) {
const key = this.readUnrealString(reader,true);
const value = this.readUnrealString(reader,true);
if(key === 'Mutator') state.raw.mutators.push(value);
else if (key || value) state.raw.rules[key] = value;
2019-01-10 13:03:07 +01:00
}
if('GamePassword' in state.raw.rules)
state.password = state.raw.rules.GamePassword !== 'True';
}
2014-10-29 08:02:03 +01:00
2019-01-10 13:03:07 +01:00
{
const b = await this.sendPacket(2,false);
const reader = this.reader(b);
2014-10-29 08:02:03 +01:00
2019-01-10 13:03:07 +01:00
while(!reader.done()) {
const player = {};
player.id = reader.uint(4);
if(!player.id) break;
if(player.id === 0) {
// Unreal2XMP Player (ID is always 0)
reader.skip(4);
}
player.name = this.readUnrealString(reader,true);
player.ping = reader.uint(4);
player.score = reader.int(4);
reader.skip(4); // stats ID
2014-10-29 08:02:03 +01:00
2019-01-10 13:03:07 +01:00
// Extra data for Unreal2XMP players
if(player.id === 0) {
const count = reader.uint(1);
for(let iField = 0; iField < count; iField++) {
const key = this.readUnrealString(reader,true);
const value = this.readUnrealString(reader,true);
player[key] = value;
}
}
2017-08-09 12:32:09 +02:00
2019-01-10 13:03:07 +01:00
if(player.id === 0 && player.name === 'Player') {
// these show up in ut2004 queries, but aren't real
// not even really sure why they're there
continue;
}
2014-10-29 08:02:03 +01:00
2019-01-10 13:03:07 +01:00
(player.ping ? state.players : state.bots).push(player);
2017-08-09 12:32:09 +02:00
}
2019-01-10 13:03:07 +01:00
}
2017-08-09 12:32:09 +02:00
}
2019-01-10 13:03:07 +01:00
2017-08-09 12:32:09 +02:00
readExtraInfo(reader,state) {
2019-01-09 12:50:30 +01:00
this.debugLog(log => {
log("UNREAL2 EXTRA INFO:");
log(reader.uint(4));
log(reader.uint(4));
log(reader.uint(4));
log(reader.uint(4));
log(reader.buffer.slice(reader.i));
});
2017-08-09 12:32:09 +02:00
}
2019-01-10 13:03:07 +01:00
2017-08-09 12:32:09 +02:00
readUnrealString(reader, stripColor) {
let length = reader.uint(1), ucs2 = false;
if(length >= 0x80) {
2017-08-09 12:32:09 +02:00
length = (length&0x7f)*2;
2019-01-09 12:50:30 +01:00
this.debugLog(log => {
log("UCS2 STRING");
log("UCS2 Length: " + length);
log(reader.buffer.slice(reader.i,reader.i+length));
2019-01-09 12:50:30 +01:00
});
ucs2 = true;
}
// Killing floor sometimes injects 8 bytes of junk here
if (reader.remaining() >= 8 && length >= 8) {
const peek = reader.part(2);
reader.skip(-2);
if (peek[0] === 0x1b && peek[1] === 0x01) {
reader.skip(8);
length -= 8;
}
}
let out = '';
if (ucs2) {
2017-08-09 12:32:09 +02:00
out = reader.string({encoding:'ucs2',length:length});
this.debugLog("UCS2 String decoded: " + out);
} else if (length > 0) {
out = reader.string();
2017-08-09 12:32:09 +02:00
}
if(out.charCodeAt(out.length-1) === 0)
out = out.substring(0,out.length-1);
if(stripColor)
out = out.replace(/\x1b...|[\x00-\x1a]/g,'');
2014-10-29 08:02:03 +01:00
2017-08-09 12:32:09 +02:00
return out;
}
2019-01-10 13:03:07 +01:00
async sendPacket(type,required) {
const outbuffer = Buffer.from([0x79,0,0,0,type]);
2014-10-29 08:02:03 +01:00
const packets = [];
2019-01-10 13:03:07 +01:00
return await this.udpSend(outbuffer,(buffer) => {
const reader = this.reader(buffer);
const header = reader.uint(4);
const iType = reader.uint(1);
2017-08-09 12:32:09 +02:00
if(iType !== type) return;
packets.push(reader.rest());
}, () => {
if(!packets.length && required) return;
2019-01-10 13:03:07 +01:00
return Buffer.concat(packets);
2017-08-09 12:32:09 +02:00
});
}
}
module.exports = Unreal2;