Files
gioco-mobile/server/README.md
T
davide a3c0892fea docs: update READMEs for new features
Document SVG map, mission improvements (chips, chance bar, abandon,
loot, fine), market trend tags, leaderboard live updates, and the
new abandon API endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 00:12:29 +02:00

125 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Contrabbandieri MMO — Server
Backend per il gioco mobile multiplayer asincrono "Contrabbandieri". Tutte le regole di gioco vivono sul server: il client non è mai considerato affidabile.
## Stack
Node.js 22 · TypeScript · Fastify · Prisma · PostgreSQL · Redis · Socket.IO · Zod · JWT · Docker Compose
## Avvio rapido
```bash
cp .env.example .env # poi cambia JWT_SECRET e POSTGRES_PASSWORD
mkdir -p data/postgres data/redis data/backups
docker compose up -d --build
```
Al primo avvio il container `api` applica automaticamente le migrazioni (`prisma migrate deploy`). Poi esegui il seed (città, item, listini):
```bash
docker compose exec api npx prisma db seed
```
Il server è su `http://localhost:3000` (health check: `GET /healthz`).
### Dati persistenti
Tutti i dati vivono in bind-mount locali dentro `data/` (`postgres/`, `redis/`, `backups/`): copiare la cartella del progetto su un altro server trasferisce tutto. Per il backup vedi §Backup.
## Struttura
```
src/
├── app.ts # Fastify, plugin, error handler, registrazione route
├── server.ts # entrypoint: HTTP + Socket.IO + scheduler
├── config/
│ ├── env.ts # variabili d'ambiente validate con Zod
│ └── gameBalance.ts # ★ TUTTI i parametri di bilanciamento del gioco
├── db/ # client Prisma e Redis
├── modules/ # un modulo per dominio: routes + service
│ ├── auth/ players/ cities/ market/ inventory/
│ ├── missions/ events/ leaderboard/
├── jobs/ # scheduler node-cron + job periodici
├── realtime/socket.ts # Socket.IO (path /ws)
└── shared/ # authGuard, errori, validazione, utility
```
**Bilanciamento**: ogni costante di gioco (costi viaggio, formule mercato, probabilità di successo missioni, eventi, XP) sta in [src/config/gameBalance.ts](src/config/gameBalance.ts), commentata. Nessun numero "magico" altrove.
## API REST (`/api/v1`)
Tutte le route tranne register/login richiedono `Authorization: Bearer <jwt>`.
| Metodo | Path | Descrizione |
|---|---|---|
| POST | `/auth/register` | crea utente+giocatore, ritorna JWT (rate limit 5/min) |
| POST | `/auth/login` | login, ritorna JWT (rate limit 5/min) |
| GET | `/auth/me` | utente + giocatore correnti |
| GET | `/player/me` | profilo, città corrente, XP per il prossimo livello |
| POST | `/player/travel` | `{cityId}` — viaggia (costo scalato dal server) |
| GET | `/cities` · `/cities/:cityId` | città con costo viaggio |
| GET | `/market/current` | listino della città corrente + eventi attivi |
| POST | `/market/buy` · `/market/sell` | `{itemId, quantity}` — prezzi decisi dal server |
| GET | `/inventory` | inventario con peso totale |
| GET | `/missions/available` | missioni nella città corrente, con probabilità di successo stimata e requisito di livello |
| POST | `/missions/:missionId/start` | avvia (verifica livello, consuma subito gli item richiesti) |
| POST | `/missions/:playerMissionId/claim` | riscuote: successo (premi + bottino) o fallimento (multa) |
| POST | `/missions/:playerMissionId/abandon` | abbandona una missione attiva (1 reputazione) |
| GET | `/missions/active` | missioni in corso |
| GET | `/events/current` | eventi mondo attivi |
| GET | `/leaderboard/money` · `/leaderboard/reputation` | classifiche (Redis), `?limit=N` |
Gli errori hanno sempre la forma `{ "error": { "code": "...", "message": "..." } }`.
## Realtime (Socket.IO)
Path `/ws`, handshake con `auth: { token: "<jwt>" }`.
Eventi server→client: `market:updated`, `mission:completed`, `worldEvent:started`, `worldEvent:ended`, `player:notification`, `leaderboard:updated`. Client→server: `client:ping`.
## Regole di gioco (formule)
I parametri citati sono tutti in `gameBalance.ts`.
- **Mercato** (`rawPrice = basePrice * economyModifier * demand / max(supply, 0.25) * eventModifier`): buy = raw×1.12, sell = raw×0.88, limiti [0.35×, 3.5×] del prezzo base. Acquisti/vendite spostano domanda/offerta; ogni minuto il mercato decade verso l'equilibrio (fattore 0.98).
- **Viaggio**: `costo = 50 + riskLevel(destinazione) × 25`.
- **Missioni**: `successo = 0.95 risk×0.6 + livello×0.01 + reputazione×0.0005 (pressione polizia1)×0.05`, limitato a [5%, 95%]; la stessa stima è esposta al client in `/missions/available`. Ogni tipo ha un carattere (`typeConfig`): Consegna trasporta merce, Furto paga meno ma dà bottino in merce, Contrabbando paga molto e rischia di più, Informazioni dà XP e reputazione doppia. Difficoltà alte richiedono un livello minimo. Successo: denaro + XP + reputazione + eventuale bottino. Fallimento: 3 reputazione e multa (30% del premio × pressione polizia, mai oltre il denaro posseduto). Una missione attiva può essere abbandonata (`POST /missions/:id/abandon`, 1 reputazione, merce consumata persa). Massimo 3 missioni attive per giocatore.
- **XP**: per passare dal livello L a L+1 servono `100 × L^1.5` XP.
- **Eventi**: ogni 15 min possibile evento locale (25%), ogni ora possibile evento globale (20%); modificano prezzi e pressione di polizia finché attivi.
## Scheduler
- **Ogni minuto**: ricalcolo prezzi, notifica missioni completate, pulizia missioni scadute, generazione missioni (minimo 5 attive per città).
- **Ogni 15 minuti**: possibile evento locale, refresh classifiche Redis.
- **Ogni ora**: possibile evento globale, notifica eventi terminati.
## Sviluppo senza Docker
Servono PostgreSQL e Redis raggiungibili (puoi usare solo i container `db` e `redis`):
```bash
npm install
# in .env usa localhost al posto di db/redis
npx prisma migrate dev
npx prisma db seed
npm run dev
```
## Backup
```bash
# dump (a caldo, sicuro)
docker compose exec db pg_dump -U gameuser game > ./data/backups/game_$(date +%Y%m%d_%H%M).sql
# restore
cat ./data/backups/game_XXX.sql | docker compose exec -T db psql -U gameuser game
# migrazione completa su altro host: stop, archivio, riavvio
docker compose down
tar -czf contrabbandieri-data.tar.gz data .env docker-compose.yml
```
## Produzione
Il `Dockerfile` ha uno stage `production` (build TypeScript, solo dipendenze runtime, `node dist/server.js`). Prima del deploy: cambia `JWT_SECRET` e `POSTGRES_PASSWORD`, non esporre le porte 5432/6379, metti un reverse proxy HTTPS (es. Nginx) davanti alla porta 3000.