# Ping-Pong 1v1 Node.js Match Server Ten serwer obsługuje mecze 1v1 przez WebSocket, trzyma stan w Redis (reconnect), zapisuje postęp do MySQL i po zakończeniu meczu wywołuje PHP endpoint do przydziału nagród. ## Uruchomienie lokalnie 1. Wejdź do folderu: - `public_html/disciplines/ping-pong/1v1/node-server` 2. Zainstaluj zależności: - `npm install` 3. Skopiuj konfigurację: - skopiuj `.env.example` → `.env` i uzupełnij wartości 4. Start: - `npm run start` ## Uruchomienie produkcyjne Ten serwer musi działać jako osobny proces Node.js na hoście aplikacji. Samo PHP nie wystarczy. Minimalne wymagania: - Node.js 20+ - MySQL dostępny pod danymi z `.env` - reverse proxy z `https://togethere.cloud/ping-pong-1v1` do `ws://127.0.0.1:8088/` Redis jest zalecany, ale nie jest już obowiązkowy dla pojedynczej instancji. Jeśli `REDIS_URL` jest niedostępny, serwer przełączy się na fallback in-memory dla kolejki i snapshotów reconnect. Przykład startu przez PM2: - `cd public_html/disciplines/ping-pong/1v1/node-server` - `npm install` - `pm2 start ecosystem.config.cjs` - `pm2 save` Test po starcie procesu: - `http://127.0.0.1:8088/health` - przez domenę: `https://togethere.cloud/ping-pong-1v1/health` Jeżeli domena zwraca `503`, to najczęściej oznacza to, że Apache/Nginx już próbuje proxy, ale proces Node.js nie działa albo nie nasłuchuje na porcie `8088`. Jeżeli `/health` zwraca `ok: true` i `redisMode: memory`, to serwer działa bez Redisa w trybie pojedynczej instancji. ## Protokół WebSocket (JSON) Klient wysyła: - `{"type":"hello","ticket":"..."}` - `{"type":"queue.join"}` - `{"type":"queue.leave"}` - `{"type":"match.input","seq":123,"move":-1|0|1}` Serwer wysyła: - `{"type":"hello.ok","userId":1}` - `{"type":"queue.status","status":"searching","queueSize":4}` - `{"type":"queue.status","status":"idle"}` - `{"type":"match.found","matchId":"...","side":"left|right"}` - `{"type":"match.state", ... }` - `{"type":"match.end", ... }` ## Skalowanie - Redis trzyma kolejkę matchmakingu i snapshot stanu meczu. - Przy wielu instancjach potrzebujesz sticky sessions (L4/L7) albo wspólnej warstwy routingowej na matchId. ## Bezpieczeństwo Ticket wydaje PHP endpoint `/api/matches/ping-pong/1v1/ticket.php` (wymaga sesji PHP). Node weryfikuje ticket HMAC (`PINGPONG_1V1_SHARED_SECRET`). Po zakończeniu meczu Node robi POST do `/api/matches/ping-pong/1v1/` (folder z `index.php`) i dostaje `jobId`. Klient może potem odpalić animacje i odpytywać `/api/matches/ping-pong/1v1/status.php?jobId=...`.