Express + Socket.IO + SQLite. Veto-Engine mit konfigurierbaren Map-Pools und validierten Abläufen, rollenbasierte Berechtigungen, Server-Lebenszyklus mit Demo-, Docker- und Static-Pool-Adapter (RCON), automatisches Herunterfahren. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57 lines
1.9 KiB
JavaScript
57 lines
1.9 KiB
JavaScript
import { configureViaRcon, 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 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(() => {});
|
|
}
|
|
}
|