# 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=...`.
