Files
Cs2-Mapveto/server/src/servers/cs2Config.js
T
SPP UserandClaude Haiku 5.5 a4218c47d1 Rollback: Runden aus CS2-Sicherungen zurückspielen (nur Admin)
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.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-25 23:24:49 +01:00

72 lines
3.0 KiB
JavaScript

// Erzeugt die Konsolenbefehle, mit denen ein laufender CS2-Server für eine Map der Serie konfiguriert wird.
// Alle Werte werden so bereinigt, dass sie keine weiteren Befehle einschleusen können.
import { BACKUP_PATTERN } from '../rounds.js';
export function sanitizeConsoleText(value, maxLength = 32) {
return String(value ?? '')
.replace(/["';\\\r\n\t]/g, '')
.replace(/[^\p{L}\p{N} ._\-+#!?()[\]|]/gu, '')
.trim()
.slice(0, maxLength) || 'Team';
}
/**
* Liefert zwei Befehlsgruppen:
* - beforeMapChange: Spielmodus, Passwörter, CSTV und der Mapwechsel selbst.
* game_type/game_mode und tv_enable greifen erst beim Laden einer Map.
* - afterMapChange: Match-Einstellungen. Diese werden erst nach dem Laden gesetzt,
* weil die gamemode_*.cfg des Spiels sie beim Mapwechsel sonst überschreibt.
*
* @param {object} p
* @param {object} p.match Match-Datensatz (Teamnamen)
* @param {object} p.pool Map-Pool (mode/gameType/gameMode)
* @param {object} p.seriesMap { map, ctTeam: 'A'|'B'|null }
* @param {object} p.secrets { serverPassword, tvPassword }
* @param {object} p.settings config.match
*/
export function buildMapCommands({ match, pool, seriesMap, secrets, settings, logUrl, backupPrefix }) {
const isWingman = pool.mode === 'wingman';
const ctTeam = seriesMap.ctTeam ?? 'A';
const tTeam = ctTeam === 'A' ? 'B' : 'A';
const names = { A: sanitizeConsoleText(match.team_a_name), B: sanitizeConsoleText(match.team_b_name) };
const beforeMapChange = [
`game_type ${Number(pool.gameType)}`,
`game_mode ${Number(pool.gameMode)}`,
`sv_password "${secrets.serverPassword}"`,
`tv_enable ${settings.tvEnabled ? 1 : 0}`,
`tv_delay ${Number(settings.tvDelaySeconds)}`,
`tv_password "${secrets.tvPassword}"`,
// Live-Ergebnisse: Server schickt seine Logzeilen per HTTP an das Backend
...(logUrl && /^https?:\/\/[\w.:/-]+$/.test(logUrl)
? ['log on', 'logaddress_delall_http', `logaddress_add_http "${logUrl}"`]
: []),
// Rundensicherung: CS2 speichert nach jeder Runde den Spielzustand, damit sich Runden zurückspielen lassen
...(backupPrefix ? [
`mp_backup_round_file "${backupPrefix}"`,
`mp_backup_round_file_pattern "${BACKUP_PATTERN}"`,
'mp_backup_round_auto 1',
'mp_backup_restore_load_autopause 1',
] : []),
`changelevel ${seriesMap.map}`,
];
const afterMapChange = [
// mp_teamname_1 = Team, das auf CT startet; mp_teamname_2 = Team, das auf T startet
`mp_teamname_1 "${names[ctTeam]}"`,
`mp_teamname_2 "${names[tTeam]}"`,
`mp_maxrounds ${isWingman ? settings.maxRoundsWingman : settings.maxRoundsCompetitive}`,
`mp_overtime_enable ${settings.overtime ? 1 : 0}`,
'mp_match_can_clinch 1',
'mp_autoteambalance 0',
'mp_limitteams 0',
// Aufwärmphase läuft, bis der Organisator das Match startet (mp_warmup_end)
'mp_warmup_pausetimer 1',
...settings.extraCommands,
];
return { beforeMapChange, afterMapChange };
}