primo commit

This commit is contained in:
2026-09-23 18:59:27 +02:00
parent 029e9f67fb
commit bd900099a0
451 changed files with 143273 additions and 2 deletions
+351
View File
@@ -0,0 +1,351 @@
# OGame Galaxy Viewer — Documentazione tecnica
Visualizzatore di galassie per **OGame (s170-ar, universo "Rosalind")** con archivio
personale dei rapporti di spionaggio e classifiche filtrate.
Questa documentazione descrive l'architettura, i sorgenti e, per ciascuno, funzioni
e metodi.
---
## 1. Panoramica
Il progetto è una **web app personale** composta da:
| Componente | Tecnologia | Ruolo |
|---|---|---|
| Backend | Python 3 + Flask | API REST, aggiornamento dati, archivio rapporti |
| Frontend | HTML/CSS/JS vanilla | Galaxy view, dettagli rapporti, classifiche |
| Dati | File JSON su disco | Nessun database: snapshot + rapporti come file |
| Servizi | systemd (utente) | App sempre attiva + aggiornamenti schedulati |
**Flusso dati (dati universo)**:
```
Gameforge API pubblica (XML + ?toJson=1)
│ ogni giorno (03:30)
▼
update_data.py ──► data/latest/*.json ──► world.json (indici)
│ ▲
└── highscore ── ogni ora (:17) ─────┘
│
app.py (Flask :8899) ──► browser (galassia/classifiche)
```
**Flusso dati (rapporti di spionaggio)**:
```
In gioco (pulsante "API" del rapporto) ──► API string "sr-ar-170-<40 hex>"
│ incollata nella pagina
▼
POST /api/reports ──► report_store.py ──► proxy community (ogapi.faw-kes.de)
▼
data/reports/<sr_id>.json (+ index.json)
▼
clic sul pianeta in galassia ──► dettagli archiviati
```
---
## 2. Struttura delle directory
```
/opt/ogame_db/
├── app.py # Backend Flask: pagine + API + aggiornamento all'avvio
├── update_data.py # Download snapshot pubblici + classifiche + world.json
├── report_store.py # Import/archivio/consultazione rapporti di spionaggio
├── technames.py # Nomi tecnologici (edifici/ricerche/navi/difese) lato server
├── config.json # Configurazione (universo, proxy, account personale)
├── requirements.txt # Dipendenze Python
├── README.md # Guida rapida d'uso
├── DOCUMENTAZIONE.md # (questo file)
├── data/
│ ├── latest/ # Snapshot correnti + world.json
│ ├── snapshots/ # Storico giornaliero (ritenzione: keep_snapshots)
│ └── reports/ # Rapporti importati (1 file = 1 rapporto) + index.json
└── static/
├── index.html # Pagina galassia
├── rankings.html # Pagina classifiche
├── style.css # Stili (tema spaziale, responsive)
├── common.js # Utility condivise (fetch, avatar SVG, icone, stelle)
├── app.js # Logica della galassia e dei rapporti
├── rankings.js # Logica della pagina classifiche
├── icons.js # Icone SVG incorporate (game-icons, CC BY 3.0)
└── icons/ # (riservata a eventuali svg esterni; non usata a runtime)
```
---
## 3. `config.json` — configurazione
```json
{
"server_number": 170, // numero universo
"community": "ar", // community (lingua/regione)
"domain": "s170-ar.ogame.gameforge.com",
"my_player_id": 100634, // tuo account: pianeti evidenziati in verde
"my_player_name": "MaNaki",
"proxy_base": "https://ogapi.faw-kes.de", // proxy ufficiale per i rapporti
"refresh_hours": 24, // dati "stale" oltre N ore → update all'avvio
"keep_snapshots": 30 // giorni di storico conservati
}
```
---
## 4. Backend — moduli Python
### 4.1 `app.py` — server Flask (porta 8899)
Avviato da systemd come `ogame-galaxy.service`. All'avvio verifica l'età dei dati
(`_ensure_fresh_data`) e, se più vecchi di `refresh_hours`, lancia `update_data.main()`.
**Middleware**
| Funzione | Descrizione |
|---|---|
| `_no_cache(resp)` | `after_request`: imposta `Cache-Control: no-store` su pagine e file statici (niente cache nel browser) |
**Helper**
| Funzione | Descrizione |
|---|---|
| `load_config()` | Legge `config.json` |
| `load_world()` | Legge `data/latest/world.json` e vi fonde i **pianeti noti solo dai rapporti** (player recenti assenti da `universe.xml`), marcandoli `report_only` |
| `fmt_ts(epoch)` | Converte epoch in stringa `YYYY-MM-DD HH:MM UTC` |
| `_report_view(rec, full)` | Trasforma un record d'archivio + payload in una **vista sintetica** per la UI (date, attività, risorse, conteggi per categoria, flag `failed_*`) |
| `_ensure_fresh_data()` | Aggiornamento automatico all'avvio se i dati sono datati |
**Endpoint HTTP**
| Rotte | Descrizione |
|---|---|
| `GET /` | Pagina galassia (`static/index.html`) |
| `GET /rankings` | Pagina classifiche (`static/rankings.html`) |
| `GET /api/bootstrap` | Stato: `server` (nome, galassie, sistemi, velocità…), `myPlayer`, `fetched` (date ultimo aggiornamento per fonte), `reportStats` (n. rapporti/pianeti) |
| `GET /api/galaxy?g=&s=` | Celle del sistema (15 posizioni): per ogni cella pianeta, luna, giocatore (+stato, alleanza, **rank totale/armamenti**), rapporti disponibili |
| `GET /api/planet?g=&s=&p=` | Dettaglio pianeta: info, giocatore con rank, altri pianeti dello stesso giocatore, rapporti del pianeta e della luna |
| `GET /api/player?id=` | Dettaglio giocatore: tutti i suoi pianeti e **tutti** i rapporti (su qualunque pianeta o luna), ordinati per data |
| `POST /api/reports` | Importa un rapporto: body `{"token": "sr-ar-170-<id>"}` (o solo l'id a 40 hex). Idempotente: se già presente ritorna `created:false` |
| `GET /api/reports/<sr_id>` | JSON completo di un rapporto archiviato (`{meta, report}`) |
| `DELETE /api/reports/<sr_id>` | Elimina un rapporto archiviato (file su disco + voce in `index.json`) |
| `GET /api/rankings?type=&status=&start=&end=&q=` | Classifiche filtrate (vedi 4.4) |
| `POST /api/refresh` | Forza `update_data.main()` in background |
### 4.2 `update_data.py` — aggiornamento dati
Scarica l'API pubblica ufficiale di Gameforge (endpoint XML con `?toJson=1`).
**Costanti**
| Costante | Valore |
|---|---|
| `ENDPOINTS` | `serverData`, `universe`, `players`, `alliances`, `localization` |
| `HIGHSCORES` | `highscore_total` (cat.1, type 0), `highscore_military` (cat.1, type 3) |
| `UA` | User-Agent dichiarato nelle richieste |
**Funzioni**
| Funzione | Descrizione |
|---|---|
| `load_config()` | Legge `config.json` |
| `http_get(url, timeout)` | GET con User-Agent |
| `fetch_xml_json(cfg, name, query)` | Scarica `https://s<num>-<comm>.ogame.gameforge.com/api/<name>.xml?toJson=1<query>` e aggiunge `_fetched_at` |
| `fetch_endpoint(cfg, name)` | Wrapper per gli endpoint semplici |
| `fetch_highscore(...)` | *(helper storico)* scarica un highscore |
| `save_latest(name, data)` | Salva in `data/latest/<name>.json` |
| `save_snapshot(name, data)` | Copia giornaliera in `data/snapshots/<name>_<AAAA-MM-GG>.json` (una per giorno; prune oltre `keep_snapshots`) |
| `_as_dict(payload, key, attr_key)` | Normalizza `{"player":[{"@attributes":{...}}]}` → `{id: {...}}` |
| `build_world(cfg)` | Costruisce `world.json` (vedi §6) |
| `_file_time(name)` | Epoch dell'ultimo fetch di un file `latest` |
| `update_highscores(cfg, with_snapshot)` | Scarica i 2 highscore |
| `main()` | Esegue l'aggiornamento; con argomento `--highscore-only` aggiorna **solo** le classifiche (usato dal timer orario) |
### 4.3 `report_store.py` — archivio rapporti di spionaggio
Gestisce il ciclo di vita dei rapporti: parsing API string → recupero dal proxy →
salvataggio JSON → indicizzazione per coordinate.
**Funzioni**
| Funzione | Descrizione |
|---|---|
| `load_config()` | Legge `config.json` |
| `_load_index()` / `_save_index(idx)` | Legge/scrive `data/reports/index.json` |
| `parse_token(token, cfg)` | Valida l'API string `(cr\|sr\|rr\|mr)-<comm>-<num>-<40hex>`; verifica che community/server combacino con la config; accetta anche il solo id esadecimale (assunto `sr`) |
| `fetch_from_proxy(api_string, cfg)` | `GET {proxy}/v1/report/{api_string}/1`; gestisce 429 (limite proxy ~10 req/min) e `RESULT_CODE != 1000` |
| `_defender_info(report)` | Estrae dal payload `generic` i dati del bersaglio (coordinate, tipo pianeta/luna, nomi, data evento, attività, % bottino) |
| `import_report(api_string, cfg)` | Parsa → scarica → salva `data/reports/<sr_id>.json` → aggiorna `index.json`. Ritorna `(record, creato?)`. Idempotente |
| `get_report(rid)` | Legge il file completo `{meta, report}` |
| `delete_report(rid)` | Elimina un rapporto (file + indice, ripulisce `by_coords`). Ritorna `True` se rimosso |
| `list_by_coords(coords, scope)` | Elenca i rapporti di un pianeta. `scope`: `planet` \| `moon` \| `all`, ordinati per `event_timestamp` desc |
| `summary(rec)` | *(utilità)* alias del record sintetico |
| `stats(cfg)` | Conta rapporti totali e pianeti coperti |
### 4.4 Filtri `/api/rankings`
Parametri: `type` (`total`|`military`|`fleet`), `status`, `start`/`end` (range posizione), `q` (testo su nome giocatore, tag o nome alleanza).
Con `type=fleet` (**Top Flotte**) la classifica non usa gli highscore: elenca solo i
giocatori che hanno almeno un rapporto importato, ordinati per **numero di navi**.
Le navi sono sommate prendendo, per ogni pianeta/luna spiato, il **rapporto più
recente** (campo `total_ship_count`, memorizzato nell'indice all'import); la
posizione `rank` è quella della classifica flotte e il valore è in `score`.
Valori di `status` e relativa condizione sullo stato API:
| status | stato API ammesso |
|---|---|
| `all` | qualsiasi |
| `active` | *(vuoto)* |
| `inactive` | `i`, `I` |
| `vacation` | `v` |
| `vacation_inactive` | `vi`, `vI` |
| `admin` | `a` |
> **Nota UI:** la pagina `/rankings` espone solo le voci **Tutti** (`all`),
> **Attivi** (`active`) e **Inattivi** (`inactive`). Gli altri valori restano
> accettati dall'API per compatibilità, ma non sono più selezionabili
> dall'interfaccia.
Le righe restituite includono: id, nome, stato, alleanza (tag+nome), posizione, punteggio, coordinate del primo pianeta (per l'avatar), numero pianeti, flag `mine`, numero di rapporti di spionaggio sul giocatore (`reports`) e coordinate del rapporto più recente (`report_coords`, usata per il salto dal badge "Spie").
### 4.5 `technames.py` — dizionario nomi tech (server)
Mappe `id → nome` per la numerazione **client attuale** (diversa da quella storica):
edifici 1–44, ricerca 106–199, navi 202–219, difese 401–503, edifici lifeform 11101+.
Nota: il frontend ha una copia aggiornata nei propri sorgenti (vedi §5.4, `NAMES`);
`technames.py` resta come riferimento/utilità lato server.
| Funzione | Descrizione |
|---|---|
| `_localized_names()` | Carica i nomi localizzati da `data/latest/localization.json` (fallback) |
| `item_name(category, tid)` | Nome di un elemento (IT → localizzato → `"Categoria #id"`) |
| `category_name(cat)` | Etichetta della categoria |
---
## 5. Frontend — `static/`
### 5.1 `common.js` — utility condivise
| Funzione | Descrizione |
|---|---|
| `esc(s)` | Escape HTML |
| `num(v)` | Formatta numeri in stile italiano (`1.234.567`), `—` se nullo |
| `hashStr(s)` | Hash deterministico (per i colori degli avatar) |
| `api(path, opts)` | `fetch` JSON con gestione errori (`{ok:false, error}`) |
| `icon(name, size)` | Ritorna l'SVG inline dell'icona da `window.ICONS` (icons.js) con `currentColor` |
| `artSVG(seedKey, size, moon)` | Avatar pianeta/luna in SVG: colore scelto da palette in base all'hash, con ombra e (per la luna) crateri |
| `initStars()` | Sfondo stellare su canvas (twinkle); **non bloccante** se canvas non disponibile |
### 5.2 `app.js` — galassia e rapporti
| Funzione | Descrizione |
|---|---|
| `loadBootstrap()` | Popola header: nome universo, range galassie/sistemi, date dati, conteggio spie |
| `loadGalaxy()` | `GET /api/galaxy` e render della tabella |
| `renderGalaxy(data)` | Costruisce le 15 righe: posizione, pianeta (avatar SVG + nome), luna, giocatore (colore per stato + 🏆/⚔ rank), alleanza, badge "spie" |
| `openPlanet(pos, focusMoon)` | Apre il popup del pianeta (`GET /api/planet`) |
| `openPlanetAt(coords, focusMoon)` | Apre il popup di un pianeta date le coordinate `g:s:p`, anche di un altro sistema |
| `openPlayer(pid)` / `renderPlayerPopup(d)` | Apre la finestra **giocatore** (`GET /api/player`): elenco di tutti i pianeti + tutti i rapporti di spionaggio sul giocatore, in un'unica vista |
| `renderPlanetPopup(data, focusMoon)` | Header con avatar; chip giocatore/stato/classifica/luna; altri pianeti del giocatore (cliccabili); **griglia di card** per gli spionaggi del pianeta e della luna |
| `rptCard(rec)` | Card riepilogo di un rapporto (data, attività colorata, bersaglio con **coordinate** `[g:s:p]`, attaccante, conteggi con icone categoria) |
| `openReport(srId)` / `renderReportDetail(meta, rep)` | Vista dettaglio: risorse in tile colorate, meta (difensore/attaccante/attività/bottino/detriti), avvisi `failed_*`, sezioni in **griglia di card** con icona per ogni edificio/ricerca/nave/difesa |
| `techSection(...)` | Griglia card per categoria |
| `itemName(cat, id)` / `itemIconName(cat, id)` | Nome e chiave-icona di un elemento (fallback sulla categoria) |
| `actClass/actLabel(activity)` | Colore/etichetta dell'attività rilevata (0/15/30/60 min) |
| `fmtEv(evt)` | Data evento in formato locale italiano |
| `setSystem/setGalaxy/closePopup/goToCoords` | Navigazione (con clamp sui limiti universo) |
| `setMsg(kind, text)` | Messaggi di stato nella barra di import |
| `importReport()` | Legge l'API string, `POST /api/reports`, aggiorna la vista |
Stato globale: `state = {galaxy, system, maxGalaxy, maxSystem, boot}`.
All'avvio legge `?g=` e `?s=` dalla URL (usato dai click dalle classifiche).
**Colori stato giocatore** (come in galaxy OGame): attivo = giallo · inattivo (`i/I`) =
grigio · vacanza+inattivo = grigio · in vacanza = celeste · admin = rosso.
### 5.3 `rankings.js` — classifiche
| Funzione | Descrizione |
|---|---|
| `initHead()` | Header con nome universo e data aggiornamento classifiche |
| `load()` | `GET /api/rankings` con i filtri correnti |
| `render(d)` | Tabella: posizione (top3 colorati oro/argento/bronzo), avatar pianeta, giocatore (colore stato, ● = tu), alleanza, stato, pianeti, **Spie** (badge col numero di rapporti sul giocatore, cliccabile verso il pianeta del rapporto più recente), punti |
| `goGalaxy(coords)` | Salta alla galassia `/?g=..&s=..` al pianeta del giocatore |
| `readFilters()` | Legge i controlli (stato, range, testo) |
Tabs: **Generale** (total), **Armamenti** (military) e **Top Flotte** (fleet — solo
giocatori con rapporti, ordinati per navi), con icone SVG.
### 5.4 Mappe dati nel frontend (`NAMES`, `ITEM_ICON`)
`NAMES[buildings|research|ships|defense|lfbuildings]` → `{id: nome}` e
`ITEM_ICON[...]` → `{id: chiave icona}`. Nomi allineati al client attuale
(es. `213 → "Corazzata"`, `215 → "Incrociatore da battaglia"`,
`210 → "Sonda di spionaggio"`).
### 5.5 `icons.js` / icone
`window.ICONS = { chiave: {vb, body} }` con **87 icone**: 79 da game-icons.net
(CC BY 3.0, attribuzione nei footer) + icone UI custom (refresh, chevron, chiusura,
logo, avviso). Tutte usano `fill="currentColor"`: il colore segue il CSS di categoria
(`--tc`), quindi **nessuna dipendenza dai font emoji del sistema**.
---
## 6. Dati su disco — `data/`
### 6.1 `data/latest/*.json`
Snapshot grezzi dell'API (`serverData`, `universe`, `players`, `alliances`,
`localization`, `highscore_total`, `highscore_military`), ciascuno con `_fetched_at`.
### 6.2 `data/latest/world.json` (indici precompilati)
| Chiave | Contenuto |
|---|---|
| `server` | numero, community, dominio, nome universo, galassie, sistemi, velocità, versione, lingua, timezone, ACS |
| `fetched` | epoch dell'ultimo fetch per ogni fonte |
| `players` | `{id: {name, status, alliance, alliance_tag, alliance_name}}` |
| `alliances` | `{id: {name, tag, founder}}` |
| `planets` | `{"g:s:p": {id, player, name, coords, moon:{...}}}`. Le voci con `report_only: true` non vengono dall'API pubblica ma da un rapporto di spionaggio (pianeti di player recenti assenti da `universe.xml`) |
| `avatar` | `{playerId: prime-coordinate}` (per gli avatar in classifica) |
| `ranks` | `{playerId: {total, military, total_score, military_score}}` |
### 6.3 `data/snapshots/`
Copie giornaliere `*_AAAA-MM-GG.json` per fonte (ritenzione configurabile).
### 6.4 `data/reports/`
- `index.json`: `{by_coords: {"g:s:p": [sr_id,…], "g:s:p#moon": [… ]}, reports: {sr_id: {meta…}}}` → ricerca immediata per pianeta/luna.
- `<sr_id>.json`: `{"meta": {…}, "report": {generic, details}}` (payload completo del proxy).
---
## 7. Servizi systemd (utente)
| Unità | Quando | Cosa fa |
|---|---|---|
| `ogame-galaxy.service` | sempre attivo | web app su `0.0.0.0:8899` (restart on failure) |
| `ogame-data-update.timer` | giornaliero 03:30 | `update_data.py` (pianeti/giocatori/alleanze + highscore) |
| `ogame-highscore.timer` | ogni ora (:17) | `update_data.py --highscore-only` (classifiche) |
Comandi utili: `systemctl --user status/restart <unità>`,
`journalctl --user -u ogame-galaxy.service -f`, `loginctl show-user principale | grep Linger`
(linger=yes ⇒ i servizi partono al boot senza login).
---
## 8. API esterne utilizzate
| Sorgente | Uso | Note |
|---|---|---|
| `https://s170-ar.ogame.gameforge.com/api/*.xml?toJson=1` | API **pubblica** ufficiale: universe, players, alliances, serverData, localization, highscore | `highscore.xml?category=1&type=0\|3`; cadenze server: universe ~settimanale, players ~giornaliero, highscore ~orario |
| `https://ogapi.faw-kes.de/v1/report/{api_string}/1` | **Proxy community ufficiale** per il recupero dei rapporti (CR/SR/RR/MR) | Nessuna API key necessaria; limite ~10 richieste/min |
| `https://forum.origin.ogame.gameforge.com` | Documentazione ufficiale (thread "OGame API", procedure accesso) | Riferimento del protocollo |
---
## 9. Note operative e di sicurezza
1. **Accesso**: app su `0.0.0.0:8899` **senza autenticazione**: se la rete non è
privata, mettere un reverse proxy con login.
2. **Backup**: copiare `data/` (report inclusi) e `config.json`.
3. **Cache**: i file statici sono serviti con `no-store`: un refresh basta dopo ogni
modifica (nessun `Ctrl+F5` necessario, ma non guasta).
4. **Privacy**: i rapporti restano su questo server; il proxy viene contattato solo
al momento dell'import di un nuovo token (che il giocatore controlla e condivide
volontariamente).
5. **Licenza icone**: game-icons.net, CC BY 3.0 (attribuzione nei footer di pagina).