Files
Cs2-Mapveto/server/src/servers/rconConfigure.js
T
mregosar e18b8b4430 Absicherung: Socket-Eingaben, Geheimnisse in Fehlern, Logparser, Start/Stopp, RCON, Stream-Zugang
- Socket-Nachrichten werden geprüft, Handlerfehler abgefangen, Abfragen pro Verbindung begrenzt
  (vorher beendete stream:subscribe mit null ohne Anmeldung das Backend).
- Fehlermeldungen von Prozessen ohne Befehlszeile und mit geschwärzten Passwörtern/Tokens.
- Logparser wertet nur ganze Systemzeilen nach dem Zeitstempel aus; Chat kann keine Map beenden.
- Verspätet ankommende Starts nach Stopp, Neuanforderung oder Löschen bauen ihren Container ab.
- RCON: kaputte Pakete trennen nur die Verbindung, Befehle ohne Antwort gelten als Fehler.
- Organisatoren bekommen einen Stream-Schlüssel nur für ihr Match statt des globalen STREAM_KEY.
- Rate-Limiter mit regelmäßiger Bereinigung und Obergrenze, alte Admin-Sitzungen werden entfernt.
2026-11-01 20:25:45 +01:00

63 lines
2.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { parseBackupFiles } from '../rounds.js';
import { parseStatus, runRcon } from './rcon.js';
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
/**
* Gemeinsamer Konfigurationsablauf für echte Server per RCON:
* Befehle vor dem Mapwechsel senden, warten bis die Map geladen ist, dann Match-Einstellungen setzen.
*/
export async function configureViaRcon(target, plan, { loadTimeoutMs = 120_000, pollMs = 3000 } = {}) {
try {
await runRcon(target, plan.beforeMapChange);
} catch (err) {
// Beim changelevel trennt der Server die RCON-Verbindung teilweise oder antwortet erst nach dem Laden –
// beides ist kein Fehler, ob die Map lädt, prüft die Schleife unten.
if (!/geschlossen|keine Antwort auf "changelevel"/.test(err.message)) throw new Error(`Befehle konnten nicht gesendet werden: ${err.message}`);
}
const deadline = Date.now() + loadTimeoutMs;
let lastError = null;
for (;;) {
await sleep(pollMs);
try {
const [status] = await runRcon(target, ['status']);
const parsed = parseStatus(status);
if (parsed.map === plan.map || (!parsed.map && status.includes(plan.map))) break;
lastError = `aktuell geladen: ${parsed.map ?? 'unbekannt'}`;
} catch (err) {
lastError = err.message;
}
if (Date.now() > deadline) {
throw new Error(`Map ${plan.map} wurde nicht rechtzeitig geladen (${lastError ?? 'keine Antwort'})`);
}
}
try {
await runRcon(target, plan.afterMapChange);
} catch (err) {
throw new Error(`Match-Einstellungen konnten nicht gesetzt werden: ${err.message}`);
}
}
export async function rconStatus(target) {
const [output] = await runRcon(target, ['status'], { timeoutMs: 4000 });
return parseStatus(output);
}
/** Sicherungsdateien dieses Matches vom Server holen. */
export async function listRoundBackups(target, matchId) {
const [out] = await runRcon(target, ['mp_backup_restore_list_files 60'], { timeoutMs: 8000, quietMs: 500 });
return parseBackupFiles(out, matchId);
}
/** Sicherung laden. Der Dateiname muss vorher mit isValidBackupFile geprüft sein. */
export async function loadRoundBackup(target, file) {
// mp_backup_restore_load_autopause wirkt in CS2 nicht zuverlässig, deshalb pausieren wir ausdrücklich
await runRcon(target, [
'mp_backup_restore_load_autopause 1',
`mp_backup_restore_load_file "${file}"`,
'mp_pause_match',
], { timeoutMs: 15000, quietMs: 800 });
}