Add eslint (#364)

* Add initial prettier and eslint configs

* Modify prettierrc

* Run eslint on everything

* Actually remove prettier

* Fix some eslints

* Remove label in gs2

* Update CHANGELOG

* Update eslintrc to specify es2021
This commit is contained in:
CosminPerRam 2023-09-19 19:52:35 +03:00 committed by GitHub
parent bff9507189
commit 93a9095d99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 6960 additions and 5211 deletions

View file

@ -1,196 +1,194 @@
import Core from './core.js';
export default class gamespy3 extends Core {
constructor() {
super();
this.sessionId = 1;
this.encoding = 'latin1';
this.byteorder = 'be';
this.useOnlySingleSplit = false;
this.isJc2mp = false;
}
async run(state) {
const buffer = await this.sendPacket(9, false, false, false);
const reader = this.reader(buffer);
let challenge = parseInt(reader.string());
this.logger.debug("Received challenge key: " + challenge);
if (challenge === 0) {
// Some servers send us a 0 if they don't want a challenge key used
// BF2 does this.
challenge = null;
}
let requestPayload;
if(this.isJc2mp) {
// they completely alter the protocol. because why not.
requestPayload = Buffer.from([0xff,0xff,0xff,0x02]);
} else {
requestPayload = Buffer.from([0xff,0xff,0xff,0x01]);
}
/** @type Buffer[] */
const packets = await this.sendPacket(0,challenge,requestPayload,true);
// iterate over the received packets
// the first packet will start off with k/v pairs, followed with data fields
// the following packets will only have data fields
state.raw.playerTeamInfo = {};
for(let iPacket = 0; iPacket < packets.length; iPacket++) {
const packet = packets[iPacket];
const reader = this.reader(packet);
this.logger.debug("Parsing packet #" + iPacket);
this.logger.debug(packet);
// Parse raw server key/values
if(iPacket === 0) {
while(!reader.done()) {
const key = reader.string();
if(!key) break;
let value = reader.string();
while(value.match(/^p[0-9]+$/)) {
// fix a weird ut3 bug where some keys don't have values
value = reader.string();
}
state.raw[key] = value;
this.logger.debug(key + " = " + value);
}
}
// Parse player, team, item array state
if(this.isJc2mp) {
state.raw.numPlayers2 = reader.uint(2);
while(!reader.done()) {
const player = {};
player.name = reader.string();
player.steamid = reader.string();
player.ping = reader.uint(2);
state.players.push(player);
}
} else {
let firstMode = true;
while(!reader.done()) {
if (reader.uint(1) <= 2) continue;
reader.skip(-1);
let fieldId = reader.string();
if(!fieldId) continue;
const fieldIdSplit = fieldId.split('_');
const fieldName = fieldIdSplit[0];
const itemType = fieldIdSplit.length > 1 ? fieldIdSplit[1] : 'no_';
if(!(itemType in state.raw.playerTeamInfo)) {
state.raw.playerTeamInfo[itemType] = [];
}
const items = state.raw.playerTeamInfo[itemType];
let offset = reader.uint(1);
firstMode = false;
this.logger.debug(() => "Parsing new field: itemType=" + itemType + " fieldName=" + fieldName + " startOffset=" + offset);
while(!reader.done()) {
const item = reader.string();
if(!item) break;
while(items.length <= offset) { items.push({}); }
items[offset][fieldName] = item;
this.logger.debug("* " + item);
offset++;
}
}
}
}
// Turn all that raw state into something useful
if ('hostname' in state.raw) state.name = state.raw.hostname;
else if('servername' in state.raw) state.name = state.raw.servername;
if ('mapname' in state.raw) state.map = state.raw.mapname;
if (state.raw.password === '1') state.password = true;
if ('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers);
if ('hostport' in state.raw) state.gamePort = parseInt(state.raw.hostport);
if('' in state.raw.playerTeamInfo) {
for (const playerInfo of state.raw.playerTeamInfo['']) {
const player = {};
for(const from of Object.keys(playerInfo)) {
let key = from;
let value = playerInfo[from];
if(key === 'player') key = 'name';
if(key === 'score' || key === 'ping' || key === 'team' || key === 'deaths' || key === 'pid') value = parseInt(value);
player[key] = value;
}
state.players.push(player);
}
}
}
async sendPacket(type,challenge,payload,assemble) {
const challengeLength = challenge === null ? 0 : 4;
const payloadLength = payload ? payload.length : 0;
const b = Buffer.alloc(7 + challengeLength + payloadLength);
b.writeUInt8(0xFE, 0);
b.writeUInt8(0xFD, 1);
b.writeUInt8(type, 2);
b.writeUInt32BE(this.sessionId, 3);
if(challengeLength) b.writeInt32BE(challenge, 7);
if(payloadLength) payload.copy(b, 7+challengeLength);
let numPackets = 0;
const packets = {};
return await this.udpSend(b,(buffer) => {
const reader = this.reader(buffer);
const iType = reader.uint(1);
if(iType !== type) {
this.logger.debug('Skipping packet, type mismatch');
return;
}
const iSessionId = reader.uint(4);
if(iSessionId !== this.sessionId) {
this.logger.debug('Skipping packet, session id mismatch');
return;
}
if(!assemble) {
return reader.rest();
}
if(this.useOnlySingleSplit) {
// has split headers, but they are worthless and only one packet is used
reader.skip(11);
return [reader.rest()];
}
reader.skip(9); // filler data -- usually set to 'splitnum\0'
let id = reader.uint(1);
const last = (id & 0x80);
id = id & 0x7f;
if(last) numPackets = id+1;
reader.skip(1); // "another 'packet number' byte, but isn't understood."
packets[id] = reader.rest();
if(this.debug) {
this.logger.debug("Received packet #"+id + (last ? " (last)" : ""));
}
if(!numPackets || Object.keys(packets).length !== numPackets) return;
// assemble the parts
const list = [];
for(let i = 0; i < numPackets; i++) {
if(!(i in packets)) {
throw new Error('Missing packet #'+i);
}
list.push(packets[i]);
}
return list;
});
}
}
import Core from './core.js'
export default class gamespy3 extends Core {
constructor () {
super()
this.sessionId = 1
this.encoding = 'latin1'
this.byteorder = 'be'
this.useOnlySingleSplit = false
this.isJc2mp = false
}
async run (state) {
const buffer = await this.sendPacket(9, false, false, false)
const reader = this.reader(buffer)
let challenge = parseInt(reader.string())
this.logger.debug('Received challenge key: ' + challenge)
if (challenge === 0) {
// Some servers send us a 0 if they don't want a challenge key used
// BF2 does this.
challenge = null
}
let requestPayload
if (this.isJc2mp) {
// they completely alter the protocol. because why not.
requestPayload = Buffer.from([0xff, 0xff, 0xff, 0x02])
} else {
requestPayload = Buffer.from([0xff, 0xff, 0xff, 0x01])
}
/** @type Buffer[] */
const packets = await this.sendPacket(0, challenge, requestPayload, true)
// iterate over the received packets
// the first packet will start off with k/v pairs, followed with data fields
// the following packets will only have data fields
state.raw.playerTeamInfo = {}
for (let iPacket = 0; iPacket < packets.length; iPacket++) {
const packet = packets[iPacket]
const reader = this.reader(packet)
this.logger.debug('Parsing packet #' + iPacket)
this.logger.debug(packet)
// Parse raw server key/values
if (iPacket === 0) {
while (!reader.done()) {
const key = reader.string()
if (!key) break
let value = reader.string()
while (value.match(/^p[0-9]+$/)) {
// fix a weird ut3 bug where some keys don't have values
value = reader.string()
}
state.raw[key] = value
this.logger.debug(key + ' = ' + value)
}
}
// Parse player, team, item array state
if (this.isJc2mp) {
state.raw.numPlayers2 = reader.uint(2)
while (!reader.done()) {
const player = {}
player.name = reader.string()
player.steamid = reader.string()
player.ping = reader.uint(2)
state.players.push(player)
}
} else {
while (!reader.done()) {
if (reader.uint(1) <= 2) continue
reader.skip(-1)
const fieldId = reader.string()
if (!fieldId) continue
const fieldIdSplit = fieldId.split('_')
const fieldName = fieldIdSplit[0]
const itemType = fieldIdSplit.length > 1 ? fieldIdSplit[1] : 'no_'
if (!(itemType in state.raw.playerTeamInfo)) {
state.raw.playerTeamInfo[itemType] = []
}
const items = state.raw.playerTeamInfo[itemType]
let offset = reader.uint(1)
this.logger.debug(() => 'Parsing new field: itemType=' + itemType + ' fieldName=' + fieldName + ' startOffset=' + offset)
while (!reader.done()) {
const item = reader.string()
if (!item) break
while (items.length <= offset) { items.push({}) }
items[offset][fieldName] = item
this.logger.debug('* ' + item)
offset++
}
}
}
}
// Turn all that raw state into something useful
if ('hostname' in state.raw) state.name = state.raw.hostname
else if ('servername' in state.raw) state.name = state.raw.servername
if ('mapname' in state.raw) state.map = state.raw.mapname
if (state.raw.password === '1') state.password = true
if ('maxplayers' in state.raw) state.maxplayers = parseInt(state.raw.maxplayers)
if ('hostport' in state.raw) state.gamePort = parseInt(state.raw.hostport)
if ('' in state.raw.playerTeamInfo) {
for (const playerInfo of state.raw.playerTeamInfo['']) {
const player = {}
for (const from of Object.keys(playerInfo)) {
let key = from
let value = playerInfo[from]
if (key === 'player') key = 'name'
if (key === 'score' || key === 'ping' || key === 'team' || key === 'deaths' || key === 'pid') value = parseInt(value)
player[key] = value
}
state.players.push(player)
}
}
}
async sendPacket (type, challenge, payload, assemble) {
const challengeLength = challenge === null ? 0 : 4
const payloadLength = payload ? payload.length : 0
const b = Buffer.alloc(7 + challengeLength + payloadLength)
b.writeUInt8(0xFE, 0)
b.writeUInt8(0xFD, 1)
b.writeUInt8(type, 2)
b.writeUInt32BE(this.sessionId, 3)
if (challengeLength) b.writeInt32BE(challenge, 7)
if (payloadLength) payload.copy(b, 7 + challengeLength)
let numPackets = 0
const packets = {}
return await this.udpSend(b, (buffer) => {
const reader = this.reader(buffer)
const iType = reader.uint(1)
if (iType !== type) {
this.logger.debug('Skipping packet, type mismatch')
return
}
const iSessionId = reader.uint(4)
if (iSessionId !== this.sessionId) {
this.logger.debug('Skipping packet, session id mismatch')
return
}
if (!assemble) {
return reader.rest()
}
if (this.useOnlySingleSplit) {
// has split headers, but they are worthless and only one packet is used
reader.skip(11)
return [reader.rest()]
}
reader.skip(9) // filler data -- usually set to 'splitnum\0'
let id = reader.uint(1)
const last = (id & 0x80)
id = id & 0x7f
if (last) numPackets = id + 1
reader.skip(1) // "another 'packet number' byte, but isn't understood."
packets[id] = reader.rest()
if (this.debug) {
this.logger.debug('Received packet #' + id + (last ? ' (last)' : ''))
}
if (!numPackets || Object.keys(packets).length !== numPackets) return
// assemble the parts
const list = []
for (let i = 0; i < numPackets; i++) {
if (!(i in packets)) {
throw new Error('Missing packet #' + i)
}
list.push(packets[i])
}
return list
})
}
}