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.
This commit is contained in:
1 parent
43ecb8a1ad
commit
3bfe087b7d
13 files changed
+327
-5
No files matched your search
@@ -0,0 +1,54 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { backupPrefix, describeBackup, isValidBackupFile, parseBackupFiles, pointsForMap, trimLiveState } from '../src/rounds.js';
|
||||
|
||||
const id = 'Ab_9-xY';
|
||||
const file = (map, round, s1, s2) => `${backupPrefix(id)}_2026-10-25_18-30-00_${map}_round${round}_score_${s1}_${s2}.txt`;
|
||||
|
||||
test('Gültige Sicherungsdateien nur für dieses Match', () => {
|
||||
assert.equal(isValidBackupFile(file('de_inferno', 3, 2, 1), id), true);
|
||||
assert.equal(isValidBackupFile(file('de_inferno', 3, 2, 1), 'anderes'), false, 'fremdes Match');
|
||||
assert.equal(isValidBackupFile(`${backupPrefix(id)}_../../etc/passwd.txt`, id), false, 'Pfad');
|
||||
assert.equal(isValidBackupFile(`${backupPrefix(id)}_x;quit_round1_score_0_0.txt`, id), false, 'Befehl');
|
||||
assert.equal(isValidBackupFile(`${backupPrefix(id)}_de_inferno.txt`, id), false, 'ohne Runde');
|
||||
assert.equal(isValidBackupFile(undefined, id), false);
|
||||
});
|
||||
|
||||
test('Liste aus einer RCON-Antwort auslesen, fremde Dateien ignorieren', () => {
|
||||
const text = [
|
||||
'Backups:',
|
||||
file('de_inferno', 1, 1, 0),
|
||||
file('de_inferno', 2, 1, 1),
|
||||
'round_backup_fremd_2026_de_nuke_round4_score_3_1.txt',
|
||||
file('de_nuke', 1, 1, 0),
|
||||
].join('\n');
|
||||
const found = parseBackupFiles(text, id);
|
||||
assert.equal(found.length, 3);
|
||||
assert.deepEqual(found[0], { file: file('de_inferno', 1, 1, 0), map: 'de_inferno', round: 1, score: { s1: 1, s2: 0 } });
|
||||
});
|
||||
|
||||
test('Nur die Map der laufenden Serie, neueste Runde zuerst', () => {
|
||||
const all = parseBackupFiles([file('de_inferno', 1, 1, 0), file('de_inferno', 3, 2, 1), file('de_nuke', 2, 1, 1)].join('\n'), id);
|
||||
const points = pointsForMap(all, 'de_inferno');
|
||||
assert.deepEqual(points.map((p) => p.round), [3, 1]);
|
||||
});
|
||||
|
||||
test('Stand nach Rollback: Runden bis zur Sicherung, Punkte neu gerechnet', () => {
|
||||
const state = {
|
||||
map: 'de_inferno', status: 'live', warmupEnded: true, score: { A: 3, B: 2 }, winner: 'A', pendingRound: null,
|
||||
rounds: [
|
||||
{ number: 1, winner: 'A' }, { number: 2, winner: 'B' }, { number: 3, winner: 'A' },
|
||||
{ number: 4, winner: 'B' }, { number: 5, winner: 'A' },
|
||||
],
|
||||
};
|
||||
const back = trimLiveState(state, 3);
|
||||
assert.deepEqual(back.rounds.map((r) => r.number), [1, 2, 3]);
|
||||
assert.deepEqual(back.score, { A: 2, B: 1 });
|
||||
assert.equal(back.winner, null);
|
||||
assert.equal(back.status, 'live');
|
||||
assert.equal(back.warmupEnded, true, 'Zustand bleibt erhalten');
|
||||
});
|
||||
|
||||
test('Beschreibung eines Dateinamens', () => {
|
||||
assert.deepEqual(describeBackup('nichts.txt'), { file: 'nichts.txt', map: null, round: null, score: null });
|
||||
});
|
||||
Reference in new issue
Block a user