Um runtime, um protocolo, o jogo como ambiente programável
O W3 Runtime é injetado no jogo original e, na thread do jogo, coleta o mundo inteiro, executa comandos e reporta os resultados. Sua IA só precisa dizer “o que fazer”, conversando com ele por um protocolo de memória compartilhada versionado.
A camada de baixo não sabe que a de cima existe
A camada de interface não tem uma única linha de lógica de “atacar ou não”; os cérebros não fazem import uns dos outros e dependem só do SDK. É isso que torna a Arena possível — ela só precisa de “camada de interface + árbitro”, e qualquer cérebro pode se conectar.
O mapa inteiro, a cada 50 ms
O runtime coleta tudo de uma vez na thread do jogo e envia para a memória compartilhada; o cliente lê direto, sem entrar na fila da thread do jogo. Uma coleta leva uma mediana de 0.5 ~ 0.9 ms (100 ~ 120 unidades), e os tempos por etapa ficam sempre registrados no cabeçalho do bloco do mundo.
Jogadores ×16
Ouro, madeira, comida e limite, total coletado, raça
Unidades ×1024
Tipo, dono, coordenadas, vida e mana, ordem atual e alvo, quem está realmente atacando, nível / experiência / pontos de habilidade, visibilidade para cada jogador
Detalhes de unidade ×256
12 habilidades (nível, recarga restante), 8 buffs, 6 espaços de inventário; heróis têm prioridade
Tabela de produção ×128
Treino / pesquisa / construção / melhoria — fila, duração total, tempo decorrido, se está travada
Itens no chão ×256
Tipo, posição, durabilidade; pegar ou usar gera um evento
Árvores ×4096
Posição e vida dos destrutíveis, atualizadas a cada 2 segundos
Mapa
Grade andável / construível em células de 128, área jogável, pontos de início; calculado nos primeiros segundos da partida
Tempo
Relógio do motor, horário do jogo (dia e noite), velocidade, se há partida em andamento
O runtime escolhe o caminho
Coletar, recuar, lançar feitiços… tudo é “fazer uma unidade agir”, mas cada um segue um caminho diferente dentro do motor. Essa experiência toda fica no runtime — você diz o que fazer, ele executa com uma receita verificada e, no mesmo frame, lê de volta a ordem antes e depois como recibo.
- Um lote por envio, executado no mesmo frame
- Recibo para cada um — código de status + código de motivo do motor + tempo de execução
- Fila com Shift, pontos de caminho, construção em sequência
- Consultas — contagem de tecnologia, viabilidade, visibilidade, ouro restante, alvo de ataque do computador
O lento nunca foi cruzar processos
Ler e escrever memória compartilhada leva nanossegundos; ler um snapshot leva ~0.05 ms. O canal antigo era lento porque cada requisição disputava um lock único da máquina e depois esperava o jogo buscar a próxima mensagem (uma vez por frame, 16 ~ 33 ms). 93% do tempo de relógio do cérebro de referência ia nessa espera.
- Disputar um mutex compartilhado pela máquina inteira
- Postar uma mensagem para a thread do jogo
- Esperar o jogo buscar a próxima mensagem (uma vez por frame)
- Esperar o evento de conclusão, liberar o lock
Com 6 processos usando juntos, a vazão travava em ~88 chamadas/s.
- Cada cliente tem uma via exclusiva — um escritor, um leitor, sem lock
- A execução acontece na distribuição de eventos da thread do jogo (centenas de vezes por segundo); sem envios, o custo é só algumas comparações de inteiros
- 16 comandos por envio, todos executados na mesma distribuição; cada esvaziamento tem orçamento de 4 ms
- Cada comando tem prazo — depois de sair da pausa, comandos vencidos não são executados
O recibo de cada comando traz o tempo de execução, e o cabeçalho da via registra o comando mais lento e quanto levou o último esvaziamento — dá para ver na hora onde está a lentidão.
| Faixa | Canal | Latência | Quem usa | Status |
|---|---|---|---|---|
| 0 | Snapshot enviado + fluxo de eventos | Ler uma cópia leva ~0.4 ms; uma nova a cada 50 ms (ajustável até 16 ms) | Todos os Bots | Verificado em jogo |
| 1 | Via rápida | ~1 frame; mediana de 0.06 ms com 6 processos em paralelo, vazão de ~3000 cmd/s | Padrão do SDK | Verificado em jogo |
| 2 | Canal de controle | 20 ~ 40 ms | Fallback, algumas operações de interface | Verificado em jogo |
| 3 | Gateway (WebSocket / JSON) | Faixa 1 + ~1 ms | Qualquer linguagem, páginas de navegador, LLMs (MCP), outra máquina | Verificado em jogo |
Medido (instância de teste 1.27, 2026-09-23 / 24)
Nota de engenharia: nos 3 primeiros minutos, cada comando ficava 1000 vezes mais lento
Depois de registrar o tempo de execução de cada comando, descobrimos que, nos primeiros minutos da partida, do segundo comando de cada lote em diante cada um levava 4 ~ 10 ms, e por volta dos 180 segundos de jogo isso caía de repente para alguns microssegundos. Com o amostrador da thread do jogo embutido no runtime, coletamos 1700 amostras — 93% caíam na própria função de log do runtime, que abria e fechava o arquivo de log de forma síncrona a cada linha escrita, e no início da partida cada ordem do motor gerava uma linha. Com o log de depuração desligado por padrão e a gravação do log passando a ser assíncrona, até os comandos aos 14 segundos de partida levam só 4 ~ 8 µs.
Acreditamos em números medidos, não em palpites.
Cada via tem um papel
A verificação de propriedade e o filtro de visão ficam no runtime — a camada de execução do motor não verifica a quem a unidade pertence, então isso só pode ser feito aqui. Duas IAs se enfrentando são duas vias player na mesma partida.
| Papel | Vê | Pode |
|---|---|---|
| player | Tudo do próprio lado + inimigos e neutros na visão (modo justo) | Só comandar unidades do próprio lado Jogadores, o seu Bot |
| observer | O mapa inteiro | Não dá ordens; pode consultar, controlar a câmera e ler o HUD Direção, narração, revisão de partidas |
Não adivinhar, não quebrar
As versões 1.24 ~ 1.28 compartilham a mesma estrutura de motor, ideal para “um runtime + vários profiles”. A 1.29 em diante e o Reforged usam outro motor, sem promessa de compatibilidade automática.
- Identificação Lê o recurso de versão e o hash do Game.dll e escolhe o profile
- Tabela de símbolos As receitas referenciam nomes de símbolos, não números; cada símbolo traz convenção de chamada e formato dos parâmetros
- Assinatura como fallback Em versões desconhecidas, varre pelos bytes do início das funções e só usa se achar uma única correspondência
- Autoteste na inicialização Cada símbolo é verificado sem efeitos colaterais; o que não passar é marcado como indisponível
- Lista de capacidades Se o SDK vir que uma capacidade está indisponível, a chamada lança um erro claro em vez de retornar 0 em silêncio
O bug de um Bot não deveria derrubar a partida
É por isso que cada IA roda no próprio processo — um ponteiro nulo dentro do processo do jogo derruba a partida inteira; um processo separado que cai só para aquele lado.
| Mecanismo | Como | Status |
|---|---|---|
| Não derrubar o jogo | Toda chamada ao jogo tem proteção contra exceções; orçamento de 4 ms por esvaziamento (timer de alta precisão) | Pronto |
| Clientes isolados | Uma via e uma cota para cada cliente; um travado não bloqueia os outros | Pronto |
| Vencido não executa | Cada comando tem prazo; vencidos são só marcados, sem execução, e não são reenviados ao sair da pausa | Pronto |
| Observável | Tempo de execução de cada comando; tempos por etapa de cada coleta no cabeçalho do bloco do mundo | Pronto |
| Reconexão automática | O SDK acompanha a troca de partida e de processo e reconecta sozinho | Parcial |
| Disjuntor de capacidades | Uma capacidade que falha N vezes seguidas → marcada como indisponível, com evento; as outras seguem normais | Planejado |
Dentro do jogo, faça as suas próprias coisas
Os comandos semânticos deixam a IA operar como um jogador; a camada de extensão muda o que o jogador vê e vivencia — desenhe sua própria interface clicável, chame todas as funções de um autor de mapas. Os dois caminhos se dividem por “é seguro em partidas multijogador ou não”.
- Caixas de texto, painéis, barras de progresso, imagens, círculos colados ao terreno, rotas com setas
- Presos a unidades, a coordenadas do mundo ou a posições da tela; fonte chinesa, cantos arredondados, transparência, qualquer cor
- Desenhado pelo próprio runtime — não cria objetos de jogo nem altera o estado do jogo
- Uma linha de Python por elemento; também via HTTP ou escrevendo direto na memória compartilhada
- Botões e cartões de escolha clicáveis, com destaque ao passar o mouse; desenhados abaixo do cursor do mouse, e o jogo não recebe o clique dado neles
- Criar unidades, alterar atributos, efeitos, painéis, diálogos, sons, câmera, névoa de guerra, clima…
- Console Farsight, linha de comando, HTTP, Python — a mesma forma de escrever scripts
- Efeitos comuns em uma linha cada — texto flutuante, linhas, círculos de alcance, diálogo com retrato, filtros de tela cheia
- Em partidas multijogador, só funções de leitura são liberadas, para evitar dessincronização
| Canvas | Funções de tela do JASS | |
|---|---|---|
| Quem desenha | O runtime | O próprio jogo |
| Multijogador | Seguro: desenha só na sua tela | Só em partida solo |
| Estilo | Livre: fontes, cantos arredondados, transparência, imagens | Estilo nativo do jogo |
| Acompanha | Unidade / coordenadas do mundo / posição da tela | Depende da função |
| Custo | 0.2 ~ 0.35 ms por frame | ~13 ms por chamada |
O que o jogador fez vai direto para o fluxo de eventos
O runtime informa diretamente as ações do jogador: em qual botão desenhado clicou, que tecla de atalho apertou, onde clicou no chão, quem selecionou, que feitiço lançou, o que digitou no chat. Os eventos de gatilho do próprio jogo (entrada em regiões, botões de diálogo, setas do teclado) ainda podem ser captados com um gatilho vazio: só registrar o evento, sem condições nem ações, e contar quantas vezes ele foi executado.