Überblick#
Deterministische Retro-Multiplayer-Engine auf dem ESP32-S3 mit ESP-IDF.
Architektur: Fixed-Step-Simulation (nur Host) -> Snapshot-Replikation -> reines Snapshot-Rendering -> DMA-Ausgabe.
Keine Client-Prediction. Kein Rendering im ISR-Kontext. Pixelidentische Frames.
Technische Highlights#
- Double-Buffered Rendering
- Display-Ausgabe per DMA
- Tile-/Sprite-first-Pipeline
- Deterministische Synchronisation über ESP-NOW
- Minimaler Netzwerk-Overhead
- Auf realer Hardware validiert
Wichtige Erkenntnisse#
- Determinismus vereinfacht das Debugging
- Rendering muss strikt von der Simulation getrennt sein
- Einfache Netzwerkmuster sind robuster als clevere Hacks
Technologien#
- ESP32-S3
- ESP-IDF
- ESP-NOW
- SPI-DMA-Display-Pipeline
- ILI9341
- Double-Buffered Rendering
Ablaufdiagramm
flowchart TD
classDef phase fill:#eef4ff,stroke:#2563eb,stroke-width:1px,color:#0f172a;
classDef io fill:#e8fff0,stroke:#16a34a,stroke-width:1px,color:#0f172a;
classDef invariant fill:#fff7e6,stroke:#d97706,stroke-width:1px,color:#0f172a;
A["Phase 1: Simulation (nur Host)"] --> B["Phase 2: Snapshot und Replikation"]
B --> C["Phase 3: Rendering (alle Geräte)"]
C --> D["Phase 4: Ausgabe per SPI DMA"]
A --> A1["Fixed-Step-Update von Gameplay und Animationszustand"]
B --> B1["Vollständiger Snapshot ist die Netzwerk-Wahrheit"]
C --> C1["Reine Snapshot-Funktion (idempotent)"]
D --> D1["Back rendern -> Back ausgeben -> Front synchronisieren -> Swap"]
D --> E["LCD sieht nur vollständige, konsistente Frames"]
E --> F["Pixelidentische Ausgabe bei identischen Snapshots"]
class A,B,C,D phase;
class A1,B1,C1,D1 io;
class E,F invariant;
Details
RetroLink folgt einem strikten Systemvertrag. Das sind Architekturregeln, keine Stilfragen.
### Phasenreihenfolge (ändert sich nie)
- `Simulation -> Snapshot -> Rendering -> Present`
- Nur der Host simuliert den autoritativen Zustand.
- Clients simulieren, glätten, prognostizieren oder erfinden niemals Zustand.
- Rendering arbeitet ausschließlich auf Snapshots und muss idempotent sein.
### Regeln für deterministisches Rendering
- Jede sichtbare Pixeländerung muss aus genau einem Simulationstakt stammen.
- `render()` und Draw-Helper dürfen keine `millis()`, `micros()`, Timer oder Zufallsquellen verwenden.
- Rendering darf keinen Gameplay-Zustand mutieren.
- Keine rollenabhängigen Verzweigungen in der Renderlogik.
### Display- und Buffer-Regeln
- Immer ausschließlich in den Back Buffer rendern.
- Den Back Buffer immer über die SPI-DMA-Queue von RetroGoDisplay ausgeben.
- Nach dem Present: Back nach Front kopieren, dann swappen.
- Der Front Buffer dient nur der Synchronisation, nie als Renderziel.
- Tearing, Flicker, Ghosting oder Frame-Desync sind kritische Defekte.
### Interrupt- und Netzwerksicherheit
- Keine Display-API-Aufrufe aus ISR- oder ESP-NOW-Callbacks.
- Im ISR-Pfad dürfen nur Paketdaten gespeichert und Flags gesetzt werden.
- Display-Arbeit wird in den Main Loop bzw. Task-Kontext verschoben.
- SPI-Transaktionsdaten müssen in stabilem Speicher liegen, nie auf dem Stack.
### Speicher- und Asset-Regeln
- DMA-Buffer müssen in internem DMA-fähigem RAM liegen, nicht in PSRAM.
- Große temporäre Decode-Buffer dürfen PSRAM verwenden.
- Kern-Gameplay-Grafiken bestehen aus Tiles und Sprites, nicht aus Full-Frame-RGB565-Assets.
- PNG-Assets in SPIFFS sind für UI- und Ladepfade erlaubt, dekodiert außerhalb von `render()`.
### Warum das funktioniert
Dieser Vertrag hält die Multiplayer-Ausgabe über mehrere Geräte hinweg deterministisch und bewahrt gleichzeitig die Display-Stabilität auf begrenzter Hardware. Die Architektur wurde durch reale Fehlerfälle geprägt: White Screens, Bus-Korruption, DMA-Bugs durch Stack-Lebensdauer und Desynchronisation. Deshalb ist sie bewusst streng formuliert.
Gallery
4 images