Roster parser: header-driven fixed-width, handle Plutonium T5's 9 columns
The raw hex dump showed Plutonium T5's `status` reply is a 9-column
format, not the 5-column shape the parser assumed:
num score ping guid name lastmsg address qport rate
--- ----- ---- ------- ---------------- ------- --------------------- ----- -----
0 3040 36 2741626 JoshWright^7 0 68.235.46.100:28960 -14733 25000
Three bugs in the old regex: it required 16+ hex chars for guid (T5
uses 7-digit decimal IDs), it expected name to run to end-of-line
(4 trailing columns get glued on), and it left Plutonium color codes
(^0..^9) in the rendered name.
Rewrote parsePlayerRoster to detect column boundaries from the header
line word positions instead — column N runs from word N's start to
word N+1's start, matching how CoD's status output actually lays out
(values borrow the padding between adjacent columns). Same code path
handles the old 5-column format and the T5 9-column format cleanly.
stripColorCodes() removes ^\d sequences from names in both dialects.
PlayerRoster component only appends `…` when the guid is actually
long enough to need truncation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -118,6 +118,51 @@ describe('parsePlayerRoster', () => {
|
||||
expect(parsePlayerRoster(empty)).toEqual([]);
|
||||
expect(parsePlayerRoster('nonsense')).toEqual([]);
|
||||
});
|
||||
|
||||
it('parses Plutonium T5 9-column status with numeric GUID + color-coded name', () => {
|
||||
// Verbatim shape from a real T5 status reply — 7-digit GUID (not hex),
|
||||
// 4 trailing columns after name (lastmsg, address, qport, rate),
|
||||
// and Plutonium color code (^7) in the name.
|
||||
const body = [
|
||||
'print',
|
||||
'map: zombie_cosmodrome',
|
||||
'num score ping guid name lastmsg address qport rate',
|
||||
'--- ----- ---- ------- ---------------- ------- -------------------------- ----- -----',
|
||||
' 0 3040 36 2741626 JoshWright^7 0 68.235.46.100:28960 -14733 25000'
|
||||
].join('\n');
|
||||
const roster = parsePlayerRoster(body);
|
||||
expect(roster).toHaveLength(1);
|
||||
expect(roster[0]).toEqual({
|
||||
num: 0,
|
||||
score: 3040,
|
||||
ping: 36,
|
||||
guid: '2741626',
|
||||
name: 'JoshWright' // ^7 stripped
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves multi-word names in the 9-column format', () => {
|
||||
const body = [
|
||||
'print',
|
||||
'map: mp_kino',
|
||||
'num score ping guid name lastmsg address qport rate',
|
||||
'--- ----- ---- ------- ---------------- ------- --------------------- ----- -----',
|
||||
' 1 500 48 8675309 Jenny Two Names 0 10.0.0.5:28960 -12345 25000'
|
||||
].join('\n');
|
||||
const roster = parsePlayerRoster(body);
|
||||
expect(roster).toHaveLength(1);
|
||||
expect(roster[0].name).toBe('Jenny Two Names');
|
||||
});
|
||||
|
||||
it('strips color codes from statusResponse-dialect names too', () => {
|
||||
const body = [
|
||||
'statusResponse',
|
||||
'\\mapname\\mp_kino\\g_gametype\\zclassic',
|
||||
'10 45 "^1Red^7Player"'
|
||||
].join('\n');
|
||||
const roster = parsePlayerRoster(body);
|
||||
expect(roster[0].name).toBe('RedPlayer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseStatusHeader', () => {
|
||||
|
||||
Reference in New Issue
Block a user