forked from xziino/ff14-mitigator
Two-tab app: report viewer + analysis tab with AoE timeline, per-player mitigation icons (local XIVAPI PNGs), and fight-wide buff/debuff window tracking. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
185 lines
8.7 KiB
Markdown
185 lines
8.7 KiB
Markdown
# ff14-mitigator — FFLogs Mitigation Analyzer
|
||
|
||
## Projekt
|
||
PHP/HTML/JS-Tool zum Analysieren von FFXIV-Raidlogs via FFLogs OAuth2 PKCE + GraphQL API.
|
||
Kein Framework, kein Composer, kein npm — Plain PHP für Shared Hosting.
|
||
|
||
Zwei Tabs:
|
||
- **Report-Tab**: Report-Code eingeben, Fight auswählen, Raw-JSON-Ausgabe
|
||
- **Analyse-Tab**: Spielerübersicht + AoE-Timeline mit Mitigation-Tracking
|
||
|
||
## Architektur & Konventionen
|
||
|
||
### Trennung von PHP, HTML und JS
|
||
- **PHP-Logik** gehört ausschließlich in `index.php` (und API/Auth-Endpunkte). Keine Geschäftslogik in Templates.
|
||
- **HTML** gehört in `templates/`. Jede logisch in sich geschlossene Komponente ist eine eigene Datei.
|
||
- **CSS** gehört in `css/`. Jede CSS-Datei hat einen klar abgegrenzten Scope (base, layout, components, analysis).
|
||
- **JavaScript** gehört in `js/`. Keine Inline-Scripts in Templates außer dem `<script src="...">` Tag in `page.php`.
|
||
|
||
### Template-System
|
||
`index.php` setzt alle Variablen und ruft dann `require templates/page.php` auf. Templates sind reine Ausgabe — sie lesen Variablen aus dem Scope, setzen aber keine.
|
||
|
||
### Geteilter JS-State
|
||
`window.App` in `app.js` hält den gemeinsamen State für alle Tabs:
|
||
```js
|
||
window.App = { reportCode, fightId, fightStart, fightEnd }
|
||
```
|
||
`window.analysisTab` (definiert in `analysis.js`) stellt Hooks bereit:
|
||
- `onFightSelected()` — wird von `app.js` aufgerufen wenn ein Fight gewählt wird
|
||
- `onTabOpen()` — wird von `tabs.js` aufgerufen wenn der Analyse-Tab geöffnet wird
|
||
- `reset()` — wird von `app.js` aufgerufen wenn ein neuer Report geladen wird
|
||
|
||
## Dateistruktur
|
||
```
|
||
index.php — PHP-Logik: Auth-Check, Variablen, require page.php
|
||
config.php — Konstanten (CLIENT_ID, URIs) + session_start_safe()
|
||
templates/
|
||
page.php — HTML-Skeleton (head, body), routet zu login oder app
|
||
login.php — Login-Overlay (nicht authentifiziert / Token abgelaufen)
|
||
topbar.php — Topbar mit Logo + Tab-Navigation + Token-Ablaufzeit
|
||
tab-report.php — Report-Tab: includes report-form, fight-select, output-card
|
||
tab-analysis.php — Analyse-Tab: Spieler-Grid + AoE-Timeline HTML
|
||
report-form.php — Report-Code-Eingabe Card
|
||
fight-select.php — Fight-Auswahl Dropdown Card
|
||
output-card.php — Terminal-Ausgabe Card + Initial-Hint
|
||
css/
|
||
base.css — CSS-Variablen, Reset, Basis-Styles, Feedback-Klassen
|
||
layout.css — App-Shell, Topbar, Tabs, Login-Overlay, Form-Helpers
|
||
components.css — Cards, Inputs, Buttons, Badges, Terminal
|
||
analysis.css — Spieler-Grid, AoE-Timeline, Mitigation-Icons
|
||
js/
|
||
app.js — Formular, Fight-Dropdown, Fetch, window.App State
|
||
tabs.js — Tab-Switching, ruft window.analysisTab.onTabOpen() auf
|
||
analysis.js — Analyse-Tab: Daten laden, Spieler rendern, Timeline rendern
|
||
auth/
|
||
start.php — PKCE generieren, Session speichern, Redirect zu FFLogs
|
||
callback.php — Code gegen Token tauschen, Token in Session speichern
|
||
api/
|
||
fight.php — POST-Endpunkt: Fight-Liste via GraphQL → JSON
|
||
analysis.php — POST-Endpunkt: Spieler + AoE-Events + Mitigations → JSON
|
||
assets/
|
||
icons/mitigation/ — Lokal gespeicherte Ability-Icons (PNG, von XIVAPI)
|
||
debug/
|
||
schema.php — Einmaliges Schema-Explorer Tool (nicht produktiv deployen)
|
||
```
|
||
|
||
## Design-System
|
||
CSS-Variablen in `css/base.css`:
|
||
- Hintergründe: `--bg0` (#08090d) bis `--bg3` (#1c2130), `--bgcard` (#121620)
|
||
- Akzentfarbe: `--gold` (#c8a84b)
|
||
- Text: `--t1` (hell) / `--t2` (gedimmt) / `--t3` (sehr gedimmt)
|
||
- Farben: `--blue`, `--green`, `--red`, `--orange`
|
||
- Fonts: `--font-d` Cinzel (Titel/Logo), `--font-b` Inter (Body)
|
||
- Border-Radius: `--r` (klein), `--rl` (groß)
|
||
|
||
## Analyse-Tab — Konzepte & Entscheidungen
|
||
|
||
### AoE-Erkennung
|
||
- Nur `calculateddamage` Events (post-Mitigation Snapshot) — **nicht** `damage` (Application), da sonst doppelte Events
|
||
- Gruppierung: 300ms-Zeitfenster × `abilityGameID` → Bucket
|
||
- AoE = Bucket mit ≥ 3 unterschiedlichen `targetID`s
|
||
- Auto-Attacks und Fähigkeiten mit `abilityGameID ≤ 7` werden gefiltert
|
||
- Tick-Schaden (`ev['tick'] = true`) wird ignoriert
|
||
|
||
### Spielernamen statt IDs
|
||
- `masterData.abilities` (gameID → Name) wird zusammen mit `playerDetails` in einem einzigen Query abgerufen
|
||
- `$mitigIdMap` (gameID → Mitigation-Meta) wird nur für Abilities gebaut, die tatsächlich im Report vorkommen
|
||
|
||
### Mitigation-Tracking
|
||
Getrackte party-wide Buffs + Boss-Debuffs (definiert in `MITIGATION_ABILITIES` in `api/analysis.php`):
|
||
|
||
| Ability | DR | Typ |
|
||
|---|---|---|
|
||
| Passage of Arms | 15% | buff |
|
||
| Divine Veil | Barrier | buff |
|
||
| Shake It Off | Barrier | buff |
|
||
| Dark Missionary | 10% | buff |
|
||
| Heart of Light | 10% | buff |
|
||
| Temperance | 10% | buff |
|
||
| Sacred Soil | 10% | buff |
|
||
| Expedient | 10% | buff |
|
||
| Fey Illumination | 5% | buff |
|
||
| Collective Unconscious | 10% | buff |
|
||
| Holos | 10% | buff |
|
||
| Kerachole | 10% | buff |
|
||
| Panhaima | Barrier | buff |
|
||
| Troubadour | 15% | buff |
|
||
| Tactician | 15% | buff |
|
||
| Shield Samba | 15% | buff |
|
||
| Reprisal | 10% | debuff |
|
||
| Feint | 10% | debuff |
|
||
| Addle | 10% | debuff |
|
||
|
||
**Fenster-Tracking:** `applybuff`/`applydebuff` öffnet Fenster (nur erstes pro `abilityId_sourceId`-Key, da party-wide Buffs einmal pro Partymitglied feuern). `removebuff`/`removedebuff` schließt das Fenster. Noch offene Fenster am Fight-Ende werden mit `endTime` geschlossen.
|
||
|
||
### Mitigation-Icons
|
||
- Icons lokal gespeichert in `assets/icons/mitigation/` als PNG
|
||
- Quelle: XIVAPI v2 (`https://v2.xivapi.com/api/asset?path=...&format=png`)
|
||
- Icon-Pfade per Action-Row-ID abgerufen: `https://v2.xivapi.com/api/sheet/Action/{id}?fields=Name,Icon`
|
||
- Dateinamen: kebab-case des Ability-Namens (z.B. `passage-of-arms.png`)
|
||
- Mapping in `analysis.js`: `MITIG_ICONS` Objekt (Ability-Name → lokaler Pfad)
|
||
- Darstellung: 16×16px Icon unter jedem Spieler-Target, kein Text, `title`-Tooltip mit Name + DR% + Caster
|
||
|
||
## Geplante Features
|
||
|
||
### HP-Balken pro Spieler (nächstes Feature)
|
||
Für jeden Spieler in der AoE-Timeline einen 3-Segment-Balken anzeigen, der den HP-Stand im Kontext des Treffers zeigt:
|
||
|
||
```
|
||
[████████████▓▓▓░░░░░░░░]
|
||
^verbleibend ^Schaden ^vorher schon fehlend
|
||
```
|
||
|
||
- **Grün** (links): HP nach dem Treffer (als % von MaxHP)
|
||
- **Rot/Orange** (Mitte): erlittener Schaden (als % von MaxHP)
|
||
- **Dunkelgrau** (rechts): HP die bereits vor dem Treffer fehlten
|
||
- Grünton dynamisch: >50% grün, 25–50% gelb/amber, <25% rot
|
||
- Balken sitzt direkt unter Name+Schaden-Zeile in der Spieler-Box
|
||
|
||
**Datenbedarf:** FFLogs `calculateddamage` Events enthalten `hitPoints` (HP vor dem Treffer) und `maxHitPoints`. Diese müssen in `api/analysis.php` pro Target mitgegeben werden.
|
||
|
||
**Backend-Änderung:** In `$buckets[$key]['targets'][$tgtId]` zusätzlich `hp` und `maxHp` aus dem letzten Event speichern und in der Response durchreichen.
|
||
|
||
## Konfiguration
|
||
- `config.php`: `CLIENT_ID`, `REDIRECT_URI`, `DEV_MODE` anpassen
|
||
- `DEV_MODE = true` deaktiviert SSL-Verifizierung (nur lokal, nie in Produktion)
|
||
- `session.cookie_secure` ist bei `DEV_MODE` automatisch `false`
|
||
|
||
## FFLogs API
|
||
- OAuth2 PKCE (kein Client Secret, öffentliche App)
|
||
- App registrieren: https://www.fflogs.com/api/clients/
|
||
- GraphQL Endpoint (user-scoped): `https://www.fflogs.com/api/v2/user`
|
||
- Token Endpoint: `https://www.fflogs.com/oauth/token`
|
||
- Kein Refresh Token für öffentliche Clients — abgelaufene Sessions starten PKCE neu
|
||
- Event-Typen: `calculateddamage` (Snapshot nach Mitigation, den wir nutzen) vs. `damage` (Application, ignorieren)
|
||
|
||
## XIVAPI
|
||
- Basis-URL: `https://v2.xivapi.com`
|
||
- Action-Lookup per Row-ID: `/api/sheet/Action/{id}?fields=Name,Icon`
|
||
- Asset-Download: `/api/asset?path={tex_path}&format=png`
|
||
- Icons nicht hotlinken — lokal speichern (Community-Service, kein SLA)
|
||
- XIVAPI-Suche (`/api/search`) gibt bei manchen Abilities ClassJob-Daten statt Action-Daten zurück → direkt per Row-ID abrufen
|
||
|
||
## Repository
|
||
- Remote: `https://git.epow0.org/xziino/ff14-mitigator`
|
||
- Platform: Gitea (git.epow0.org)
|
||
- Branch: `main`
|
||
|
||
## Lokale Entwicklung
|
||
```
|
||
php -S localhost:8080
|
||
```
|
||
Dann `http://localhost:8080` im Browser öffnen.
|
||
Redirect URI in FFLogs App und config.php: `http://localhost:8080/auth/callback.php`
|
||
|
||
## Bekannte Schema-Infos (ReportFight)
|
||
Verfügbare aber noch nicht genutzte Felder: `friendlyPlayers`, `enemyNPCs`,
|
||
`lastPhase`, `standardComposition`, `hasEcho`, `combatTime`, `phaseTransitions`
|
||
Vollständiges Schema: siehe `debug/schema.php` oder `fflogs-schema.json`
|
||
|
||
## Deployment
|
||
- `DEV_MODE` auf `false` setzen
|
||
- `REDIRECT_URI` auf produktive HTTPS-URL anpassen
|
||
- `debug/` Ordner nicht deployen
|
||
- `assets/` Ordner deployen (enthält lokale Icons)
|