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 }); }