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>
65 lines
2.2 KiB
JavaScript
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(() => {});
|
|
}
|
|
}
|