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.

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
- HTML (Hypertext Markup Language) ist keine Programmiersprache, sondern eine Auszeichnungssprache: Sie beschreibt, welche Teile eine Seite hat (Überschrift, Knopf, Regler, Tabelle). In OmegaSim stehen dort alle Menüs, Optionen mit ihren ⓘ-Texten und die Patchnotes.
- CSS (Cascading Style Sheets) beschreibt das Aussehen: Farben, Abstände, Schriften, Anordnung, Animationen.
- JavaScript ist die Programmiersprache der Webbrowser. Sie macht die Seite lebendig: Sie liest den Controller, rechnet die Fahrphysik, baut die Bluetooth-Pakete und zeichnet die Karte.
- Java ist eine Programmiersprache, in der klassisch Android-Apps geschrieben werden. OmegaSim nutzt sie nur für die dünne Android-Hülle.
- Python ist eine Programmiersprache, die hier nur am Rechner des Entwicklers läuft: zum Bauen, zum Erzeugen von Klängen und Druckvorlagen und zum Auswerten von Bluetooth-Mitschnitten.
- Apps Script ist das JavaScript von Google für Tabellen; damit läuft die Online-Bestenliste der Challenges in einer Google-Tabelle.
- YAML ist ein Textformat für Einstellungen; die Bauaufträge von GitHub Actions sind darin geschrieben. JSON ist ein Textformat für Daten (Sicherungsdatei, Abstimmungen, Aktualisierungsliste).
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
- Bauen (build) heißt, aus den Quelldateien das auslieferbare Ergebnis herzustellen. Bei OmegaSim ist das ein Zusammenfügen ohne Übersetzung: Die Dateien in
src/werden in Namensreihenfolge aneinandergehängt. - Eine Versionsnummer benennt einen Stand eindeutig. OmegaSim zählt sie automatisch aus der Git-Historie.
- Eine Prüfsumme (hier SHA-256, geschrieben als 64 Hex-Ziffern) ist ein Fingerabdruck einer Datei: Ändert sich ein Byte, ändert sich die Prüfsumme. Die Selbstaktualisierung der App lädt nur Dateien, deren Prüfsumme sich geändert hat.
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):
- Quelltext in
src/ändern, nie die erzeugten Dateienindex.html,sw.js,app-update.json. - Neue sichtbare Texte in die Wörterbücher für Englisch und Spanisch in
src/10-ble-explorer.jseintragen. - Einen Selbsttest mit
stAdd(…)insrc/95-selftest.jsergänzen. python tools/build.pyausführen; der Bau meldet fehlende Verweise, verwaiste Wörterbucheinträge, Zugriffe auf nicht vorhandene Elemente und unausgeglichene Klammern.- Die App öffnen, Entwickler → Selbsttest laufen lassen.
- Patchnotes ergänzen,
python tools/bump_version.pyausführen, committen. - 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:
- Kommentare: 27 857 der 91 001 Zeilen in
src/*.js(30,6 %) beginnen mit//. Der Code erklärt sich ausführlich selbst, oft mit Messwerten und dem Anlass einer Änderung. - Prüfcode: Allein der Selbsttest (
95-selftest.js, 22 468 Zeilen) und die Messstände (93-testbench.js, 8 672 Zeilen) machen zusammen gut ein Drittel des JavaScript aus. - Größte Fachdateien:
90-ghosts.js(NPCs, Garage, Ortung; 12 656 Zeilen),70-race.js(Rennen; 7 588),60-track.js(Strecke; 5 869),10-ble-explorer.js(Werkbank und Wörterbücher; 5 342).
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:
- Verweise: Jedes lokale
src=undhref=muss auf eine vorhandene Datei zeigen, auch in auskommentierten Stellen. - Wörterbuch: Eine verwaiste Zeile im englischen Wörterbuch wäre ein Syntaxfehler, der die ganze IIFE abbricht.
- Element-Kennungen: Zugriffe auf Elemente, die es im Dokument nicht gibt.
- Klammern: Zählung im gebauten JavaScript außerhalb von Zeichenketten, Kommentaren und regulären Ausdrücken. Eine Klammer zu viel ist ebenfalls ein Abbruch.
- Service Worker:
sw.jswird aus der Vorlagetools/sw.js.ingeschrieben; eine Marke in der Zeileconst VERSION = …wird durch die Version ausindex.htmlersetzt. - Aktualisierungsliste:
app-update.jsonwird 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
PROJEKT_START– fest der 03.08.2026 (Woche 0), weil die Historie mehrfach zu einem einzigen Commit zusammengefasst wurdePUSH_VERSATZ– gibt einer Woche die Commits zurück, die beim Zusammenfassen verschwunden sind+ 1– die Nummer gilt für den Commit, der gerade erst entstehtVERSION1_WOCHE = 10– ab der Woche vom 12.10.2026 beginnt die Zählung 1.x
Web-App und PWA
- Manifest (
manifest.webmanifest): Name „OmegaSim“, Start./index.html, Darstellung standalone (ohne Browserleisten), Hintergrund- und Themenfarbe#000000, Symbole in 192 und 512 Pixeln, eines davon „maskable“ für runde Symbolformen. - Service Worker (
sw.js): Beim Installieren legt er die „Hülle“ ab (index.html, Manifest, Favicon, Symbole) im Cacheomegasim-<Version>; beim Aktivieren löscht er ältere OmegaSim-Caches. Die wichtigste Entscheidung:index.htmlwird Netz zuerst geholt, nicht Cache zuerst. Weil mehrmals am Tag neu veröffentlicht wird, würde ein Cache-zuerst-Arbeiter sonst eine alte Fassung zeigen, während die neue schon da ist. Nicht zwischengespeichert werden Anfragen, die nicht GET sind (etwa Meldungen an den Mehrspieler-Host), und Anfragen an fremde Ursprünge.[3] - Anmeldung (
src/91-pwa.js): nur in einem sicheren Kontext, nicht überfile://und nicht in der Android-App, denn dort liefert die App ihre Dateien selbst und ein Service Worker wäre ein zweiter, veraltender Speicher. Der Knopf „Installieren“ erscheint nur, wenn der Browser die Installation anbietet (Ereignisbeforeinstallprompt); auf iOS also nie, wo ohnehin kein Web Bluetooth zur Verfügung steht.
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]
- Rechner mit Java 21 und Node 22 vorbereiten.
python3 tools/build.pyundpython3 tools/apk_www.py: Web-App bauen und genau die Dateien ausapp-update.jsonnachandroid-app/wwwkopieren.- Versionsname aus dem Tag (
apk-v1.2.9→1.2.9), Versionscode alsHaupt × 100000 + Neben × 1000 + Push. npm ciundnpx cap sync android: Web-Dateien und Plugins ins Android-Projekt übernehmen.- Signaturschlüssel aus den geheimen Einstellungen des Repositorys bereitstellen.
./gradlew assembleRelease: die APK bauen und signieren.- 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]
- Beim Bau schreibt
app_update.pydie Dateiapp-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. - Beim Start fragt die App diese Liste von der veröffentlichten Seite ab. Ist die Version neuer, zeigt sie einen Hinweis.
- Verlangt die Liste eine höhere APK-Stufe als die installierte, meldet sie „neue APK nötig“ statt eines Web-Updates.
- 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.
- Die WebView wird auf den neuen Ordner umgestellt (Capacitor: serverBasePath).
- 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. - 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
- localStorage: Alles, was die App sich merkt (Einstellungen, Strecken, Autos, Rundenzeiten, Tastenbelegung, Sitzungen), liegt im
localStoragedes Browsers bzw. der WebView. Die Schlüssel beginnen mitchc.,carrera-hybridoderomegasim; die Sitzungshistorie steht unterchc.sessions.v1(höchstens 200 Sitzungen). Seit 0.8.96 bittet die App mitnavigator.storage.persist()darum, dass der Browser diese Daten nicht von selbst räumt. - Kein IndexedDB: Ein Umzug auf die größere Browser-Datenbank IndexedDB ist im Code ausdrücklich erwogen und nicht gemacht.[7]
- Sicherungsdatei: Unter Optionen → Daten sichern schreibt die App alle Schlüssel mit den drei Präfixen in eine JSON-Datei
omegasim-sicherung-JJJJ-MM-TT.jsonund liest sie wieder ein. Erfasst wird nach Präfixregel statt nach Liste, damit auch künftige Schlüssel mitkommen; ein Selbsttest prüft, dass jede bekannte Schlüsselkonstante unter die Regel fällt. - Android: Zusätzlich schreibt die App eine Sicherung nach Dokumente/OmegaSim. Weil eine Neuinstallation die Datei der alten Installation ab Android 11 nicht einfach lesen darf, öffnet sie sie über den Dateiwähler des Systems.
Ü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]
- Schlüssel ist der deutsche Text selbst (Leerraum zusammengefasst), nicht eine Kennung wie
options.rain.label. So musste am Markup nichts ausgezeichnet werden, und ein noch nicht übersetzter Satz bleibt deutsch stehen, statt zu verschwinden. - Spanisch fällt auf Englisch zurück, wenn ein Eintrag fehlt.
- Ein Durchlauf ersetzt Textknoten und die Attribute
title,aria-labelundplaceholder; ausgenommen sindSCRIPT,STYLE,CODE,KBDund alles mitdata-i18n-skip. EinMutationObserverübersetzt Inhalte, die später eingefügt werden. - Zusammengesetzte Texte („8 gemessene Runden, beste …“) laufen über die Funktion
t(); Ansichten, die so zusammensetzen, melden sich an und werden beim Sprachwechsel vor der Übersetzung neu gezeichnet. - Nicht übersetzt werden Protokollzeilen und Bytewerte.
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:
- Jede Zeile nennt ihr Maß, nicht nur grün oder rot, damit man sieht, wie knapp es war.
- „nicht prüfbar“ ist ein eigenes Ergebnis, kein Fehler (etwa Tondateien, die über
file://nicht ladbar sind). - 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]
- Der
AudioContextdarf erst nach einer Nutzerhandlung starten (Regel der Browser gegen Autoplay); OmegaSim legt ihn beim ersten Klick oder Tastendruck an und verlangt die Latenzklasse „interactive“, weil eine Android-WebView sonst einen großen Puffer wählt und der Motor 100 ms und mehr hinter dem Gas herklingt. - Motorklang: Je Auto drei Schleifen (Leerlauf, mittel, hoch), deren Tonhöhe über
playbackRateder Drehzahl folgt und zwischen denen überblendet wird. Die Schleifen liegen als Ogg-Dateien inaudio/(insgesamt 532 Ogg-Dateien, Herkunft inaudio/CREDITS.md). - Erzeugt, nicht aufgenommen: Die Motorschleifen rechnet
tools/engine_synth.pynach dem Ansatz des quelloffenen Projekts engine-sim (MIT-Lizenz): Ein Kolbenmotor klingt nach einer Folge von Verbrennungsstößen, geformt von der Resonanz des Auspuffs. Was Motoren unterscheidet, ist das Zündmuster: Ein gleichmäßig zündender Sechszylinder klingt glatt, ein V8 mit Cross-Plane-Kurbelwelle zündet je Zylinderbank ungleichmäßig und „blubbert“. Die Schleifenlänge ist eine ganze Zahl von 720-Grad-Arbeitsspielen, und alle Filter werden zyklisch angewendet, damit die Schleife ohne Knacken nahtlos wiederholt. Abtastrate 22 050 Hz. - Weitere Python-Werkzeuge erzeugen Umgebungsgeräusche, Boxen- und Hupenklänge (
ambience.py,pit_sounds.py,horn_sounds.py) und Sprachclips für die Ansagen. - Die Motorwerkstatt (
94-engine-workshop.js) rechnet dasselbe Modell im Browser, sodass Änderungen an den elf Stellgrößen sofort hörbar sind.
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
- Heute ist Montag, der 26.10.2026; Montag dieser Woche ist derselbe Tag.
- Vom 03.08.2026 bis 26.10.2026 sind es 84 Tage, 84 / 7 = 12, also
woche = 12. - 12 ≥ 10, also Hauptnummer 1 und Mittelzahl 12 − 10 = 2.
- 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). - 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
- Die Zeilenzählung sagt wenig über Aufwand oder Schwierigkeit; ein Drittel des JavaScript ist Kommentar, ein weiteres Drittel Prüfcode.
- Eine einzelne große IIFE ist empfindlich: Ein Syntaxfehler oder ein Lesezugriff in der temporalen Todeszone lässt die ganze App leer. Der Bau prüft deshalb Klammern und Wörterbuch, bevor die Datei ausgeliefert wird.
localStorageist begrenzt und kann vom Browser geräumt werden (privater Modus, Aufräumen von Websitedaten, auf iPhones nach längerer Zeit ohne Besuch). Die Sicherungsdatei ist die Absicherung.- Web Bluetooth gibt es nur in Chrome und Edge; auf iPhone und iPad läuft die App nur als Info-Bildschirm.
- Die Wörterbücher wachsen von Hand mit; Spanisch ist nicht vollständig und fällt dann auf Englisch zurück.
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
- ↑
tools/build.py,pieces(),build() - ↑
tools/bump_version.py,version(),main() - ↑
tools/sw.js.in, Kopfkommentar - ↑
tools/apk_www.py, Kopfkommentar;src/05-app-bruecke.js - ↑
.github/workflows/android.yml - ↑
tools/app_update.py;OmegaUpdatePlugin.java, Kopfkommentar;src/97b-app.js - ↑
src/98b-sicherung.js, Kopfkommentar - ↑
src/10-ble-explorer.js,I18N_EN,I18N_ES,i18nLookup(),i18nApply(),setLang() - ↑
src/95-selftest.js,stAdd(), Kopfkommentar - ↑
src/80-sound.js;tools/engine_synth.py, Kopfkommentar
Siehe auch
Quellen im Code
tools/build.py–pieces(),build(),check_refs(),check_dict(),check_ids(),check_klammern(),build_sw()tools/bump_version.py–version(),PROJEKT_START,PUSH_VERSATZ,VERSION1_WOCHEtools/app_update.py–schreiben(),APK_STUFEtools/apk_www.py,tools/sw.js.in,manifest.webmanifest.github/workflows/android.yml,.github/workflows/challenges.ymlandroid-app/capacitor.config.json,android-app/package.jsonandroid-app/android/app/src/main/java/de/lroeseler/omegasim/*.javasrc/91-pwa.js,src/97b-app.js,src/98b-sicherung.js,src/97-sessions.jssrc/10-ble-explorer.js–I18N_EN,I18N_ES,setLang()src/95-selftest.js–stAdd(),ST_TESTSsrc/80-sound.js,src/94-engine-workshop.js,tools/engine_synth.py,audio/CREDITS.mdLICENSE
Weblinks
- Browserfassung, Android-App und Quellcode: Links auf der Hauptseite