mirror of
https://github.com/gamedig/node-gamedig.git
synced 2024-11-19 02:00:38 +01:00
Improve packet ordering and deduplication of players from gamespy1 protocol
This commit is contained in:
parent
2a87360a0e
commit
94c263669d
2 changed files with 103 additions and 16 deletions
|
@ -109,6 +109,10 @@ class Core extends EventEmitter {
|
|||
delete state.gameHost;
|
||||
delete state.gamePort;
|
||||
|
||||
this.logger.debug(log => {
|
||||
log("Size of players array: " + state.players.length);
|
||||
log("Size of bots array: " + state.bots.length);
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ class Gamespy1 extends Core {
|
|||
}
|
||||
{
|
||||
const data = await this.sendPacket('players');
|
||||
const players = {};
|
||||
const teams = {};
|
||||
const playersById = {};
|
||||
const teamNamesById = {};
|
||||
for (const ident of Object.keys(data)) {
|
||||
const split = ident.split('_');
|
||||
let key = split[0];
|
||||
|
@ -32,26 +32,74 @@ class Gamespy1 extends Core {
|
|||
let value = data[ident];
|
||||
|
||||
if (key === 'teamname') {
|
||||
teams[id] = value;
|
||||
teamNamesById[id] = value;
|
||||
} else {
|
||||
if (!(id in players)) players[id] = {};
|
||||
if (!(id in playersById)) playersById[id] = {};
|
||||
if (key === 'playername') key = 'name';
|
||||
else if (key === 'team') value = parseInt(value);
|
||||
else if (key === 'score' || key === 'ping' || key === 'deaths') value = parseInt(value);
|
||||
players[id][key] = value;
|
||||
else if (key === 'score' || key === 'ping' || key === 'deaths' || key === 'kills') value = parseInt(value);
|
||||
playersById[id][key] = value;
|
||||
}
|
||||
}
|
||||
state.raw.teams = teamNamesById;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
state.raw.teams = teams;
|
||||
for (const id of Object.keys(players)) {
|
||||
state.players.push(players[id]);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async sendPacket(type) {
|
||||
const queryId = '';
|
||||
let receivedQueryId;
|
||||
const output = {};
|
||||
const parts = new Set();
|
||||
let maxPartNum = 0;
|
||||
|
||||
return await this.udpSend('\\'+type+'\\', buffer => {
|
||||
const reader = this.reader(buffer);
|
||||
const str = reader.string({length:buffer.length});
|
||||
|
@ -63,12 +111,47 @@ class Gamespy1 extends Core {
|
|||
const value = split.shift() || '';
|
||||
data[key] = value;
|
||||
}
|
||||
if(!('queryid' in data)) return;
|
||||
if(queryId && data.queryid !== queryId) return;
|
||||
for(const i of Object.keys(data)) output[i] = data[i];
|
||||
if('final' in output) {
|
||||
delete output.final;
|
||||
delete output.queryid;
|
||||
|
||||
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");
|
||||
return output;
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue