Un runtime, un protocolo, el juego como entorno programable
W3 Runtime se inyecta en el juego original y, en el hilo del juego, recoge el mundo entero, ejecuta comandos e informa de los resultados. Tu IA solo tiene que decir “qué hacer” y habla con él mediante un protocolo versionado de memoria compartida.
Las capas inferiores no saben que existen las superiores
La capa de interfaz no tiene ni una línea de lógica sobre “atacar o no”; los cerebros no se importan entre sí y solo dependen del SDK. Es la condición para que exista la Arena: la plataforma solo necesita “capa de interfaz + árbitro”, y cualquier cerebro se puede conectar.
Todo el mapa, cada 50 ms
El runtime recoge la información de una vez en el hilo del juego y la publica en memoria compartida; el cliente la lee directamente, sin volver a hacer cola en el hilo del juego. Una recogida tarda una mediana de 0.5 ~ 0.9 ms (100 ~ 120 unidades), y el desglose de tiempos queda siempre en la cabecera del bloque del mundo.
Jugadores ×16
Oro, madera, comida y límite, total recolectado, raza
Unidades ×1024
Tipo, dueño, coordenadas, vida y maná, orden actual y su objetivo, a quién ataca realmente, nivel, experiencia y puntos de habilidad, visibilidad para cada jugador
Detalle de unidades ×256
12 habilidades (nivel, enfriamiento restante), 8 buffs, 6 casillas de inventario; prioridad a los héroes
Producción ×128
Entrenamiento / investigación / construcción / mejora: cola, duración total, tiempo transcurrido, si está bloqueada
Objetos en el suelo ×256
Tipo, posición, durabilidad; recogerlos o usarlos genera un evento
Árboles ×4096
Posición y vida de los destructibles, actualizadas cada 2 segundos
Mapa
Malla transitable / edificable en celdas de 128, área jugable, puntos de inicio; se calcula en los primeros segundos de partida
Tiempo
Reloj de juego del motor, hora del juego (día y noche), velocidad, si hay partida en curso
El camino lo decide el runtime
Recolectar, retirarse, lanzar hechizos… todo es “que una unidad haga algo”, pero dentro del motor cada caso sigue un camino distinto. Toda esa experiencia vive en el runtime: tú dices qué hacer y él lo ejecuta con una receta verificada y, en el mismo fotograma, devuelve como recibo la orden de antes y después del comando.
- Un lote por envío, ejecutado en el mismo fotograma
- Cada comando con recibo: código de estado + código de motivo del motor + tiempo de ejecución
- Cola con Shift, waypoints, construcción en cadena
- Consultas: niveles de tecnología, viabilidad, visibilidad, oro restante en la mina, objetivo de ataque de la IA rival
Lo lento nunca fue cruzar procesos
Leer y escribir memoria compartida cuesta nanosegundos; leer un snapshot, ~0.05 ms. El canal antiguo era lento porque cada petición competía por un único cerrojo de toda la máquina y luego esperaba a que el juego volviera a leer mensajes (una vez por fotograma, 16 ~ 33 ms). El cerebro de referencia pasaba el 93% de su tiempo real esperando eso.
- Competir por el mutex global de la máquina
- Enviar un mensaje al hilo del juego
- Esperar a que el juego lea mensajes (una vez por fotograma)
- Esperar el evento de finalización y liberar el cerrojo
Con 6 procesos a la vez, el caudal se atasca en unas 88 peticiones/s.
- Cada cliente tiene su propio carril: un escritor y un lector, sin cerrojos
- Se ejecuta dentro del despacho de eventos del hilo del juego (cientos de veces por segundo); sin envíos pendientes, el coste es de unas pocas comparaciones de enteros
- 16 comandos por envío, todos ejecutados en el mismo despacho; cada vaciado tiene un presupuesto de 4 ms
- Cada comando lleva un plazo: al reanudar tras una pausa, los comandos caducados no se ejecutan
El recibo de cada comando incluye su tiempo de ejecución, y la cabecera del carril guarda el más lento y cuánto tardó el último vaciado: se ve al instante dónde está la lentitud.
| Nivel | Canal | Latencia | Quién lo usa | Estado |
|---|---|---|---|---|
| 0 | Snapshot publicado + flujo de eventos | ~0.4 ms por lectura; uno nuevo cada 50 ms (ajustable a 16 ms) | Todos los Bots | Verificado en partida |
| 1 | Carril rápido | ~1 fotograma; mediana de 0.06 ms con 6 procesos concurrentes, ~3000 comandos/s | Por defecto en el SDK | Verificado en partida |
| 2 | Canal de control | 20 ~ 40 ms | Respaldo y algunas operaciones de interfaz | Verificado en partida |
| 3 | Pasarela (WebSocket / JSON) | Nivel 1 + ~1 ms | Cualquier lenguaje, páginas web, LLM (MCP), otra máquina | Verificado en partida |
Medido (instancia de prueba 1.27, 2026-09-23 / 24)
Notas de ingeniería: en los 3 primeros minutos, cada comando era 1000 veces más lento
Tras añadir el tiempo de ejecución a cada comando, vimos que en los primeros minutos de partida, a partir del segundo comando de cada lote, cada uno tardaba 4 ~ 10 ms, y hacia los 180 segundos de juego bajaba de golpe a unos pocos microsegundos. Con el muestreador del hilo del juego integrado en el runtime tomamos 1700 muestras, y el 93% caía en la propia función de log del runtime, que abría y cerraba el archivo de log de forma síncrona en cada línea; y al inicio de la partida, cada orden del motor escribía una línea. Con el log de depuración desactivado por defecto y la escritura a disco asíncrona, un comando en el segundo 14 de partida tarda solo 4 ~ 8 µs.
Creemos en lo que se mide, no en lo que se supone.
Cada carril tiene un rol
La validación de propiedad y el filtrado por visión se hacen en el runtime: la capa de ejecución del motor no comprueba a quién pertenece una unidad, así que hay que hacerlo aquí. Dos IA enfrentadas son simplemente dos carriles player en la misma partida.
| Rol | Qué ve | Qué puede hacer |
|---|---|---|
| player | Todo lo propio + enemigos y neutrales dentro de su visión (modo justo) | Solo puede mandar unidades propias Jugadores, tu Bot |
| observer | Todo el mapa | No puede dar órdenes; puede consultar, controlar la cámara y leer el HUD Dirección, comentaristas, análisis posterior |
Sin adivinar, sin cuelgues
Las versiones 1.24 ~ 1.28 comparten la misma estructura de motor, ideal para “un runtime + varios perfiles”. A partir de 1.29, y en Reforged, el motor es otro; no se promete compatibilidad automática.
- Identificación Lee el recurso de versión y el hash de Game.dll para elegir el perfil
- Tabla de símbolos Las recetas referencian nombres de símbolos, no números; cada símbolo incluye su convención de llamada y la forma de sus parámetros
- Firmas de respaldo En versiones desconocidas, busca firmas de bytes al inicio de las funciones y solo usa una coincidencia única
- Autocomprobación al arrancar Cada símbolo se verifica sin efectos secundarios; las capacidades que fallan se marcan como no disponibles
- Lista de capacidades Si el SDK ve que una capacidad no está disponible, lanza un error claro al llamarla, en vez de devolver 0 en silencio
Un bug en un Bot no debe tumbar la partida
Por eso cada IA corre en su propio proceso: un puntero nulo dentro del proceso del juego tumba la partida entera; si cae un proceso independiente, solo se detiene ese bando.
| Mecanismo | Cómo | Estado |
|---|---|---|
| No arrastrar al juego | Todas las llamadas al juego protegidas contra excepciones; presupuesto de 4 ms por vaciado (temporizador de alta precisión) | Listo |
| Clientes aislados | Un carril por cliente, cada uno con su cuota; si uno se cuelga, no bloquea a los demás | Listo |
| Lo caducado no se ejecuta | Cada comando lleva un plazo; al caducar solo se marca, no se ejecuta, y no se repite al reanudar tras una pausa | Listo |
| Observable | Tiempo de ejecución de cada comando; el desglose de cada recogida va en la cabecera del bloque del mundo | Listo |
| Reconexión | El SDK se reconecta solo al cambiar de partida o de proceso | Parcial |
| Disyuntor por capacidad | Si una capacidad falla N veces seguidas → se marca como no disponible y se emite un evento; el resto sigue igual | Planificado |
Crea lo tuyo dentro del juego
Los comandos semánticos permiten a la IA jugar como un jugador; la capa de extensión te permite cambiar lo que el jugador ve y vive: dibujar tu propia interfaz clicable y llamar a todas las funciones que usan los autores de mapas. Los dos caminos se separan según “¿es seguro en multijugador?”.
- Cuadros de texto, paneles, barras de progreso, imágenes, círculos pegados al terreno, rutas con flechas
- Anclado a una unidad, a coordenadas del mundo o a una posición de pantalla; cualquier fuente (incluido el chino), esquinas redondeadas, transparencia, cualquier color
- Lo dibuja el propio runtime: no crea objetos del juego ni cambia su estado
- Una línea de Python por elemento; también por HTTP o escribiendo directamente en memoria compartida
- Botones y tarjetas de elección clicables, resaltados al pasar el ratón; se dibujan debajo del puntero del ratón y el juego no recibe el clic que les das
- Crear unidades, cambiar atributos, efectos, paneles, diálogos, sonido, cámara, niebla, clima…
- Consola Farsight, línea de comandos, HTTP y Python, con la misma forma de escribir scripts
- Los efectos habituales, en una línea cada uno: texto flotante, líneas entre puntos, círculos de alcance, diálogo con retrato, filtros a pantalla completa
- En multijugador solo se permiten funciones de lectura, para evitar desincronizaciones
| Lienzo | Funciones gráficas de JASS | |
|---|---|---|
| Quién dibuja | El runtime | El propio juego |
| Multijugador | Seguro: solo se dibuja en tu pantalla | Solo en partidas de un jugador |
| Estilo | Libre: fuentes, esquinas redondeadas, transparencia, imágenes | Estilo nativo del juego |
| Sigue a | Unidad / coordenadas del mundo / posición de pantalla | Depende de la función |
| Coste | 0.2 ~ 0.35 ms por fotograma | ~13 ms por llamada |
Lo que hace el jugador entra directamente en el flujo de eventos
El runtime informa directamente de lo que hace el jugador: qué botón dibujado pulsó, qué atajo de teclado usó, en qué punto del suelo hizo clic, a quién seleccionó, qué habilidad lanzó y qué escribió en el chat. Los eventos de disparador del propio juego (entrar en una región, botones de diálogo, flechas del teclado) se pueden seguir conectando con un disparador vacío: se registran solo los eventos, sin condiciones ni acciones, y se cuenta cuántas veces se ha ejecutado.