Beim Konfigurieren aktiviert CS2 Mapveto die Rundensicherung des Servers (mp_backup_round_auto, Präfix mit Match-ID). Im Admin-Modus lassen sich die Sicherungen der laufenden Map auflisten und zurückspielen. Der Live-Stand wird auf die Runden bis zur Sicherung gekürzt. Organisatoren haben keinen Zugang zu den Rollback-Routen. Sicherheit: Dateinamen werden gegen Match-ID und Muster geprüft, damit keine fremden Dateien oder Befehle in die RCON-Zeile gelangen.
62 lines
2.3 KiB
JavaScript
62 lines
2.3 KiB
JavaScript
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 – das ist kein Fehler.
|
||
if (!/geschlossen/.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 });
|
||
}
|