Startskript, Regeln und Probelauf für M1b

play.bat baut die Brücke und startet das Spiel. CLAUDE.md beschreibt
jetzt Kern, Brücke und Hülle mit allen Prüfbefehlen, die Versionsregel
für festgeschriebene Werte und die Zuschreibung in Commits. Ein
Fragebogen begleitet den ersten Probelauf.

Der erste Godot-Import in einem frischen Checkout stürzt beim Beenden
ab; play.bat wiederholt ihn deshalb einmal.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Steven Wroblewski
2026-10-01 12:26:28 +02:00
parent 754d7f1e92
commit 68d063209c
4 changed files with 83 additions and 13 deletions
+2
View File
@@ -1,2 +1,4 @@
# Same line endings on every machine.
* text=auto eol=lf
# Windows batch files need CRLF.
*.bat text eol=crlf
+40 -13
View File
@@ -9,48 +9,75 @@ Das verbindliche Konzept steht in [docs/KONZEPT.md](docs/KONZEPT.md). Lies es, b
| Ordner | Inhalt |
|---|---|
| `core/` | Rust-Kern: die gesamte gemeinsame Welt. Ohne Zugriff auf die Engine, ohne fremde Crates. |
| `bridge/` | Rust-Brücke (GDExtension): macht den Kern für Godot aufrufbar und enthält keine Spielregeln. |
| `game/` | Godot-Projekt mit der GDScript-Hülle: Darstellung, Eingabe, Oberfläche, Dateien. |
| `docs/KONZEPT.md` | Spielkonzept und Entscheidungen |
| `docs/superpowers/plans/` | Umsetzungspläne |
| `docs/playtests/` | Fragebögen für Playtests |
Die Brücke zu Godot (`bridge/`), das Godot-Projekt (`game/`) und die Inhaltsdateien (`content/`) kommen mit späteren Plänen dazu.
Faustregel: Was im Speicherstand steht oder alle Spieler betrifft, gehört in den Kern.
Faustregel: Was im Speicherstand steht oder alle Spieler betrifft, gehört in den Kern. Die Hülle darf Gleitkommazahlen nutzen, weil sie nichts an der gemeinsamen Welt ändert; sie schickt nur Befehle.
## Regeln für den Kern
Alle Spieler simulieren die Welt selbst und tauschen nur Befehle aus (Gleichschritt). Das funktioniert nur, wenn der Kern auf jedem Rechner exakt gleich rechnet. Deshalb gilt für den Quelltext des Kerns in `core/src/`. Die Tests in `core/tests/` dürfen zum Beispiel Dateien lesen.
Alle Spieler simulieren die Welt selbst und tauschen nur Befehle aus (Gleichschritt). Das funktioniert nur, wenn der Kern auf jedem Rechner exakt gleich rechnet. Deshalb gelten für den Quelltext des Kerns in `core/src/` diese Regeln (die Tests in `core/tests/` dürfen zum Beispiel Dateien lesen):
- Nur ganze Zahlen, keine Gleitkommazahlen.
- Keine Uhr, keine Dateien, kein Netzwerk, keine Threads, keine Umgebungsvariablen.
- Keine Uhr, keine Dateien, kein Netzwerk, keine Threads, keine Umgebungsvariablen, kein globaler veränderlicher Zustand.
- Zufall nur über `rng::Rng` mit fester Startzahl.
- Keine Sammlung mit wechselnder Reihenfolge. Statt einer Hash-Tabelle nimmst du `Vec`, `BTreeMap` oder `BTreeSet`.
- Die Welt ändert sich nur durch einen `Command` in `World::step`. Einzige Ausnahme ist `World::set_all_offline`: Es darf nur direkt nach dem Laden eines Speicherstands aufgerufen werden, bevor jemand beigetreten ist.
- Alles, was zum Zustand der Welt gehört, steht im Snapshot. Wer ein Feld ergänzt, ergänzt es in `snapshot.rs` und erhöht `SNAPSHOT_VERSION`.
- Wo eine Rechnung im Spiel überlaufen kann, steht eine ausdrückliche Wahl: `saturating_`, `wrapping_` oder `checked_`. Taktzähler (`u64` bei 20 Takten pro Sekunde) laufen praktisch nie über und werden normal addiert. Werte aus geladenen Dateien müssen deshalb beim Laden geprüft werden.
- Die Welt ändert sich nur durch einen `Command` in `World::step`. Einzige Ausnahme ist das Laden eines Speicherstands mit `World::load`, das alle Spieler abmeldet.
- Wer eine Sitzung aufzeichnet, ruft `Recording::step` statt `World::step` auf. So enthält die Aufzeichnung genau die ausgeführten Befehle.
- Alles, was zum Zustand der Welt gehört, steht im Snapshot. Wer ein Feld ergänzt, ergänzt es in `snapshot.rs`, prüft es in `check_restorable` und erhöht `SNAPSHOT_VERSION`.
- Wo eine Rechnung im Spiel überlaufen kann, steht eine ausdrückliche Wahl: `saturating_`, `wrapping_` oder `checked_`. Taktzähler laufen praktisch nie über und werden normal addiert: Die Welt zählt in `u64`, eine Aufzeichnung in `u32`, was bei 20 Takten pro Sekunde für über sechs Jahre reicht. Takte aus geladenen Dateien begrenzt `MAX_RESTORABLE_TICK`.
- Keine fremden Crates im Kern.
Der Test `core/tests/forbidden_apis.rs` durchsucht den Quelltext des Kerns nach verbotenen Begriffen. Sie dürfen dort auch in Kommentaren nicht vorkommen.
Drei Wächter prüfen das. `core/clippy.toml` sperrt Pfade wie `std::collections::HashMap` oder `std::fs::read`, auch über Aliase und gruppierte Importe. Der Test `core/tests/forbidden_apis.rs` durchsucht zusätzlich den Text nach verbotenen Begriffen; sie dürfen dort auch in Kommentaren nicht vorkommen. `core/tests/no_dependencies.rs` hält die Abhängigkeiten leer.
## Regeln für Brücke und Hülle
- Die Brücke übersetzt nur. Neue Spielregeln gehören in den Kern, nie in `bridge/` oder `game/`.
- GDScript mit Typangaben schreiben (`var x := …`, Rückgabetypen an jeder Funktion).
- Eingabeaktionen legt `game/scripts/input_setup.gd` an, nicht `project.godot`.
- Alle Texte, die Spieler sehen, stehen in `game/i18n/texts.csv` (Deutsch und Englisch) und werden über `tr()` geholt.
- Tests, die Dateien schreiben, nutzen `TestFiles.temporary_dir()`, nie den echten Benutzerordner.
- Godot erzeugt beim Import zu jedem Skript eine `.uid`-Datei. Sie gehört ins Archiv. Der Ordner `game/.godot/` gehört nicht hinein.
## Prüfen
Vor jedem Commit müssen alle drei Befehle fehlerfrei laufen:
Vor jedem Commit müssen alle Befehle fehlerfrei laufen:
```bash
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo build -p grovewright-bridge
godot --headless --path game --import
godot --headless --path game -s res://tests/run_tests.gd
```
Schlägt `core/tests/golden.rs` fehl, haben sich Regeln oder ein Format geändert. Die Fehlermeldung nennt die Versionskonstante, die dann erhöht werden muss. Erst danach wird der festgeschriebene Wert bewusst angepasst.
Der erste Import in einem frischen Checkout stürzt mit Godot 4.7.2 und der Rust-Erweiterung beim Beenden ab (Exit-Code 139 in der Bash). Der Import ist dann trotzdem erfolgt; ein zweiter Aufruf läuft fehlerfrei durch. `play.bat` wiederholt den Import deshalb einmal.
Schlägt `core/tests/golden.rs` fehl, haben sich Regeln oder ein Format geändert. Sind Speicherstände oder Aufzeichnungen betroffen, erhöhst du die Versionskonstante, die die Fehlermeldung nennt. Erst danach passt du den festgeschriebenen Wert bewusst an.
Für Änderungen am Aussehen gibt es ein Werkzeug, das kurz ein Fenster öffnet und ein Bildschirmfoto speichert:
```bash
godot --path game -s res://tools/screenshot.gd -- <Pfad der PNG-Datei>
```
## Spielen
`play.bat` im Hauptordner baut die Brücke und startet das Spiel. Spielstand, Spielerschlüssel und Aufzeichnungen liegen unter `%APPDATA%\Godot\app_userdata\Grovewright`.
## Sprache
- Code, Bezeichner und Kommentare im Code: Englisch.
- Dokumente, Pläne, Commit-Nachrichten, Issues und Pull Requests: Deutsch.
- Texte im Spiel: Deutsch und Englisch über Übersetzungsdateien.
- Texte im Spiel: Deutsch und Englisch über `game/i18n/texts.csv`.
## Git
- Archiv: `https://git.race-cave.cloud/steven/Grovewright.git` (Gitea).
- Archiv: `https://git.race-cave.cloud/steven/Grovewright.git` (Gitea). Issues und Pull Requests sind freigegeben.
- Gearbeitet wird auf einem Branch pro Plan, nicht direkt auf `main`.
- Nach jedem abgeschlossenen Arbeitsschritt wird committet und hochgeladen. Das ist freigegeben.
- Commit-Nachrichten enden mit einer Zeile `Co-Authored-By: Claude <Modell> <noreply@anthropic.com>`, die das Claude-Modell nennt, das den Commit erstellt hat.
+23
View File
@@ -0,0 +1,23 @@
# Probelauf M1b: Brücke und Hülle
M1b ist noch kein Spiel, sondern das Fundament zum Anfassen: Du läufst über die Testkarte und schlägst Stein. Farben und Formen sind Platzhalter; den Grafikstil wählst du in M1d aus drei Stilproben.
## So startest du
1. Doppelklick auf `play.bat` im Projektordner. Beim ersten Start baut Rust die Brücke, das dauert einige Minuten.
2. Steuerung:
- WASD: laufen
- Linke Maustaste: Stein schlagen. Ein grün umrandetes Feld ist in Reichweite, ein rot umrandetes nicht.
- F5: speichern, F9: gespeicherten Stand laden
- Esc: Pause
3. Beim Schließen des Fensters wird gespeichert. Beim nächsten Start geht es dort weiter, und die Figur steht wieder am Startpunkt.
## Fragen
1. Fühlt sich das Laufen gut an: Tempo, Halten an Wänden, Gleiten an Wänden entlang?
2. Passt die Reichweite der Spitzhacke: zu kurz, zu weit oder genau richtig?
3. Fühlt sich der Rhythmus des Schlagens gut an? Ein Fels braucht drei Schläge, höchstens vier Schläge gehen pro Sekunde.
4. Ist auf einen Blick klar, was Boden, weicher Fels, harter Fels und der Rand der Karte ist?
5. Ist dir etwas Merkwürdiges passiert? Die Aufzeichnungen deiner Sitzungen liegen unter `%APPDATA%\Godot\app_userdata\Grovewright\recordings`. Schick mir die neueste Datei, dann spiele ich die Sitzung exakt nach.
Antworte formlos im Chat oder als Kommentar in Gitea.
+18
View File
@@ -0,0 +1,18 @@
@echo off
rem Builds the bridge and starts Grovewright from this folder.
cd /d "%~dp0"
cargo build -p grovewright-bridge || goto failed
rem The first import of a fresh checkout crashes while Godot shuts down
rem (Godot 4.7.2 with a Rust extension). The import has happened by then,
rem and a second run succeeds. Batch reports the crash as a negative exit
rem code, so the check compares with zero instead of using errorlevel 1.
godot --headless --path game --import
if %errorlevel% neq 0 godot --headless --path game --import || goto failed
godot --path game
goto :eof
:failed
echo.
echo Grovewright konnte nicht gestartet werden. Die Meldungen oben nennen den Grund.
pause
exit /b 1