352 lines
18 KiB
Markdown
352 lines
18 KiB
Markdown
# 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).
|