# progetto2 `progetto2` ospita un **client MUD CLI** pensato per collegarsi a un MUD LP‑MUD basato sul driver **FluffOS** (per esempio **Dead Souls**, libreria 3.8.6). Il client parla telnet con estensioni standard: `ECHO`, `NAWS`, `TTYPE`, e rifiuta (`WONT`) tutte le altre (`GMCP`, `MSDP`, `MCCP`, `MXP` per ora). Tiene traccia delle stanze visitate e le renderizza come **mappa ASCII** on-demand. ## Requisiti - Python ≥ 3.10 - Una libreria Python: [`prompt_toolkit`](https://pypi.org/project/prompt-toolkit/) ≥ 3.0 ## Setup ```bash cd progetto2 python3 -m venv .venv source .venv/bin/activate pip install -e ".[dev]" ``` Verrà installato il comando `mudclient`. ## Uso ```bash # Default: si collega a localhost:6666 con un finestra iniziale 120×40 mudclient # Espliciti mudclient --host localhost --port 6666 --cols 120 --rows 40 # Auto-login: passa il nome; il client lo invia quando il server chiede # "What name do you wish?". Se serve la password, il client chiede # /password PWD oppure la puoi passare con --password (o env MUDCLIENT_PASSWORD). mudclient --user zork --password hunter2 # Log di sessione: ogni evento (testo, GMCP, stanze, comandi locali, linee inviate) # viene serializzato in append‑only, flushed dopo ogni write — adatto a `tail -f`. mudclient --log-json ~/mud-session.jsonl --log-md ~/mud-session.md ``` > I file `--log-json` / `--log-md` vengono **truncati** all'apertura: > ogni nuova sessione ricomincia da zero. Per ruotare manualmente > prima della sessione: > ```bash > mv ~/mud-session.jsonl{,.prev} && mudclient --log-json ~/mud-session.jsonl > ``` Schema eventi (un oggetto JSON per riga): | `kind` | Scopo | Note | | --- | --- | --- | | `session_start` | apertura sessione | `host`, `port`, `user` | | `session_end` | chiusura normale | `reason` | | `disconnect` | chiusura socket | `reason` | | `text` | chunk ANSI ricevuto dal server | `raw` preservato | | `room_enter` | mapper ha registrato una stanza | `id`, `name`, `exits` | | `gmcp` | frame GMCP accettato | `module`, `payload` | | `vitals` | snapshot hp/sp/mv | solo i campi presenti | | `line_sent` | riga inoltrata al MUD | password redatte (`***`) | | `local_cmd` | comando `/...` intercettato | password/segreti redatti | | `login` | prompt/risposta auto-login | `value` redatto di default; passare `redact=False` per i nomi account | Una volta connesso: | Tasto | Effetto | | --- | --- | | Testo qualsiasi | inviato al MUD terminato da `\r\n` | | `/map` | disegna la mappa ASCII delle stanze visitate (multi-source BFS) | | `/where` | mostra la stanza corrente e le uscite | | `/look` | invia `look` al MUD | | `/vitals` | mostra hp/sp/mv (via GMCP `Char.Vitals`) | | `/stats` | locali scoperti, GMCP stato, vitali | | `/password PWD` | invia una password al MUD | | `/clear` | pulisce lo schermo | | `/help` (`/?`) | mostra l'aiuto | | `/quit` (`/q`, `Ctrl‑D`) | esce | Il prompt è sempre del tipo: ``` [NomeStanza]> _ ``` con il nome della stanza corrente in cyan brillante. In basso c'è una **status bar** che mostra: ``` [stanza: Outer Courtyard] [GMCP: on] HP 100/100 SP 50/50 MV 80/80 ``` Quando il server invia `Char.Vitals` via GMCP, hp/sp/mv compaiono automaticamente. ## Layout dei sorgenti ``` progetto2/ ├── pyproject.toml # metadata + dipendenze + entry point `mudclient` ├── README.md # questo file ├── .gitignore ├── docs/ # documentazione completa (vedi sotto) │ ├── README.md # indice della cartella docs │ ├── architecture.md # analisi architetturale (data flow + boundary) │ ├── modules.md # descrizione modulo‑per‑modulo │ ├── protocol.md # telnet, GMCP, ANSI, euristiche Dead Souls │ ├── usage.md # flag CLI, esempi, auto‑login, comandi slash │ ├── events.md # schema eventi del logger │ ├── development.md # setup dev, pytest, ruff, come estendere │ └── gitea.md # flusso di pubblicazione sul Gitea + sicurezza token ├── src/progetto2/ # package │ ├── __init__.py │ ├── __main__.py # supporta `python -m progetto2` │ ├── cli.py # entry point + UI a riga di comando │ ├── telnet.py # state machine IAC + asyncio │ ├── deadsouls.py # parser di stanze Dead Souls / LP‑MUD │ ├── gmcp.py # parser GMCP (Char.Vitals / Room.Info) │ ├── mapper.py # grafo delle stanze + render ASCII │ ├── logger.py # EventLogger (JSONL + Markdown sinks) │ └── ansi.py # stripper ANSI per il parser └── tests/ # unit test (pytest) ├── test_telnet.py ├── test_ansi.py ├── test_deadsouls.py ├── test_mapper.py └── test_logger.py ``` ## 📚 Documentazione completa La documentazione vive nella cartella [`docs/`](./docs/README.md) e copre: - **[`docs/architecture.md`](./docs/architecture.md)** — analisi architetturale completa: dipendenze tra moduli, data flow di un frame dal server, boundary concerns, refactor raccomandati. - **[`docs/modules.md`](./docs/modules.md)** — cosa fa ogni modulo, API pubblica, responsabilità, tolleranze. - **[`docs/protocol.md`](./docs/protocol.md)** — IAC state machine, GMCP wire format, euristiche Dead Souls, ANSI handling. - **[`docs/usage.md`](./docs/usage.md)** — riferimento flag CLI, esempi pratici, flusso auto‑login, comandi slash, rotazione log. - **[`docs/events.md`](./docs/events.md)** — schema completo degli eventi del logger (JSONL + Markdown), regole di redazione, esempi `jq`. - **[`docs/development.md`](./docs/development.md)** — workflow sviluppatore: setup, test, lint, come aggiungere moduli, debugging. - **[`docs/gitea.md`](./docs/gitea.md)** — flusso di pubblicazione sul server Gitea `192.168.0.183:8085` (utente `truetype`) **+ regole di sicurezza del token**. ## Note sul protocollo Il client negozia: - `ECHO`: invia `WILL ECHO` in risposta a `DO ECHO`. Il server non eseguirà più echo remoto, mentre l'eco locale è gestita da `prompt_toolkit`. - `NAWS`: se attivo, invia la dimensione della finestra iniziale. - `TTYPE`: risponde con `XTERM-256COLOR` quando il server lo richiede. - `GMCP`: viene **offerto proattivamente** (`WILL` al connect). Dead Souls lo accetta e invia pacchetti `Char.Vitals`, `Room.Info`, `Char.Login`, ecc. Quando una stanza arriva via `Room.Info`, si usa il `num` (vnum) del server come id stabile della stanza. Le righe di stato del parser considerano `IAC GA` (`249`) e `IAC EOR` (`239`) come delimitatori di "record/prompt", così da poter chiudere un blocco di stanza anche quando il prompt testuale non è immediatamente riconoscibile. La rilevazione automatica della geometria del mondo si basa sul **movimento inviato dall'utente** + **descrizione della stanza** osservata subito dopo: quando digiti `n`, il client informa il mapper; quando la risposta del MUD arriva, `Mapper.visit()` collega automaticamente `corrente → nuova` lungo la direzione digitata. Il render della mappa è un **multi-source BFS** dal current room, con fallback di **posizionamento orfano** per le stanze visitate in sequenza indietro (es. `n→s`) e stack nell'angolo in basso a destra per stanze completamente isolate. ## Limitazioni note (v0.2) - **MSDP / MCCP / MCP / MXP** non sono ancora abilitati: una sessione che li richiede ottiene `WONT` dal nostro lato. Dead Souls usa GMCP, che è ben coperto; altri MUD basati su MSDP possono essere aggiunti seguendo lo schema di `gmcp.py`. - La detection delle stanze **senza GMCP** resta **euristica** sul testo. - **Login automatico**: il client riconosce il prompt `What name do you wish?` / `Password:` e invia automaticamente solo la prima volta. Per la password senza `--password` viene mostrato un suggerimento `usa /password ` (niente prompt annidato, troppo fragile). - Gli `u`/`d` (verticali) sono rappresentati nella mappa ma non influenzano le coordinate 2D (offset `(0,0)`). - La status bar si aggiorna ad ogni rilettura della riga del prompt; per refresh continui in idle si può aggiungere `refresh_interval` al `PromptSession` come estensione futura. ## Sviluppo ```bash pip install -e ".[dev]" pytest # gira tutta la suite ruff check src tests # lint ``` ## Licenza MIT per il codice del client. Il server **Dead Souls** non è GPL — assicurati di rispettarne la licenza prima di redistribuirlo.