Eine Runtime, ein Protokoll – das Spiel als programmierbare Umgebung
Die W3 Runtime wird ins Originalspiel injiziert, erfasst auf dem Spiel-Thread die ganze Welt, führt Befehle aus und meldet Ergebnisse. Deine KI sagt nur, was passieren soll, und spricht über ein versioniertes Shared-Memory-Protokoll mit ihr.
Untere Schichten wissen nichts von oberen
In der API-Schicht steht keine einzige Zeile „Soll ich angreifen?“-Logik; Brains importieren sich nicht gegenseitig und hängen nur vom SDK ab. Das ist die Voraussetzung für die Arena: Die Plattform braucht nur „API-Schicht + Schiedsrichter“, und jedes Brain lässt sich einstecken.
Die ganze Karte, alle 50 ms
Die Runtime erfasst alles in einem Durchgang auf dem Spiel-Thread und pusht es in Shared Memory; Clients lesen direkt und stehen nicht mehr beim Spiel-Thread an. Eine Erfassung dauert im Median 0.5–0.9 ms (100–120 Einheiten); die Zeiten der einzelnen Abschnitte stehen stets im Kopf des Weltblocks.
Spieler ×16
Gold, Holz, Nahrung und Limit, insgesamt gesammelte Menge, Volk
Einheiten ×1024
Typ, Besitzer, Koordinaten, HP/Mana, aktuelle Order und Ziel, tatsächliches Angriffsziel, Stufe/Erfahrung/Fähigkeitspunkte, Sichtbarkeit für jeden Spieler
Einheitendetails ×256
12 Fähigkeiten (Stufe, verbleibende Abklingzeit), 8 Buffs, 6 Inventarplätze; Helden zuerst
Produktionstabelle ×128
Ausbilden / Erforschen / Bauen / Aufwerten: Warteschlange, Gesamtdauer, verstrichene Zeit, ob blockiert
Gegenstände am Boden ×256
Typ, Position, Haltbarkeit; Aufheben oder Verbrauchen löst ein Event aus
Bäume ×4096
Position und HP zerstörbarer Objekte, alle 2 s aktualisiert
Karte
Begehbar-/Bebaubar-Raster (128 pro Zelle), spielbarer Bereich, Startpositionen; wenige Sekunden nach Spielstart berechnet
Zeit
Engine-Spieluhr, Tageszeit (Tag/Nacht), Spielgeschwindigkeit, ob ein Spiel läuft
Welchen Weg ein Befehl nimmt, entscheidet die Runtime
Abbauen, Rückzug, Zaubern … Alles heißt „eine Einheit soll etwas tun“, doch in der Engine nimmt jedes einen anderen Weg. Dieses Wissen steckt komplett in der Runtime: Du sagst, was passieren soll; sie führt es mit einem verifizierten Rezept aus und liest im selben Frame die Order vor und nach dem Befehl als Quittung zurück.
- Ein Batch pro Übermittlung, Ausführung im selben Frame
- Jeder Befehl mit Quittung: Statuscode + Reason-Code der Engine + Ausführungszeit
- Shift-Warteschlange, Wegpunkte, Serienbau
- Abfragen: Technologiezähler, Machbarkeit, Sichtbarkeit, Restgold, Angriffsziel des Computergegners
Langsam war nie die Prozessgrenze
Shared-Memory-Zugriffe kosten Nanosekunden, ein Snapshot liest sich in ca. 0.05 ms. Der alte Kanal war langsam, weil jede Anfrage erst ein maschinenweites Lock ergattern und dann warten musste, bis das Spiel das nächste Mal Nachrichten abholt (einmal pro Frame, 16–33 ms). Das Referenz-Brain verbrachte 93 % seiner Wanduhrzeit mit diesem Warten.
- Maschinenweiten Mutex ergattern
- Eine Nachricht an den Spiel-Thread schicken
- Warten, bis das Spiel das nächste Mal Nachrichten abholt (einmal pro Frame)
- Auf das Fertig-Event warten, Lock freigeben
Mit 6 Prozessen gleichzeitig blieb der Durchsatz bei ca. 88 Aufrufen/s stecken.
- Jeder Client hat eine eigene Spur: ein Schreiber, ein Leser, kein Lock
- Ausführung im Event-Dispatch des Spiel-Threads (hunderte Male pro Sekunde); ohne Übermittlung kostet das nur ein paar Integer-Vergleiche
- 16 Befehle pro Übermittlung, alle im selben Dispatch ausgeführt; 4 ms Zeitbudget pro Durchlauf
- Jeder Befehl mit Deadline: Nach einer Pause werden abgelaufene Befehle nicht mehr ausgeführt
Jede Quittung enthält die Ausführungszeit; der Spurkopf hält den langsamsten Befehl und die Dauer des letzten Durchlaufs fest – wo es hakt, sieht man sofort.
| Stufe | Kanal | Latenz | Nutzer | Status |
|---|---|---|---|---|
| 0 | Gepushter Snapshot + Event-Stream | Lesen ca. 0.4 ms; alle 50 ms ein neuer (bis 16 ms einstellbar) | Alle Bots | Live verifiziert |
| 1 | Schnellspur | ca. 1 Frame; Median bei 6 parallelen Prozessen 0.06 ms, Durchsatz ca. 3000/s | SDK-Standard | Live verifiziert |
| 2 | Steuerkanal | 20–40 ms | Fallback, einige UI-Operationen | Live verifiziert |
| 3 | Gateway (WebSocket / JSON) | Stufe 1 + ca. 1 ms | Jede Sprache, Browserseiten, LLMs (MCP), andere Rechner | Live verifiziert |
Gemessen (1.27-Testinstanz, 2026-09-23 / 24)
Aus der Praxis: In den ersten 3 Minuten war jeder Befehl 1000-mal langsamer
Nachdem wir jeden Befehl mit einer Zeitmessung versehen hatten, zeigte sich, dass in den ersten Spielminuten ab dem zweiten Befehl eines Batches jeder 4–10 ms brauchte – bis die Zeit bei etwa 180 Spielsekunden plötzlich auf wenige Mikrosekunden fiel. Der eingebaute Sampler der Runtime für den Spiel-Thread sammelte 1700 Samples; 93 % davon lagen in der Log-Funktion der Runtime selbst – jede Zeile öffnete und schloss die Logdatei synchron, und zu Spielbeginn schreibt jede Engine-Order eine Zeile. Seit das Debug-Log standardmäßig aus ist und asynchron geschrieben wird, braucht auch ein Befehl in Sekunde 14 nur 4–8 µs.
Wir glauben gemessenen Zahlen, nicht Vermutungen.
Jede Spur hat eine Rolle
Besitzprüfung und Sichtfilterung passieren in der Runtime – die Ausführungsschicht der Engine prüft den Besitz von Einheiten selbst nicht, also muss es hier geschehen. Zwei KIs gegeneinander heißt: zwei player-Spuren im selben Spiel.
| Rolle | Sieht | Darf |
|---|---|---|
| player | Alles Eigene + Gegner und Neutrale in Sicht (Fair-Modus) | Nur eigene Einheiten befehligen Spieler, dein Bot |
| observer | Ganze Karte | Keine Befehle; abfragen, Kamera steuern, HUD lesen Regie, Kommentar, Analyse |
Nicht raten, nicht abstürzen
1.24–1.28 teilen dieselbe Engine-Struktur – ideal für „eine Runtime + mehrere Profile“. Ab 1.29 und bei Reforged ist es eine andere Engine; automatische Kompatibilität wird nicht zugesagt.
- Erkennen Versionsressource und Datei-Hash der Game.dll lesen, Profil wählen
- Symboltabelle Rezepte referenzieren Symbolnamen, keine Zahlen; jedes Symbol mit Aufrufkonvention und Parameterform
- Signatur-Fallback Auf unbekannten Versionen nach Byte-Signaturen am Funktionsanfang scannen; nur bei eindeutigem Treffer verwenden
- Selbsttest beim Start Jedes Symbol wird nebenwirkungsfrei geprüft; was durchfällt, wird als nicht verfügbar markiert
- Fähigkeitsliste Erkennt das SDK, dass eine Fähigkeit nicht verfügbar ist, wirft der Aufruf einen klaren Fehler, statt stillschweigend 0 zurückzugeben
Ein Bug in einem Bot darf nicht das ganze Spiel abstürzen lassen
Deshalb läuft jede KI in einem eigenen Prozess: Ein Nullzeiger im selben Prozess bringt das ganze Spiel zum Absturz; stürzt ein eigener Prozess ab, fällt nur diese Seite aus.
| Mechanismus | Umsetzung | Status |
|---|---|---|
| Spiel nicht ausbremsen | Alle Spielaufrufe mit Ausnahmeschutz; 4 ms Zeitbudget pro Durchlauf (hochauflösender Timer) | Vorhanden |
| Clients isoliert | Eine Spur und ein eigenes Kontingent pro Client; hängt einer, blockiert er die anderen nicht | Vorhanden |
| Abgelaufenes wird nicht ausgeführt | Jeder Befehl trägt eine Deadline; abgelaufene werden nur markiert, nicht ausgeführt, und nach einer Pause nicht nachgeholt | Vorhanden |
| Beobachtbar | Ausführungszeit jedes Befehls; Abschnittszeiten jeder Erfassung im Kopf des Weltblocks | Vorhanden |
| Reconnect | Das SDK verbindet sich bei neuem Spiel oder neuem Prozess automatisch neu | Teilweise |
| Circuit Breaker pro Fähigkeit | Fällt eine Fähigkeit N-mal hintereinander aus → als nicht verfügbar markiert, Event gesendet, alle anderen laufen weiter | Geplant |
Bau deine eigenen Dinge ins Spiel
Semantische Befehle lassen die KI wie ein Spieler handeln; die Erweiterungsschicht verändert, was Spieler sehen und erleben – eigene klickbare Oberflächen zeichnen, alle Funktionen der Kartenautoren aufrufen. Die beiden Wege sind danach getrennt, ob sie im Multiplayer sicher sind.
- Textfelder, Panels, Fortschrittsbalken, Bilder, Kreise auf dem Gelände, Routen mit Pfeilen
- Folgt Einheiten, Weltkoordinaten oder Bildschirmpositionen; eigene Schriftarten, abgerundete Ecken, Transparenz, beliebige Farben
- Die Runtime zeichnet selbst – keine Spielobjekte, keine Änderung am Spielzustand
- Eine Zeile Python pro Element, alternativ über HTTP oder direkt per Shared Memory
- Buttons und Auswahlkarten sind klickbar und leuchten beim Hover auf; sie liegen unter dem Mauszeiger, und der Klick darauf kommt beim Spiel nicht an
- Einheiten erzeugen, Werte ändern, Effekte, Panels, Dialoge, Sound, Kamera, Nebel, Wetter …
- Farsight-Konsole, Kommandozeile, HTTP, Python – dieselbe Skriptschreibweise
- Häufige Effekte in einer Zeile: schwebender Text, Linien, Reichweitenkreise, Porträt-Dialog, Vollbildfilter
- Im Multiplayer sind nur lesende Funktionen freigegeben, um Desyncs zu vermeiden
| Canvas | JASS-Grafikfunktionen | |
|---|---|---|
| Wer zeichnet | Die Runtime selbst | Das Spiel selbst |
| Multiplayer | Sicher: nur im eigenen Bild | Nur Einzelspieler |
| Stil | Frei: Schriftart, abgerundete Ecken, Transparenz, Bilder | Nativer Spielstil |
| Folgt Objekten | Einheit / Weltkoordinaten / Bildschirmposition | Je nach Funktion |
| Kosten | 0.2 ~ 0.35 ms pro Frame | ca. 13 ms pro Aufruf |
Was der Spieler tut, landet direkt im Event-Stream
Die Runtime meldet die Aktionen des Spielers direkt: welchen gezeichneten Button er angeklickt hat, welchen Hotkey er gedrückt hat, wohin er auf den Boden geklickt hat, was er ausgewählt hat, welche Fähigkeit er gewirkt hat und was er ins Chatfeld getippt hat. Die spieleigenen Trigger-Events (Betreten von Regionen, Dialog-Buttons, Pfeiltasten) lassen sich weiterhin über einen leeren Trigger anbinden: nur Events registrieren, keine Bedingung und keine Aktion schreiben, dann zählen, wie oft er ausgelöst wurde.