Initial commit: MUD client v0.2

This commit is contained in:
truetype committed 2026-07-07 15:38:28 +00:00
commit 0030fd7d82
26 files changed
+4421

No files matched your search

+210
View File
@@ -0,0 +1,210 @@
# 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 <pwd>` (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.