Files
Cs2-Mapveto/server/src/servers/cs2Config.js
T
mregosar 52c07c88b8 CS2: sv_deltaticks_enforce 0 gegen NETWORK_DISCONNECT_BADDELTATICK
Mit dem Standard 2 trennt CS2 Spieler und CSTV, wenn deren Bestätigungen außer der Reihe kommen. Neue Container starten mit +sv_deltaticks_enforce 0, beim Einrichten jeder Map und auf Messer-Servern setzt die App den Wert zusätzlich per RCON.
2026-11-01 21:44:55 +01:00

83 lines
3.4 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',
// Keine Map-Abstimmung nach Spielende: die nächste Map legt das Veto fest
'mp_endmatch_votenextmap 0',
// Strenge Prüfung der Delta-Ticks (Standard 2) trennt Spieler und CSTV ohne echten Grund
'sv_deltaticks_enforce 0',
'mp_autoteambalance 0',
'mp_limitteams 0',
// Aufwärmphase läuft, bis der Organisator das Match startet (mp_warmup_end)
'mp_warmup_pausetimer 1',
// Turnierregeln (FACEIT/ESL-Richtwerte): Pausen erlauben, feste Rundenzeiten, Halbzeit
'sv_pausable 1',
'mp_halftime 1',
'mp_freezetime 20',
'mp_buytime 20',
'mp_startmoney 800',
'mp_roundtime 1.92',
...settings.extraCommands,
];
return { beforeMapChange, afterMapChange };
}