No description
  • Elixir 59.9%
  • Svelte 25.1%
  • TypeScript 10.8%
  • Shell 2.2%
  • Dockerfile 1%
  • Other 1%
Find a file
Karsten Bonhuis 1c97aa6dbc Sicherheit: Benutzernamen einheitlich bereinigen
Players.sanitize_display_name/1 als zentrale Funktion:
- Steuerzeichen (x00–x1f, x7f) entfernen
- Whitespace trimmen
- Auf 50 Zeichen begrenzen (String.length, nicht byte_size)
- Fallback auf "Spieler"

Angewandt an allen Eingabe-Stellen:
- GameController: do_create, do_create_vs_bot, join
- GameChannel: join (display_name → GameServer)
- DashboardChannel: find_match (validate_display_name)

Private Kopie in GameController entfernt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 23:15:11 +02:00
client Layout: Repo-Link unauffälliger gestaltet 2026-04-08 23:05:18 +02:00
protocol Wartungsmodus: Banner und Buttons auf Startseite deaktivieren 2026-03-26 14:04:23 +01:00
server Sicherheit: Benutzernamen einheitlich bereinigen 2026-04-08 23:15:11 +02:00
.gitignore container_start.sh: Setup-Script mit automatischer Secret-Verwaltung 2026-03-26 16:35:33 +01:00
CLAUDE.md Docs: CLAUDE.md und README.md aktualisiert 2026-04-08 23:08:04 +02:00
container_start.sh container_start.sh: PLAYER_TOKEN_SECRET in bestehende .battleship-env nachrüsten 2026-04-08 18:48:39 +02:00
dev.sh Migrations-Support für dev.sh, Dockerfile und README 2026-03-26 01:35:44 +01:00
Dockerfile Dockerfile: Runtime auf elixir:1.19-alpine umgestellt (OpenSSL-Fix) 2026-03-26 16:22:35 +01:00
README.md Docs: CLAUDE.md und README.md aktualisiert 2026-04-08 23:08:04 +02:00

Schiffe versenken

Multiplayer-Browserspiel auf Basis von Elixir/Phoenix und Svelte 5.

Architektur

battleship/
├── server/      # Elixir/Phoenix  Spiellogik, WebSocket-API
├── client/      # Svelte 5 SPA  Browser-Client
├── protocol/    # Kanonische API-Typen (TypeScript) + OpenAPI-Spec
├── Dockerfile         # Multi-stage Build (node → elixir → alpine)
└── container_start.sh # Build + Start mit automatischer Secret-Verwaltung

Der Server ist die authoritative Wahrheit für jeden Spielzustand Schummeln ist nicht möglich. Kommunikation läuft ausschließlich über Phoenix Channels (WebSockets). SSL wird extern terminiert.

Spielfluss

Gegen einen anderen Spieler:

  1. Spieler A erstellt ein Spiel → erhält einen Join-Code (z. B. A3F1-9C2E)
  2. Spieler A teilt den Code oder die URL mit Spieler B
  3. Beide platzieren ihre Schiffe gleichzeitig
  4. Rundenbasiertes Schießen bei Treffer darf nochmal geschossen werden
  5. Wer zuerst alle gegnerischen Schiffe versenkt, gewinnt

Gegen den Computer: Wie oben, aber Spieler B ist Admiral Silizium ein serverseitiger Bot mit Hunt/Target-Strategie: zufälliges Schießen, nach Treffer gezielte Nachbarfelder bis zum Versenken. Der Admiral tritt als eigener Spieler in der Rangliste an.

Voraussetzungen

Tool Version
Elixir ≥ 1.17
Erlang/OTP ≥ 27
Node.js ≥ 22
Docker (Linux) oder Apple container (macOS) beliebig

Lokale Entwicklung

# Beides zusammen starten (empfohlen)
./dev.sh

Oder getrennt:

# Server einrichten und starten (Port 4000)
cd server
mix setup          # deps.get + Datenbank anlegen + Migrationen (einmalig)
mix phx.server

# Client starten (Port 5173)  in einem zweiten Terminal
cd client
npm install
npm run dev

Der Client ist dann unter http://localhost:5173 erreichbar. Der Vite-Dev-Server leitet /api, /socket und /dashboard_socket automatisch an Phoenix weiter.

Tests & Qualität

cd server
mix test           # alle Tests
mix precommit      # compile --warnings-as-errors + format + test (vor jedem Commit)

Admin-Oberfläche

Erreichbar unter /admin mit dem ADMIN_TOKEN als Passwort:

  • Wartungsmodus ein-/ausschalten (sperrt neue Spiele)
  • Spielerliste mit Statistiken einzelne Spieler löschbar
  • Statistiken zurücksetzen (Spieler-Accounts bleiben erhalten)
  • Aktive Gefechte und Gefechtshistorie einsehen

API-Dokumentation

Alle Datenstrukturen zwischen Server und Clients sind in protocol/ definiert:

Datei Inhalt
protocol/events.ts TypeScript-Typen für HTTP und WebSocket (Single Source of Truth)
protocol/openapi.yaml REST-API-Spec (POST /api/games, POST /api/games/:code/join)

Deployment (Container)

Schnellstart

./container_start.sh

Das Script erledigt alles automatisch:

  1. Erkennt selbstständig, ob docker (Linux) oder container (macOS) verfügbar ist
  2. Beim ersten Aufruf: fragt nach Hostname und Port, generiert SECRET_KEY_BASE, PLAYER_TOKEN_SECRET und ADMIN_TOKEN und speichert sie in .battleship-env
  3. Bei jedem weiteren Aufruf: lädt die gespeicherten Werte Secrets bleiben unverändert (wichtig: PLAYER_TOKEN_SECRET muss stabil bleiben, sonst werden alle laufenden Spieler-Tokens ungültig)
  4. Baut das Image neu
  5. Stoppt einen ggf. laufenden Container und startet einen neuen im Hintergrund

Die Datei .battleship-env liegt im Projektverzeichnis, ist nur für den Besitzer lesbar und wird nicht eingecheckt.

Manuell (fortgeschritten)

Wer Build und Start selbst steuern möchte:

macOS:

container build -t battleship .
container run -d -p 4000:4000 \
  -v battleship-data:/data \
  -e SECRET_KEY_BASE=<64-byte-hex> \
  -e PLAYER_TOKEN_SECRET=<64-byte-hex> \
  -e PHX_HOST=battleship.example.com \
  -e ADMIN_TOKEN=geheimes-passwort \
  --restart unless-stopped \
  battleship

Linux (Docker):

docker build -t battleship .
docker run -d -p 4000:4000 \
  -v battleship-data:/data \
  -e SECRET_KEY_BASE=<64-byte-hex> \
  -e PLAYER_TOKEN_SECRET=<64-byte-hex> \
  -e PHX_HOST=battleship.example.com \
  -e ADMIN_TOKEN=geheimes-passwort \
  --restart unless-stopped \
  battleship

Secrets einmalig generieren: openssl rand -hex 64

Das Image enthält Client und Server in einem einzigen Container. Beim Start werden Datenbank-Migrationen automatisch ausgeführt.

Container verwalten

# Status prüfen
docker ps

# Logs verfolgen
docker logs -f battleship

# Stoppen / Neustarten
docker stop battleship
docker restart battleship

Konfiguration (Umgebungsvariablen)

Variable Pflicht Beschreibung
SECRET_KEY_BASE Phoenix-Session-Signing (64 Byte hex)
PLAYER_TOKEN_SECRET Signing-Secret für Spieler-Tokens (64 Byte hex, muss über Neustarts stabil bleiben)
ADMIN_TOKEN Bearer-Token für /api/admin-Endpoints
PHX_HOST Öffentlicher Hostname (z. B. battleship.example.com)
PORT HTTP-Port (Standard: 4000)
DATABASE_PATH Pfad zur SQLite-Datei (Standard: /data/battleship.db)

Reverse Proxy

Der Container exponiert nur Port 4000. Darüber laufen HTTP, die REST-API und WebSockets gemeinsam. Der Proxy muss lediglich WebSocket-Upgrades durchleiten.

nginx

server {
    listen 443 ssl;
    server_name battleship.example.com;

    ssl_certificate     /etc/ssl/certs/battleship.crt;
    ssl_certificate_key /etc/ssl/private/battleship.key;

    location / {
        proxy_pass http://127.0.0.1:4000;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Apache (mod_proxy)

Benötigte Module: proxy, proxy_http, proxy_wstunnel, ssl, rewrite.

<VirtualHost *:443>
    ServerName battleship.example.com

    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/battleship.crt
    SSLCertificateKeyFile /etc/ssl/private/battleship.key

    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteRule ^/(.*)$ ws://127.0.0.1:4000/$1 [P,L]

    ProxyPass        / http://127.0.0.1:4000/
    ProxyPassReverse / http://127.0.0.1:4000/

    RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>