# Protocolo W3P

> Todo el contrato entre el runtime y los programas externos: ocho bloques de memoria compartida, lectura del estado del mundo, lectura de eventos, envío de comandos, recibos, roles de carril, lienzo e interfaz y entrada. Si te integras desde un lenguaje distinto de Python, lee esta página.

Fuente: https://war3ai.com/es/docs/protocol/

El runtime y los programas externos intercambian datos **solo a través de memoria compartida**. Lo que sigue es todo lo que hay.

- La implementación de referencia es la de Python: `sdk/python/w3world.py` (lectura) y `sdk/python/w3fast.py` (escritura); ahí están definidos el tamaño y los offsets de cada estructura, fijados por tests;
- **El protocolo solo describe semántica y no depende de la versión del juego.** Cuando cambia la versión del juego, el runtime se adapta solo y el protocolo no cambia; los campos nuevos solo se añaden al final de un bloque, así que los clientes antiguos siguen funcionando.

> **Nota**
>
> La mayoría no necesita leer esta página: basta con usar el SDK de Python. Solo la necesitas si quieres integrarte directamente desde C++ / C# / Rust / Go u otro lenguaje, o si quieres saber qué ocurre por debajo del SDK.

## 1. Ocho bloques de memoria compartida

`<pid>` es el ID del proceso del juego.

| Nombre | Dirección | Contenido | Sincronización |
|---|---|---|---|
| `Local\War3World_<pid>` | runtime → tú | Estado del mundo: cabecera + 16 jugadores + hasta 1024 unidades + 256 detalles de unidad + 256 objetos en el suelo + zona de extensión + tabla de producción | seqlock |
| `Local\War3Trees_<pid>` | runtime → tú | Hasta 4096 destructibles (árboles, etc.), se refresca cada 2 segundos | seqlock |
| `Local\War3Events_<pid>` | runtime → tú | Anillo de eventos, 8192 entradas | cada entrada lleva su propio número de secuencia |
| `Local\War3Map_<pid>` | runtime → tú | Mapa: celdas de terreno (128 por celda, hasta 256×256) + límites del área jugable + puntos de inicio; se calcula por lotes durante los primeros segundos de la partida | seqlock (no cambia una vez calculado) |
| `Local\War3Fast_<pid>` | bidireccional | Carriles de comandos: 16 carriles × 16 slots; cada slot contiene un comando + su recibo; cada carril tiene un rol | un escritor y un lector por slot |
| `Local\War3Canvas_<pid>` | tú → runtime | [Lienzo](https://war3ai.com/es/docs/canvas/): cabecera de 64 bytes + 256 elementos × 112 bytes + 64 KB de pool de texto / puntos; solo se crea después de enviar una vez `canvas_enable` | seqlock (tú escribes, el runtime lee en cada fotograma) |
| `Local\War3Msgs_<pid>` | runtime → tú | Anillo de mensajes en pantalla: texto completo de avisos del juego, chat y mensajes del sistema, 128 entradas × 256 bytes | cada entrada lleva su propio número de secuencia |
| `Local\War3Input_<pid>` | bidireccional | [Interfaz y entrada](https://war3ai.com/es/docs/ui-input/): el runtime escribe la posición del ratón, el punto del suelo al que apunta y el elemento bajo el cursor; tú escribes la tabla de atajos de teclado y los interruptores del ratón; el runtime solo empieza a capturar la entrada después de enviar una vez `input_enable` | seqlock para la tabla de atajos |

**Varios clientes usando el lienzo y la entrada a la vez**: de estos dos bloques solo hay una copia, y si cada uno escribe por su cuenta se sobrescriben entre sí. Esta es la convención, y tu propio cliente también tiene que seguirla:

- **Lienzo**: con el mutex con nombre `Local\War3CanvasMutex_<pid>` tomado, haz lectura - modificación - escritura, cambia solo tus propios elementos y deja intactos los de los demás (recolocando los offsets del pool); elimina los que no tienen dueño y los de dueños cuyo proceso ya terminó. En cada elemento, `reserved[1]` = ID del proceso dueño y `reserved[2]` = número de secuencia dentro del proceso; los números de elemento se asignan desde el contador en el offset 60 de la cabecera del bloque (empezando en `0x10000`).
- **Entrada**: cada cliente registra sus atajos de teclado y sus interruptores del ratón en `Local\War3InputClients_<pid>` (cabecera de 16 bytes + 16 clientes × 528 bytes); con `Local\War3InputMutex_<pid>` tomado, actualiza su propia entrada y luego escribe en el bloque de entrada la combinación de todos los clientes vivos: los atajos se deduplican por «código de tecla + modificadores» y los interruptores del ratón se unen. Los eventos se envían a todos los clientes, y cada uno reconoce sus atajos por «código de tecla + modificadores». Mientras queden otros clientes vivos en la tabla de registro, no envíes `input_enable 0`.
- **Runtime**: los elementos clicables cuyo proceso dueño ya terminó dejan de interceptar clics; cada 2 segundos revisa la tabla de registro y, si todos los clientes registrados han terminado, pone a cero la tabla de atajos y los interruptores del ratón del bloque de entrada.

## 2. Leer el estado del mundo (seqlock)

```text
loop:
    s1 = block.seq                (offset 8, int32)
    if s1 es impar: reintentar    (el runtime está escribiendo)
    copiar cabecera + players + units[unitCount] + details[detailCount] + items[itemCount]
    if block.seq != s1: reintentar
```

- **Cabecera**: contador de publicaciones (si no aumenta, la publicación se ha cortado), reloj de juego del motor, un epoch que suma 1 en cada partida, número del jugador local, si hay una partida en curso, velocidad de juego, periodo de publicación, microsegundos que costó recopilar esta copia en el hilo del juego, número de secuencia de eventos y tiempos por etapa. El cliente puede escribir `requestedPeriodMs` para pedir un periodo de publicación (16 ~ 1000 ms).
- **Unidad** (112 bytes): par de handles (**identifica las unidades por el par de handles**: las direcciones se reutilizan), código de cuatro caracteres del tipo, dueño, flags, coordenadas, vida / maná (con sus máximos), orden actual + objetivo de la orden, objetivo de tarea (a quién ataca realmente), nivel / experiencia / puntos de habilidad del héroe, índice de detalle, `visibleTo` (bit p = el jugador p la ve en este momento).
- **Detalle** (288 bytes; héroes > unidades de jugador > creeps; hasta 256): 12 habilidades (código / nivel / flags / segundos de enfriamiento restantes), 8 códigos de buff, 6 casillas de inventario.
- **Extensiones al final del bloque** (solo se añaden; los offsets anteriores nunca se mueven, así que los clientes antiguos siguen funcionando): zona de extensión `EXT1` (hora del día dentro del juego, velocidad del ciclo día/noche, número de entradas de la tabla de producción) y tabla de producción `prods[128]` (edificios que están entrenando / investigando / construyendo / mejorando, cola, duración total, tiempo transcurrido, si está atascada). **Úsalas solo si el magic coincide.**

## 3. Leer eventos

```text
head = ring.writeSeq               (offset 8)
for seq in (cursor, head]:
    e = ring.events[(seq - 1) % 8192]
    if e.seq > seq:  se perdió una (sobrescrita por leer demasiado despacio)
    elif e.seq != seq: aún no está escrita del todo, léela la próxima vez
    else: procesar e
```

La estructura de evento ocupa 64 bytes: `seq kind clockMs addr handleLo handleHi typeId owner a b x y value extra`.

- Obtenidos comparando dos publicaciones consecutivas (precisión = periodo de publicación): `unit.appeared / unit.died / unit.removed / unit.damaged / order.changed / hero.levelup / owner.changed / item.appeared / item.removed / game.started`;
- A nivel de motor (el runtime los registra en el hilo del juego en el momento en que ocurren, así que hay uno por **cada golpe**): `damage` (origen, tipo de daño, tipo de ataque, posición, vida perdida real, daño antes de armadura), `killed` (el asesino);
- Obtenido siguiendo la tabla de producción: `production.done` (código de cuatro caracteres de lo terminado, categoría, cuántos segundos de juego tardó; también se emite para los rivales);
- Comprobados por el runtime en cada publicación: `spell.cast` (la habilidad entra en enfriamiento: `a` código de cuatro caracteres de la habilidad, `b` nivel, `value` segundos de enfriamiento, `x/y` punto de lanzamiento), `player.left` (`a` número de jugador, `b` nuevo estado de la ranura), `selection.changed` (selección del jugador local; la tabla completa está en la zona de extensión del bloque del mundo), `game.ended` (se sale de la partida);
- Mensajes en pantalla: `message` (`a` = número de secuencia del mensaje; el texto completo se busca en la memoria compartida `Local\War3Msgs_<pid>`: 128 entradas × 256 bytes, con avisos del juego, chat y mensajes del sistema; `b` = número del marco de mensajes);
- Interfaz y entrada (después de `input_enable`): `ui.click` (`a` id del elemento del lienzo, `b` 1 botón izquierdo / 2 botón derecho), `ui.hover`, `hotkey` (`a` id del atajo, `b` código de tecla virtual), `mouse.world` (`x/y` coordenadas del suelo, `value` = 1 significa que el clic se tragó), con las teclas modificadoras siempre en `extra`.

## 4. Enviar comandos

1. **Cada objeto cliente ocupa un carril**: con `Local\War3FastMutex_<pid>` tomado, busca un carril libre (o cuyo proceso dueño haya muerto) y escribe el rol, el número de jugador y tu propio pid. Si un mismo proceso necesita dos roles, abre dos carriles;
2. Rellena los slots: flag de comando semántico, código de operación, `args[11]` y plazo límite `deadlineMs`;
3. Cuando todos los slots estén escritos, márcalos como enviados y suma 1 al `submitSeq` del carril;
4. Espera al evento `Local\War3FastDone_<pid>_<lane>` (o haz polling), lee los recibos y devuelve los slots.

El runtime ejecuta los comandos por lotes dentro del despacho de eventos del hilo del juego: cada vaciado tiene un presupuesto de **4 ms** (temporizador real de alta precisión); lo que no cabe se deja para el siguiente despacho. **Los slots que han superado su plazo nunca se ejecutan**, así que no ocurre que «al reanudar tras una pausa se vuelvan a ejecutar comandos viejos».

Índices de `args`: `0..2` unidad (dirección, handle lo, handle hi), `3` ID de orden o código de cuatro caracteres, `4..6` objetivo, `7/8` x / y (bits de float), `9` extra (número de jugador / número de casilla / interruptor / modo de cola), `10` mode (0 sin objetivo / 1 a un punto / 2 a un objetivo).

### Códigos de operación

| Código | Nombre | Descripción |
|---|---|---|
| 1 | `point` | Orden de una unidad a un punto (mover / atacar-mover / patrullar / atacar el suelo / hechizo a un punto). extra bit0 = en cola (se inserta tras la orden actual) |
| 2 | `target` | Orden de una unidad a un objetivo (atacar con clic derecho / recolectar / reparar / hechizo a una unidad / recoger objeto); el objetivo tiene que ser visible |
| 3 | `immediate` | Comando sin objetivo (detener / mantener posición / entrenar / investigar / mejorar / hechizo sin objetivo) |
| 4 | `build` | Un trabajador construye un edificio (coordenadas alineadas a 32) |
| 5 | `learn` | El héroe aprende una habilidad |
| 6 | `use_item` | Usar la casilla extra del inventario |
| 7 | `revive` | Revivir un héroe en el altar |
| 8 | `rally` | Punto de reunión (a un punto / a un objetivo) |
| 9 | `buy` | Una tienda vende un objeto al héroe que está al lado |
| 10 | `item_drop` | Soltar un objeto: dárselo a un aliado, vendérselo a una tienda (`code` = la unidad que lo recibe) o dejarlo en el suelo |
| 20 ~ 25 | Consultas | `q_tech` recuento de tecnología, `q_feasible` viabilidad, `q_visible` visibilidad, `q_mine_gold` oro restante en una mina, `q_captain` capitán del ordenador, `q_dead_heroes` lista de héroes muertos |
| 30 | `pause` | Pausar / reanudar |
| 40 ~ 50 | Cámara | Leer el estado de la cámara, fijar un campo, mirar a un punto, seguir, restablecer, rotar, límites, suavizado, mostrar / ocultar la interfaz, imagen limpia, niebla |
| 60 ~ 63 | HUD | Texto del botón de misiones, título y descripción del panel de misiones, refrescar, leer si el panel está abierto |
| 70 | `jass` | Llama a una native de JASS por su nombre (1291 en total): el nombre y los parámetros de tipo string van en la zona adicional del slot, el resto de parámetros se colocan en `args` según la firma; el valor de retorno queda en `value[0]`. Solo para carriles de herramientas locales; se rechaza cualquier llamada con parámetros de tipo función o que pueda suspender el hilo de scripts. Ver [Canal JASS](https://war3ai.com/es/docs/jass/) |
| 71 / 72 | `jass_handle_of` / `jass_unit_of` | Convierte entre unidad de la instantánea ↔ handle de JASS (el par de handles de la instantánea no es un handle de JASS) |
| 73 | `canvas_enable` | Crea la memoria compartida del lienzo e instala el hook de dibujo; cualquier carril puede enviarlo (el lienzo solo se dibuja en la pantalla local). La primera vez hay que instalar el hook: da un timeout de 2 segundos o más |
| 74 | `input_enable` | `extra` = 1 captura la entrada de la ventana del juego (clic / hover sobre elementos del lienzo, atajos de teclado, clics en el suelo); 0 = la devuelve. Bloque de entrada `Local\War3Input_<pid>`: cabecera de 128 bytes + 32 atajos × 16 bytes; tú escribes la tabla de atajos y los interruptores del ratón, y el runtime escribe la posición del ratón, el punto del suelo al que apunta y el elemento bajo el cursor. Cualquier carril puede enviarlo (solo afecta a la entrada local). Ver [Interfaz y entrada](https://war3ai.com/es/docs/ui-input/) |

## 5. Recibos

Un recibo ocupa 52 bytes (+8 bytes de tiempos): `status`, `engineReturn`, `verdict` (código de motivo del rechazo), `orderBefore / orderAfter` (la orden de la unidad leída en el mismo frame), `value[8]` (resultados de consultas), `execUs` (microsegundos que tardó este comando en ejecutarse en el hilo del juego), `engineUs` (la parte que corresponde a la propia función de órdenes del motor).

Todos los códigos de estado y de motivo están en [Recibos y códigos de motivo](https://war3ai.com/es/docs/reason-codes/).

## 6. Roles de carril

| Rol | Qué puede hacer |
|---|---|
| `dev` | Herramientas locales: comandos semánticos (mandar las unidades del jugador local) + canal JASS |
| `player` | Solo comandos semánticos, y solo para las unidades del jugador al que pertenece el carril (las de otro = `not_owner`) |
| `observer` | Solo consultas, cámara, lectura del estado de los paneles del HUD y activar el lienzo y la entrada local; todo lo demás es `forbidden` |

Dos IA enfrentadas = dos carriles `player` en la misma partida (player 0 / player 1).

> **Atención**
>
> En modo local, el rol lo declara el propio cliente (es una convención, no una frontera de seguridad). En la [Arena](https://war3ai.com/es/arena/), el proceso árbitro crea los carriles y solo entrega el carril `player` a cada participante.

## 7. Semántica verificada en partidas reales

- Clic derecho (smart) sobre un enemigo = atacar **a ese en concreto** (el objetivo de la orden y el objetivo de tarea son esa unidad); la orden de ataque en bruto enviada como comando a un objetivo solo cambia a la orden de ataque sin recordar el objetivo, así que la unidad se va a atacar a otro cercano;
- El motor no permite comandos a un objetivo sobre unidades que no ves: al anochecer, los campamentos lejanos quedan en la niebla de guerra y todos los clics derechos se rechazan (1001);
- Que una construcción sea «aceptada» solo significa que el trabajador recibió la orden: un punto dentro de un bosque también se acepta en el acto, y el trabajador solo falla al llegar; los puntos claramente ocupados se rechazan en el acto;
- Un héroe solo puede revivirse unos 3 segundos de juego después de morir; también se rechaza si no hay comida suficiente (los héroes ocupan comida);
- Los objetos del inventario no cuentan como objetos en el suelo; al recoger uno se emite `item.removed`;
- Durante la pausa el reloj del motor se detiene, pero se pueden seguir enviando comandos;
- Una partida iniciada minimizada tiene la simulación detenida (el reloj no avanza).
