Plateforme

Un runtime, un protocole, pour faire du jeu un environnement programmable

W3 Runtime est injecté dans le jeu d’origine ; sur le thread du jeu, il capture le monde entier, exécute les commandes et renvoie les résultats. Votre IA n’a qu’à dire « quoi faire », en dialoguant avec lui via un protocole en mémoire partagée versionné.

Couches

Les couches basses ignorent l’existence des couches hautes

La couche d’interface ne contient pas une ligne de logique « faut-il attaquer » ; les cerveaux ne s’importent pas entre eux et ne dépendent que du SDK. C’est la condition pour que l’Arène fonctionne : elle n’a besoin que de « couche d’interface + arbitre », et n’importe quel cerveau peut s’y brancher.

Votre agent / Bot
N’importe quel modèle, n’importe quel langage
Claude CodeCursorChatGPTQwen en localBot PythonIA de référence
Décision
import openwar3 · ou passerelle WebSocket / JSON · MCP
SDK OpenWar3
Python · openwar3
Game / Botinstantanés w3worldvoie rapide w3fastcombat · calculs de combatpathing · recherche de cheminw3claim · arbitragecanvas · canevasjass · canal JASSschemes · schémas d’IA
Interface
État du monde poussé toutes les 50 ms · commandes appliquées en ~1 frame · accusé de réception pour chacune
Protocole W3P v2
Mémoire partagée · zéro copie · versionné
War3WorldWar3EventsWar3MapWar3TreesWar3Fast · voie de commandesWar3Canvas · canevas
Protocole
Exécution par lots sur le thread du jeu · 4~8 µs par commande · budget de 4 ms par vidage
W3 Runtime
S’exécute sur le thread du jeu
publication de l’état du mondeflux d’événementsexécution des commandes sémantiquesrequêtespermissions et points de vuecanevas dessiné par le runtimeappels JASScompatibilité des versionssanté et disjoncteurs
Runtime
War3.exe 1.27 · jeu d’origine, aucun fichier modifié sur le disque
Couche d’information en temps réel

Toute la carte, toutes les 50 ms

Le runtime capture l’information en une fois sur le thread du jeu et la pousse en mémoire partagée ; le client la lit directement, sans faire la queue sur le thread du jeu. Une capture prend en médiane 0.5 à 0.9 ms (100 à 120 unités), et le détail des temps est toujours inscrit dans l’en-tête du bloc monde.

Joueurs ×16

Or, bois, nourriture et plafond, total récolté, race

Unités ×1024

Type, propriétaire, coordonnées, PV et mana, ordre et cible en cours, cible réellement attaquée, niveau, expérience et points de compétence, visibilité pour chaque joueur

Détails d’unité ×256

12 compétences (niveau, recharge restante), 8 buffs, 6 cases d’inventaire ; héros en priorité

Table de production ×128

Entraînement / recherche / construction / amélioration : file, durée totale, temps écoulé, blocage éventuel

Objets au sol ×256

Type, position, durabilité ; un événement quand un objet est ramassé ou utilisé

Arbres ×4096

Position et PV des destructibles, rafraîchis toutes les 2 secondes

Carte

Grilles de déplacement / construction en cases de 128, zone jouable, points de départ ; calculées en quelques secondes en début de partie

Temps

Horloge du moteur, heure de jeu (jour/nuit), vitesse de jeu, en partie ou non

Flux d’événements Tampon circulaire de 8192 entrées numérotées ; si la lecture est trop lente et que des entrées sont écrasées, le SDK le détecte
unit.appearedunit.diedunit.removedunit.damagedorder.changedowner.changedhero.levelupitem.appeareditem.removedspell.castselection.changedmessageplayer.leftgame.startedgame.ended damage · Chaque coup, au niveau du moteurkilled · Chaque coup, au niveau du moteur production.done · Adversaire compris
Commandes sémantiques

Le chemin, c’est le runtime qui le choisit

Récolter, battre en retraite, lancer un sort… « faire agir une unité » emprunte des chemins très différents dans le moteur. Toute cette expérience est intégrée au runtime : vous dites quoi faire, il exécute avec une recette vérifiée et relit dans la même frame l’ordre avant et après, qui sert d’accusé de réception.

  • Un lot envoyé en une fois, exécuté dans la même frame
  • Un accusé pour chaque commande : code d’état + code de raison du moteur + durée d’exécution
  • File Shift, points de passage, constructions enchaînées
  • Requêtes : compteurs de technologie, faisabilité, visibilité, or restant, cible d’attaque de l’IA de l’ordinateur
Accusés de réception et codes de raison
moveattack_moveattackstopholdpatrolattack_groundgatherrepairbuildbuild_nearbuild_queuetraincancellearncastrallyrevivepick_upuse_itemdrop_itemgive_itemsell_itembuypathcall_to_armspausebatch
Faible latence

Ce n’est jamais le passage entre processus qui est lent

La lecture et l’écriture en mémoire partagée se comptent en nanosecondes ; lire un instantané prend environ 0.05 ms. L’ancien canal était lent parce que chaque requête devait prendre un verrou unique pour toute la machine, puis attendre le prochain relevé des messages par le jeu (une fois par frame, 16 à 33 ms). Le cerveau de référence passait 93 % de son temps réel à attendre.

Ancien · canal de contrôle20 ~ 40 ms / appel
  1. Prendre le mutex partagé par toute la machine
  2. Poster un message au thread du jeu
  3. Attendre le prochain relevé des messages par le jeu (une fois par frame)
  4. Attendre l’événement de fin, libérer le verrou

Avec 6 processus simultanés, le débit plafonne à environ 88 appels/s.

Nouveau · voie rapide~1 frame, par lots
  1. Chaque client a sa propre voie : un écrivain, un lecteur, sans verrou
  2. Exécution dans la distribution des événements du thread du jeu (des centaines de fois par seconde) ; sans commande en attente, le coût se limite à quelques comparaisons d’entiers
  3. 16 commandes par envoi, toutes exécutées dans la même distribution ; chaque vidage dispose d’un budget de 4 ms
  4. Chaque commande a une échéance : après une pause, les commandes expirées ne sont jamais exécutées

Chaque accusé indique la durée d’exécution de la commande, et l’en-tête de la voie garde la plus lente ainsi que la durée du dernier vidage — on voit immédiatement où ça coince.

NiveauCanalLatenceUtilisateursStatut
0 Instantané poussé + flux d’événements Environ 0.4 ms pour lire une copie ; nouvelle copie toutes les 50 ms (réglable jusqu’à 16 ms) Tous les Bots Vérifié en jeu
1 Voie rapide ~1 frame ; médiane de 0.06 ms avec 6 processus, débit d’environ 3000 appels/s Par défaut dans le SDK Vérifié en jeu
2 Canal de contrôle 20 ~ 40 ms Solution de repli, quelques opérations d’interface Vérifié en jeu
3 Passerelle (WebSocket / JSON) niveau 1 + ~1 ms Tout langage, pages web, LLM (MCP), autre machine Vérifié en jeu

Mesuré (instances de test 1.27, 2026-09-23 / 24)

Débit de commandes
Avant 88
3 000 cmd/s
6 processus en parallèle
Attente médiane en parallèle
Avant 67 ms
0.06 ms
ancien canal de contrôle → voie rapide
16 commandes
Avant 121 ms
13 ms
une par une → un seul lot
8 déplacements
Avant 68~99 ms
6.5~10 ms
with g.batch()
Une capture de l’état du monde
Avant 11.8 ms
0.58 ms
sur le thread du jeu ; réutilise les zones mémoire déjà confirmées lisibles
Une commande en début de partie
Avant 4~10 ms
4~8 µs
l’échantillonneur a repéré un journal synchrone, passé en asynchrone
Un cycle du cerveau de référence
Avant 0.15~0.56 s
0.02~0.07 s
partie complète de 39 minutes, 0 tâche en erreur
Cycle le plus lent du cerveau de référence
Avant 1.3~3.2 s
0.24~0.42 s
aucun cycle ≥ 2 s sur toute la partie

Notes d’ingénierie : en début de partie, des commandes 1000 fois plus lentes pendant 3 minutes

En ajoutant la durée d’exécution à chaque commande, nous avons constaté que pendant les premières minutes, à partir de la deuxième commande d’un lot, chacune prenait 4 à 10 ms, avant de tomber brusquement à quelques microsecondes vers 180 secondes de jeu. L’échantillonneur du thread du jeu intégré au runtime a capturé 1700 échantillons, dont 93 % dans la fonction de journalisation du runtime lui-même — chaque ligne ouvrait puis fermait le fichier de journal de façon synchrone, et en début de partie chaque ordre du moteur produit une ligne. Avec le journal de débogage désactivé par défaut et une écriture asynchrone, une commande à la 14e seconde de jeu ne prend plus que 4 à 8 µs.

Nous croyons aux chiffres mesurés, pas aux suppositions.

Permissions et points de vue

Chaque voie a un rôle

Le contrôle de propriété et le filtrage selon la vision se font dans le runtime — la couche d’exécution du moteur ne vérifie pas elle-même à qui appartient une unité, il faut donc le faire ici. Pour faire s’affronter deux IA, on ouvre deux voies player dans la même partie.

RôleVoitPeut
player Tout son camp + ennemis et neutres dans la vision (mode équitable) Commander uniquement ses propres unités
Joueurs, votre Bot
observer Toute la carte Aucun ordre ; requêtes, contrôle de la caméra, lecture du HUD
Réalisation, commentaire, analyse
Compatibilité multiversion · P4

Ne pas deviner, ne pas planter

Les versions 1.24 à 1.28 partagent la même structure de moteur, idéale pour « un runtime + plusieurs profils ». À partir de la 1.29, comme pour Reforged, il s’agit d’un autre moteur, sans promesse de compatibilité automatique.

  1. Identification Lire la ressource de version et le hash du Game.dll pour choisir le profil
  2. Table de symboles Les recettes référencent des noms de symboles, pas des valeurs numériques ; chaque symbole porte sa convention d’appel et la forme de ses paramètres
  3. Repli par signatures Sur une version inconnue, recherche des motifs d’octets en début de fonction ; utilisé seulement si une correspondance unique est trouvée
  4. Autotest au démarrage Chaque symbole est vérifié sans effet de bord ; les capacités qui échouent sont marquées indisponibles
  5. Liste des capacités Si le SDK voit qu’une capacité est indisponible, l’appel lève une erreur explicite au lieu de renvoyer 0 en silence
Haute disponibilité

Le bug d’un Bot ne doit pas faire planter toute la partie

C’est aussi pourquoi chaque IA tourne dans son propre processus : dans le même processus, un pointeur nul fait planter toute la partie ; si un processus séparé plante, seul son camp s’arrête.

MécanismeMéthodeStatut
Ne pas faire tomber le jeu Tous les appels au jeu sont protégés contre les exceptions ; budget de 4 ms par vidage (minuterie haute précision) Fait
Clients isolés Une voie et un quota par client ; un client bloqué ne gêne pas les autres Fait
Pas d’exécution après expiration Chaque commande a une échéance ; une fois expirée, elle est marquée mais pas exécutée, et n’est pas rejouée après une pause Fait
Observabilité Durée d’exécution de chaque commande ; détail des temps de chaque capture dans l’en-tête du bloc monde Fait
Reconnexion Le SDK se reconnecte automatiquement lors d’un changement de partie ou de processus Partiel
Disjoncteur par capacité Une capacité en panne N fois de suite → marquée indisponible, événement émis ; les autres continuent normalement Prévu
Couche d’extension

Vos propres créations, dans le jeu

Les commandes sémantiques permettent à une IA de jouer comme un joueur ; la couche d’extension vous permet de changer ce que les joueurs voient et vivent — dessiner votre propre interface cliquable, appeler toutes les fonctions des créateurs de cartes. Les deux approches se distinguent selon qu’elles sont sûres ou non en multijoueur.

Canevas Sûr en multijoueur
0.27 ~ 0.34 ms coût par frame (9 éléments, environ 63 frames/s)
  • Zones de texte, panneaux, barres de progression, images, cercles qui épousent le terrain, itinéraires fléchés
  • Ancrés à une unité, à des coordonnées du monde ou à une position à l’écran ; polices CJK, coins arrondis, transparence, n’importe quelle couleur
  • Dessinés par le runtime lui-même, sans créer d’objet de jeu ni modifier l’état du jeu
  • Une ligne de Python par élément, ou via HTTP, ou en écrivant directement la mémoire partagée
  • Boutons et cartes de choix cliquables, surbrillance au survol ; dessinés sous le curseur de la souris, le jeu ne reçoit pas le clic qui les touche
Documentation du canevas
Canal JASS Solo · outils locaux
1291 fonctions JASS, appelées directement par leur nom
  • Créer des unités, modifier des caractéristiques, effets, panneaux, boîtes de dialogue, sons, caméra, brouillard, météo…
  • Console Farsight, ligne de commande, HTTP, Python — la même syntaxe de script partout
  • Les effets courants en une ligne chacun : texte flottant, liaisons, cercles de portée, dialogue avec portrait, filtres plein écran
  • En multijoueur, seules les fonctions en lecture seule sont autorisées, pour éviter les désynchronisations
Documentation du canal JASS
CanevasFonctions d’affichage JASS
Qui dessine Le runtime Le jeu lui-même
Multijoueur Sûr : dessiné uniquement sur votre écran Solo uniquement
Style Libre : polices, coins arrondis, transparence, images Style natif du jeu
Suit un objet Unités / coordonnées du monde / position à l’écran Selon la fonction
Coût 0.2 ~ 0.35 ms par frame Environ 13 ms par appel
1291 fonctions, classées par usage
Effets visuels 80 Panneaux d’interface 146 Caméra 44 Sons et musique 50 Brouillard et vision 25 Unités 161 Objets 63 Héros 32 Joueurs / alliances / ressources 71 Déclencheurs / minuteurs 62 Terrain / météo 45 Déroulement de la partie 57

Ce que fait le joueur arrive directement dans le flux d’événements

Le runtime rapporte directement les actions du joueur : quel bouton dessiné il a cliqué, quel raccourci clavier il a pressé, où il a cliqué au sol, ce qu’il a sélectionné, quel sort il a lancé, ce qu’il a tapé dans le chat. Les événements de déclencheur propres au jeu (entrée dans une zone, boutons de boîte de dialogue, touches fléchées) peuvent toujours se brancher par un déclencheur vide : n’enregistrer que l’événement, sans condition ni action, puis compter combien de fois il s’est exécuté.