Backend: Match-Lobbys, Map-Veto, Echtzeit und CS2-Serveradapter

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>
This commit is contained in:
SPP UserandClaude Opus 5.5 committed 2026-10-25 16:50:22 +01:00
commit 104aa1a67b
27 files changed
+6355

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
import { parseStatus, runRcon } from './rcon.js';
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
/**
* Gemeinsamer Konfigurationsablauf für echte Server per RCON:
* Befehle vor dem Mapwechsel senden, warten bis die Map geladen ist, dann Match-Einstellungen setzen.
*/
export async function configureViaRcon(target, plan, { loadTimeoutMs = 120_000, pollMs = 3000 } = {}) {
try {
await runRcon(target, plan.beforeMapChange);
} catch (err) {
// Beim changelevel trennt der Server die RCON-Verbindung teilweise – das ist kein Fehler.
if (!/geschlossen/.test(err.message)) throw new Error(`Befehle konnten nicht gesendet werden: ${err.message}`);
}
const deadline = Date.now() + loadTimeoutMs;
let lastError = null;
for (;;) {
await sleep(pollMs);
try {
const [status] = await runRcon(target, ['status']);
const parsed = parseStatus(status);
if (parsed.map === plan.map || (!parsed.map && status.includes(plan.map))) break;
lastError = `aktuell geladen: ${parsed.map ?? 'unbekannt'}`;
} catch (err) {
lastError = err.message;
}
if (Date.now() > deadline) {
throw new Error(`Map ${plan.map} wurde nicht rechtzeitig geladen (${lastError ?? 'keine Antwort'})`);
}
}
try {
await runRcon(target, plan.afterMapChange);
} catch (err) {
throw new Error(`Match-Einstellungen konnten nicht gesetzt werden: ${err.message}`);
}
}
export async function rconStatus(target) {
const [output] = await runRcon(target, ['status'], { timeoutMs: 4000 });
return parseStatus(output);
}