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
This commit is contained in:
1 parent
6b048dc0cc
commit
eacc946868
23 files changed
+1786
-61
No files matched your search
@@ -0,0 +1,202 @@
|
||||
# Database Schema
|
||||
|
||||
**File**: `data/dcc_rpg.db` (auto-creato)
|
||||
**Engine**: SQLite 3 con WAL mode
|
||||
**Connessione**: Thread-local via `threading.local()`, check_same_thread=False
|
||||
|
||||
## Tabelle
|
||||
|
||||
### `campaigns` — Campagne di gioco
|
||||
```sql
|
||||
CREATE TABLE campaigns (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
adventure TEXT,
|
||||
turn INTEGER NOT NULL DEFAULT 0,
|
||||
current_area_id TEXT DEFAULT '',
|
||||
current_area_name TEXT DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
```
|
||||
|
||||
### `characters` — Personaggi giocanti
|
||||
```sql
|
||||
CREATE TABLE characters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
campaign_id INTEGER NOT NULL REFERENCES campaigns(id),
|
||||
name TEXT NOT NULL,
|
||||
level INTEGER NOT NULL DEFAULT 0,
|
||||
char_class TEXT,
|
||||
alignment TEXT CHECK(alignment IN ('Legge','Caos','Neutralità')),
|
||||
occupation TEXT,
|
||||
str INTEGER NOT NULL DEFAULT 10,
|
||||
agi INTEGER NOT NULL DEFAULT 10,
|
||||
sta INTEGER NOT NULL DEFAULT 10,
|
||||
per INTEGER NOT NULL DEFAULT 10,
|
||||
int INTEGER NOT NULL DEFAULT 10,
|
||||
lck INTEGER NOT NULL DEFAULT 10,
|
||||
hp_current INTEGER NOT NULL DEFAULT 1,
|
||||
hp_max INTEGER NOT NULL DEFAULT 1,
|
||||
ac INTEGER NOT NULL DEFAULT 10,
|
||||
armor_type TEXT DEFAULT 'none',
|
||||
luck_current INTEGER NOT NULL DEFAULT 10,
|
||||
xp INTEGER NOT NULL DEFAULT 0,
|
||||
weapon TEXT,
|
||||
equipment TEXT DEFAULT '[]',
|
||||
status TEXT NOT NULL DEFAULT 'alive'
|
||||
CHECK(status IN ('alive','unconscious','dead'))
|
||||
);
|
||||
CREATE INDEX idx_char_campaign ON characters(campaign_id);
|
||||
```
|
||||
|
||||
### `adventure_state` — Stato esplorazione aree
|
||||
```sql
|
||||
CREATE TABLE adventure_state (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
campaign_id INTEGER NOT NULL REFERENCES campaigns(id),
|
||||
area_id TEXT NOT NULL,
|
||||
visited INTEGER NOT NULL DEFAULT 0,
|
||||
state TEXT DEFAULT '{}'
|
||||
);
|
||||
CREATE UNIQUE INDEX idx_area_campaign ON adventure_state(campaign_id, area_id);
|
||||
```
|
||||
Il campo `state` contiene JSON con lo stato di enigmi/trappole risolte per l'area.
|
||||
|
||||
### `combat_state` — Stato combattimento (PF persistenti mostri)
|
||||
```sql
|
||||
CREATE TABLE combat_state (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
campaign_id INTEGER NOT NULL REFERENCES campaigns(id),
|
||||
monster_id TEXT NOT NULL,
|
||||
monster_name TEXT NOT NULL DEFAULT '',
|
||||
current_hp INTEGER NOT NULL,
|
||||
max_hp INTEGER NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active'
|
||||
CHECK(status IN ('active', 'defeated', 'fled')),
|
||||
UNIQUE(campaign_id, monster_id)
|
||||
);
|
||||
```
|
||||
|
||||
### `character_items` — Inventario PG
|
||||
```sql
|
||||
CREATE TABLE character_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
character_id INTEGER NOT NULL REFERENCES characters(id),
|
||||
item_def_id TEXT,
|
||||
name TEXT NOT NULL,
|
||||
quantity INTEGER DEFAULT 1,
|
||||
equipped INTEGER DEFAULT 0,
|
||||
properties TEXT DEFAULT '{}'
|
||||
);
|
||||
```
|
||||
|
||||
### `quests` — Missioni attive/completate/fallite
|
||||
```sql
|
||||
CREATE TABLE quests (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
campaign_id INTEGER NOT NULL REFERENCES campaigns(id),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'active'
|
||||
CHECK(status IN ('active','completed','failed'))
|
||||
);
|
||||
```
|
||||
|
||||
### `turn_log` — Log azioni e risultati
|
||||
```sql
|
||||
CREATE TABLE turn_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
campaign_id INTEGER NOT NULL REFERENCES campaigns(id),
|
||||
turn INTEGER NOT NULL,
|
||||
character_id INTEGER REFERENCES characters(id),
|
||||
action TEXT,
|
||||
result TEXT,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX idx_turnlog_campaign ON turn_log(campaign_id, turn);
|
||||
```
|
||||
|
||||
### Tabelle di sistema: critici e fumble
|
||||
|
||||
```sql
|
||||
CREATE TABLE crit_fumble_tables (
|
||||
id TEXT PRIMARY KEY, -- "fumble", "crit_I"..."crit_V"
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
table_type TEXT NOT NULL CHECK(table_type IN ('crit', 'fumble')),
|
||||
die TEXT,
|
||||
modifier TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE crit_fumble_entries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
table_id TEXT NOT NULL REFERENCES crit_fumble_tables(id),
|
||||
roll_min INTEGER,
|
||||
roll_max INTEGER,
|
||||
result_text TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_entries_table ON crit_fumble_entries(table_id);
|
||||
|
||||
CREATE TABLE fumble_armor_die (
|
||||
armor_type TEXT PRIMARY KEY, -- "none", "light", "medium", "heavy"
|
||||
die TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
## Migrazioni
|
||||
|
||||
Le migrazioni sono gestite tramite `_migrate_schema()` chiamata a ogni avvio.
|
||||
Ogni migrazione è un ALTER TABLE avvolto in try/except (se la colonna esiste già, skippa).
|
||||
|
||||
Migrazioni attuali:
|
||||
```python
|
||||
migrations = [
|
||||
"ALTER TABLE campaigns ADD COLUMN current_area_id TEXT DEFAULT ''",
|
||||
"ALTER TABLE campaigns ADD COLUMN current_area_name TEXT DEFAULT ''",
|
||||
]
|
||||
```
|
||||
|
||||
## Popolazione Iniziale
|
||||
|
||||
`populate_from_json()` carica i dati da `data/crit_fumble_tables.json`:
|
||||
- 6 tabelle (fumble + crit_I → crit_V)
|
||||
- 112 entries totali
|
||||
- 4 mappature armatura→dado fumble
|
||||
|
||||
`seed_adventure_state()` e `seed_quests_from_adventure()` caricano aree e quest da `avventura.json`.
|
||||
|
||||
## CRUD Pubbliche (`db.py`)
|
||||
|
||||
| Funzione | Descrizione |
|
||||
|---|---|
|
||||
| `init_db()` | Schema + migrazioni + popolamento |
|
||||
| `lookup_crit_fumble(table_id, roll, luck_mod=0)` | Cerca risultato crit/fumble con modificatore |
|
||||
| `get_fumble_die(armor_type)` | Dado fumble per tipo armatura |
|
||||
| `create_campaign(name, adventure)` | Nuova campagna |
|
||||
| `get_campaign(id)` | Campagna by ID |
|
||||
| `create_character(campaign_id, name, class, occupation)` | Nuovo PG |
|
||||
| `get_characters(campaign_id)` | Tutti i PG di una campagna |
|
||||
| `get_character(id)` | PG by ID |
|
||||
| `update_character(id, **kwargs)` | Update qualsiasi campo |
|
||||
| `advance_turn(campaign_id)` | Incrementa turno |
|
||||
| `log_turn(...)` | Logga azione |
|
||||
| `get_log(campaign_id, limit)` | Ultimi N turni |
|
||||
| `set_area_state(...)` | Marca area visitata/stato |
|
||||
| `set_puzzle_state(...)` | Stato enigma (solved/unsolved) |
|
||||
| `is_puzzle_solved(...)` | Check enigma |
|
||||
| `get_area_state(campaign_id)` | Stato tutte le aree |
|
||||
| `set_current_area(...)` | Imposta area corrente PG |
|
||||
| `get_current_area(campaign_id)` | Area corrente PG |
|
||||
| `set_combat_state(...)` | Inizializza mostro in combattimento |
|
||||
| `update_combat_hp(...)` | Danno mostro, status defeated |
|
||||
| `get_combat_state(campaign_id)` | Stato combattimento |
|
||||
| `add_character_item(...)` | Aggiunge oggetto (stacking) |
|
||||
| `remove_character_item(...)` | Rimuove oggetto |
|
||||
| `get_character_items(...)` | Inventario PG |
|
||||
| `equip_character_item(...)` | Equipaggia/Unequipaggia |
|
||||
| `get_all_areas()` | Tutte le aree da JSON |
|
||||
| `get_area_by_id(id)` | Area specifica |
|
||||
| `get_monster_by_id(id)` | Stat block mostro |
|
||||
| `get_monsters_in_area(area_id)` | Mostri in area con stats |
|
||||
| `format_area_context(area_id)` | Testo area per prompt LLM |
|
||||
| `format_all_areas_context()` | Tutte le aree per prompt iniziale |
|
||||
Reference in new issue
Block a user