# W3P-Protokoll

> Der komplette Vertrag zwischen Runtime und externen Programmen: acht Shared-Memory-Blöcke, Weltzustand lesen, Events lesen, Befehle erteilen, Quittungen, Spur-Rollen, Canvas sowie Oberfläche & Eingabe. Lies diese Seite, wenn du aus einer anderen Sprache als Python andockst.

Quelle: https://war3ai.com/de/docs/protocol/

Runtime und externe Programme tauschen Daten **ausschließlich über Shared Memory** aus. Das Folgende ist alles, was es dazu gibt.

- Die Referenzimplementierung ist in Python: `sdk/python/w3world.py` (Lesen) und `sdk/python/w3fast.py` (Schreiben); Größe und Offsets jeder Struktur sind dort definiert und durch Tests festgenagelt;
- **Das Protokoll beschreibt nur Semantik und ist unabhängig von der Spielversion.** Wechselt die Spielversion, passt sich die Runtime selbst an, das Protokoll bleibt gleich; neue Felder werden nur ans Blockende angehängt, alte Clients funktionieren weiter.

> **Info**
>
> Die meisten brauchen diese Seite nicht – nimm einfach das Python-SDK. Du brauchst sie nur, wenn du direkt aus C++ / C# / Rust / Go oder einer anderen Sprache andocken willst oder wissen möchtest, was unter dem SDK passiert.

## 1. Acht Shared-Memory-Blöcke

`<pid>` ist die Prozess-ID des Spiels.

| Name | Richtung | Inhalt | Synchronisation |
|---|---|---|---|
| `Local\War3World_<pid>` | Runtime → du | Weltzustand: Header + 16 Spieler + bis zu 1024 Einheiten + 256 Einheitendetails + 256 Gegenstände am Boden + Erweiterungsbereich + Produktionstabelle | seqlock |
| `Local\War3Trees_<pid>` | Runtime → du | Bis zu 4096 zerstörbare Objekte (Bäume usw.), alle 2 Sekunden aktualisiert | seqlock |
| `Local\War3Events_<pid>` | Runtime → du | Event-Ring, 8192 Einträge | jeder Eintrag trägt eine eigene Sequenznummer |
| `Local\War3Map_<pid>` | Runtime → du | Karte: Geländezellen (128 pro Zelle, bis zu 256×256) + Grenzen des spielbaren Bereichs + Startpositionen; wird in den ersten Sekunden der Partie in Etappen berechnet | seqlock (ändert sich nach der Berechnung nicht mehr) |
| `Local\War3Fast_<pid>` | beide Richtungen | Befehlsspuren: 16 Spuren × 16 Slots; jeder Slot enthält einen Befehl + Quittung; jede Spur hat eine Rolle | pro Slot ein Schreiber, ein Leser |
| `Local\War3Canvas_<pid>` | du → Runtime | [Canvas](https://war3ai.com/de/docs/canvas/): Header 64 Bytes + 256 Elemente × 112 Bytes + 64 KB Text- / Punkt-Pool; wird erst nach einem `canvas_enable` angelegt | seqlock (du schreibst, die Runtime liest in jedem Frame) |
| `Local\War3Msgs_<pid>` | Runtime → du | Ring der Bildschirmnachrichten: voller Text von Spielhinweisen, Chat und Systemnachrichten, 128 Einträge × 256 Bytes | jeder Eintrag trägt eine eigene Sequenznummer |
| `Local\War3Input_<pid>` | beide Richtungen | [Oberfläche & Eingabe](https://war3ai.com/de/docs/ui-input/): Die Runtime schreibt Mausposition, den Bodenpunkt unter dem Mauszeiger und das Element unter dem Mauszeiger zurück; du schreibst die Hotkey-Tabelle und die Maus-Schalter; die Runtime übernimmt die Eingabe erst nach einem `input_enable` | Hotkey-Tabelle per seqlock |

**Mehrere Clients nutzen Canvas und Eingabe gleichzeitig**: Beide Blöcke gibt es nur einmal; schreibt jeder für sich, überschreiben sie sich gegenseitig. Es gelten diese Konventionen, auch für deinen eigenen Client:

- **Canvas**: Den benannten Mutex `Local\War3CanvasMutex_<pid>` halten und lesen – ändern – schreiben: nur die eigenen Elemente ersetzen, die der anderen unverändert lassen (Pool-Offsets neu anordnen); Elemente, deren Besitzerprozess beendet ist oder die keinen Besitzer haben, entfernen. Im Element ist `reserved[1]` = Prozess-ID des Besitzers, `reserved[2]` = laufende Nummer im Prozess; Elementnummern vergibt der Zähler bei Offset 60 im Blockheader (ab `0x10000`).
- **Eingabe**: Jeder Client trägt seine Hotkeys und Maus-Schalter in `Local\War3InputClients_<pid>` ein (Header 16 Bytes + 16 Clients × 528 Bytes), aktualisiert unter `Local\War3InputMutex_<pid>` seinen eigenen Eintrag und schreibt dann die lebenden Clients zusammengeführt in den Eingabeblock: Hotkeys werden nach „Tastencode + Modifikatortasten“ dedupliziert, Maus-Schalter vereinigt. Events gehen an alle Clients, jeder erkennt seine Hotkeys an „Tastencode + Modifikatortasten“. Solange im Register noch andere lebende Clients stehen, kein `input_enable 0` senden.
- **Runtime**: Klickbare Elemente, deren Besitzerprozess beendet ist, fangen keine Klicks mehr ab; alle 2 Sekunden prüft die Runtime das Register, und haben sich alle eingetragenen Clients beendet, setzt sie Hotkey-Tabelle und Maus-Schalter im Eingabeblock auf null.

## 2. Weltzustand lesen (seqlock)

```text
loop:
    s1 = block.seq                (Offset 8, int32)
    if s1 ungerade: erneut        (Runtime schreibt gerade)
    kopiere Header + players + units[unitCount] + details[detailCount] + items[itemCount]
    if block.seq != s1: erneut
```

- **Header**: Veröffentlichungszähler (steigt er nicht, ist die Veröffentlichung abgerissen), Spieluhr der Engine, eine Epoche, die pro Partie um 1 steigt, eigene Spielernummer, ob gerade eine Partie läuft, Spielgeschwindigkeit, Veröffentlichungsperiode, Mikrosekunden, die das Erfassen dieser Kopie im Spiel-Thread gekostet hat, Event-Sequenznummer, Zeiten pro Abschnitt. Clients können `requestedPeriodMs` schreiben, um eine Veröffentlichungsperiode anzufordern (16 ~ 1000 ms).
- **Einheit** (112 Bytes): Handle-Paar (**Einheiten am Handle-Paar erkennen** – Adressen werden wiederverwendet), Typ als Vier-Zeichen-Code, Besitzer, Flags, Koordinaten, TP / Mana (mit Maximum), aktuelle Order + Order-Ziel, Aufgabenziel (was sie tatsächlich angreift), Heldenstufe / EP / Fertigkeitspunkte, Detail-Index, `visibleTo` (Bit p = Spieler p sieht sie gerade).
- **Details** (288 Bytes, Helden > Spielereinheiten > Creeps, bis zu 256): 12 Fähigkeiten (Code / Stufe / Flags / verbleibende Abklingzeit in Sekunden), 8 Buff-Codes, 6 Inventarplätze.
- **Erweiterungen am Blockende** (nur angehängt, frühere Offsets verschieben sich nie, alte Clients funktionieren weiter): Erweiterungsbereich `EXT1` (Tageszeit im Spiel, Tag-/Nacht-Geschwindigkeit, Anzahl der Einträge in der Produktionstabelle) und Produktionstabelle `prods[128]` (Gebäude, die gerade trainieren / forschen / bauen / aufwerten, Warteschlange, Gesamtdauer, bereits vergangene Zeit, ob sie hängen). **Nur verwenden, wenn die Magic passt.**

## 3. Events lesen

```text
head = ring.writeSeq               (Offset 8)
for seq in (cursor, head]:
    e = ring.events[(seq - 1) % 8192]
    if e.seq > seq:  einer verloren (zu langsam gelesen, überschrieben)
    elif e.seq != seq: noch nicht fertig geschrieben, beim nächsten Mal lesen
    else: e verarbeiten
```

Die Event-Struktur hat 64 Bytes: `seq kind clockMs addr handleLo handleHi typeId owner a b x y value extra`.

- Aus dem Vergleich zweier aufeinanderfolgender Veröffentlichungen abgeleitet (Genauigkeit = Veröffentlichungsperiode): `unit.appeared / unit.died / unit.removed / unit.damaged / order.changed / hero.levelup / owner.changed / item.appeared / item.removed / game.started`;
- Auf Engine-Ebene (die Runtime zeichnet sie sofort im Spiel-Thread auf, es gibt also einen für **jeden einzelnen Treffer**): `damage` (Quelle, Schadenstyp, Angriffstyp, Position, tatsächlicher TP-Verlust, Schaden vor Rüstung), `killed` (Killer);
- Aus dem Verfolgen der Produktionstabelle abgeleitet: `production.done` (Vier-Zeichen-Code des Fertiggestellten, Kategorie, benötigte Spielsekunden; wird auch für Gegner ausgegeben);
- Von der Runtime bei jeder Veröffentlichung nebenbei geprüft: `spell.cast` (Abklingzeit einer Fähigkeit startet: `a` Vier-Zeichen-Code der Fähigkeit, `b` Stufe, `value` Abklingzeit in Sekunden, `x/y` Zielpunkt), `player.left` (`a` Spielernummer, `b` neuer Slot-Status), `selection.changed` (Auswahl des lokalen Spielers; die vollständige Liste steht im Erweiterungsbereich des Weltblocks), `game.ended` (Partie verlassen);
- Bildschirmnachrichten: `message` (`a` = Nachrichten-Sequenznummer; den vollen Text schlägst du im Shared Memory `Local\War3Msgs_<pid>` nach: 128 Einträge × 256 Bytes, mit Spielhinweisen, Chat und Systemnachrichten; `b` = Nummer des Nachrichtenfelds);
- Oberfläche & Eingabe (nach `input_enable`): `ui.click` (`a` ID des Canvas-Elements, `b` 1 linke / 2 rechte Maustaste), `ui.hover`, `hotkey` (`a` Hotkey-ID, `b` virtueller Tastencode), `mouse.world` (`x/y` Bodenkoordinaten, `value` = 1 heißt verschluckt); Modifikatortasten stehen jeweils in `extra`.

## 4. Befehle erteilen

1. **Ein Client-Objekt belegt eine Spur**: `Local\War3FastMutex_<pid>` halten, eine freie Spur suchen (oder eine, deren Besitzerprozess tot ist), Rolle, Spielernummer und die eigene pid eintragen. Braucht derselbe Prozess zwei Rollen, öffnet er zwei Spuren;
2. Slots füllen: Flag für semantischen Befehl, Opcode, `args[11]`, Deadline `deadlineMs`;
3. Sind alle Slots geschrieben, als eingereicht markieren und `submitSeq` der Spur um 1 erhöhen;
4. Auf das Event `Local\War3FastDone_<pid>_<lane>` warten (oder pollen), Quittungen lesen, Slots zurückgeben.

Die Runtime führt die Befehle gebündelt im Event-Dispatch des Spiel-Threads aus: Ein Abarbeitungsdurchlauf hat ein Zeitbudget von **4 ms** (echter hochauflösender Timer); was darüber hinausgeht, bleibt für den nächsten Dispatch liegen. **Slots mit abgelaufener Deadline werden nie mehr ausgeführt** – es kann also nicht passieren, dass alte Befehle nach dem Fortsetzen einer Pause noch einmal laufen.

`args`-Indizes: `0..2` Einheit (Adresse, Handle lo, Handle hi), `3` Order-ID oder Vier-Zeichen-Code, `4..6` Ziel, `7/8` x / y (Float-Bits), `9` extra (Spielernummer / Slotnummer / Schalter / Warteschlangenposition), `10` mode (0 ohne Ziel / 1 auf Punkt / 2 auf Ziel).

### Opcodes

| Opcode | Name | Beschreibung |
|---|---|---|
| 1 | `point` | Order einer Einheit auf einen Punkt (Bewegen / Angriffsbewegung / Patrouillieren / Boden angreifen / Zauber auf Punkt). extra bit0 = anstellen (hinter die aktuelle Order einreihen) |
| 2 | `target` | Order einer Einheit auf ein Ziel (Rechtsklick-Angriff / Ernten / Reparieren / Zauber auf Ziel / Gegenstand aufheben); das Ziel muss sichtbar sein |
| 3 | `immediate` | Befehl ohne Ziel (Stopp / Position halten / Trainieren / Forschen / Aufwerten / Zauber ohne Ziel) |
| 4 | `build` | Arbeiter errichtet ein Gebäude (Koordinaten auf 32 ausgerichtet) |
| 5 | `learn` | Held erlernt eine Fähigkeit |
| 6 | `use_item` | Inventarplatz Nummer extra benutzen |
| 7 | `revive` | Held am Altar wiederbeleben |
| 8 | `rally` | Sammelpunkt (auf Punkt / auf Ziel) |
| 9 | `buy` | Ein Laden verkauft einem Helden in der Nähe einen Gegenstand |
| 10 | `item_drop` | Gegenstand abgeben: einem Verbündeten geben, an einen Laden verkaufen (`code` = empfangende Einheit) oder auf den Boden legen |
| 20 ~ 25 | Abfragen | `q_tech` Tech-Zähler, `q_feasible` Machbarkeit, `q_visible` Sichtbarkeit, `q_mine_gold` Restgold einer Mine, `q_captain` Computer-Captain, `q_dead_heroes` Liste toter Helden |
| 30 | `pause` | Pause / Fortsetzen |
| 40 ~ 50 | Kamera | Kamerazustand lesen, Feld setzen, auf einen Punkt blicken, folgen, zurücksetzen, drehen, Grenzen, Glättung, UI ein-/ausblenden, sauberes Bild, Nebel |
| 60 ~ 63 | HUD | Text des Quest-Buttons, Titel und Beschreibung des Quest-Panels, aktualisieren, lesen, ob das Panel geöffnet ist |
| 70 | `jass` | JASS-Native per Name aufrufen (1291 Stück): Name und String-Parameter stehen im Zusatzbereich des Slots, die übrigen Parameter laut Signatur in `args`; der Rückgabewert steht in `value[0]`. Nur für die Spur lokaler Tools; Aufrufe mit Funktionsparametern oder solche, die den Skript-Thread anhalten würden, werden grundsätzlich abgelehnt. Siehe [JASS-Kanal](https://war3ai.com/de/docs/jass/) |
| 71 / 72 | `jass_handle_of` / `jass_unit_of` | Einheit im Snapshot ↔ JASS-Handle umrechnen (das Handle-Paar im Snapshot ist kein JASS-Handle) |
| 73 | `canvas_enable` | Legt den Canvas-Shared-Memory an und installiert den Zeichen-Hook; jede Spur darf es senden (das Canvas zeichnet nur ins lokale Bild). Beim ersten Mal muss der Hook installiert werden – Timeout von mindestens 2 Sekunden vorsehen |
| 74 | `input_enable` | `extra` = 1 übernimmt die Eingabe des Spielfensters (Klicks / Hover auf Canvas-Elemente, Hotkeys, Klicks auf den Boden), 0 = zurückgeben. Eingabeblock `Local\War3Input_<pid>`: Header 128 Bytes + 32 Hotkeys × 16 Bytes; du schreibst die Hotkey-Tabelle und die Maus-Schalter, die Runtime schreibt Mausposition, den Bodenpunkt unter dem Mauszeiger und das Element unter dem Mauszeiger zurück. Jede Spur darf es senden (betrifft nur die lokale Eingabe). Siehe [Oberfläche & Eingabe](https://war3ai.com/de/docs/ui-input/) |

## 5. Quittungen

Eine Quittung hat 52 Bytes (+8 Bytes Zeitmessung): `status`, `engineReturn`, `verdict` (Grundcode der Ablehnung), `orderBefore / orderAfter` (Order der Einheit, im selben Frame zurückgelesen), `value[8]` (Abfrageergebnisse), `execUs` (wie viele Mikrosekunden dieser Befehl im Spiel-Thread lief), `engineUs` (davon in der Order-Funktion der Engine selbst).

Alle Statuscodes und Grundcodes findest du unter [Quittungen und Grundcodes](https://war3ai.com/de/docs/reason-codes/).

## 6. Spur-Rollen

| Rolle | Was sie darf |
|---|---|
| `dev` | Lokale Tools: semantische Befehle (für Einheiten des lokalen Spielers) + JASS-Kanal |
| `player` | Nur semantische Befehle, und nur für Einheiten des Spielers, dem die Spur gehört (fremde = `not_owner`) |
| `observer` | Nur Abfragen, Kamera, Lesen des HUD-Panel-Zustands, Canvas und lokale Eingabe aktivieren; alles andere ist `forbidden` |

Zwei KIs gegeneinander = zwei `player`-Spuren in derselben Partie (player 0 / player 1).

> **Achtung**
>
> Im lokalen Modus deklariert der Client seine Rolle selbst (eine Konvention, keine Sicherheitsgrenze). In der [Arena](https://war3ai.com/de/arena/) legt der Schiedsrichter-Prozess die Spuren an und übergibt jedem Teilnehmer nur die `player`-Spur.

## 7. Im Live-Spiel verifizierte Semantik

- Rechtsklick (smart) auf einen Gegner = **genau diesen** angreifen (Order-Ziel und Aufgabenziel sind beide diese Einheit); ein roher Angriffsbefehl als Zielbefehl setzt nur die Angriffs-Order, ohne sich das Ziel zu merken – die Einheit greift dann etwas anderes in der Nähe an;
- Die Engine lässt keine Zielbefehle auf Einheiten zu, die du nicht siehst: Nach Einbruch der Nacht liegen entfernte Lager im Kriegsnebel, und jeder Rechtsklick wird abgelehnt (1001);
- Ein „angenommener“ Bau bedeutet nur, dass der Arbeiter den Befehl übernommen hat: Auch ein Punkt im Wald wird sofort angenommen, der Arbeiter scheitert erst, wenn er dort ankommt; offensichtlich belegte Punkte werden sofort abgelehnt;
- Ein Held lässt sich erst etwa 3 Spielsekunden nach seinem Tod wiederbeleben; auch bei zu wenig Nahrung wird abgelehnt (Helden kosten Nahrung);
- Gegenstände im Inventar zählen nicht als Gegenstände am Boden; beim Aufheben wird `item.removed` ausgegeben;
- Während der Pause steht die Engine-Uhr still, Befehle lassen sich aber weiterhin erteilen;
- Wird das Spiel minimiert gestartet, steht die Simulation (die Uhr läuft nicht).
