ALIENFALL.IO

Journal de développement

Comment se construit un jeu d'arène multijoueur temps réel qui doit tourner dans un navigateur, tenir jusqu'à 70 joueurs par arène, et rester agréable sur une connexion mobile. Ce journal décrit les techniques mises en place, ce qu'elles coûtent, et ce qu'elles ont rapporté — mesures à l'appui.

La pile est volontairement sobre : TypeScript de bout en bout, PixiJS pour le rendu, Node et Colyseus pour le serveur autoritaire, SQLite pour les données qui comptent, Redis pour ce qui doit être partagé entre processus. Pas de moteur maison, pas de dépendance exotique. L'intérêt n'est pas dans la pile — il est dans ce qu'on en fait quand la marge est mince.


Le réseau et la sensation de jeu

25 juillet 2026 — Serveur autoritaire et prédiction côté client

Le serveur simule le monde à 60 Hz et diffuse l'état 20 fois par seconde. Le client ne se contente pas d'attendre : il prédit ses propres actions immédiatement et interpole les autres joueurs entre deux instantanés. C'est le schéma de référence, documenté publiquement depuis longtemps — Source Multiplayer Networking de Valve, les conférences GDC sur l'architecture réseau d'Overwatch, les articles de Glenn Fiedler sur l'interpolation d'instantanés.

Le choix structurant est ailleurs : client et serveur exécutent littéralement le même code de simulation. La physique, les collisions et les règles vivent dans un module partagé, importé des deux côtés. Deux implémentations d'une même règle divergent toujours ; une seule ne peut pas.

31 juillet 2026 — Un protocole qui compte ses octets

À 20 instantanés par seconde et plusieurs dizaines d'entités visibles, le format du fil est le budget réseau. Trois techniques cumulées :

Ces trois mécanismes reposent sur un ordre de champs strictement figé : un champ inséré au milieu, et le lecteur d'en face décale tout le reste et lit des nombres parfaitement plausibles — et faux. Le contrat porte donc un numéro de version, et les champs ne s'ajoutent qu'à la fin.

5 août 2026 — Interest management : n'envoyer que ce qui est visible

Chaque client déclare un rectangle de vue dimensionné sur son écran, et ne reçoit que les entités et les cases qu'il contient. Une entité qui sort du champ est oubliée côté client et repart complète à sa prochaine entrée en vue — pas de delta appliqué à un état qu'on n'a plus.

C'est ce qui permet à une arène très peuplée de rester lisible sur le fil : le coût réseau suit ce que le joueur voit, pas ce qui existe.

15 août 2026 — L'annonce d'attaque, et le jugement au présent

Pour qu'une esquive existe, il faut deux choses, et elles sont couplées :

Le second point mérite d'être explicité, parce qu'il va à l'encontre de la pratique courante. Beaucoup de jeux de tir rembobinent l'état pour juger le coup à l'instant où le tireur a visé — Valve documente ce compromis depuis 2001 sous le nom favor the shooter. C'est excellent quand la précision est le cœur du jeu. Dans une arène où le déplacement est le jeu, cela revient à punir une cible qui a réagi correctement : au moment où elle voit le coup partir, le serveur a déjà tranché sur une image du passé.

Le principe retenu n'a rien d'original, et c'est une force : Riot documente publiquement sa doctrine du counterplay — une attaque à laquelle l'adversaire peut répondre. Les durées d'annonce des jeux grand public servent de repère : de l'ordre de la seconde pour Clash Royale, de quelques secondes pour Bomberman. Nous sommes délibérément en deçà : l'arène est nerveuse et le sol se dérobe, un préavis d'une seconde rendrait toute attaque inutile. L'écart est assumé et écrit, et la valeur retenue est traitée comme un plancher qu'on ne descend pas sans nouvelle mesure.

16 août 2026 — Gel et rattrapage, réglés comme un seul système

Quand la position prédite s'écarte trop de celle confirmée par le serveur, le client freine le personnage local : c'est ce qui évite de courir dans le vide puis d'être replacé brutalement.

Le point non évident, et c'est le cœur du réglage : ce frein creuse lui-même l'écart que la réconciliation devra ensuite absorber. Gel et rattrapage ne sont pas deux paramètres indépendants — les régler séparément ne converge pas.

Ce qui est en place, comme un ensemble : une tolérance avant gel, une rampe de reprise progressive, un rattrapage plafonné en vitesse, un raccord doux toujours armé après une glissée, et une extrapolation bornée en distance comme en durée. Sans cette dernière borne, une poussée extrapolée projette un fantôme de trois cases dans un trou.

Validé à la manette sur un partage de connexion 4G, pas seulement au banc.

16 août 2026 — Des indicateurs de liaison lisibles par tout le monde

Trois barres donnent la qualité de la connexion. Le choix de conception est délibéré : c'est la forme qui porte l'information, pas seulement la couleur. Le trio vert / orange / rouge n'est pas distingué par environ 8 % des hommes ; un indicateur qui repose sur lui seul n'informe pas tout le monde.

24 août 2026 — La chute prédite, et l'état-ombre

Le sol s'use et cède. Jusqu'à cette date, le client ne prédisait pas sa propre chute : on atterrissait visuellement sur du vide, puis la mort arrivait « rétroactivement » un demi-ping plus tard.

La technique mise en place : le client rejoue à l'identique le test de support du serveur. La fonction qui décide si un personnage est porté est exportée depuis le module partagé et appelée des deux côtés. Le visuel suit donc immédiatement, tandis que la mort, elle, reste au serveur — la prédiction ne décide de rien, elle affiche.

S'y ajoute un état-ombre des cases : le client teste le support sur l'état le plus frais qu'il ait reçu, plutôt que sur l'état daté qu'il est en train d'interpoler. Une seule écriture de la règle, deux lectures : si la règle de support change un jour, la prédiction suit toute seule.

25-26 août 2026 — Le leeway : promettre moins au tireur, tenir exactement pour la cible

Un joueur touché veut comprendre pourquoi. C'est l'objet du leeway : un bonus de corps jugé, accordé à la cible, calculé à partir du retard d'affichage compensé de l'attaquant et borné à une fourchette étroite. Il s'applique de la même manière à la bousculade et aux trois fronts d'armes — une seule écriture, tous les lecteurs.

La partie élégante est la répartition de l'information :

C'est une règle de conception documentée publiquement (Riot, WildStar) : mieux vaut un coup qui touche alors qu'on ne l'espérait plus, qu'un coup annoncé qui ne touche pas.


La performance et le coût machine

12 août 2026 — Mesurer avant d'optimiser

Une campagne de mesure a été montée avant toute optimisation, avec des clients simulés et une instrumentation du serveur. Verdict : le coût ne croissait pas linéairement avec le nombre de joueurs mais avec son carré, et une seule boucle en portait plus de la moitié.

Ce n'est pas le résultat qui compte ici, c'est l'ordre des opérations. Sans cette campagne, l'effort serait parti ailleurs : le coût ressenti et le coût mesuré ne désignaient pas le même code.

14 août 2026 — Indexation spatiale de la diffusion

La diffusion compare naturellement chaque joueur à chaque entité. Les entités sont désormais indexées par rangée de la grille hexagonale, et la diffusion ne balaie que les rangées couvertes par le rectangle de vue de chaque client. Le coût suit la surface regardée, plus la population totale.

14 août 2026 — Un chemin de simulation sans allocation

Le chemin exécuté 60 fois par seconde a été allégé de 22 % mesurés, par des techniques classiques appliquées systématiquement :

Ce dernier point est le plus rentable en JavaScript : ce qu'on n'alloue pas est ce que le ramasse-miettes n'aura pas à reprendre — et il le reprend toujours au pire moment, c'est-à-dire pendant une image chargée.

14 août 2026 — Un budget d'octets sur le chemin critique

Le projet s'impose un plafond de poids sur le chemin critique : ce qu'il faut télécharger avant de voir le menu. Trois techniques le tiennent :

Le budget lui-même a été recalibré pour mesurer le vrai chemin critique — le graphe d'entrée réellement chargé — et non la totalité du dossier livré, qui contient aussi ce qui n'arrive jamais chez un joueur donné.

20 août 2026 — Des cadences comptées en temps, jamais en images

Économiser du travail en ne rafraîchissant certaines animations qu'« une image sur deux » est tentant. À 60 images par seconde, c'est 30 Hz : imperceptible. À 30 images par seconde, c'est 15 Hz — pendant que le personnage local, lui, reste parfaitement fluide.

La règle appliquée depuis : si ce qu'on saute porte du temps, la garde se compte en millisecondes. Le compteur d'images reste utile pour répartir la charge entre plusieurs entités ; il ne décide plus seul de sauter une mise à jour.

Deux autres réglages du même ordre sont posés au même moment : l'horloge de lecture n'avance plus sur un delta plafonné par le moteur de rendu (elle prenait du retard sur le temps réel jusqu'à un recalage sec), et un plafond d'images par seconde évite qu'un écran à 144 Hz fasse fabriquer au client la saturation qu'il subit ensuite.

20 août 2026 — Les écritures en base sortent du chemin chaud

Les écritures de statistiques et de classement étaient exécutées dans le fil de la simulation. Elles sont désormais groupées en transaction et sorties du pas de simulation, avec des délais d'attente courts sur les bases non critiques — un verrou pris sur une base partagée ne peut plus retarder une arène.


L'architecture serveur et la montée en charge

31 juillet 2026 — Un processus par cœur, et un annuaire partagé

Node est mono-fil. Faire tourner un processus par cœur ne suffit pas : les arènes doivent se connaître d'un processus à l'autre, sinon les joueurs se répartissent dans des mondes qui s'ignorent.

La pièce centrale est un annuaire partagé dans Redis, en trois parties indissociables : présence, aiguillage, adresse publique. Le point de vigilance, relevé pendant la recherche préalable et confirmé depuis : si une seule de ces trois pièces manque, le framework retombe en silence sur ses versions en mémoire. Rien n'échoue, rien n'avertit.

D'où une règle de conception appliquée partout depuis : en production, une dépendance manquante fait échouer le démarrage. Jamais de repli silencieux.

31 juillet 2026 — Les sessions chaudes

Créer une arène coûte quelques millisecondes. Les payer pendant qu'un joueur attend, c'est de la latence gratuite. Une arène est donc préparée à l'avance, prête à accueillir dès l'arrivée.

Le détail qui compte est de sécurité : le drapeau « session chaude » ne peut pas être demandé par un client. Il n'est accepté qu'accompagné d'un jeton dérivé au démarrage et jamais transmis — sans quoi n'importe qui pourrait faire naître des arènes immortelles.

31 juillet 2026 — Un modèle de capacité mesuré, et sa contre-épreuve

Plutôt qu'une capacité annoncée au jugé, elle est mesurée : coût d'un joueur qui joue, coût d'une arène vide, coût d'une arrivée. Le modèle qui en découle est ensuite confronté à un point de mesure indépendant — écart de 0,2 %.

La mesure a aussi corrigé une estimation trop pessimiste : les clients de test passaient leur vie à se reconnecter, ce qui charge le serveur bien plus qu'une population réelle. On mesurait le banc, pas le jeu.

31 juillet 2026 — Les contrôles vivent là où la décision se prend

Le plafond de joueurs par arène est appliqué à l'endroit où le framework attribue la place, et non dans le gestionnaire d'arrivée qui s'exécute après. Un contrôle placé après la décision ne garde rien : c'est une règle générale, et elle est vérifiée par un banc dédié qui fait démarrer des dizaines de joueurs d'un coup.


Les données, l'économie et l'intégrité

26 juillet 2026 — Un grand livre, pas un compteur

La monnaie du jeu n'est pas un nombre stocké quelque part. Chaque mouvement écrit une ligne au grand livre : gain de partie, achat, récompense, ajustement. Le solde en est la somme.

C'est plus coûteux qu'un compteur, et c'est précisément l'intérêt : un écart devient détectable. Un contrôle vérifie que la somme du grand livre égale le total des soldes ; une différence n'est pas une approximation à tolérer, c'est un défaut à trouver.

31 juillet 2026 — L'idempotence garantie par la base, pas par le code

Toute monnaie attire les doublons : deux requêtes simultanées passent le même contrôle avant que l'une n'ait crédité.

La garantie ne repose donc pas sur une variable en mémoire — elle ne survit ni à un redémarrage, ni au processus voisin. Chaque partie reçoit un identifiant à son apparition, et un index unique partiel rend un second crédit impossible au niveau de la base. Contrôle et crédit tiennent dans une seule transaction.

Éprouvé sur les processus concurrents réels : six demandes simultanées, un crédit, cinq refus. Et éprouvé en retirant la garde : trois demandes sur six créditaient.

22 août 2026 — Une boîte d'envoi qui survit aux pannes

Les courriels du jeu ne partent pas depuis les routes qui les déclenchent. Ils sont déposés en base, et un seul processus expédie — celui qui détient un bail arbitré par la base et qui expire tout seul. Trois raisons : une route doit répondre tout de suite ; plusieurs processus qui parlent au même service multiplient les doublons ; et une panne du service ne doit rien perdre.

Les réessais sont espacés puis abandonnés en le disant. Sans transport configuré, en production, le courrier attend et le journal proteste — il ne disparaît pas.


La sécurité

Cette section reste volontairement au niveau des principes : décrire précisément ses propres seuils, c'est les offrir.

21 août 2026 — Argon2id, et une mesure avant de choisir

Les mots de passe sont hachés avec Argon2id, paramètres au-dessus du plancher recommandé par l'OWASP, sel par mot de passe et poivre stocké hors de la base. Une base volée ne suffit donc pas.

Deux points ont été tranchés par la mesure plutôt que par l'habitude :

Un banc dédié a mesuré l'impact sur la boucle de jeu : en version synchrone, le hachage coûte assez pour faire perdre une image et demie à toute l'arène ; en version asynchrone, le blocage passe sous le bruit de fond. Le jeu n'a donc pas eu besoin d'un processus dédié — décision prise sur une mesure, pas sur une intuition.

21 août 2026 — Ne pas dire qui existe

Une page de connexion honnête répond plus vite quand l'identifiant n'existe pas, puisqu'elle n'a rien à hacher. Cet écart suffit à dresser la liste des comptes avant d'aller les tester contre des mots de passe fuités.

La parade est une empreinte leurre : un identifiant inconnu coûte exactement le même travail qu'un identifiant connu. Le contrôle est chronométré et automatisé, en médiane sur plusieurs séries alternées — une protection de ce type se perd au premier remaniement si personne ne la surveille.

22 août 2026 — Ce qui vaut partout ailleurs


L'interface, le contenu et l'accessibilité

2 août 2026 — Une charte, et une source unique de couleurs

Toute couleur du jeu vient d'un fichier unique de jetons. Le CSS les lit directement ; le moteur de rendu lit les mêmes jetons convertis en nombres. Les pages statiques — aide, mentions légales, ce journal — chargent un thème fabriqué depuis cette même source.

Quatre familles sémantiques sont figées et ne se réattribuent pas : bonus, activable, danger, monnaie. Le vert d'action, lui, vit délibérément hors de ces quatre : il désigne ce qui se clique, et rien d'autre.

6-7 août 2026 — Le son, découpé à l'attaque audible

Un enregistrement ne commence jamais exactement à zéro. Mesuré sur nos propres fichiers : jusqu'à 125 ms de silence avant l'attaque du son, soit plus de sept images à 60 Hz. Ça ne s'entend pas — ça se sent : le son paraît mou, en retard sur l'écran.

La chaîne de traitement découpe donc automatiquement, et le choix du point de coupe est le cœur du sujet : couper au premier échantillon non nul ne gagne rien (un son commence par une montée inaudible) ; couper au sommet décapite le transitoire et produit un clic. Elle coupe à l'attaque audible, garde quelques millisecondes en amont, et pose un fondu très court. Elle refuse le fichier si la coupe devait tomber dans du son franc.

8 août 2026 — Un contrôle qui refuse les couleurs écrites en dur

La règle « aucune couleur en dur » était énoncée partout et vérifiée nulle part. Un contrôle automatique la fait maintenant respecter à chaque enregistrement : tout littéral de couleur dans le code ou une feuille de style est refusé.

Il est calibré, pas brutal : il tolère explicitement les neutres — un gris n'est pas de la palette, c'est de la lumière — et une poignée de sources déclarées, chacune accompagnée de sa raison écrite dans l'outil lui-même.

26-27 août 2026 — Les chiffres du jeu, dérivés du code

Les valeurs affichées dans l'aide et la FAQ — portées, rayons, durées, plafonds — ne sont pas recopiées. Elles sont dérivées du code à la génération des pages : un rééquilibrage se propage tout seul.

Le mécanisme est fail-closed : un nom de mesure inconnu fait échouer la construction, et le hook de vérification régénère les pages dès qu'une source de chiffres bouge. Il en va de même pour les textes : tout ce que lit un joueur vient d'un catalogue bilingue, et une clé inconnue fait échouer la construction — jamais de trou en production, jamais une phrase anglaise oubliée dans une interface française.


La méthode et l'outillage

Ce qui suit n'est pas une préférence : c'est ce qui rend les décisions ci-dessus vérifiables.

29 juillet 2026 — Des garde-fous qui refusent, et qu'on éprouve en les cassant

Le projet ne s'appuie pas sur la vigilance. Une série de contrôles automatiques refuse un enregistrement qui casse une règle : compilation, frontières entre modules, cohérence des palettes, couleurs en dur, renvois documentaires morts.

Chacun a été éprouvé en le cassant exprès, et le résultat de cette contre-épreuve est écrit à côté du contrôle. La raison est vécue : un outil d'analyse annonçait fièrement « aucune violation » — après avoir analysé zéro fichier. D'où une règle simple : toujours regarder le nombre d'éléments analysés, jamais seulement le verdict.

12 août 2026 — Mesurer les creux, pas la moyenne

Une saccade s'annule dans une moyenne. Un jeu qui affiche 59 images par seconde en moyenne peut être désagréable si le creux tombe à 12 pendant un dixième de seconde — et c'est le creux que le joueur ressent.

Les mesures retiennent donc le minimum et le nombre d'épisodes sous un seuil. Sur le GPU, le temps est relevé par requête temporelle matérielle, pas estimé depuis la boucle d'images : une image longue peut attendre le GPU sans que le GPU travaille.

20 août 2026 — Chercher à réfuter, pas à confirmer

Chaque constat d'analyse passe devant une contre-expertise dont la consigne est de le démolir. Ce qui survit est retenu ; le reste tombe — et il en tombe.

Deux exemples récents. Une recherche documentaire concluait, sources à l'appui, qu'un algorithme d'agrégation réseau ajoutait de la latence à nos connexions : faux, vérifié dans le code de la dépendance, qui le désactive explicitement. Et un coût GPU annoncé à environ 70 % s'est révélé être de l'ordre de 27 % le jour même, une fois mesuré au bon endroit.

Un audit qui confirme tout ce qu'il cherche n'a rien audité.

En continu — Écrire le piège une fois, le payer une fois

Chaque défaut trouvé est consigné là où le prochain le cherchera, avec sa date, son symptôme et le raisonnement faux qui y avait mené. Le projet en porte plusieurs dizaines, rangés par zone. Ce n'est pas de la documentation de confort : plusieurs de ces notes ont empêché la répétition d'un défaut coûteux, des semaines plus tard.


Ce qui vient

Le jeu est jouable et il continue d'évoluer. Les chantiers ouverts touchent l'expérience mobile, la progression, et l'affinage continu de l'équilibrage sur retours de joueurs.

Une question technique, une proposition, ou l'envie de travailler ensemble ? La page contact est là pour ça.