Darkwood Blog Blog
  • Artikel
de
  • en
  • fr
Anmeldung
  • Blog
  • Artikel

🔀 Darkwaar33 – Seedwake: Asynchroner Multiplayer ohne Server

vom 18. Oktober 2026

Anmelden um auf diesen Beitrag zu reagieren

🚀 1

Derselbe Startwert. Dieselbe Dunkelheit. Unterschiedliche Entscheidungen.

Was wäre, wenn für den Mehrspielermodus nicht erforderlich wäre, dass alle Spieler gleichzeitig online sind? Was wäre, wenn der Vergleich zweier Spiele weder einen Server noch eine Datenbank erfordern würde, sondern lediglich einen Startwert und eine Abfolge von Aktionen?

Genau das habe ich mit Darkwaar33 – Seedwake untersucht, einem kleinen, rundenbasierten taktischen Roguelike, das mit Godot 4.6 entwickelt wurde.

Zwei Spieler können genau dieselben sieben Etagen erkunden, unterschiedliche Entscheidungen treffen und ihre Durchläufe anschließend vergleichen. Ein importierter Durchlauf wird zu einem „Ghost“, der zeigt, wohin ein anderer Spieler gegangen ist, wie er überlebt hat und in welchem Moment genau sich ihre Entscheidungen unterschieden haben.

Das Interessante daran ist nicht nur das Gameplay. Es ist die Architektur, die einen asynchronen Wettbewerb ohne Online-Infrastruktur ermöglicht.

Ein Startwert, eine Welt

Die prozedurale Generierung sorgt in der Regel für Abwechslung. Jedes neue Spiel erzeugt eine andere Welt, was die Erkundung und den Wiederspielwert fördert.

Die prozedurale Generierung kann jedoch auch einen gemeinsamen Bezugspunkt zwischen den Spielern schaffen.

In „Seedwake“ bestimmt ein 30-Bit-Seed die gesamte Umgebung:

  • Sieben Etagen, die jeweils ein taktisches 7×7-Raster enthalten.
  • Positionen der Wände und Standorte der Treppen.
  • Platzierung der Gegner und deren anfängliches Verhalten.
  • Sammelbare Glanzpunkte und deren Ablaufzeiten.
  • Schrein-Angebote zwischen den Etagen.

Zwei Spieler, die mit demselben Startwert beginnen, treffen auf dieselben Ausgangsbedingungen.

Noch wichtiger ist, dass während des Spiels keine Zufallszahlen generiert werden.

Sobald eine Ebene generiert wurde, folgt jeder Spielverlauf deterministischen Regeln. Gegner verhalten sich entsprechend ihrem aktuellen Zustand, Angriffe sind vorhersehbar, und dieselbe Aktion, die auf denselben Zustand angewendet wird, führt immer zum gleichen Ergebnis.

Der Startwert bestimmt die Welt. Der Spieler bestimmt, was darin geschieht.

Determinismus als architektonische Vorgabe

Godot bietet die Zufallszahlengenerierung über RandomNumberGenerator an, doch der zugrunde liegende Algorithmus wird als Implementierungsdetail dokumentiert.

Für ein herkömmliches Spiel mag diese Unterscheidung keine große Rolle spielen. Für ein Spiel, bei dem Replay-Codes zwischen Spielern ausgetauscht werden, ist sie jedoch von erheblicher Bedeutung.

Ein heute erstelltes Replay sollte nach einem Engine-Update nicht stillschweigend zu einem anderen Ergebnis führen.

Seedwake verwendet daher einen kleinen, explizit definierten xorshift32-Generator mit unabhängigen Strömen, die unter Verwendung von FNV-1a abgeleitet werden.

Die Ströme sind nach Zuständigkeiten getrennt:

Strom Zuständigkeit
map Wände, Treppen, Lichtreflexe und Etagenlayout
enemy Positionen, Ausrichtungen und Zyklen der Gegner
offer Belohnungen aus Schreinen

Jeder Stream ist zudem pro Etage isoliert.

Eine Änderung bei der Generierung von Etage drei sollte nicht unerwartet Auswirkungen auf Etage vier haben.

Diese Trennung vermeidet ein häufiges Problem bei der prozeduralen Generierung: Der Verbrauch einer zusätzlichen Zufallszahl in einem Teilsystem kann die gesamte von einem anderen Teilsystem verwendete Sequenz verschieben.

Das bedeutet auch, dass Spielentscheidungen die zukünftige Generierung nicht durch versehentlichen RNG-Verbrauch beeinflussen können.

Eine von ihrer Benutzeroberfläche unabhängige Simulation

Seedwake trennt seine Regeln von der Godot-Präsentationsschicht.

Die Architektur ist bewusst klein gehalten:

main.gd
    Eingabe, Bildschirme, HUD, Animationen
 |
 v
rules.gd
    Generierung, Aktionen, Kampf,
    Punktewertung und Simulation
 |
 v
board_view.gd
    Raster, Einheiten, Absichten,
    Geist und visuelles Feedback

codec.gd
    Startwerte, Ausführungscodes und Prüfsummen

Die zentrale Funktionsweise ist konzeptionell einfach:

Rules.apply(state, action)

Sie nimmt einen Zustand und eine Aktion entgegen, validiert den Zug und gibt den resultierenden Zustand zusammen mit Ereignissen zurück, die beschreiben, was passiert ist.

Die Benutzeroberfläche nutzt diese Ereignisse, um Bewegungen, Schaden, die Vernichtung von Gegnern und Übergänge zu animieren.

Animationen verändern die Simulation nicht.

Diese Unterscheidung ist wichtig. Ein 120 Millisekunden dauernder Tween darf das Ergebnis eines Spielzugs nicht verändern – unabhängig von der Bildrate oder der Browserleistung.

Die Spielelogik bleibt unabhängig von Rendering, Timing und Eingabegeräten.

Dieselbe Architektur macht das gesamte Spiel zudem wiederholbar.

Eine Wiederholung ist lediglich eine Liste von Entscheidungen

Herkömmliche Spielaufzeichnungen speichern Bilder oder Videobilder.

Seedwake benötigt beides nicht.

Da die Simulation deterministisch ist, sind für die Reproduktion eines Durchgangs nur zwei Dinge erforderlich:

  1. Der ursprüngliche Startwert.
  2. Die geordnete Abfolge der Spieleraktionen.

Eine Wiederholung wird rekonstruiert, indem diese Aktionen auf einen neu generierten Anfangszustand angewendet werden.

Rules.simulate(seed, actions)

Das Ergebnis umfasst den endgültigen Spielzustand und eine Aufzeichnung des Spielverlaufs.

Es gibt keine separate Wiederholungs-Engine und keine zweite Implementierung der Kampfregeln.

Dieselbe Simulation wird für den normalen Spielablauf, die Validierung von Wiederholungen und gegnerische Geister verwendet.

Die Wiederspielbarkeit ergibt sich aus den Regeln selbst und ist kein nachträglich eingebautes Subsystem.

Einen Durchlauf ohne Backend teilen

Am Ende eines Spiels generiert Seedwake einen kompakten Durchlaufcode.

Zum Beispiel:

SW1-04V9P-NR2B5-…

Der Code enthält die Regelversion, den Startwert, das Spieler-Tag, die aufgezeichneten Aktionen, das beanspruchte Ergebnis und eine Prüfsumme.

Aktionen verwenden 4-Bit-Identifikatoren, sodass zwei Aktionen in ein Byte passen. Die resultierenden Daten werden mit Crockford Base32 kodiert, einem menschenfreundlichen Alphabet, das mehrere mehrdeutige Zeichen vermeidet.

Ein Code kann in eine Nachricht kopiert und von einem anderen Spieler importiert werden.

Das importierende Spiel vertraut dem Ergebnis nicht blind.

Es dekodiert die Daten, überprüft deren Integrität, validiert die Regelversion und führt anschließend die gesamte Aktionssequenz erneut aus.

Wenn sich die simulierte Punktzahl, die abgeschlossenen Ebenen oder die Anzahl der Züge vom angegebenen Ergebnis unterscheiden, wird der Code abgelehnt.

Dies ist keine kryptografische Authentifizierung und kein Anti-Cheat-System. Eine Prüfsumme kann eine absichtliche Fälschung nicht verhindern, und Offline-Spieler können einen Startwert so oft wiederholen, wie sie möchten.

Aber es liefert ein reproduzierbares, in sich konsistentes Ergebnis, ohne dass Konten, Datenbanken, APIs oder serverseitige Simulationen erforderlich sind.

Für ein kleines Spiel, das unter Freunden gespielt wird, ist das ein sinnvoller Kompromiss.

Der Geist ist die Spielhistorie eines anderen Spielers

Sobald ein gegnerischer Code validiert wurde, wird dessen Simulationsverlauf zu einem Geist.

Der Geist erscheint als hohler, cyanfarbener Diamant, der sich durch dieselben Etagen bewegt wie der Spieler.

Er nimmt nicht am Kampf teil, blockiert keine Bewegungen und beeinflusst keine Entscheidungen der Gegner.

Stattdessen liefert er Informationen.

Auf Ebene drei, Zug fünf, kann das Spiel anzeigen, wo der Gegner auf Ebene drei, Zug fünf, stand.

Wenn der Gegner die Treppe bereits erreicht hat, zeigt das Spiel an, wann er sie verlassen hat. Ist er gestorben, wird seine letzte Position zu einer Todesmarkierung.

Eine kurze Spur früherer Positionen macht die Bewegung des Geistes nachvollziehbar.

Das ist asynchrones Multiplayer-Spiel, reduziert auf seine informative Essenz.

Die beiden Spieler tauschen niemals Nachrichten mit einem Server aus. Ihre Spiele müssen niemals gleichzeitig ausgeführt werden. Sie teilen sich einfach eine deterministische Welt und vergleichen ihre Spielverläufe.

Den genauen Moment finden, in dem sich zwei Spielverläufe unterscheiden

Das Feature, das ich am interessantesten finde, ist die Divergenzmarkierung.

Stell dir vor, zwei Spieler beginnen mit demselben Startwert.

Sie machen denselben ersten Zug, dann denselben zweiten Zug, dann denselben dritten Zug.

Da die Simulation deterministisch ist, bleiben ihre Zustände identisch.

Im vierten Zug bewegt sich der eine nach Norden, während der andere einen Gegner nach Osten drängt.

Das ist die erste Abweichung.

Seedwake zeichnet diese Entscheidung auf und markiert ihre Position auf dem Spielfeld mit einem Gabel-Symbol.

Von diesem Moment an können die beiden Spielverläufe völlig unterschiedliche Ergebnisse nehmen.

Ein Spieler könnte einen bald verfallenden Glanz einsammeln. Der andere könnte einen Gegner ausschalten, seine Gesundheit erhalten oder die Treppe früher erreichen.

Das Spiel kann den genauen Zeitpunkt identifizieren, an dem sich der gemeinsame Verlauf in zwei verschiedene Pfade aufgespalten hat.

Das ist interessanter, als nur die Endpunktzahlen zu vergleichen.

Eine Punktzahl sagt dir, wer gemäß dem Punktesystem besser abgeschnitten hat.

Ein Divergenzmarker zeigt dir, wo sich ihre Entscheidungen zum ersten Mal unterschieden.

Der Vergleich wird so zu einer Möglichkeit, das Spiel selbst zu verstehen.

Die taktischen Regeln verständlich machen

Die deterministische Architektur hätte wenig Bedeutung, wenn die Spieler die Konsequenzen ihrer Handlungen nicht verstehen könnten.

Seedwake verwendet daher ein kompaktes, vollständig einsehbares taktisches Spielfeld.

Es gibt drei Gegnertypen:

  • Brutes verfolgen den Spieler und greifen benachbarte Felder an.
  • Lancers feuern in einem Dreirundenzyklus entlang einer festen Richtung.
  • Sentries greifen in regelmäßigen Abständen ihre vier benachbarten Felder an.

Ihre Absichten werden angezeigt, bevor der Spieler handelt.

Rot schraffierte Felder zeigen an, wo Angriffe landen werden.

Der Spieler kann sich bewegen, warten, sprinten oder Gegner wegstoßen. Wird ein Gegner gegen eine Wand, einen anderen Gegner oder den Rand des Spielfelds gestoßen, wird er vernichtet.

Gegnerische Angriffe können auch andere Gegner treffen.

Dies führt zu kleinen taktischen Problemen, bei denen die Positionierung wichtiger ist als die Reaktionsgeschwindigkeit.

Glanzpunkte fügen eine zweite Entscheidungsebene hinzu. Sie verschwinden nach einer begrenzten Anzahl von Zügen und können entweder als Punktestand gespeichert oder an Schreinen für Überlebens-Upgrades ausgegeben werden.

Der Spieler muss ständig zwischen dem Sammeln von Ressourcen, dem Ausweichen vor Gefahren und dem Vorankommen in Richtung der Treppe wählen.

Da die Regeln keine Zufälligkeiten während des Spielverlaufs enthalten, sind die Konsequenzen dieser Entscheidungen reproduzierbar.

Warum kein Server?

Eine Online-Bestenliste wäre möglich.

Ein Backend könnte Punktestände erfassen, tägliche Herausforderungen verwalten, Einsendungen validieren und Läufe von Gegnern bereitstellen.

Allerdings würde dies auch Probleme hinsichtlich Infrastruktur, Authentifizierung, Speicherung, Missbrauchsprävention, Wartung und Bereitstellung mit sich bringen.

Bei „Darkwaar33“ wollte ich testen, wie weit das Konzept des asynchronen Multiplayers ohne all das gehen könnte.

Das Ergebnis unterstützt drei einfache Interaktionen:

  • Einen Startwert teilen, um dieselbe Herausforderung zu spielen.
  • Einen Laufcode teilen, um die Gesamtleistungen zu vergleichen.
  • Einen Laufcode importieren, um gegen den Geist eines anderen Spielers anzutreten.

Der Browser führt alles lokal aus.

Die Einschränkung ist ebenso klar: Es gibt keine automatische Gegnersuche, keine globale Rangliste und keine verbindliche Durchsetzung von täglichen Versuchen.

Diese Funktionen würden ein anderes Infrastrukturmodell erfordern.

Für dieses Experiment sind sie jedoch nicht notwendig.

Ein kleines Spiel, eine wiederverwendbare Idee

Darkwaar ist ein fortlaufendes Experiment, bei dem regelmäßig kleine spielbare Ideen entwickelt und veröffentlicht werden.

Seedwake folgt diesem Ansatz: ein begrenztes Spielfeld, eine kleine Auswahl an Aktionen, sieben Ebenen, drei Gegnertypen und eine charakteristische asynchrone Mechanik.

Das Projekt nutzt Godot 4.6 und GDScript, wobei ein HTML5-Export über die bestehende Darkwaar-Pipeline veröffentlicht wird.

Die allgemeinere Erkenntnis geht über Roguelikes hinaus.

Wenn eine Anwendung auf deterministischen Zustandsübergängen basiert, kann eine Abfolge von Eingaben zu einer kompakten, portablen Darstellung einer gesamten Ausführung werden.

Das eröffnet Möglichkeiten für Wiederholungssysteme, Debugging, reproduzierbare Tests, Simulationen und asynchrone Zusammenarbeit.

In „Seedwake“ ist das Ergebnis ein Geist, der neben dir durch einen Dungeon wandelt.

Im Grunde handelt es sich jedoch lediglich um eine weitere Ausführung derselben Regeln.

Derselbe Startwert. Dieselbe Dunkelheit. Unterschiedliche Entscheidungen.

Spiele „Darkwaar33 – Seedwake“

Spiele „Seedwake“ auf itch.io

Sieben Ebenen. Ein gemeinsamer Startwert. Kein Server erforderlich.

Spiele unabhängig, teile deinen Laufcode und fordere andere heraus, in genau derselben Welt bessere Entscheidungen zu treffen.

Anmelden um auf diesen Beitrag zu reagieren

🚀 1

Site

  • Sitemap
  • Kontakt
  • Impressum

Network

  • Hello
  • Blog
  • Apps
  • Photos

Social

Darkwood 2026, alle Rechte vorbehalten