Files
dcc-rpg/docs/codebase-reference.md
T
truetype eacc946868 fix: JSON repair, WS thread pool, switch to DeepSeek V4 Flash, funnel Lv0
LLM fixes:
- _try_repair_json() — ripara JSON malformato (apici, virgole trailing)
- _strip_json_from_text() — rimuove residui JSON dalla narration
- parse_tool_call() — rimuove troncamento 200 caratteri dal fallback

DM fixes:
- Fallback euristico quando params vuoti (dm.py)
- Estrazione narrazione da dm_override/narrate params.params.text

Prompt (prompts.py):
- Vietati termini tecnici/scientifici/anacronistici (HPLC, laser, ...)
- Aggiunto vocabolario fantasy obbligatorio

Server (server.py):
- dm_cycle in ThreadPoolExecutor per non bloccare WS ping
- WebSocketDisconnect gestiti
- Rimosso import inesistente db_schema

Frontend (index.html):
- Nascosti tool name dalla chat
- Personaggio -> PG, connessioni aree con nomi

Infrastruttura:
- Switch da openrouter/free a DeepSeek V4 Flash via OpenCode Go
- pyproject.toml, Dockerfile, docker-compose.yml, start.sh
- .env auto-load (start.sh)
- Funnel Lv0 (funnel.py) — creazione PG livello 0

Docs: architecture, database, setup, game mechanics, codebase ref
2026-06-28 08:18:24 +02:00

8.4 KiB

Codebase Reference

Directory Layout

rpg/
├── src/
│   ├── engine/
│   │   ├── __init__.py          # Public API del modulo engine
│   │   ├── config.py            # ProviderConfig, load_from_env()
│   │   ├── db.py                # SQLite schema, CRUD, adventure data access
│   │   ├── dice.py              # DCC dice system (roll, dice_chain)
│   │   ├── dm.py                # DM cycle orchestrator + CLI interactive
│   │   ├── game_context.py      # GameContext dataclass (campagna + stato)
│   │   ├── heuristic_fallback.py# Keyword parser per fallback LLM
│   │   ├── llm.py               # LLM abstraction, ask(), parse_tool_call()
│   │   ├── prompts.py           # System prompt, context builder, tool docs
│   │   ├── providers.py         # Ollama/OpenRouter/Local providers
│   │   └── tools.py             # @tool registry, 10 Approved Tools
│   │
│   └── web/
│       ├── __init__.py
│       ├── server.py            # FastAPI app, REST, WebSocket, main()
│       └── static/
│           └── index.html       # SPA frontend (888 righe CSS+JS+HTML)
│
├── data/
│   ├── dcc_rpg.db              # SQLite database (auto-creato)
│   ├── adventure/
│   │   └── avventura.json       # Avventura machine-readable (9 aree)
│   └── crit_fumble_tables.json  # Tabelle critici e fumble (207 righe)
│
├── docs/
│   └── plans/
│       └── 2026-06-25-dcc-rpg-design.md  # Design doc originale (314 righe)
│
├── docs_dev/                   # Documentazione sviluppo (questo)
├── knowledge/                   # 15 file KB (regole DCC, ~3708 righe)
├── input/                       # PDF sorgente, reference booklet
├── ocr_full/                    # OCR output (58K parole)
├── graphify-out/                # Grafo concettuale
└── tests/                       # Test (pytest)
    ├── test_dice.py
    ├── test_tools.py
    ├── test_integration.py
    └── test_websocket.py

Modulo: src/engine/config.py

ProviderKind = "ollama" | "openrouter" | "local"

ProviderConfig (dataclass)

Campo Default Env Var
kind "openrouter" DCC_LLM_PROVIDER
ollama_base_url http://192.168.0.50:11434 DCC_OLLAMA_URL
ollama_model gemma2:9b DCC_OLLAMA_MODEL
openrouter_api_key "" DCC_OPENROUTER_KEY
openrouter_model "openrouter/free" DCC_OPENROUTER_MODEL
openrouter_free_fallback True DCC_OR_FREE_FALLBACK
local_base_url http://192.168.0.240/v1 DCC_LOCAL_URL
local_api_key "" DCC_LOCAL_KEY
local_model "big-pickle" DCC_LOCAL_MODEL
timeout 60 DCC_LLM_TIMEOUT

Modulo: src/engine/dice.py

DICE_CHAIN

d3 → d4 → d5 → d6 → d7 → d8 → d10 → d12 → d14 → d16 → d20 → d24 → d30

SPECIAL_DICE

d% (100), d100 (100), d0 (0), d1 (1), d2 (2)

API

Funzione Parametri Returns
roll(die, count=1, modifier=0) die: str RollResult {die, count, modifier, results[], total, crit, fumble, raw}
dice_chain(die, steps) steps: int (+=su, -=giù) DiceChainResult {input_die, steps, output_die, chain} o ValidationError
roll_str(raw) "2d20+3" RollResult
validate_die(die) die: str `str

Regole crit/fumble

  • Solo su d20: 20 = crit, 1 = fumble
  • Critico: moltiplica danno, tiro su tabella critici per classe
  • Fumble: tiro su tabella fumble (dado dipende da armatura)

Modulo: src/engine/tools.py

@tool decorator

Ogni funzione decorata viene automaticamente registrata in TOOL_REGISTRY.

run(tool_name, **kwargs) — dispatcher centralizzato

Cerca in TOOL_REGISTRY, chiama la funzione, cattura eccezioni.

10 Approved Tools

Tool Funzione Parametri Chiave
roll_dice Tira dado/i die, count, modifier
dice_chain Risolve catena dadi die, steps
combat Ciclo combattimento completo character_id, target_ac, attack_bonus, damage_die, mighty_deed_die, monster_id, armor_type
ability_check Prova abilità character_id, dc, modifier
saving_throw Tiro salvezza character_id, save_type (Tempra/Riflessi/Volontà), dc
movement Movimento distance_m, terrain, stealth
spellcasting Incantesimo character_id, spell_name, spell_check, dc
update_inventory Gestione inventario character_id, item, action (add/remove/equip/unequip/list)
award_xp Assegna PE character_id, xp
dm_override DM override command, params

Tool combat — Dettaglio flusso

  1. Carica PG e mostro (con PF persistenti da combat_state)
  2. Tiro iniziativa (d20)
  3. Tiro attacco (d20 + bonus)
  4. Mighty Deed (se guerriero, dado d3/d4/d5 aggiuntivo)
  5. Crit/Fumble su 20/1 naturale
  6. Lookup tabella critici (per classe/livello) o fumble (per armatura)
  7. Calcolo danno + danno extra da critico
  8. Persistenza PF mostro in combat_state

Tool spellcasting — Tier

Tiro Risultato
1 Fallimento critico (corruzione!)
< DC Fallimento
≤ 11 Successo minore (effetto base)
≤ 17 Successo moderato (effetto migliorato)
≤ 19 Successo maggiore (effetto potente)
20+ Successo massimo (effetto devastante)

Modulo: src/engine/providers.py

Provider Classes

Classe Endpoint Auth
OllamaProvider {base_url}/api/generate Nessuna
OpenRouterProvider openrouter.ai/api/v1/chat/completions Bearer API key
LocalProvider {base_url}/chat/completions Bearer key (opzionale)

create_provider(cfg) → Factory

Legge cfg.kind, istanzia provider corretto con parametri appropriati.

Modulo: src/engine/dm.py

dm_cycle(campaign_id, player_action) → ciclo principale

  1. GameContext.load(campaign_id) — assembla contesto
  2. _llm_decision(ctx, action) — LLM + fallback
  3. tools.run(tool_name, **params) — esecuzione
  4. db.advance_turn() + db.log_turn() — persistenza
  5. Return response dict

interactive(campaign_id) — CLI interattiva

Loop input/output con prompt `⚔️ », comandi: esci, stato

Modulo: src/engine/prompts.py

SYSTEM_PROMPT — Prompt di sistema (~60 righe)

  • Stile narrazione
  • Meccaniche base DCC
  • Tool disponibili con parametri (json)
  • Esempi di chiamata
  • Formato risposta obbligatorio

build_prompt(ctx, player_action, current_area_id) → prompt completo

  • format_context(ctx): contesto campagna
  • format_area_context(area_id): descrizione area attuale
  • Azione giocatore

Modulo: src/engine/db.py

Vedi database-schema.md per schema completo.

Lifecycle

  1. init_db() → init_schema + migrate + populate_from_json
  2. Connessioni thread-local con threading.local()
  3. WAL mode + busy_timeout per concorrenza

Modulo: src/web/server.py

Entry Point

python -m src.web.server
# oppure uvicorn src.web.server:app --host 0.0.0.0 --port 8300

REST Endpoints

Metodo Path Descrizione
GET /api/campaigns Lista campagne
GET /api/campaign/{id} Stato campagna completo
POST /api/campaign?name=... Crea campagna
POST /api/campaign/{id}/character?name=...&char_class=... Aggiunge PG default
GET /health Health check
GET / Frontend SPA

WebSocket /ws/{campaign_id}

Messaggio Direzione Descrizione
{"type": "action", "payload": "..."} Client→Server Azione giocatore
{"type": "ping"} Client→Server Keepalive
{"type": "state", "campaign": {...}} Server→Client Stato completo
{"type": "response", "turn": N, ...} Server→Client Risultato turno
{"type": "pong"} Server→Client Keepalive response
{"type": "error", "message": "..."} Server→Client Errore

File: src/web/static/index.html

Single-page application (~888 righe) con:

  • Welcome screen: lista campagne, creazione nuova
  • Sidebar (320px): scheda PG (statistiche, HP bar), missioni, mappa aree
  • Chat area: messaggi player/DM con stili differenziati
  • Input bar: campo testo + bottone invio
  • WebSocket con auto-reconnect ogni 3s
  • Tema scuro "sword & sorcery" con font Cinzel + Cormorant Garamond