12 KiB
U4 · Netzwerk
Stand: 2. Oktober 2026 · fertig (luneraria/u4 = 769e622). Nachtrag vom 7. Oktober 2026: Startbildschirm in der Szene (unten).
Es wurde keine neue Architektur erfunden. Übertragen ist der M4-Umfang (lune_session.gd, Protokoll 4), serverautoritativ:
- Clients senden Absichten.
- Der Host berechnet Gold, Käufe, Kampf, Schaden, Besitz, Boss und Sieg.
Umsetzung
| Baustein | Ort | Inhalt |
|---|---|---|
LuneSession |
Assets/Luneraria/Core/Net/LuneSession.cs |
autoritative Sitzung (Port von lune_session.gd): Rollen, Sitze, Lobby, Hallo/Beitritt/Wiedereinstieg, Befehle, Bereit, Kampfbeginn, Momentaufnahmen, Abrechnung, Revanche, Disconnect |
NetModel |
Assets/Luneraria/Core/Net/NetModel.cs |
Sitzungsmodelle (Lobby, öffentlicher Zustand, Einheitenätzeze, Rahmen) und ILuneNetLink |
LuneLoopback |
Assets/Luneraria/Core/Net/LuneLoopback.cs |
In-Prozess-Transport für die Protokoll- und Mehr-Runden-Tests |
LuneWire |
Assets/Luneraria/Net/LuneWire.cs |
Codierung der Nachrichten (Art + Payload, Cursor-Codec) und der Kampfrahmen |
LuneTcp |
Assets/Luneraria/Net/LuneTcp.cs |
TCP-Transport (Ersatz für ENet): ein Socket je Gast, zuverlässige Nachrichten und Rahmen, Pump auf dem Spielthread |
LuneMainMenu |
Assets/Luneraria/UI/Game/LuneMainMenu.cs |
Startbildschirm: Allein spielen, Hosten, Beitreten, Bund, Lane, Name, Adresse, Port |
LuneNetPlay |
Assets/Luneraria/UI/Game/LuneNetPlay.cs |
Schale um Sitzung und Transport (Port von lune_main.gd): Menü, Partie, Rückkehr, Revanche. Seit dem Nachtrag legt LuneWorldBuilder sie in die Szene |
LuneSmoke |
Assets/Luneraria/UI/Game/LuneSmoke.cs |
Selbsttest eines Players über den Startbildschirm (nach lune_smoke.gd), -luneSmoke <Ordner>; Nachtrag |
LuneGame |
Assets/Luneraria/UI/Game/LuneGame.cs |
Befehle, Rückgängig und Bereit gehen im Netzspiel über die Sitzung; Gast zeigt nur Host-Rahmen |
Offline ist ein lokaler Host mit Bot-Plätzen (LuneSession.HostOffline). Hosten und Beitreten nutzen LuneTcp mit LuneWire. Peer 1 ist der Host; freie Plätze bleiben Bots.
Erfasster M4-Umfang
| Funktion | Verhalten in der Referenz | im Port |
|---|---|---|
| Rollen | offline (lokaler Host mit Bot-Plätzen), Host (ENet-Server, bis 5 Gäste), Gast | erfüllt (offline, Host, Gast; TCP statt ENet) |
| Sechs Plätze | ein Peer genau ein Platz; freie Plätze spielen Bots; Lobbyeinträge mit Name, Bereit, Bot | erfüllt |
| Beitritt | Hallo mit Protokollversion und Name; falsche Version wird abgelehnt; Namen gekürzt (24 Zeichen); volle Partie abgelehnt | erfüllt |
| Private Planung | ein Gast sendet Befehle für seinen Platz; fremde Plätze werden abgelehnt (Spoof-Schutz); der Zustand enthält nur den eigenen Entwurf | erfüllt |
| Bereit | nach Bereit ist die Planung verbindlich; alle bereit → Kampf; Bots planen einmal je Runde | erfüllt |
| Kampfsynchronisation | Kampfbeginn mit Einheitenätzen und Gebäude-HP; alle 0,05 s neue Einheiten und ein Frame (13 Werte je Einheit, ungeordnet-zuverlässig); Gast interpoliert, verspätete Frames werden verworfen | erfüllt (Rahmen mit Serial; späte Frames verworfen) |
| Abrechnung | Host sendet den öffentlichen Zustand nach dem Kampf | erfüllt |
| Disconnect | Platz geht an einen Bot, der in der Planung sofort plant; niemals Kernverlust | erfüllt |
| Wiedereinstieg | gleicher Name, solange der Platz bot-gehalten ist → Platz zurück, volle Synchronisation | erfüllt |
| Übernahme | ein neuer Mensch verwirft den unbestätigten Bot-Entwurf und muss selbst bereit melden | erfüllt |
| Revanche | Host startet neu, Plätze bleiben | erfüllt |
| Hostverlust | Gast kehrt mit Meldung ins Menü zurück | erfüllt |
| Tests | test_lune_net 43 Prüfungen, net_rounds.py Loopback mit zwei Prozessen 12 Prüfungen |
erfüllt: NetSuite 43/43, NetRoundsSuite 12/12 (In-Prozess-Loopback statt zwei Prozesse) |
Prüfung
| Test | Ergebnis |
|---|---|
NetSuite (Port von test_lune_net.gd) |
43/43: Sitzplatzbesitz und Spoof, veraltete Befehle, Doppelkauf, fremde UID, Disconnect ohne Kernschaden, sechs Plätze und Phasen, Hallo und Wiedereinstieg, Bot-Übernahme und einmaliges Bot-Planen, Bereit verbindlich und Privatsphäre der Entwürfe |
NetRoundsSuite (Port von net_rounds.py) |
12/12: Host und Gast spielen 5 Runden, Prüfsummen nach jeder Abrechnung gleich, Gast rechnet 0 Kampfschritte und erhält Rahmen, Gast A trennt im Kampf von Runde 2, Bot übernimmt, Gast B kehrt mit demselben Namen auf denselben Sitz zurück |
LuneWireTests |
Round-trip von Befehlen, öffentlichem Zustand (inkl. Entwurf, Hero, Buchungen), Einheitenätzen, Rahmen, Lobby und Optionen |
| EditMode gesamt | 29/29 (Kern 9 inkl. NetProtocol und NetRounds, Wire 4, Integration 2, UI-Modell 12, Bildraten 4) |
| PlayMode gesamt | 13/13 (Szene, Vertical Function Test, Capture, Verify A–I, UI-Toolkit-Probe); Bildwerkzeuge und Spieltest Explicit |
| Kerntests .NET | 457/457 (75 + 91 + 73 + 157 + 6 + 43 + 12) |
| Fingerabdrücke der Bot-Partien | unverändert DCE95C96556F3A1A, 7B8E43AEFC0D60C5 |
Das Loopback der Mehr-Runden-Partie läuft im Prozess mit zwei LuneSession-Instanzen und LuneLoopback (Nachrichten synchron in Sende-Reihenfolge). Damit sind Protokoll, Prüfsummen, Übernahme und Wiedereinstieg abgedeckt; eine echte Zwei-Prozess-Partie über Sockets bleibt eine optionale Erweiterung.
Abnahme U4
| Vorgabe | Stand |
|---|---|
| Rollen offline / Host / Gast | erfüllt |
| Sechs Plätze, ein Peer ein Platz, freie Plätze Bots | erfüllt |
| Beitritt mit Protokollprüfung, Namenskürzung, volle Partie abgelehnt | erfüllt |
| Private Planung, Spoof-Schutz, nur eigener Entwurf im Zustand | erfüllt |
| Bereit verbindlich, alle bereit → Kampf, Bots einmal je Runde | erfüllt |
| Kampfsynchronisation (Beginn, neue Einheiten, Rahmen 20/s, späte Frames verworfen) | erfüllt |
| Abrechnung mit öffentlichem Zustand | erfüllt |
| Disconnect → Bot, kein Kernverlust | erfüllt |
| Wiedereinstieg über den Namen | erfüllt |
| Übernahme verwirft den Bot-Entwurf | erfüllt |
| Revanche durch den Host | erfüllt |
| Hostverlust → Meldung und Rückkehr ins Menü | erfüllt |
| Mindestumfang: Ready, private Planung, Bot-Übernahme, Wiedereinstieg, Snapshots, Revanche, Disconnect, sechs Plätze | erfüllt |
| Tests 43 + 12 | erfüllt |
Nachtrag: Startbildschirm in der Szene (7. Oktober 2026)
Zweig luneraria/u4-nachtrag (d590fde), aufgesetzt auf luneraria/u5.2 (1b31f9b, mit dem U3-Nachtrag).
Befund aus dem Spieltest des U3-Nachtrags: Nichts legte LuneNetPlay in die Szene. G1_Map.unity enthielt die Komponente nicht, LuneWorldBuilder fügte sie nicht hinzu, und es gab keinen Bootstrap. Das Spiel begann deshalb direkt in einer Partie (LuneGame.Start startet sie, wenn Net fehlt), und der Startbildschirm war unerreichbar. Der Verlassen-Dialog versprach das Hauptmenü, beendete aber das Spiel.
Beim Einbinden zeigten sich zwei weitere Fehler. Sie hätten den Startbildschirm auch mit der Komponente in der Szene verborgen:
LuneNetPlay.Startlegte einUIDocumentohnePanelSettingsan und hängte das Menü an kein Panel.- Das HUD übernimmt das
UIDocumentdes Match-Objekts und leert es bei jedem Partiestart (LuneHud.Init). Ein Menü dort wäre mit der ersten Partie verschwunden.
Behebung:
| Teil | Änderung |
|---|---|
| Szene | LuneWorldBuilder legt LuneNetPlay auf das Match-Objekt neben LuneGame, LuneMatchDriver, LuneMatchView und LuneBenchmark, mit Verweisen auf Spielcontroller, Treiber, Ansicht, Kamera und UI-Assets. Szene neu gebaut |
| Menüfläche | eigenes UIDocument (Objekt „Menu“) mit eigenen PanelSettings, über dem HUD (Sortierung 20 über 10). Die Wurzel nimmt keine Klicks, ein verborgenes Menü lässt das HUD frei |
| Start | Spielcontroller und Treiber warten. Die Kamera steht in der Übersicht still, denn die Felder Name und Adresse nehmen W, A, S, D, Q und E |
| Partie | „Allein spielen“, Hosten und Beitreten wie in U4. Jede Partie beginnt mit ungedrehter Kamera; Godot legt dafür je Partie ein neues Spielobjekt an |
| Rückkehr | „Menü“ → „Zum Menü“: HUD und Aufstellungsflächen verschwinden, der Spielcontroller ruht, die Sitzung endet. Der Treiber hält eine neue Partie, die nie läuft; die Karte ist leer wie beim Start |
| Bund-Wahl | Glutpakt und Wurzelbund zeigen die Wahl wie die Lane-Knöpfe; vorher fehlte die Markierung |
| Menübreite | Die vier Aktionsknöpfe brauchen etwa 650 px und ragten bei höchstens 640 px über den Rahmen (erstes Player-Bild); die Tafel darf jetzt 760 px breit werden |
| Verlassen-Dialog | mit Startbildschirm „ZUM MENÜ“; ohne ihn (Tests, Benchmark) sagt der Dialog, dass das Spiel endet, und der Knopf heißt „BEENDEN“ |
Tests und Benchmark ohne Startbildschirm: LuneMatchDriver.DirectStart (die beiden Szenenlader der Tests setzen ihn) und -luneBenchmark (LuneBenchmark.Running) halten LuneNetPlay heraus. Dann startet LuneGame die Partie der Szene wie vor U4, und die Fingerabdrücke der Bot-Partien bleiben gleich.
Selbsttest im Player (nach lune_smoke.gd, ergänzt um die Rückkehr): <Player> -luneSmoke <Ordner> drückt die echten Knöpfe (LuneButton.Click; Godots Selbsttest löst pressed aus) und prüft im Player auch, dass jeder Knopf in seiner Mitte zuoberst liegt. Jeder Schritt wird vom Bildschirm fotografiert. Am Ende schreibt der Test report.json und beendet sich mit 0 (bestanden) oder 1. Ablauf: Startbildschirm → „Allein spielen“ → Planung → „Startaufstellung“ und „Bereit“ → Kampf → „Menü“ → „Zum Menü“ → Startbildschirm → zweite Partie.
Prüfung:
| Prüfung | Ergebnis |
|---|---|
StartScreenTests (PlayMode, neu) |
26/26 Prüfungen im Editor (Selbsttest ohne Bilder) |
macOS-Player, -luneSmoke |
26/26 in 10,8 s (Release, Metal, 1920×1080). Bilder screenshots/u4/startbildschirm/, Bericht logs/u4/smoke-macos.json, Protokoll logs/u4/smoke-macos-log.txt |
| EditMode | 29/29 |
| PlayMode | 14/14 (13 + StartScreenTests), Verify A–I 718/0. Zu den Läufen siehe unten |
| Fingerabdrücke der Bot-Partien | Seed 2 DCE95C96556F3A1A (12.690 Schritte), Seed 9 ab Runde 21 7B8E43AEFC0D60C5 (4.195 Schritte): gleich dem Kern und unverändert, in der Szene auch bei 30, 60, 120 fps und unbegrenzt |
Benchmark -luneBenchmark |
LuneNetPlay bleibt aus, die Bot-Partie bleibt (Code; die Szenentests nehmen denselben Weg ohne Startbildschirm). Ein Player-Lauf steht aus |
Zu den PlayMode-Läufen: Der erste vollständige Lauf (7. Oktober, 23:28) ergab 14/14. Ihm fehlte nur die Menübreite, eine Layoutzeile, die kein Test liest. Danach wurde der Mac zugeklappt. Er ging in den Ruhezustand („Thermal Emergency Sleep“ um 23:35) und wachte nur noch alle 15 Minuten kurz für Wartung auf. Drei weitere Läufe auf dem Endstand liefen nur in diesen Wachphasen; jeder Test bestand darin mindestens einmal. Der Lauf vom 8. Oktober (05:14) ergab 12/14, mit Bildraten und Vertical Function Test; F_Input und StartScreenTests bestanden in den Läufen davor und danach. Fehlschläge gab es nur an Echtzeitgrenzen, wenn der Mac dazwischen schlief: Bildratenläufe über 600 s, der 120-s-Wächter des Selbsttests und eine Kameraprüfung 0,6 s nach einem Lane-Klick. Ein zusammenhängender Lauf auf dem Endstand bei geöffnetem Mac steht aus.
Bekannte Grenzen
- Transport: TCP statt ENet. Kampfrahmen laufen auf demselben Socket und werden über die Serial verworfen (wie
unreliable_ordered). Unter Paketverlust kann TCP Head-of-Line-Blocking erzeugen; für eine Internetpartie wäre ein unzuverlässiger Kanal wie bei ENet der nächste Schritt. - Zwei-Prozess-Loopback (
net_rounds.py) ist als In-Prozess-Test umgesetzt (NetRoundsSuite). Ein Sockets-Lauf über zwei Player ist optional. - Windows auf x64-Hardware und Netz über mehrere Rechner sind nicht gemessen (Build-Module fehlen, siehe U1.5-UNITY.md).
- Hauptmenü ist der Startbildschirm mit Offline/Hosten/Beitreten; das erweiterte Menü der Referenz (Emblem, Version, erweiterte Optionen, 3D-Bühne) ist vereinfacht. Bis zum Nachtrag vom 7. Oktober 2026 war er nicht in der Szene. F1, M und F11 wirken erst in der Partie; in der Referenz auch im Menü.
- „Menü“ im HUD fragt vor der Rückkehr (Dialog aus U3); Godot kehrt sofort zurück.
- Kein Audio-Netzwerk und keine Lobby-Chat-Nachrichten (waren nicht Teil des M4-Mindestumfangs).