OmegaSim Wiki

OmegaSim Wiki – die Idee dazu stammt von Gnen54. Hier stehen die offenen Artikel von Nutzer Luuke42 unter der Lizenz CC BY-SA 4.0.

Aufbau von OmegaSim

Stand dieses Artikels: OmegaSim 1.2.9 vom 26.10.2026 · Firmware des Autos: nicht bekannt (OmegaSim liest die Firmware-Version nicht aus)
Dieser Text wurde mithilfe von KI erstellt (Claude) und ist nicht in jedem Punkt von Menschen geprüft. Fehler bitte direkt verbessern oder auf der Diskussionsseite melden, siehe Mitmachen. Die öffentliche Fassung auf luuke42.github.io/omegasim kann älter sein als der hier beschriebene Stand.

Der Aufbau von OmegaSim beschreibt, woraus die App OmegaSim technisch besteht: in welchen Programmiersprachen sie geschrieben ist, wie aus rund 50 Quelldateien eine einzige auslieferbare HTML-Datei wird, wie dieselbe Datei als installierbare Web-App (PWA) und als Android-App läuft, wie sie sich selbst aktualisiert, wo sie Daten speichert, wie sie übersetzt und geprüft wird und wie der Motorklang entsteht. OmegaSim ist im Kern eine Web-Anwendung in JavaScript, die ohne Server und ohne Installation im Browser läuft und mit dem Carrera HYBRID über Bluetooth Low Energy spricht. Die Android-Fassung legt um dieselbe Datei eine dünne Hülle aus Java. Der Quellcode steht unter der MIT-Lizenz.

Startseite von OmegaSim

Auf einen Blick

Merkmal Wert
Hauptsprache JavaScript (rund 79 % der gezählten Zeilen)
Weitere Sprachen HTML, CSS, Python (Werkzeuge), Java (Android-Hülle), Google Apps Script (Bestenliste), YAML (Bauaufträge), PowerShell und Batch (Hilfsskripte)
Auslieferung eine Datei index.html (Fassung 1.2.9: rund 6,6 MB) plus Bilder, Klänge und Symbole
Quellen 46 JavaScript-Dateien und 2 HTML-Teile in src/, in Ladereihenfolge nummeriert
Bau python tools/build.py (fügt src/ zusammen, prüft, schreibt sw.js und app-update.json)
Version python tools/bump_version.py, Schema 1.<Woche>.<Push>
Browser Chrome oder Edge (Web Bluetooth); installierbar als PWA mit Service Worker
Android Capacitor 8.5.2, App-Kennung de.lroeseler.omegasim, APK über GitHub Actions bei Git-Tag apk-v*
Speicher localStorage (kein IndexedDB), Sicherungsdatei als JSON
Sprachen der Oberfläche Deutsch (Quelle), Englisch, Spanisch
Selbsttest 522 Prüfungen (stAdd) in src/95-selftest.js
Lizenz MIT

Grundlagen

Programmiersprache, Auszeichnungssprache, Stilsprache

Web-App, PWA und Service Worker

Eine Web-App ist ein Programm, das im Browser läuft und als Webseite ausgeliefert wird. Eine Progressive Web App (PWA) ist eine Web-App, die man zusätzlich wie eine App „installieren“ kann: mit eigenem Symbol, im Vollbild und ohne Adresszeile. Dafür braucht sie eine Manifest-Datei (Name, Symbole, Farben) und meist einen Service Worker. Das ist ein kleines Skript, das der Browser neben der Seite laufen lässt und das Anfragen an den Server abfangen und aus einem Zwischenspeicher (Cache) beantworten kann; so startet die App auch ohne Netz.

WebView und Capacitor

Eine WebView ist ein Browserfenster ohne Adresszeile, das eine Android-App in sich einbettet. Capacitor ist ein Rahmenwerk, das eine Web-App in eine solche WebView packt und ihr über Plugins Fähigkeiten des Telefons gibt, die eine Webseite nicht hat. Ein Plugin besteht aus nativem Code (hier Java oder Kotlin) und einer Schnittstelle, die JavaScript aufrufen kann. So kann OmegaSim in der App Bluetooth nutzen, obwohl die Android-WebView kein Web Bluetooth kennt.

Bauen, Version, Prüfsumme

Gültigkeitsbereich und „temporale Todeszone“

Alle JavaScript-Dateien ab 10-ble-explorer.js bilden zusammen eine große Funktion, die sofort ausgeführt wird (eine IIFE, immediately invoked function expression). Sie teilen sich damit einen Gültigkeitsbereich: Eine Variable aus Datei 20 ist in Datei 90 sichtbar. Der Preis ist die temporale Todeszone: Wird eine mit let oder const angelegte Variable gelesen, bevor die Zeile mit ihrer Anlage durchlaufen wurde, bricht JavaScript die ganze Funktion ab, und die App bleibt leer. Laut tools/build.py hat das mindestens viermal zu Ladeabbrüchen geführt. Darum steht die Ladereihenfolge sichtbar im Dateinamen (20-, 40-, 90- …), und Variablen, die von mehreren Dateien gebraucht werden, stehen in der frühesten davon.

Bedienung

Für Nutzer ist der Aufbau unsichtbar. Für Mitwirkende sieht der Ablauf einer Änderung so aus (Einzelheiten in der Anleitung für KIs und unter Mitmachen):

  1. Quelltext in src/ ändern, nie die erzeugten Dateien index.html, sw.js, app-update.json.
  2. Neue sichtbare Texte in die Wörterbücher für Englisch und Spanisch in src/10-ble-explorer.js eintragen.
  3. Einen Selbsttest mit stAdd(…) in src/95-selftest.js ergänzen.
  4. python tools/build.py ausführen; der Bau meldet fehlende Verweise, verwaiste Wörterbucheinträge, Zugriffe auf nicht vorhandene Elemente und unausgeglichene Klammern.
  5. Die App öffnen, Entwickler → Selbsttest laufen lassen.
  6. Patchnotes ergänzen, python tools/bump_version.py ausführen, committen.
  7. Für eine neue Android-App einen Git-Tag apk-v… setzen; GitHub Actions baut die APK.

Funktionsweise

Programmiersprachen und ihr Anteil

Gezählt wurden am 26.10.2026 im Repository mit einem kleinen Python-Skript die Zeilen aller Quelltextdateien (jede Zeile einschließlich Leer- und Kommentarzeilen). Bei den HTML-Teilen wurden die Zeilen zwischen <style> und </style> als CSS und die zwischen <script> und </script> als JavaScript gezählt, der Rest als HTML.

Sprache Wo Dateien Zeilen Anteil
JavaScript src/*.js, dazu tools/sw.js.in (131) und Skriptblöcke in den HTML-Teilen (41) 47 91 173 78,9 %
HTML src/00-index.head.html, src/99-index.tail.html 2 8 341 7,2 %
Python tools/*.py 28 8 426 7,3 %
CSS ein <style>-Block im Kopf von 00-index.head.html (1) 4 699 4,1 %
Java android-app/android/app/src/main/java/… 8 2 026 1,8 %
Apps Script tools/challenges-apps-script.gs 1 602 0,5 %
YAML .github/workflows/*.yml 2 145 0,1 %
PowerShell tools/*.ps1, serve.ps1 3 129 0,1 %
Batch *.bat im Hauptordner 2 83 0,1 %
Summe 115 624 100 %

Nicht mitgezählt: die erzeugten Dateien (index.html, sw.js, app-update.json, die Kopie in der APK), fremde Pakete (node_modules, darunter das in Kotlin geschriebene Bluetooth-Plugin), Gradle- und XML-Dateien der Android-Hülle, Daten (JSON, SVG), Entwürfe in mockup/ und Dokumentation.

Bemerkenswert:

Eine Datei aus vielen: tools/build.py

Der Wert der App ist, dass index.html ohne Installation, ohne Server und ohne Bündelungswerkzeug läuft, auch direkt von der Festplatte. Die Quelle ist aber in Teile zerlegt. build.py hängt alle Dateien aus src/ mit der Endung .js oder .html in alphabetischer Reihenfolge byteweise aneinander, ohne Trenner.[1] Danach prüft es das Ergebnis:

  1. Verweise: Jedes lokale src= und href= muss auf eine vorhandene Datei zeigen, auch in auskommentierten Stellen.
  2. Wörterbuch: Eine verwaiste Zeile im englischen Wörterbuch wäre ein Syntaxfehler, der die ganze IIFE abbricht.
  3. Element-Kennungen: Zugriffe auf Elemente, die es im Dokument nicht gibt.
  4. Klammern: Zählung im gebauten JavaScript außerhalb von Zeichenketten, Kommentaren und regulären Ausdrücken. Eine Klammer zu viel ist ebenfalls ein Abbruch.
  5. Service Worker: sw.js wird aus der Vorlage tools/sw.js.in geschrieben; eine Marke in der Zeile const VERSION = … wird durch die Version aus index.html ersetzt.
  6. Aktualisierungsliste: app-update.json wird neu geschrieben (siehe unten).

Mit python tools/build.py --check baut das Werkzeug nichts, sondern vergleicht nur und nennt bei Abweichung Zeichen und Zeile der ersten Differenz.

Versionsnummer: tools/bump_version.py

Die Nummer wird vor jedem Commit aus der Git-Historie berechnet und in die Quelle (<span id="app-version"> in src/00-index.head.html) geschrieben; danach baut das Werkzeug index.html, sw.js und app-update.json neu.[2]

woche  = (Montag dieser Woche − PROJEKT_START) / 7 Tage, abgerundet
push   = Commits seit Montag dieser Woche + 1 + PUSH_VERSATZ[Montag]
Version = 1.(woche − 10).push       wenn woche ≥ 10
Version = 0.woche.push               sonst

Web-App und PWA

Die Android-Hülle

Die Android-App ist eine Capacitor-App mit der Kennung de.lroeseler.omegasim. Sie enthält dieselbe index.html unverändert (bestellt war eine APK, „die identisch zur HTML-App ist“); was nur in der App gebraucht wird, steht in der Seite hinter einer Abfrage von window.Capacitor und schläft im Browser.[4]

Baustein Sprache Aufgabe
MainActivity.java Java Rückfall einer unbestätigten Aktualisierung vor dem Laden, eigene Plugins anmelden, Bildschirm anlassen, Vollbild, Bluetooth beim Schließen trennen
@capacitor-community/bluetooth-le 8.3.0 Kotlin (fremdes Paket) Suchen, Verbinden, Abonnieren, Lesen
OmegaBle (OmegaBlePlugin.java) Java schneller Schreibweg für Fahrbefehle, hartes Schließen von Verbindungen
OmegaPadNativ.java Java Gamepad direkt aus Android lesen
OmegaUpdatePlugin.java Java Selbstaktualisierung der Web-App
OmegaHostPlugin.java, HostServer.java, OmegaHostService.java Java Handy als Host für den WLAN-Mehrspieler
OmegaSicherungPlugin.java Java Sicherungsdatei in Dokumente/OmegaSim, die eine Neuinstallation überlebt
src/05-app-bruecke.js JavaScript baut navigator.bluetooth auf dem Plugin nach

Die Einstellung androidScheme: https lässt die Seite unter dem Ursprung https://localhost laufen. Weil dieser Ursprung bei jeder Aktualisierung gleich bleibt, bleiben auch alle gespeicherten Daten erhalten.

Bau der APK

Der Bauauftrag .github/workflows/android.yml startet, wenn ein Git-Tag der Form apk-v* hochgeladen wird (oder von Hand):[5]

  1. Rechner mit Java 21 und Node 22 vorbereiten.
  2. python3 tools/build.py und python3 tools/apk_www.py: Web-App bauen und genau die Dateien aus app-update.json nach android-app/www kopieren.
  3. Versionsname aus dem Tag (apk-v1.2.9 → 1.2.9), Versionscode als Haupt × 100000 + Neben × 1000 + Push.
  4. npm ci und npx cap sync android: Web-Dateien und Plugins ins Android-Projekt übernehmen.
  5. Signaturschlüssel aus den geheimen Einstellungen des Repositorys bereitstellen.
  6. ./gradlew assembleRelease: die APK bauen und signieren.
  7. Ein GitHub-Release mit der APK unter zwei Namen anlegen (mit Versionsnummer und als OmegaSim.apk).

Ein zweiter Bauauftrag (challenges.yml) holt stündlich zur Minute 17 die Bestenliste der Challenges und committet data/challenges.json nur, wenn sie sich geändert hat.

Selbstaktualisierung der App

Eine neue APK ist nur nötig, wenn sich die Android-Schicht ändert. Alles andere kommt als Web-Aktualisierung:[6]

  1. Beim Bau schreibt app_update.py die Datei app-update.json: Version, die nötige APK-Stufe (APK_STUFE = 2) und für jede Datei Pfad, SHA-256-Prüfsumme und Größe. Für 1.2.9 sind das 638 Dateien mit zusammen rund 23,6 MB. Textdateien werden mit Unix-Zeilenenden gehasht, weil die Arbeitskopie Windows-Zeilenenden hat, die veröffentlichte Fassung aber nicht.
  2. Beim Start fragt die App diese Liste von der veröffentlichten Seite ab. Ist die Version neuer, zeigt sie einen Hinweis.
  3. Verlangt die Liste eine höhere APK-Stufe als die installierte, meldet sie „neue APK nötig“ statt eines Web-Updates.
  4. Auf Tipp lädt das Plugin OmegaUpdate nur die Dateien mit geänderter Prüfsumme; alle anderen kopiert es aus der laufenden Fassung. Jede geladene Datei wird beim Schreiben nachgehasht.
  5. Die WebView wird auf den neuen Ordner umgestellt (Capacitor: serverBasePath).
  6. Die neue Fassung muss sich binnen 25 s als lauffähig melden (bestaetigen()). Tut sie das nicht, etwa wegen eines Syntaxfehlers, schaltet ein Wachhund auf die vorige Fassung zurück; wurde die App vorher beendet, geschieht das beim nächsten Start.
  7. Eine neu installierte APK setzt den Pfad zurück; dann gilt die mitgelieferte Fassung bis zur nächsten Aktualisierung.

Module in src/ in Ladereihenfolge

Datei Inhalt
00-index.head.html Kopf der Seite: CSS, das gesamte Markup aller Menüs und Optionen mit ⓘ-Texten, Doku-Seiten und Patchnotes.
05-app-bruecke.js Eigene IIFE, nur in der Android-App aktiv: baut Web Bluetooth auf dem Capacitor-Plugin nach und bindet OmegaBle an.
10-ble-explorer.js Beginn der gemeinsamen IIFE: BLE-Werkbank (GATT-Baum), crc8(), Protokoll-Hilfen und die Wörterbücher Englisch und Spanisch samt Übersetzungsmechanik.
15-pause.js Die lokale Rennpause: eigene Uhr, Wiederanlauf-Ampel und pausierbare Renn-Timeouts.
20-protocol.js Der 20-Byte-Fahrbefehl, der 45-ms-Herzschlag, das Schreibschloss je Charakteristik und der RC-Gasverlauf.
30-input.js Vorrang zwischen Tastatur, Gamepad, Touch und Neigung, Mitschnitt der Telemetrie und Tastenkürzel.
40-physics.js Die Fahrphysik als Klasse: Reibkreis, Gewichtsverlagerung, Gänge, Reifentemperatur, Ausrollen (siehe Fahrverhalten).
49-spieler.js Spielerplätze für Spieler 2 und 3 im Mehrspieler an einem Gerät.
50-drive.js Die Instanz der Physik, Regler der Optionen, Kalibrierungslauf, Aufnahme und Wiedergabe von Fahrten.
50b-menu-nav.js Menüführung mit Steuerkreuz und Pfeiltasten (WIP).
51-konsole.js Menü im Stil einer Rennspiel-Konsole mit Kacheln und PS5-Tastenlogik (WIP).
51b-tutorial.js Geführte Einführung auf den echten Bildschirmen (experimentell).
59-lade.js Ladeanimation mit sich drehendem Streckenteil.
60-track.js Streckenmodell, Geometrie, Editor, Streckencode, Ideallinie, Live-Scan und Rohcode-Monitor (siehe Strecke, Strecken- und Raum-Editor).
60b-raum.js Der Raumdesigner für eigene Raumformen (experimentell).
60c-streckenbild.js Export der Strecke als PNG-Bild.
60d-virtuell.js Virtuelles Fahren: ein Auto ohne Bluetooth, das dieselben Meldungen erzeugt wie ein echtes (WIP).
61-simlinie.js Die SIMULATOR-Ideallinie: Suche über Linien und Tempopläne mit derselben Physik (WIP).
70-race.js Cockpit, Rundenzählung, Renntypen, Ampel, Ergebnisse, Boxenstopp, Wetter und Reifen (siehe Rennarten).
70b-telemetrie.js Jede Runde als Zwischenzeiten je Streckenteil (WIP).
71-rammen.js Crash-Arten und Rammen.
72-challenges.js Challenges mit Online-Bestenliste.
72b-spuren.js Spuren der schnellsten Runden in der Online-Bestenliste (WIP).
73-meisterschaft.js Meisterschaft aus mehreren Rennen (experimentell).
80-sound.js Der Ton: Motorklang, Effekte, Streckenkulisse.
80b-dualsense.js Trigger-Effekte des DualSense-Controllers über WebHID (WIP).
80c-rennstand.js Sprachansagen zum Rennstand aus vorproduzierten Sprachclips; 80d-rennintro.js das Rennintro vor der Ampel.
90-ghosts.js Garage und Bluetooth-Verbindungen, NPCs, Ortung auf der Strecke, gelbe Flagge, Gamepad-Belegung (siehe NPC-Fahrverhalten, Safety Car und Reinigungswagen).
90b-sim.js Ein Rennen ohne Autos simulieren und ablaufen lassen.
90c-macro-track.js Strecke aus einer Aufnahme lernen (WIP).
90d-challenger.js Rolle Challenger: ein echtes Auto fährt gegen eine Bestzeit (experimentell).
91-pwa.js Anmeldung des Service Workers und Installationsknopf.
92-coding-school.js Programmierschule: Gas- und Lenkkurven zeichnen, simulieren, an ein Auto schicken.
92b-num-trainer.js Binär- und Hex-Trainer (siehe Hexadezimalsystem).
93-testbench.js Messstände für Selbsttest und Konsole (OMEGA_TEST).
94-engine-workshop.js Motorwerkstatt: das Klangmodell mit elf Stellgrößen zum Ausprobieren.
95-selftest.js Der Selbsttest.
96-home.js Startbildschirm; die Zahl der Einstellungen wird zur Laufzeit gezählt.
97-sessions.js Sitzungsdaten: bis zu 200 beendete Sitzungen mit Rundenzeiten.
97b-app.js Nur in der Android-App: Selbstaktualisierung, Host ohne PC, Vollbild.
97b-fahrstil.js Fahrstil-Kennwerte aus Gas, Bremse und Lenkung je Takt (WIP).
98-presets.js Voreinstellungen (Fahrgefühle) und Austausch von Abstimmungen als Text.
98b-sicherung.js Sicherung aller gespeicherten Daten in einer Datei.
98c-opt-info.js Die ⓘ-Fenster der Optionen.
98d-profile.js Presets je Menü und globale Profile.
98e-statistik.js Statistiken aus den gespeicherten Sitzungen; 98ea-statistik-bereinigen.js und 98eb-statistik-zeiten.js die Statistikbereinigung.
98f-studie.js Die Studie OmegaLab (experimentell).
98g-telemetrie-seite.js Vergleich zweier Runden unter Optionen → Telemetrie (WIP).
98h-pause.js Die Rennpause im Cockpit (Symbol, Bedienung).
99-index.tail.html Schluss: schließt die IIFE und das Dokument.

Daten und Speicher

Übersetzungen

Die Oberfläche ist auf Deutsch geschrieben. Übersetzt wird zur Laufzeit über zwei Wörterbücher in src/10-ble-explorer.js: I18N_EN (mindestens 2 824 einzeilige Einträge) und I18N_ES (mindestens 1 102; gezählt wurden Zeilen der Form "…": "…").[8]

Selbsttest

Der Selbsttest misst die laufende App auf Knopfdruck (Entwickler → Selbsttest; mit ?st=Name in der Adresse nur passende Tests). Jeder Test wird mit stAdd(name, fn) angemeldet; fn gibt { ok, mass } zurück.[9] Drei Grundsätze stehen im Kopf der Datei:

  1. Jede Zeile nennt ihr Maß, nicht nur grün oder rot, damit man sieht, wie knapp es war.
  2. „nicht prüfbar“ ist ein eigenes Ergebnis, kein Fehler (etwa Tondateien, die über file:// nicht ladbar sind).
  3. Die Tests benutzen dieselben Funktionen wie die App, keine nachgebauten.

Beispiele: „Aufbau durchgelaufen“ verlangt mehr als 30 Regler und mehr als 400 Wörterbucheinträge; „Protokoll: CRC-8“ rechnet die Prüfsumme eines aufgezeichneten Pakets der Original-App nach. Zwischen den Tests gibt der Lauf dem Browser über einen MessageChannel Luft statt über setTimeout, weil Zeitgeber in verborgenen Fenstern gedrosselt werden. Am 26.10.2026 enthielt die Datei 522 Aufrufe von stAdd.

Klangerzeugung

Der Ton läuft über die Web Audio API des Browsers.[10]

Lizenz

Der Quellcode steht unter der MIT-Lizenz (Datei LICENSE): Er darf frei genutzt, verändert und weitergegeben werden, solange der Lizenzhinweis erhalten bleibt. Screenshots der App dürfen deshalb im Wiki verwendet werden. Für eingebundene Klänge gilt, was audio/CREDITS.md nennt.

Beispiele

Die Versionsnummer 1.2.9

  1. Heute ist Montag, der 26.10.2026; Montag dieser Woche ist derselbe Tag.
  2. Vom 03.08.2026 bis 26.10.2026 sind es 84 Tage, 84 / 7 = 12, also woche = 12.
  3. 12 ≥ 10, also Hauptnummer 1 und Mittelzahl 12 − 10 = 2.
  4. Vor dem Commit von 1.2.9 standen seit Montag 0:00 acht Commits in der Historie, und für diese Woche ist kein Versatz eingetragen: push = 8 + 1 + 0 = 9 (aus der Nummer zurückgerechnet).
  5. Ergebnis: 1.2.9. Die APK dazu bekäme den Versionscode 1 × 100000 + 2 × 1000 + 9 = 102009.

Was eine Aktualisierung lädt

Angenommen, zwischen zwei Fassungen ändert sich nur index.html (rund 6,6 MB). Die App vergleicht die 638 Prüfsummen der neuen Liste mit denen der laufenden Fassung, findet eine Abweichung, lädt nur diese eine Datei und kopiert die übrigen 637 (rund 17 MB) lokal. Ohne Prüfsummen müssten alle 23,6 MB erneut geladen werden.

Ein Anteil nachgerechnet

JavaScript: 91 001 Zeilen in src/*.js + 131 in tools/sw.js.in + 41 in Skriptblöcken = 91 173. Gesamtzahl 115 624. Anteil 91 173 / 115 624 = 0,7885, also 78,9 %.

Grenzen und bekannte Probleme

Versionsgeschichte

Version Änderung
früh Die Quelle war eine einzige HTML-Datei mit rund 14 000 Zeilen; Aufteilung in src/ mit build.py, das die alte Datei Byte für Byte reproduzieren musste (genaue Fassung nicht gesichert)
nach 0.5.38 bump_version.py baut auch sw.js mit (von 0.5.34 bis 0.5.38 trug der Cache die jeweils vorige Version)
0.8 Android-App (experimentell) mit Bluetooth-Brücke, Host ohne PC und Updates ohne Neuinstallation
0.8.96 Bitte um dauerhaften Speicher (storage.persist())
0.9.6 Sicherungsdatei in der App, die eine Neuinstallation überlebt; APK-Releases
0.9.30 fester Projektstart für die Versionszählung nach dem Zusammenfassen der Historie
1.0 (12.10.2026) Versionsschema 1.<Woche>.<Push>
1.0 Kachel „Daten sichern“

Einzelnachweise

  1. ↑ tools/build.py, pieces(), build()
  2. ↑ tools/bump_version.py, version(), main()
  3. ↑ tools/sw.js.in, Kopfkommentar
  4. ↑ tools/apk_www.py, Kopfkommentar; src/05-app-bruecke.js
  5. ↑ .github/workflows/android.yml
  6. ↑ tools/app_update.py; OmegaUpdatePlugin.java, Kopfkommentar; src/97b-app.js
  7. ↑ src/98b-sicherung.js, Kopfkommentar
  8. ↑ src/10-ble-explorer.js, I18N_EN, I18N_ES, i18nLookup(), i18nApply(), setLang()
  9. ↑ src/95-selftest.js, stAdd(), Kopfkommentar
  10. ↑ src/80-sound.js; tools/engine_synth.py, Kopfkommentar

Siehe auch

Quellen im Code