Live-Ergebnisse aus CS2-Serverlogs (Backend)

Server bekommt beim Konfigurieren logaddress_add_http mit geheimem
Match-Schlüssel. Ein Parser wertet Rundenende, Seitenzuordnung, Matchstart
und Spielende aus; Stand, Rundenverlauf und Sieger werden pro Map gespeichert
und live verteilt. Der Demo-Adapter simuliert nach der Aufwärmphase einen
Matchverlauf über dieselben Logzeilen.
This commit is contained in:
mregosar committed 2026-10-25 18:54:23 +01:00
1 parent 34e5fc7d0e
commit 306b3aa7e5
11 files changed
+515 -11

No files matched your search

+56
View File
@@ -253,6 +253,62 @@ test('Organisator spielt im selben Browser selbst mit (zwei Zugänge)', async ()
}
});
test('Live-Ergebnisse aus Serverlogs und Demo-Simulation', async () => {
const { app, call, close } = await setup({ demo: { startSeconds: 0, failFirstConfigure: false, roundSeconds: 0 } });
try {
const { id, org, tokens } = await readyWingmanMatch(call, 'bo3');
await call('POST', `/matches/${id}/veto/start`, { token: org });
await playVeto(call, id, { A: tokens.a1, B: tokens.b1 });
await app.manager.poll();
await waitFor(() => app.service.getMatch(id).status === 'live', { label: 'live' });
const { config_key: key } = app.service.serverRow(id);
const map1 = app.service.seriesMaps(id)[0];
const base = `http://127.0.0.1:${app.server.address().port}/api/logs/${id}`;
const post = (k, text) => fetch(`${base}/${k}`, { method: 'POST', headers: { 'content-type': 'text/plain' }, body: text });
assert.equal((await post('falsch', 'x')).status, 403);
const ct = map1.ct_team === 'A' ? 'Alpha' : 'Bravo';
const t = map1.ct_team === 'A' ? 'Bravo' : 'Alpha';
const res = await post(key, [
`L 10/25/2026 - 18:00:00: MatchStatus: Team playing "CT": ${ct}`,
`L 10/25/2026 - 18:00:00: MatchStatus: Team playing "TERRORIST": ${t}`,
`L 10/25/2026 - 18:00:01: World triggered "Match_Start" on "${map1.map}"`,
'L 10/25/2026 - 18:01:00: Team "CT" triggered "SFUI_Notice_CTs_Win" (CT "1") (T "0")',
'L 10/25/2026 - 18:02:00: Team "TERRORIST" triggered "SFUI_Notice_Target_Bombed" (CT "1") (T "1")',
].join('\n'));
assert.equal(res.status, 200);
const snap = (await call('GET', `/matches/${id}`, { token: org })).body;
const ctTeam = map1.ct_team;
assert.equal(snap.series[0].score[ctTeam], 1);
assert.equal(snap.series[0].live.rounds.length, 2);
assert.equal(snap.series[0].live.rounds[1].reason, 'bomb');
await post(key, `Game Over: wingman mg_active ${map1.map} score 1:1 after 3 min`);
// Gleichstand beendet die Map ohne Sieger; Serienstand bleibt 0:0
const after = (await call('GET', `/matches/${id}`, { token: org })).body;
assert.equal(after.series[0].status, 'done');
assert.deepEqual(after.seriesScore, { A: 0, B: 0 });
// Map 2 laden und den Demo-Verlauf bis zum Ende laufen lassen
await call('POST', `/matches/${id}/series/next`, { token: org });
await waitFor(() => app.service.serverRow(id).status === 'configured' && app.service.getMatch(id).current_map_number === 2, { label: 'Map 2' });
assert.equal((await call('POST', `/matches/${id}/server/warmup-end`, { token: org })).status, 200);
await waitFor(() => app.service.seriesMaps(id)[1].status === 'done', { label: 'Demo-Map beendet', timeoutMs: 15000 });
const final = (await call('GET', `/matches/${id}`, { token: org })).body;
const winner = final.series[1].winner;
assert.ok(['A', 'B'].includes(winner));
assert.equal(final.series[1].score[winner], 9, 'Wingman: 9 Runden zum Sieg');
assert.equal(final.seriesScore[winner], 1);
const list = (await call('GET', '/matches')).body.matches.find((m) => m.id === id);
assert.equal(list.seriesScore[winner], 1);
} finally {
await close();
}
});
test('Spieler verlassen die Lobby, Captain geht über', async () => {
const { call, close } = await setup();
try {