Pixelgrafik aus Code im Stil klassischer Farmspiele, aber eigenständig,
in drei Abwandlungen: Warm, Pastell und Glimmen. Eine Vitrine zeigt jeden
Stil mit Dingen späterer Meilensteine, F2 wechselt den Stil im Spiel, und
das Spiel merkt sich die Wahl. Der eigene Schlag zeigt seine Splitter
sofort, auch als Gast, und die Namen über den Figuren bleiben scharf.
151 Rust-Tests (unverändert) und 154 GDScript-Tests; jede Task und der
ganze Branch geprüft.
Closes#10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der Satz zu den Splittern sagte, harter Fels und Grundgestein ließen
sich mit der Steinspitzhacke noch nicht brechen. Harter Fels braucht
eine Kupferspitzhacke, Grundgestein bricht nie.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Das Lebensmoos bekommt links und rechts Platz für seinen Umriss, der
bei zwei von drei Formen abgeschnitten war; die Bilder der Vitrine sind
neu aufgenommen. Die Pausenanzeige kehrt zurück, wenn eine Meldung
während der Pause abläuft. Ein Test prüft die Splitter fremder
Schläge, und der Test ohne Bilderordner hängt nicht mehr davon ab, was
in res://art liegt. Der Fragebogen nennt die Felsproben und sagt, was
sofort kommt und was erst mit der Antwort des Hosts.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die Regel nannte nur Zeichnung und Farben. Eine neue Kennung braucht
auch eine Konstante in Art, einen Eintrag in Art.IDS und einen Zweig
mit Platzhalter in ArtStyle.draw. Eine fehlende Farbe führt zu einem
Fehler, nicht zu einem Platzhalter.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
CLAUDE.md beschreibt, wie die Grafik entsteht: Kennungen, Pixelgrafik
aus Code, scharfe Pixel, Bilder, die Zeichnungen ersetzen, Blöcke und
Kachelformen. Das Bildschirmfoto-Werkzeug und --style=NAME stehen bei
ihren Befehlen. Eine stille Verbindung gilt nach etwa
NetLink.TIMEOUT_MSEC als verloren. Ein Fragebogen mit Bildern der drei
Stile begleitet den Probelauf.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Pastell zeichnet mit weichen, hellen Farben und Umrissen im dunkleren
Ton der Fläche. Glimmen zeigt eine gedämpfte, kühle tote Welt, in der
alles Lebendige kräftig ist und leuchtet. Beide haben dieselben
Farbnamen wie Warm und je acht Spielerfarben.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Toter Boden hat Risse, lebendiger Boden Gras, Blumen und einen
unregelmäßigen Rand. Moos funkelt, die Ranke trägt Blätter, die
Faserpflanze wächst in sechs Stufen bis zu den Fasern und wiegt sich,
das Weidetier zieht im Sprung die Beine an, und die Ruine leuchtet,
wenn sie erwacht.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
In zwei der vier Laufbilder hob sich der Körper um ein Pixel, und der
dunkle Umriss oben am Kopf fiel aus dem Bild. Laufende Figuren
flackerten dadurch am Kopf. Jetzt sinkt der Körper, solange die Beine
gespreizt sind, und über dem Kopf bleibt immer eine Zeile frei.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
PixelStyle zeichnet jede Grafik Pixel für Pixel in ein kleines Bild,
16 mal 16 Pixel pro Kachel, und hebt es auf. Fels ist außen rund, innen
geschlossen und zeigt zum offenen Boden hin seine Vorderkante; Figuren
stehen aufrecht und haben vier Laufbilder. Warm bekommt als erster Stil
seine Palette und acht Spielerfarben. Die Texturfilterung steht auf
Nearest, damit die Pixel scharf bleiben.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
F2 wechselt im Spiel den Stil und meldet ihn, --style=NAME wählt ihn
beim Start, und das Spiel merkt sich die Wahl in den Einstellungen.
Das Bildschirmfoto-Werkzeug nimmt Bilder in einem bestimmten Stil und
von der Vitrine auf und zeigt eine zweite Figur mit Namen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ein Gast kann die Stilproben öffnen, während er sich verbindet. Kam die
Welt dann an, lag sie unter der Vitrine, und „Zurück" legte das Menü
über das laufende Spiel. Jetzt schließt die Hauptszene die Vitrine,
sobald eine Welt erscheint.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Das Startmenü führt zu einer Vitrine, die den gewählten Stil an einer
kleinen Szene zeigt: Fels, toter und lebendiger Boden, Lebensmoos, eine
Ranke, die Steine in eine erwachende Ruine trägt, eine wachsende
Faserpflanze, ein hüpfendes Weidetier und zwei Spielfiguren. Knöpfe,
die Tasten 1 bis 3 und F2 wechseln den Stil, Esc führt zurück ins Menü.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Als Gast kann die Meldung eines Schlags erst nach dem nächsten Schlag
auf dieselbe Kachel ankommen. Ein Merker pro Kachel ging dabei verloren,
und der brechende Schlag zeigte seine Splitter doch zweimal. Die
Hauptszene zählt ihre noch nicht gemeldeten Schläge deshalb pro Kachel
und vergisst sie, wenn die Welt neu abgeglichen wird.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Der Kern meldet einen Schlag, der Fels bricht, nur als geänderte Kachel
ohne Spieler. Die Hauptszene merkt sich deshalb die Kacheln ihrer
eigenen Schläge, bis der Kern sie meldet, und zeigt dann keinen zweiten
Splitterwurf. Als Gast kam der zweite eine Rundreise später und sah
wieder verzögert aus. Splitter gibt es nur noch auf Fels, nicht mehr
außerhalb der Karte.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Karte, Figuren und Zielmarke zeichnet jetzt Art. Die Karte zeichnet in
Blöcken von 16 mal 16 Kacheln und nach einem Treffer nur die Blöcke um
die Kachel neu; jede Kachel kennt ihre Nachbarn. Figuren stehen
aufrecht in ihrer Kachel, schreiten und schauen in die Richtung, in die
sie gehen. Der eigene Schlag zeigt seine Splitter sofort, auch als Gast,
und die Namen über fremden Figuren werden in Bildschirmgröße gezeichnet
und bleiben scharf.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Jede Grafik auf dem Spielfeld bekommt eine Kennung. Art zeichnet sie im
gewählten Stil oder aus einem Bild gleichen Namens unter game/art.
ArtStyle zeichnet vorerst schlichte Platzhalter. TileShape beschreibt
die Nachbarn einer Kachel, damit Felsmassen außen rund und innen
geschlossen gezeichnet werden können. Die drei Stile Warm, Pastell und
Glimmen gibt es schon mit Namen, noch ohne eigene Zeichnungen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Nach der ersten Vorschau soll das Spielfeld Pixelgrafik im Stil
klassischer Farmspiele zeigen, aus Code gezeichnet. Konzept 7.4 und 11
sagen das jetzt. Der Plan behält Kennungen, Kartenblöcke, Vitrine und
Stilwechsel und zeichnet statt der drei Vektorstile Pixelgrafik in drei
Abwandlungen: Warm, Pastell und Glimmen. Warm war nach der Probe der
Favorit. Jede Task-Grenze wurde erneut vorab in einer Kopie von main
geprüft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Acht Tasks bringen das Spielfeld in drei Stilen auf den Bildschirm, alle
aus Code: Scherenschnitt, Kontur und Geometrisch. Jede Grafik hängt an
einer Kennung, eine Vitrine zeigt jeden Stil mit Dingen späterer
Meilensteine, F2 wechselt im Spiel, und das Spiel merkt sich die Wahl.
Dazu kommen die Antworten aus dem Probelauf von M1c: Der eigene Schlag
zeigt seine Splitter sofort, und die Namen über den Figuren bleiben
scharf. Jede Task-Grenze wurde vorab in einer Kopie von main geprüft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Koop für bis zu vier Spieler im Gleichschritt: Der Host bestimmt jeden
Takt, Gäste rechnen ihn nach, Prüfsummen decken Abweichungen auf, und ein
abgewichener Gast bekommt die Welt neu. Dazu Verbindung über ENet,
Startmenü, Profile, die Figuren der anderen und eine Statuszeile.
151 Rust-Tests in Debug und Release, 96 GDScript-Tests, ein Test mit zwei
Godot-Prozessen; jede Task und der ganze Branch geprüft.
Closes#7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Der Host hält einen Schlag eines Gastes, der während der Abklingzeit
ankommt, bis zum ersten Takt danach. Übers Internet ging sonst etwa
jeder fünfte Schlag eines Gastes verloren.
- ENet drosselt die Figuren nie mehr; sonst konnten die Figuren eines
von zwei Gästen dauerhaft stehen bleiben. Figuren gehen geordnet, und
eine stille Verbindung gilt nach höchstens acht Sekunden als verloren.
- Positionen, die keine Zahlen sind, und Figuren von Spielern, die
nicht online sind, werden verworfen.
- Ein Host mit angehaltener Simulation sagt das in der Statuszeile.
- Neue Tests: Schlag während der Abklingzeit, Zielauswahl von NetLink,
verworfene Figuren, Neuabgleich über die Brücke.
- Der Fragebogen sagt, was jeder Rechner braucht, und die Freigabe in
der Firewall gehört zum Host. CLAUDE.md beschreibt das Halten der
Schläge, die Drosselung und die Zeitüberschreitung und ergänzt die
Vertrauensregel um die Schlüssel.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Der Fragebogen nennt die Statuszeile oben rechts. Er sagt wieder, wann
eine Aufzeichnung entsteht, dass eine angehaltene Sitzung keine
hinterlässt und wo die Logs liegen, und er gibt bessere Hinweise für
Verbindungen übers Internet. CLAUDE.md beschreibt jetzt auch:
- die Übernahme der Host-Welt durch Gäste,
- den festen Anfang von Gruß und Ablehnung,
- die Pausenregel und das Anhalten bei einem Kernfehler,
- die erlaubten Profilnamen,
- die gesperrte Brücken-DLL bei laufendem Spiel.
play-zweites-fenster.bat gibt weitere Argumente an das Spiel weiter.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
play.bat gibt Argumente an das Spiel weiter, und
play-zweites-fenster.bat startet ein zweites Fenster mit eigenem Profil.
CLAUDE.md beschreibt die gemeinsame Welt, das Vertrauensmodell der
Reichweite, die Netzwerkregeln und den Test zweier Instanzen. Ein
Fragebogen begleitet den Probelauf.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die Uhr des Hosts wartet jetzt auf den Gast. So kann ein langsamer Start
des zweiten Prozesses die Takte bis zum Vergleich nicht mehr verbrauchen.
Der Bot endet auch dann, wenn er seinen Bericht nicht schreiben kann,
und überlebt den Test nie. Ein leeres --storage= führt ihn nicht mehr in
den Ordner des Spielers. Ein neuer Test prüft, dass der Bot ohne
schreibbaren Bericht mit Exit-Code 1 endet.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ein Test startet einen zweiten Godot-Prozess als Gast. Der Gast tritt
über das Netzwerk bei und schlägt Stein, beide Welten enden im selben
Zustand, ohne Abgleich unterwegs. Damit ist die Prüffrage von
Meilenstein 1 Teil der automatischen Prüfungen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Hält der Kern mit einem internen Fehler an, schließt ein Host seine Welt
für Gäste, und ein Gast kehrt ins Menü zurück, statt dass die anderen
ohne Nachricht warten. Eine Pause endet erst, wenn der Host einen Gast
angenommen hat, nicht schon bei einer bloßen Verbindung. So kann ein
Gast nicht mehr in einer pausierten Welt festhängen, wenn der Host
zwischen Verbindung und Gruß pausiert. Eine Pause lässt sich immer
beenden. Meldungen, die nach dem Ende einer Sitzung im selben Stapel
folgen, bleiben unbeachtet. Vier neue Tests decken das ab.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die Hauptszene beginnt mit dem Startmenü oder mit dem Direktstart aus
der Befehlszeile. Gäste sehen die Welt des Hosts und die Figuren aller
Spieler. Nur der Host speichert, Pause gibt es nur ohne Gäste, Laden
nur beim Spielen allein. Ablehnungen, Abbrüche und Abgleiche erscheinen
als Meldung, und ein Spielstand, der sich gerade nicht lesen lässt,
bleibt unangetastet.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Das Startmenü fragt nach dem Namen und bietet allein spielen, die Welt
öffnen oder beitreten. Die Figuren der anderen Spieler gleiten zu ihrer
neuesten Position und tragen ihren Namen. Die Anzeige füllt Werte in
Meldungen ein und hat eine Statuszeile. Die Texte für Menü und Netzwerk
gibt es auf Deutsch und Englisch.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Wer eine Einstellung ändert, liest die Einstellungsdatei jetzt an
derselben Stelle wie Schlüssel und Speicherstand. Ist sie gerade nicht
lesbar, etwa weil ein anderes Programm sie hält, bleibt sie unverändert,
statt nur mit dem neuen Wert überschrieben zu werden. Von einer
beschädigten Datei bleibt, was Godot lesen konnte. Ein neuer Test deckt
die gehaltene Datei ab. Der Parameter von set_player_name heißt jetzt
new_name, damit er die Methode player_name nicht verdeckt.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die Befehlszeile wählt ein Profil, einen Namen und einen Direktstart.
Zwei Fenster auf einem Rechner sind so zwei Spieler. Ein Schlüssel oder
Spielstand, der sich gerade nicht lesen lässt, wird nicht mehr ersetzt.
Name und zuletzt genutzte Adresse bleiben gespeichert.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
NetLink merkt sich die Verbindungen, die es gerade schließt, und
schickt ihnen nichts mehr. Pakete an alle oder an alle außer einem gehen
einzeln an die offenen Verbindungen, statt Godot ein Ziel zu nennen, das
es nicht mehr gibt. Beides löste sonst Fehler in Godot aus, etwa wenn
der Host Figuren weiterreicht, während er einen abgelehnten Gast
hinauswirft. Ein neuer Test mit zwei Gästen deckt das ab.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
NetLink verbindet Host und Gäste über ENet: Nachrichten des Kerns
zuverlässig und in Reihenfolge, Positionen der Figuren unzuverlässig.
Eine Verbindung schließt erst, wenn wartende Nachrichten draußen sind.
SessionLink trägt die Nachrichten einer Sitzung über die Verbindung und
reicht Positionen weiter. Die Tests verbinden zwei Seiten über die
Loopback-Schnittstelle.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Der Test einer Panik im Kern tauscht den globalen Panik-Hook nicht mehr
aus. Das war unnötig, weil der Testläufer die Meldung einer erwarteten
Panik ohnehin zurückhält, und es konnte Meldungen anderer, gleichzeitig
scheiternder Tests verschlucken. Die Gast-Schleifen in den GDScript-Tests
der Brücke laufen höchstens so oft, wie Takte warten, damit ein
abgewichener Gast den Testlauf nicht anhält.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
GwSession führt eine Sitzung als Host oder als Gast und reicht die
Nachrichten des Kerns an GDScript weiter. Nur der Host speichert. Die
Behandlung interner Fehler liegt jetzt in einem reinen Rust-Teil und hat
einen Test, und das Ereignis join_refused nennt den Schlüssel.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Neue Tests treiben Host und Gast Nachricht für Nachricht und erzeugen,
was das Netz im Speicher nie herstellt: einen Gast, der hinterherhängt,
einen verlorenen Takt und Beitritte, die an den Grenzen warten. Sie
halten fest, dass ein abgewichener Gast nichts rechnet, bis die Welt des
Hosts kommt, dass er dann ältere Takte verwirft, dass die Zahl der
Aktionen eines Gastes in jedem Takt neu beginnt und dass wartende
Beitritte bei beiden Spielergrenzen zählen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Der Host nimmt Gäste auf, macht aus ihren Aktionen Befehle für den
Spieler ihrer Verbindung, begrenzt sie pro Takt und schickt jeden Takt
an alle Gäste. Gäste rechnen jeden Takt nach, vergleichen die
Prüfsummen und holen sich nach einer Abweichung die Welt des Hosts neu.
Tests verbinden Host und Gäste im Speicher, auch über 2000 Takte mit
Gehen und Wiederkommen.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ein Test hält fest, dass ein Gruß mit Kennung und Version beginnt und
eine Ablehnung aus Kennung und Grund besteht. Beides bleibt auch bei
einer neuen Protokollversion gleich, damit sich Spiele verschiedener
Versionen weiter sagen können, dass sie nicht zusammenpassen. Die
Meldung des festgeschriebenen Nachrichtenformats weist darauf hin.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Gäste grüßen mit Protokollversion, Regel-Kennung, Schlüssel und Name
und schicken danach nur Aktionen ohne Spielernummer. Der Host schickt
seine Welt, Ablehnungen mit Grund und jeden Takt mit seinen Befehlen,
jede Sekunde mit einer Prüfsumme. Ein Gruß einer anderen Version bleibt
erkennbar, und das Format ist in den festgeschriebenen Werten
verankert.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Die eingefrorenen Skripte ziehen von den festgeschriebenen Tests in den
Kern. Aus ihren Endzuständen leitet sich die Regel-Kennung ab, die sich
mit jeder Regel ändert, die ein Skript berührt. Aufzeichnungen tragen
sie und werden mit anderen Regeln abgelehnt, statt eine scheinbare
Abweichung zu melden. Fremde Aufzeichnungen dürfen höchstens 30 Tage
lang sein. Ein Test hält fest, dass zu lange Namen beim Dekodieren
abgelehnt werden.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Zehn Tasks machen Grovewright zum Koop-Spiel für bis zu vier Spieler:
Regel-Kennung und Aufzeichnung Version 3, Nachrichten zwischen Host und
Gästen, Host und Gast im Kern, die Brücke für beide Rollen, die
Verbindung über ENet, Profile und Startoptionen, Startmenü und fremde
Figuren, die Hauptszene für Host und Gast, ein Test mit zwei echten
Spielinstanzen und zum Schluss Startskripte, Regeln und Fragebogen.
Jede Task-Grenze wurde vorab mit rotem und grünem Lauf nachgestellt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rust-Brücke (GDExtension) und Godot-Hülle: Die Figur läuft über die
Testkarte und schlägt Stein, die Welt wird gespeichert, geladen und
aufgezeichnet. 110 Rust-Tests in Debug und Release, 44 GDScript-Tests,
jede Task und der ganze Branch geprüft.
Closes#4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Beides setzt Konzept 9 um („automatisch in festen Abständen“, Speichern
beim Schließen), und beides ließ sich entfernen, ohne dass ein Test
rot wurde.
- Schließen: Die Hauptszene bekommt NOTIFICATION_WM_CLOSE_REQUEST. Danach
meldet ein SaveStore im selben Ordner einen Spielstand und genau eine
Aufzeichnung.
- Automatisch: Die Hauptszene läuft per _run_tick bis zum Takt vor
AUTOSAVE_TICKS, ohne Bilder abzuwarten. Bis dahin gibt es keinen
Spielstand, einen Takt später gibt es einen.
Entfernt man das Speichern beim Schließen, schlägt der erste Test fehl;
entfernt man das automatische Speichern in _run_tick, der zweite. Ohne
das Schreiben der Aufzeichnung beim Schließen schlägt der erste ebenfalls
fehl.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Die Regel sagte, wer ein Feld ergänze, tue das in snapshot.rs und prüfe
es in check_restorable. Das gilt nur für Felder von World. Spielerfelder
kodiert und dekodiert core/src/player.rs, das Inventar inventory.rs, die
Karte map.rs; deren Prüfungen stehen im jeweiligen Dekodierer, nicht in
check_restorable. M2 ergänzt Welt-, Feld- und Spielerzustand und folgt
diesem Satz.
Die Regel nennt jetzt alle vier Stellen und sagt, dass ein neues Feld
beim Dekodieren oder in check_restorable geprüft wird. Die Namen wurden
gegen den Code geprüft: encode_state und from_snapshot, Player::encode
und Player::decode, Inventory::encode und Inventory::decode sowie
Map::encode und Map::decode.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Frage 3 sagte, ein Fels brauche drei Schläge. Das gilt nur für weichen
Fels. Harter Fels (ab etwa 22 Feldern vom Start) lässt sich mit der
Startspitzhacke nicht abbauen; Schläge darauf bewirken nichts, und das
Feld bleibt grün umrandet. Ein Spieltester hielte das für einen Fehler.
Der Fragebogen sagt es jetzt vorher.
Frage 5 bat um die neueste Aufzeichnung. Eine Aufzeichnung entsteht aber
nur beim Schließen des Fensters oder beim Laden mit F9. Nach einem
Absturz oder nach der Meldung „Die Simulation wurde angehalten“ gibt es
keine Aufzeichnung dieser Sitzung, und die neueste Datei gehört zu einer
früheren. Dann soll der Tester stattdessen den Ordner logs schicken, und
zwar sofort, weil jeder Start ältere Logdateien verdrängt.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Der Kommentar an dig_at sagte, der Kern prüfe alles außer der
Abklingzeit. Das stimmt nicht: Der Kern prüft Abklingzeit, Feld,
Gelände, Werkzeugstufe und freien Platz, aber nicht die Reichweite. Die
prüft nur die Hülle (in _update_marker und _try_dig), weil die Figuren
außerhalb des Gleichschritts laufen und der Kern ihren Ort nicht kennt.
Der Plan für M1c darf sich nicht darauf verlassen. Der Kommentar nennt
jetzt, was der Aufrufer und was der Kern prüft.
AUTOSAVE_TICKS wiederholte mit 2400 die Taktrate von 20 pro Sekunde. Die
Konstante wird jetzt als zwei Minuten aus GwSession.TICKS_PER_SECOND
abgeleitet; GDScript nimmt das in einer const an und rechnet 2400.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
user://tests liegt im echten Benutzerordner des Spiels, neben Spielstand,
Schlüssel und Aufzeichnungen des Spielers. TestFiles.remove_dir löschte
jeden Pfad, den es bekam; ein falsches Argument hätte echte Spielerdateien
gelöscht. Tests, die früh mit einem Laufzeitfehler abbrachen, ließen ihren
Ordner zurück (es lagen 15 solche Ordner im Benutzerordner).
- remove_dir vereinfacht den Pfad (simplify_path) und verweigert alles,
was nicht user://tests oder darunter liegt, mit push_error und ohne zu
löschen. Die Wurzel steht in der Konstante TestFiles.ROOT, die auch
temporary_dir benutzt.
- run_tests.gd entfernt user://tests zu Beginn von _run_all, vor dem
Fehlerzähler, aber nur wenn der Ordner existiert, weil Godot beim
Auflisten eines fehlenden Ordners einen Fehler meldet.
- CLAUDE.md sagt jetzt, dass die Tests unter user://tests schreiben und
nie die echten Dateien des Spielers anfassen.
Das Verweigern wurde einmal von Hand geprüft, mit einem Wegwerfskript auf
selbst angelegten Ordnern: Ohne Schutz löschte remove_dir sie, mit Schutz
bleiben sie samt Datei stehen und es erscheint ein Fehler, auch für einen
Geschwisterordner user://tests_probe und für user://tests/../…. Danach
waren die 15 Altlasten verschwunden und die Ausgabe fehlerfrei, auch ohne
vorhandenen Ordner user://tests.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
quit(1 if failed > 0 else 0) beendete einen Lauf mit "0 passed, 0 failed"
mit Code 0, zum Beispiel nachdem der Testordner umbenannt wurde oder alle
Tests falsch benannt waren. Die Prüfliste sah dann grün aus, ohne etwas
geprüft zu haben.
Der Läufer endet jetzt auch mit Code 1, wenn kein Test bestanden wurde.
Der Doc-Kommentar sagt das. Einmal von Hand geprüft: Mit einem Dateifilter,
den keine Testdatei erfüllt, meldete der alte Läufer 0 passed, 0 failed
mit Code 0, der neue endet mit Code 1.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Recording::from_bytes leitet start_tick aus der gespeicherten Startwelt
ab. Kein Test prüfte das: Setzte ein Fehler start_tick beim Dekodieren
auf 0, blieb er unbemerkt, und eine so dekodierte Aufzeichnung würde
beim erneuten Kodieren oder Verlängern abstürzen.
Der Test a_recording_can_start_in_the_middle_of_a_session kodiert die
Aufzeichnung jetzt, dekodiert sie wieder und prüft, dass sie beim
selben Takt endet und der ursprünglichen gleicht. Mit start_tick = 0
beim Dekodieren schlägt er fehl (Endtakt 70 statt 120).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Eine Welt kennt bis zu 16 Spieler, von denen höchstens vier online sind.
Kein Test lud eine Welt mit mehr als vier bekannten Spielern; ein Fehler,
der in check_restorable alle bekannten statt der Online-Spieler zählt,
hätte gültige Spielstände zurückgewiesen und blieb unbemerkt. Mit dem
Koop-Spiel in M1c sind mehr als vier bekannte Spieler der Normalfall.
Der neue Test baut die Welt über Befehle auf: 16 Schlüssel treten in
Vierergruppen bei, jede Gruppe außer der letzten geht wieder, bevor die
nächste kommt. from_snapshot muss dieselbe Welt liefern, load alle Spieler
offline. Mit dem Fehler (alle Spieler gezählt) schlägt der Test mit
Invalid("online players") fehl.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Mit `use std::{fs, process};` und anschließendem `fs::remove_file(…)`
kam bisher beides durch: Der Textwächter suchte `std::fs` wörtlich, und
Clippy kennt nur die aufgelisteten Funktionen. Globaler veränderlicher
Zustand über `static` mit Mutex, RwLock, OnceLock oder LazyLock wurde
auch nicht erkannt.
Neu in FORBIDDEN: `std::{` (gruppierte Importe verbergen dem Text die
Modulnamen) sowie Mutex, RwLock, OnceLock und LazyLock. Der Kern in
core/src nutzt nichts davon. Der Kommentar in core/clippy.toml behauptet
jetzt nur noch, was Clippy leistet, und CLAUDE.md nennt die Regel, dass
jeder std-Pfad in einer eigenen use-Zeile steht.
Der bestehende Selbsttest every_forbidden_term_is_detected deckt die
neuen Einträge ab.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
SaveStore.write_save verschob die geprüfte Temporärdatei direkt auf
world.grw. Godot löscht unter Windows ein vorhandenes Ziel, bevor es die
Datei verschiebt. Scheiterte die Verschiebung, zum Beispiel weil ein
Virenscanner world.grw.tmp festhielt, war der vorige Spielstand schon weg.
Jetzt wird der vorige Stand zuerst nach world.grw.bak verschoben, dann
rückt die Temporärdatei nach. Scheitert das, kommt der Stand aus der
Sicherung zurück; nach Erfolg wird die Sicherung gelöscht. Bleibt nach
einem Absturz zwischen den beiden Verschiebungen nur world.grw.bak übrig,
holt SaveStore sie beim Öffnen des Ordners zurück.
Neue Tests: ein offenes Handle auf world.grw.tmp, eine blockierte
Sicherung, die Wiederherstellung aus der Sicherung und dass eine
Sicherung einen vorhandenen Stand nie ersetzt.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>