Normalize T6 hexadecimal RCON GUIDs for ping matching
This commit is contained in:
@@ -115,6 +115,44 @@ describe('shared server observation', () => {
|
||||
expect(selectServerObservation(server({ rcon_status: { ...raw, last_seen_at: at(-90_001) } }), telemetry, now).roster?.map(player => player.ping)).toEqual([null, null]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['6C4C20', '7097376'], ['6c45dd', '7095773'], ['00006C4C20', '0007097376'],
|
||||
['0x6C45DD', '7095773'], ['10', '16'], ['20000000000001', '9007199254740993']
|
||||
])('matches T6 hex GUID %s to decimal %s despite different names', (hex, decimal) => {
|
||||
const telemetry = report(0, 1);
|
||||
telemetry.players[0].guid = decimal;
|
||||
const raw = server({ roster: [{ num: 0, score: 100, ping: 91, guid: hex, name: 'Previous name' }] });
|
||||
const selected = selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now);
|
||||
expect(selected.roster?.[0]).toMatchObject({ guid: decimal, name: 'Player 1', ping: 91, score: 5000 });
|
||||
expect(selected.rcon_status?.roster?.[0].guid).toBe(hex);
|
||||
});
|
||||
|
||||
it.each(['t5', undefined])('keeps decimal-looking GUIDs literal for game %s', game => {
|
||||
const telemetry = report(0, 2);
|
||||
telemetry.players[0].guid = '10';
|
||||
telemetry.players[1].guid = '16';
|
||||
const raw = server({ roster: [{ num: 0, score: 100, ping: 91, guid: '10', name: 'Previous name' }] });
|
||||
expect(selectServerObservation(server({ game, rcon_status: raw }), telemetry, now).roster?.map(player => player.ping)).toEqual([91, null]);
|
||||
});
|
||||
|
||||
it('treats digit-only T6 GUIDs as hexadecimal and reserves normalized matches before names', () => {
|
||||
const telemetry = report(0, 2);
|
||||
telemetry.players[0].guid = '10';
|
||||
telemetry.players[1].guid = '16';
|
||||
const raw = server({ roster: [{ num: 0, score: 100, ping: 91, guid: '10', name: 'Player 1' }] });
|
||||
expect(selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now).roster?.map(player => player.ping)).toEqual([null, 91]);
|
||||
});
|
||||
|
||||
it('rejects duplicate RCON GUIDs after normalization without bypassing them by name', () => {
|
||||
const telemetry = report(0, 1);
|
||||
telemetry.players[0].guid = '7097376';
|
||||
const raw = server({ roster: [
|
||||
{ num: 0, score: 100, ping: 91, guid: '6C4C20', name: 'Player 1' },
|
||||
{ num: 1, score: 100, ping: 42, guid: '006c4c20', name: 'Other player' }
|
||||
] });
|
||||
expect(selectServerObservation(server({ game: 't6', rcon_status: raw }), telemetry, now).roster?.[0].ping).toBeNull();
|
||||
});
|
||||
|
||||
it.each(['rcon', 'telemetry', 'case', 'blank', 'invalid-ping'])('rejects ambiguous or invalid %s name fallback', mode => {
|
||||
const telemetry = report(0, 2);
|
||||
const player = { num: 0, score: 100, ping: 42, guid: 'different-guid', name: telemetry.players[0].name };
|
||||
|
||||
@@ -490,18 +490,20 @@ describe('database fleet and container readiness', () => {
|
||||
expect(snapshot.serverNow).toBeLessThanOrEqual(Date.now());
|
||||
});
|
||||
|
||||
it('supplements telemetry with RCON pings consistently in REST and status events', async () => {
|
||||
it.each(['t5', 't6'])('supplements %s telemetry with RCON pings consistently in REST and status events', async game => {
|
||||
const { db, inspect, rcon, fanoutStatus } = await setup();
|
||||
const { GET } = await import('../../src/routes/api/servers/+server.js');
|
||||
const { subscribe } = await import('../../src/lib/server/pipeline/bus.js');
|
||||
db.prepare("UPDATE servers SET last_players = 4, last_seen_at = ? WHERE name = 'managed'").run(new Date().toISOString());
|
||||
const map = game === 't6' ? 'zm_transit' : 'zombie_theater';
|
||||
db.prepare("UPDATE servers SET game = ?, map = ?, last_players = 4, last_seen_at = ? WHERE name = 'managed'").run(game, map, new Date().toISOString());
|
||||
inspect.mockResolvedValue({ ok: true, state: 'running' });
|
||||
const telemetry = gameEvent('heartbeat', 15, 3, 'managed');
|
||||
const telemetry = gameEvent('heartbeat', 15, 3, 'managed', map);
|
||||
telemetry.players[0].ping = 42;
|
||||
telemetry.players[1].guid = '7097376';
|
||||
rcon.mockResolvedValue({ ok: true, endpoint: { host: 'localhost', port: 28960 }, rawReply: 'status',
|
||||
status: { players: 4, map: 'zombie_theater', gametype: 'zom', roster: [
|
||||
status: { players: 4, map, gametype: 'zom', roster: [
|
||||
{ num: 0, guid: telemetry.players[0].guid, name: 'Old name', score: 50, ping: 100 },
|
||||
{ num: 1, guid: telemetry.players[1].guid, name: 'Another old name', score: 100, ping: 30 },
|
||||
{ num: 1, guid: game === 't6' ? '6C4C20' : telemetry.players[1].guid, name: 'Another old name', score: 100, ping: 30 },
|
||||
{ num: 2, guid: '6C45DD', name: telemetry.players[2].name, score: 150, ping: 151 }
|
||||
] } });
|
||||
await postTelemetry(telemetry);
|
||||
|
||||
Reference in New Issue
Block a user