Match RCON player pings by unique name when GUIDs differ

This commit is contained in:
2026-10-06 21:25:03 -04:00
parent 1b42244858
commit 1779ae583b
5 changed files with 83 additions and 8 deletions
+26 -2
View File
@@ -46,6 +46,11 @@ export function freshTelemetry(report: GameTelemetry | undefined, now: number, a
try { parseGameEvent(report); return true; } catch { return false; }
}
// RCON strips CoD display colors and surrounding column padding from names.
function playerMatchName(name: string): string {
return name.replace(/\^\d/g, '').trim();
}
/** Display policy: use one current roster/count source, keeping raw RCON for diagnostics/fallback. */
export function selectServerObservation<T extends ServerObservation>(server: T, report: GameTelemetry | undefined, now: number): T {
if (server.telemetry && (!report || Date.parse(server.telemetry.receivedAt) > Date.parse(report.receivedAt))) report = server.telemetry;
@@ -65,10 +70,29 @@ export function selectServerObservation<T extends ServerObservation>(server: T,
// unambiguous RCON identities from the current map and server lifecycle.
const pings = new Map<string, number | null>();
if (fresh && raw.last_map === report.map) {
const byName = new Map<string, StatusPlayer | null>();
const nameCounts = new Map<string, number>();
const telemetryGuids = new Set(report.players.map(player => player.guid));
for (const player of report.players) {
const name = playerMatchName(player.name);
nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1);
}
for (const player of raw.roster ?? []) {
if (!player.guid) continue;
const name = playerMatchName(player.name);
if (name) byName.set(name, byName.has(name) ? null : player);
const ping = player.ping;
pings.set(player.guid, pings.has(player.guid) || ping === null || !Number.isSafeInteger(ping) || ping < 0 ? null : ping);
if (player.guid) pings.set(player.guid, pings.has(player.guid) || ping === null || !Number.isSafeInteger(ping) || ping < 0 ? null : ping);
}
for (const player of report.players) {
// A GUID match always wins, including ambiguous/invalid readings.
if (pings.has(player.guid)) continue;
const name = playerMatchName(player.name);
const match = byName.get(name);
// Names must be unique in both rosters. Never borrow a player
// already identified by another telemetry player's GUID.
if (!match || nameCounts.get(name) !== 1 || (match.guid && telemetryGuids.has(match.guid))) continue;
const ping = match.ping;
if (ping !== null && Number.isSafeInteger(ping) && ping >= 0) pings.set(player.guid, ping);
}
}
// New reports carry persisted anchors through both SSE and HTTP. Keep