Frontend, README und Konfigurationsvorlage
React-Oberfläche mit Übersicht, Match-Erstellung, Beitritt über Team- und Zuschauercodes, Teamkarten, visuellem Map-Veto, Serie und Serverpanel. README beschreibt Einrichtung, Umgebungsvariablen, Rollen, Veto-Abläufe, Serveradapter und CSTV-Zuschauerzugang. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
104aa1a67b
commit
568ccf9b09
24 files changed
+1938
-1
No files matched your search
@@ -0,0 +1,327 @@
|
||||
# CS2 Mapveto
|
||||
|
||||
Webanwendung zur Organisation von Counter-Strike-2-Matches: Match-Lobbys mit Team-Einladungen, abwechselndes Map-Veto der Captains, Zuschauerzugang und automatische Verwaltung eines CS2-Servers pro Match.
|
||||
|
||||
- **Frontend:** React 18 + Vite, deutschsprachig, responsives dunkles Design
|
||||
- **Backend:** Node.js (≥ 18.18), Express, Socket.IO
|
||||
- **Datenbank:** SQLite (better-sqlite3), Datei unter `data/`
|
||||
- **Echtzeit:** Socket.IO; jede Änderung schickt allen Teilnehmern ihren rollenabhängigen Match-Zustand
|
||||
- **Serververwaltung:** austauschbare Adapter hinter einer Schnittstelle (`start`, `status`, `configure`, `stop`)
|
||||
|
||||
> **Standardmäßig läuft der Demo-Adapter.** Er startet **keine** echten CS2-Server. Oberfläche, Statusanzeigen und Verbindungsdaten sind dann überall als „Demo“ bzw. „simuliert“ gekennzeichnet. Wie die echte Anbindung eingerichtet wird, steht im Abschnitt [Echte Server anbinden](#echte-server-anbinden).
|
||||
|
||||
---
|
||||
|
||||
## Schnellstart
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp .env.example .env # optional, Standardwerte funktionieren für die Demo
|
||||
npm run dev # Backend auf :3000, Frontend auf :5173
|
||||
```
|
||||
|
||||
Dann <http://localhost:5173> öffnen.
|
||||
|
||||
**Produktion (ein Prozess, Backend liefert das gebaute Frontend aus):**
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
PUBLIC_URL=https://veto.example.de npm start # lauscht auf PORT (Standard 3000)
|
||||
```
|
||||
|
||||
**Tests:**
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
Die Tests decken Veto-Engine und Ablaufvalidierung, RCON-Protokoll (gegen einen lokalen Test-RCON-Server), Befehls-Bereinigung und den kompletten API-Ablauf ab. Dazu gehören Berechtigungen, doppelte Aktionen, Server-Konfiguration mit Wiederholung, nächste Map, automatisches Herunterfahren und dass keine RCON-Daten an Clients gelangen.
|
||||
|
||||
---
|
||||
|
||||
## Ablauf: von der Match-Erstellung bis zum Serverbeitritt
|
||||
|
||||
1. **Match erstellen** (`/neu`): Der Organisator trägt beide Teamnamen ein und wählt Spielmodus (Wingman 2 gegen 2 oder Competitive 5 gegen 5), Serie (Best of 1/3/5) und wer das Veto beginnt (Münzwurf oder festes Team). Formate, für die der Map-Pool keinen gültigen Veto-Ablauf hat, sind ausgegraut und werden auch vom Backend abgelehnt.
|
||||
Der Browser des Organisators erhält einen geheimen Organisator-Token. Er wird lokal gespeichert und lässt sich über „Organisator-Zugang auf einem anderen Gerät öffnen“ übertragen.
|
||||
2. **Server vorbereiten:** Direkt beim Anlegen der Lobby fordert das Backend im Hintergrund einen Server an. Er fährt also schon während Lobby und Veto hoch. Der Status ist auf der Match-Seite live sichtbar.
|
||||
3. **Einladungen:** Das Match hat drei Zugangscodes (Format `ABCD-EFGH`) mit Links:
|
||||
- Team A: `/join/<code>`
|
||||
- Team B: `/join/<code>`
|
||||
- Zuschauer: `/watch/<code>` für Stream und Kommentatoren
|
||||
|
||||
Diese Codes gelten nur für die Webseite. Der CS2-Serverzugang wird getrennt bereitgestellt (Schritt 6). Der Organisator kann jeden Code jederzeit neu erzeugen; der alte Link wird damit ungültig.
|
||||
4. **Spieler treten bei:** Über den Teamlink gibt ein Spieler seinen Namen ein (optional die SteamID64) und landet in genau diesem Team. Pro Team sind maximal 2 bzw. 5 Spieler erlaubt.
|
||||
- Der erste Spieler eines Teams wird Captain. Organisator oder Captain können den Captain wechseln.
|
||||
- Jeder Spieler setzt sich auf „bereit“. Ein Team ist bereit, wenn es vollständig ist (abschaltbar mit `REQUIRE_FULL_TEAMS=false`), einen Captain hat und alle bereit sind.
|
||||
5. **Map-Veto:** Sind beide Teams bereit, startet der Organisator das Veto. Die Captains bannen und wählen abwechselnd nach dem festen Ablauf (siehe unten). Die Seite zeigt jederzeit:
|
||||
- welches Team am Zug ist
|
||||
- welche Aktion ansteht
|
||||
- den Zustand jeder Map (verfügbar, gebannt, gewählt, Entscheidungsmap)
|
||||
- den gesamten Verlauf mit Uhrzeit
|
||||
|
||||
Die letzte verbleibende Map wird automatisch als Entscheidungsmap übernommen. Zuschauer und Organisator sehen alles live, können aber nicht wählen.
|
||||
6. **Server konfigurieren:** Nach dem Veto wird der bereits laufende Server automatisch eingerichtet: Spielmodus, erste Map, Teamnamen und Startseiten, Match-Einstellungen, Server- und CSTV-Passwort. Die restliche Map-Reihenfolge ist gespeichert.
|
||||
- Spieler sehen danach den kopierbaren Konsolenbefehl `password …; connect host:port` und einen `steam://connect`-Link.
|
||||
- Zuschauer sehen den CSTV-Zugang (eigener Port, eigenes Passwort, Verzögerung).
|
||||
7. **Während der Serie:** Der Organisator kann die Aufwärmphase beenden (`mp_warmup_end`), die nächste Map laden, den Server stoppen und das Match abschließen oder abbrechen.
|
||||
8. **Fehlerfälle:** Startet der Server nicht, schlägt die Konfiguration fehl oder fällt der Server aus, zeigt die Seite den Grund. Der Organisator bekommt einen Knopf für den erneuten Versuch: „Server neu starten“ bzw. „Konfiguration wiederholen“.
|
||||
|
||||
### Rollen und Berechtigungen
|
||||
|
||||
| Rolle | Zugang über | Darf |
|
||||
|---|---|---|
|
||||
| Organisator | Token beim Erstellen | Veto starten, Spieler entfernen, Captain setzen, Codes erneuern, Server neu starten/stoppen/konfigurieren, nächste Map, Match beenden; sieht Einladungen, Spieler- und CSTV-Verbindung |
|
||||
| Captain | Teamcode (erster Spieler oder ernannt) | alles wie Spieler, dazu Veto-Aktionen seines Teams und Captain-Übergabe |
|
||||
| Spieler | Teamcode | bereit melden, Team verlassen; sieht die Spieler-Verbindungsdaten nach dem Veto |
|
||||
| Zuschauer | Zuschauercode | alles live verfolgen; sieht nur den CSTV-Zugang, nicht das Spielerpasswort |
|
||||
| Gast | ohne Code | nur Teamnamen und Status |
|
||||
|
||||
Alle Prüfungen passieren im Backend. Die Oberfläche blendet nur zusätzlich aus, was die Rolle nicht darf.
|
||||
|
||||
### Schutzmaßnahmen
|
||||
|
||||
- **Fremde Teambeitritte:** Jeder Code gehört zu genau einem Team eines Matches. Zuschauercodes erlauben keinen Beitritt. Volle Teams, doppelte Namen und doppelte Steam-IDs werden abgelehnt. Codebasierte Endpunkte sind pro IP begrenzt (20 Versuche pro Minute).
|
||||
- **Doppelte Aktionen:** Jede Veto-Aktion trägt die Nummer des Schritts. Sie wird in einer SQLite-Transaktion geprüft und gespeichert; `(match_id, idx)` ist Primärschlüssel. Ein doppelter Klick oder ein veralteter Stand liefert „Diese Aktion ist veraltet“. Serveraktionen sind pro Match gesperrt, solange eine läuft.
|
||||
- **Unberechtigte Befehle:** Veto nur durch den Captain des Teams am Zug, Server- und Match-Befehle nur durch den Organisator.
|
||||
- **Tokens:** Organisator- und Spieler-Tokens sind 256 Bit zufällig und werden nur als SHA-256-Hash gespeichert.
|
||||
- **Server-Zugangsdaten:** RCON-Passwort, Container-Handle und GSLT liegen ausschließlich in der Datenbank bzw. den Umgebungsvariablen des Backends. Die Snapshot-Funktion gibt sie nie aus (per Test abgesichert). Server- und CSTV-Passwort sind als Beitrittsdaten nötig und werden nur an die jeweils berechtigte Rolle geliefert.
|
||||
- **Befehls-Injection:** Teamnamen werden vor der Übergabe an die Serverkonsole von Anführungszeichen, `;` und Steuerzeichen bereinigt. Passwörter sind rein alphanumerisch.
|
||||
|
||||
### Wiederherstellung nach Neuladen oder Verbindungsabbruch
|
||||
|
||||
Der gesamte Zustand liegt in SQLite. Der Browser speichert pro Match nur seinen Zugangstoken (`localStorage`).
|
||||
|
||||
- **Neuladen der Seite:** Die Seite lädt den Stand per REST und abonniert danach den Socket.
|
||||
- **Abgebrochene Socket-Verbindung:** Nach dem Reconnect meldet sich der Client automatisch neu an und erhält den vollständigen aktuellen Stand. Socket-Nachrichten enthalten immer den kompletten Zustand und keine Deltas, deshalb geht nichts verloren.
|
||||
- **Backend-Neustart:** Unterbrochene Serverstarts werden als fehlgeschlagen markiert (mit „Server neu starten“). Unterbrochene Konfigurationen werden automatisch wiederholt.
|
||||
|
||||
### Automatisches Herunterfahren
|
||||
|
||||
Alle `SERVER_REAPER_INTERVAL_SECONDS` prüft das Backend alle aktiven Server:
|
||||
|
||||
- Ein Match ist **abgeschlossen oder abgebrochen**: Server wird gestoppt.
|
||||
- **Lobby, Veto oder Server ohne Spielerzahl:** Ist niemand mit der Match-Seite verbunden und gab es länger als `SERVER_IDLE_TIMEOUT_MINUTES` keine Aktivität, wird der Server gestoppt.
|
||||
- **Konfigurierter echter Server:** Gestoppt wird, wenn länger als die Frist keine Spieler auf dem Server waren. Die Spielerzahl kommt per RCON aus `status`.
|
||||
- **Unabhängig davon:** Nach `SERVER_MAX_LIFETIME_HOURS` wird jeder Server gestoppt.
|
||||
|
||||
Ein gestoppter Server lässt sich vom Organisator neu starten.
|
||||
|
||||
---
|
||||
|
||||
## Map-Pools und Veto-Abläufe
|
||||
|
||||
Konfiguration in [`server/config/map-pools.json`](server/config/map-pools.json) (Pfad änderbar über `MAP_POOL_FILE`). Jeder Pool hat:
|
||||
|
||||
- eine eigene Map-Liste mit ID, Anzeigename und Kartenfarbe
|
||||
- Teamgröße und `game_type`/`game_mode`
|
||||
- je Format einen Ablauf
|
||||
|
||||
Schritte im Ablauf:
|
||||
|
||||
| Schritt | Bedeutung |
|
||||
|---|---|
|
||||
| `ban:X` / `ban:Y` | Team X bzw. Y bannt eine Map |
|
||||
| `pick:X` / `pick:Y` | Team X bzw. Y wählt eine Map für die Serie |
|
||||
| `side:X` / `side:Y` | Team wählt die Startseite (CT/T) auf der zuletzt gewählten Map bzw. der Entscheidungsmap |
|
||||
| `decider` | die letzte verbleibende Map wird automatisch übernommen |
|
||||
|
||||
**X** ist das Team, das das Veto beginnt (Münzwurf oder vom Organisator festgelegt), **Y** das andere.
|
||||
|
||||
Beim Start wird jeder Ablauf geprüft:
|
||||
|
||||
- Anzahl Picks = Maps der Serie − 1
|
||||
- Bans + Picks + 1 = Pool-Größe
|
||||
- genau eine Entscheidungsmap
|
||||
- Seitenwahl nur direkt nach einem Pick oder der Entscheidungsmap
|
||||
|
||||
Ungültige oder fehlende Abläufe machen nur dieses Format unauswählbar (mit Begründung in der Oberfläche).
|
||||
|
||||
### Mitgelieferte Abläufe
|
||||
|
||||
**Competitive (7 Maps):** Ancient, Anubis, Dust II, Inferno, Mirage, Nuke, Train
|
||||
|
||||
| Format | Ablauf |
|
||||
|---|---|
|
||||
| BO1 | X bannt, Y bannt, X bannt, Y bannt, X bannt, Y bannt → Entscheidungsmap, X wählt Seite |
|
||||
| BO3 | X bannt, Y bannt, X pickt (Y wählt Seite), Y pickt (X wählt Seite), X bannt, Y bannt → Entscheidungsmap, X wählt Seite |
|
||||
| BO5 | X bannt, Y bannt, X pickt (Y Seite), Y pickt (X Seite), X pickt (Y Seite), Y pickt (X Seite) → Entscheidungsmap, Y wählt Seite |
|
||||
|
||||
**Wingman (4 Maps):** Inferno, Nuke, Overpass, Vertigo
|
||||
|
||||
| Format | Ablauf |
|
||||
|---|---|
|
||||
| BO1 | X bannt, Y bannt, X bannt → Entscheidungsmap, Y wählt Seite |
|
||||
| BO3 | X bannt, Y pickt (X Seite), X pickt (Y Seite) → Entscheidungsmap, X wählt Seite |
|
||||
| BO5 | **nicht unterstützt:** Mit 4 Maps sind keine 5 Karten möglich. Mit einem Pool von mindestens 5 Maps und einem `bo5`-Ablauf wird es automatisch auswählbar. |
|
||||
|
||||
### Regel für Startseiten
|
||||
|
||||
- Auf einer **gewählten Map** wählt immer das **Gegnerteam des wählenden Teams** die Startseite.
|
||||
- Auf der **Entscheidungsmap** wählt das Team, das im Ablauf dafür vorgesehen ist. In BO1 ist das in Competitive Team X, weil Y den letzten Ban hatte, und in Wingman Team Y. Ohne `side`-Schritt werden die Seiten im Spiel festgelegt.
|
||||
|
||||
Die Wahl wird als `mp_teamname_1` (Team mit CT-Start) und `mp_teamname_2` (T-Start) an den Server übergeben.
|
||||
|
||||
> Die mitgelieferten Map-Pools entsprechen dem Stand der Entwicklung. Valve ändert den aktiven Map-Pool regelmäßig. Map-IDs bitte gegen die eigene Serverinstallation prüfen. Eine Map, die nicht existiert, führt zu „Konfiguration fehlgeschlagen“ mit Meldung.
|
||||
|
||||
---
|
||||
|
||||
## Echte Server anbinden
|
||||
|
||||
Die Serververwaltung ist hinter [`server/src/servers/adapter.js`](server/src/servers/adapter.js) gekapselt. Auswahl über `SERVER_ADAPTER`.
|
||||
|
||||
### Ablauf, den jeder Adapter bedient
|
||||
|
||||
1. **`start({ matchId, mode, secrets })`**: Server starten bzw. reservieren.
|
||||
- Rückgabe: `{ handle, host, port, tvPort }`.
|
||||
- `secrets` (RCON-, Server- und CSTV-Passwort) erzeugt das Backend pro Match.
|
||||
2. **`status(handle)`**: liefert `starting | running | stopped | failed`, optional mit Spielerzahl.
|
||||
- Wird alle `SERVER_STATUS_POLL_SECONDS` abgefragt, solange der Server startet, danach alle 30 s.
|
||||
3. **`configure(handle, plan)`**: in zwei Phasen.
|
||||
1. `game_type`, `game_mode`, `sv_password`, `tv_enable`, `tv_delay`, `tv_password`, dann `changelevel <map>`.
|
||||
2. Warten, bis `status` die Map meldet (max. 2 Minuten), dann die Match-Einstellungen (`mp_teamname_1/2`, `mp_maxrounds`, `mp_overtime_enable`, `mp_match_can_clinch`, `mp_warmup_pausetimer 1`, `CS2_EXTRA_COMMANDS`).
|
||||
|
||||
Die zweite Phase kommt erst nach dem Mapwechsel, weil die `gamemode_*.cfg` des Spiels diese Werte beim Laden der Map sonst überschreibt.
|
||||
4. **`stop(handle)`**: Server beenden bzw. freigeben.
|
||||
|
||||
### Option A: Docker (`SERVER_ADAPTER=docker`)
|
||||
|
||||
Startet pro Match einen Container mit dem Community-Image [`cm2network/cs2`](https://github.com/CM2Walki/CS2) (SteamCMD, CS2-Dedicated-Server App 730).
|
||||
|
||||
Voraussetzungen:
|
||||
|
||||
1. Docker auf dem Backend-Host. Das Backend ruft die Docker-CLI auf und braucht dafür Zugriff auf den Docker-Daemon.
|
||||
2. **Game Server Login Token** für App-ID 730 unter <https://steamcommunity.com/dev/managegameservers> erstellen und als `CS2_GSLT` eintragen. Ohne GSLT sind Server nicht öffentlich erreichbar. Pro gleichzeitig laufendem Server ist ein eigenes Token empfehlenswert.
|
||||
3. `CS2_PUBLIC_HOST`: IP oder DNS-Name, unter dem Spieler den Host erreichen.
|
||||
4. Ports `CS2_PORT_RANGE_START`–`CS2_PORT_RANGE_END` in Firewall bzw. Router freigeben:
|
||||
- pro Match zwei aufeinanderfolgende Ports
|
||||
- Spielport per UDP und TCP (TCP wird für RCON genutzt, nur für das Backend freigeben)
|
||||
- CSTV-Port per UDP
|
||||
5. Etwa 60 GB Speicher für die Spieldateien im Volume `CS2_DOCKER_VOLUME`. Das Volume wird von allen Match-Containern geteilt. Der erste Start lädt das Spiel herunter und kann deutlich länger als 10 Minuten dauern (`CS2_START_TIMEOUT_MINUTES`). Spieldateien am besten vorab mit einem einzelnen Container laden, bevor mehrere Matches gleichzeitig starten.
|
||||
|
||||
Container heißen `cs2mv-<matchId>` und tragen das Label `cs2-mapveto=1`. Stoppen bedeutet `docker rm -f`.
|
||||
|
||||
### Option B: Vorhandene Server (`SERVER_ADAPTER=static`)
|
||||
|
||||
Für selbst betriebene, dauerhaft laufende CS2-Server:
|
||||
|
||||
```env
|
||||
SERVER_ADAPTER=static
|
||||
CS2_STATIC_SERVERS=203.0.113.10:27015:rconPasswort1:27020,203.0.113.10:27025:rconPasswort2:27030
|
||||
```
|
||||
|
||||
Auf jedem Server müssen gesetzt sein:
|
||||
|
||||
- `rcon_password`
|
||||
- CSTV-Port `tv_port`, falls abweichend von Spielport + 5 (dieser Wert wird angenommen, wenn kein vierter Teil angegeben ist)
|
||||
- ein GSLT (`sv_setsteamaccount`)
|
||||
|
||||
Bei diesem Adapter bedeutet „Start“: einen freien Server reservieren und per RCON prüfen. „Stopp“ gibt ihn frei und setzt ein neues zufälliges `sv_password`. Der Serverprozess selbst wird nicht beendet.
|
||||
|
||||
### Eigener Adapter (z. B. Hosting-API)
|
||||
|
||||
1. Klasse mit `name`, `simulated = false` und den vier Methoden anlegen. Vorlage: `dockerAdapter.js`. Für die Konfiguration per RCON gibt es `configureViaRcon` aus `rconConfigure.js`.
|
||||
2. In `createServerAdapter` in `adapter.js` registrieren.
|
||||
3. Zugangsdaten der Hosting-API ausschließlich über Umgebungsvariablen in `config.js` einlesen.
|
||||
|
||||
### Verbindung für Spieler
|
||||
|
||||
```
|
||||
password <serverpasswort>; connect <host>:<port>
|
||||
```
|
||||
|
||||
Zusätzlich bietet die Seite einen `steam://connect/<host>:<port>/<passwort>`-Link an.
|
||||
|
||||
### Zuschauer: CSTV (früher GOTV)
|
||||
|
||||
CS2 bringt **CSTV** mit (in der Konsole weiterhin `tv_*`-Befehle; früher GOTV). Die Anwendung setzt:
|
||||
|
||||
- `tv_enable 1` (greift erst nach dem Mapwechsel, deshalb vor `changelevel`)
|
||||
- `tv_delay` (Standard 90 s) als Verzögerung gegen Ghosting
|
||||
- `tv_password` als eigenes Passwort, getrennt vom Spielerpasswort
|
||||
|
||||
Zuschauer verbinden sich mit `password <tvpasswort>; connect <host>:<tvPort>`. Sie belegen keinen Spielerplatz und können die Spieler nicht beeinflussen. Für größere Zuschauerzahlen unterstützt CS2 CSTV-Relays (`tv_relay`) und HTTP-Broadcast (`tv_broadcast_url`). Beides ist nicht eingebaut und kann über `CS2_EXTRA_COMMANDS` bzw. die Serverkonfiguration ergänzt werden.
|
||||
|
||||
### Prüfstand der CS2-Angaben
|
||||
|
||||
Die offizielle Seite [Counter-Strike 2/Dedicated Servers](https://developer.valvesoftware.com/wiki/Counter-Strike_2/Dedicated_Servers) im Valve Developer Wiki war während der Entwicklung wegen eines Bot-Schutzes nicht automatisiert abrufbar. Grundlage waren:
|
||||
|
||||
- das [Source-RCON-Protokoll](https://developer.valvesoftware.com/wiki/Source_RCON_Protocol)
|
||||
- die Dokumentation des Images [cm2network/cs2](https://github.com/CM2Walki/CS2): Umgebungsvariablen, Ports, `TV_*`
|
||||
- Hoster-Anleitungen zu CSTV, z. B. [ZAP-Hosting](https://zap-hosting.com/guides/de/docs/cs2-cstv)
|
||||
- [FACEIT zu CSTV-Zuschauern](https://support.faceit.com/hc/en-us/articles/17050697184668)
|
||||
|
||||
Bekannte Werte: `game_type 0` + `game_mode 1` = Competitive, `game_mode 2` = Wingman; CSTV-Standardport 27020.
|
||||
|
||||
**Nicht gegen einen echten CS2-Server getestet:** Docker- und Static-Adapter sowie die Auswertung der `status`-Ausgabe (Map-Name, Spielerzahl). Das RCON-Protokoll ist gegen einen Test-Server geprüft. Vor dem ersten echten Einsatz bitte ein Testmatch durchspielen und bei Bedarf `parseStatus` in `server/src/servers/rcon.js` an die tatsächliche Ausgabe anpassen.
|
||||
|
||||
### Bekannte Grenzen von Vanilla-CS2
|
||||
|
||||
- Ohne Server-Plugin kann CS2 Spieler **nicht automatisch einem Team zuordnen**. Spieler wählen ihr Team im Spiel selbst. Teamnamen und Startseiten setzt die Anwendung. Für feste Teamzuordnung, Pausen und automatischen Mapwechsel eignen sich Plugins wie MatchZy oder Get5-Nachfolger. Sie lassen sich über einen eigenen Adapter anbinden; die SteamID64 der Spieler wird dafür bereits erfasst.
|
||||
- Ergebnisse werden nicht ausgelesen. Der Organisator lädt die nächste Map manuell.
|
||||
|
||||
---
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
Alle Variablen mit Standardwerten stehen in [`.env.example`](.env.example). Die wichtigsten:
|
||||
|
||||
| Variable | Standard | Zweck |
|
||||
|---|---|---|
|
||||
| `PORT` | `3000` | Port des Backends |
|
||||
| `PUBLIC_URL` | `http://localhost:5173` | Basis für Einladungslinks |
|
||||
| `DATABASE_FILE` | `data/cs2-mapveto.db` | SQLite-Datei |
|
||||
| `CREATE_MATCH_PASSWORD` | leer | Passwort zum Erstellen von Matches |
|
||||
| `REQUIRE_FULL_TEAMS` | `true` | Veto nur mit vollständigen Teams |
|
||||
| `SERVER_ADAPTER` | `demo` | `demo`, `docker` oder `static` |
|
||||
| `SERVER_IDLE_TIMEOUT_MINUTES` | `30` | Frist bis zum automatischen Herunterfahren |
|
||||
| `SERVER_MAX_LIFETIME_HOURS` | `8` | harte Obergrenze pro Server |
|
||||
| `CS2_TV_DELAY` | `90` | CSTV-Verzögerung in Sekunden |
|
||||
| `CS2_GSLT` | – | **Pflicht bei Docker:** Game Server Login Token |
|
||||
| `CS2_PUBLIC_HOST` | – | **Pflicht bei Docker:** öffentliche Adresse |
|
||||
| `CS2_STATIC_SERVERS` | – | **Pflicht bei Static:** Serverliste |
|
||||
| `DEMO_FAIL_FIRST_CONFIGURE` | `false` | Demo: Konfigurationsfehler simulieren |
|
||||
|
||||
Benötigte Zugangsdaten für den Echtbetrieb:
|
||||
|
||||
- **Docker:** GSLT, Zugriff auf den Docker-Daemon, öffentliche IP bzw. Portfreigaben
|
||||
- **Static:** RCON-Passwörter der Server
|
||||
|
||||
RCON-Passwörter für Docker-Container erzeugt das Backend selbst.
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
client/ React-Frontend (Vite)
|
||||
src/pages/ Übersicht, Match erstellen, Match-Seite, Beitritt
|
||||
src/components/ Teamkarten, Veto-Board, Serverpanel, Einladungen, Verlauf
|
||||
src/lib/ API, Socket-Hook, lokale Zugangsdaten
|
||||
server/
|
||||
config/map-pools.json Map-Pools und Veto-Abläufe
|
||||
src/veto.js reine Veto-Logik und Ablaufvalidierung
|
||||
src/matchService.js Lobby, Rollen, Veto, rollenabhängige Snapshots
|
||||
src/servers/ Adapter-Schnittstelle, Demo/Docker/Static, RCON, ServerManager
|
||||
src/http.js REST-API
|
||||
src/realtime.js Socket.IO
|
||||
test/ node:test-Tests
|
||||
```
|
||||
|
||||
### REST-API (Auszug)
|
||||
|
||||
Alle Aktionen laufen per `POST` mit `Authorization: Bearer <token>`. Der Token ist der Organisator- oder Spieler-Token bzw. der Zuschauercode.
|
||||
|
||||
| Endpunkt | Wer |
|
||||
|---|---|
|
||||
| `GET /api/config`, `GET /api/matches` | alle |
|
||||
| `POST /api/matches` | alle (ggf. mit `CREATE_MATCH_PASSWORD`) |
|
||||
| `POST /api/access` `{code}` | prüft einen Code |
|
||||
| `GET /api/matches/:id` | rollenabhängiger Zustand |
|
||||
| `POST /api/matches/:id/join` `{code, nickname, steamId?}` | mit Teamcode |
|
||||
| `…/ready`, `…/leave` | Spieler |
|
||||
| `…/veto/action` `{stepIndex, map?, side?}` | Captain am Zug |
|
||||
| `…/captain` `{playerId}` | Organisator, Captain des Teams |
|
||||
| `…/veto/start`, `…/kick`, `…/codes/regenerate`, `…/server/retry`, `…/server/stop`, `…/server/warmup-end`, `…/series/next`, `…/finish`, `…/cancel` | Organisator |
|
||||
|
||||
Socket.IO: `match:subscribe {matchId, token}` liefert `match:state` bei jeder Änderung; `overview:subscribe` liefert `overview`.
|
||||
Reference in new issue
Block a user