Files
Cs2-Mapveto/server/src/servers/staticPoolAdapter.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

65 lines
2.2 KiB
JavaScript

import { configureViaRcon, listRoundBackups, loadRoundBackup, rconStatus } from './rconConfigure.js';
import { runRcon } from './rcon.js';
/**
* Nutzt bereits laufende, selbst betriebene CS2-Server (CS2_STATIC_SERVERS).
* "Start" bedeutet hier: einen freien Server aus dem Pool reservieren und per RCON prüfen.
* "Stopp" gibt ihn wieder frei und setzt ein zufälliges Serverpasswort, damit niemand
* mit alten Zugangsdaten auf dem Server bleibt. Der Prozess selbst wird nicht beendet.
*/
export class StaticPoolServerAdapter {
constructor({ servers }, { getUsedPorts }) {
this.name = 'static';
this.simulated = false;
this.servers = servers;
this.getUsedPorts = getUsedPorts;
if (!servers.length) throw new Error('SERVER_ADAPTER=static benötigt CS2_STATIC_SERVERS');
}
async start() {
const used = this.getUsedPorts();
const free = this.servers.find((s) => !used.has(`${s.host}:${s.port}`));
if (!free) throw new Error('Alle konfigurierten Server sind belegt');
return {
handle: { host: free.host, port: free.port, poolKey: `${free.host}:${free.port}`, rconPassword: free.rconPassword },
host: free.host,
port: free.port,
tvPort: free.tvPort,
};
}
target(handle) {
return { host: handle.host, port: handle.port, password: handle.rconPassword };
}
async status(handle) {
try {
const status = await rconStatus(this.target(handle));
return { state: 'running', playerCount: status.playerCount, map: status.map };
} catch (err) {
return { state: 'starting', message: `Server nicht erreichbar: ${err.message}` };
}
}
async configure(handle, plan) {
await configureViaRcon(this.target(handle), plan);
}
async listRoundBackups(handle, matchId) {
return listRoundBackups(this.target(handle), matchId);
}
async loadRoundBackup(handle, file) {
await loadRoundBackup(this.target(handle), file);
}
async sendCommands(handle, commands) {
return runRcon(this.target(handle), commands);
}
async stop(handle, { resetPassword } = {}) {
if (!resetPassword) return;
await runRcon(this.target(handle), [`sv_password "${resetPassword}"`, 'tv_stoprecord']).catch(() => {});
}
}