Alt-F4 n°18 - En route vers Clusterio 2.0  18-12-2020

Écrit par Hornwitser, DedlySpyder, édité par stringweasel, Nanogamer7, Conor_, Therenas, nicgarner, Firerazer,
traduit par bev

Sommaire

Alors que l’année touche à sa fin, nous avons choisi cette semaine, pour le 18ème numéro de Alt-F4, deux sujets liés à des mods. Premièrement, Hornwitser nous donne un aperçu du long processus de développement de Clusterio 2.0 et des défis qu’il pose. Ensuite, DedlySpyder parle de son travail de développement d’un mod simple et des défis de compatibilité qui ont été rencontrés.

En route vers Clusterio 2.0 Hornwitser

Je vais ici vous raconter comment j’ai fini par passer un an à développer Clusterio 2.0, qui a cependant encore un long chemin à parcourir avant sa sortie. Si vous n’avez jamais entendu parler de Clusterio, c’est un logiciel libre pour serveurs écrit par Danielv123 (avec la contribution d’environ 30 autres personnes) qui permet aux mods d’interagir entre plusieurs serveurs. Il est peut-être davantage connu pour l’événement Clusterio 60k en 2018 où des coffres de téléportation ont été utilisés pour transférer des objets entre pas moins de 46 serveurs Factorio afin de construire une usine proche du jeu de base qui pouvait faire 60 000 packs de science par minute. Ces coffres de téléportation fonctionnent comme des coffres de fourniture et de demande actifs ; l’un retire des objets du jeu et les place dans un stockage partagé et l’autre prend les objets demandés de ce stockage et les place dans le jeu.

Le minerai de fer est extrait sur le serveur de gauche, puis envoyé au serveur de droite via les coffres de téléportation de Clusterio.

Clusterio a toujours été constitué de deux parties : les interactions de jeu qui sont implémentées dans le code du mod et s’exécutent à l’intérieur du jeu, et l’infrastructure côté serveur qui s’occupe de la circulation des données entre les différents serveurs de jeu. Au début, le côté serveur était codé pour gérer les coffres de téléportation, mais à mesure que le développement progressait et que de plus en plus de fonctionnalités étaient ajoutées, l’idée de ce qu’est Clusterio a changé, passant de coffres de téléportation à une plateforme modulaire côté serveur pour réaliser de tels éléments de jeu entre serveurs.

En juillet 2019, se tenait l’événement Gridlock Cluster. Au lieu de coffres de téléportation pour transporter des objets entre les serveurs, il y avait des trains qui pouvaient se téléporter du bord d’un serveur au bord d’un autre serveur, au moyen d’arrêts de train de téléportation. Le code pour la téléportation des trains a été implémenté par Godmave en tant que plugin pour Clusterio.

Train se téléportant du bord d’un serveur au bord d’un autre.

Malheureusement, le code était truffé de problèmes, et c’est là que j’entre en scène.

Des débuts modestes

J’ai commencé à bidouiller la base de données de Clusterio en juillet 2019 pour aider l’équipe du Gridlock à résoudre les nombreux problèmes qu’elle rencontrait. Les serveurs étaient en panne à gauche et à droite, les joueurs avaient des erreurs, et de nouveaux bugs et problèmes semblaient apparaître d’heure en heure. C’était intense, mais on s’est aussi beaucoup amusé. L’événement a suscité mon intérêt pour le code de Clusterio, et après celui-ci, j’ai pris sur moi d’améliorer ce code pour le prochain événement. Cela s’est avéré être un projet bien plus important que je n’aurais pu l’imaginer.

Je travaille régulièrement sur Clusterio 2.0 depuis environ 16 mois maintenant et mon estimation de la durée du projet est toujours la même que celle de mes débuts, à savoir “quelques mois seulement”. Malgré cela, ma motivation pour continuer à travailler dessus reste forte, et une des choses que je trouve particulièrement motivante est de pouvoir tester tout ce travail en organisant mon propre événement Clusterio. J’ai publié un teaser sur Reddit sur ce que j’ai en tête et, pour l’instant, l’objectif est de lancer cela au début de l’année prochaine. Peut-être en janvier, mais seul le temps nous dira quand tout sera prêt.

Mais revenons au moment où tout a commencé pour moi. J’avais installé Clusterio sur mon serveur, en essayant de régler mon propre groupe de test afin de travailler sur les corrections des problèmes rencontrés lors du Gridlock Cluster. L’une des premières choses que j’ai remarquées, c’est le millier de modules qu’il a fait entrer comme dépendances, ce qui a pris plus de 300 Mo d’espace disque. Le fait que ce projet ait besoin d’autant de librairies pour fonctionner semblait absurde. Node.js était alors nouveau pour moi, et bien que j’aie maintenant appris que ce n’est pas vraiment un nombre déraisonnable de dépendances pour une application Node.js, c’est encore beaucoup. C’était une indication du type de méthode de développement qui avait été utilisé dans le projet : une approche descendante consistant à ajouter des fonctionnalités de la manière la plus simple et la plus rapide à mettre en œuvre au fur et à mesure.

Ce style de développement a conduit à l’accumulation de beaucoup de dettes techniques, et je dis bien beaucoup. Dette technique est un terme souvent utilisé dans le domaine du développement de logiciels. C’est l’idée que le choix de raccourcis dans le développement pour gagner du temps crée souvent plus de travail en aval, lors de la maintenance et de l’extension de la base de code. D’une certaine manière, on pourrait dire que Clusterio était plus une collection de bidules empilés les uns sur les autres qu’un projet bien pensé et structuré. L’inclusion et l’utilisation de quatre clients HTTP différents dans le même fichier source en est un exemple frappant. Habituellement, un seul client est plus que suffisant pour un projet entier, mais on peut supposer que certaines choses étaient plus faciles à faire avec un client qu’avec d’autres et qu’au fil du temps, les différents clients se sont accumulés.

J’ai donc dû travailler à l’amélioration et au nettoyage de la base de code de Clusterio. L’une des premières choses que j’ai faites a été de réduire ces quelque mille dépendances. Il s’est avéré que la plupart d’entre elles n’étaient pas vraiment nécessaires pour faire fonctionner Clusterio. Environ la moitié était constituée d’outils de développement qui n’avaient pas besoin d’être installés dans un environnement de production et un quart était ce que je qualifierais de solutions rapides : on a fait appel à de grandes librairies pour n’en utiliser qu’une seule fonction. Beaucoup de ces librairies étaient facile à supprimer, soit en recréant la fonction localement, soit en utilisant une autre librairie qui était déjà une dépendance du projet. Au final, j’ai réussi à supprimer le recours à quelque 700 modules (244 Mo), bien qu’il faille noter que la plupart d’entre eux étaient des dépendances de dépendances.

J’ai ensuite abordé la question des tests automatisés. Si vous n’êtes pas familier avec les tests automatisés, c’est l’idée d’écrire du code pour vérifier que le code principal fonctionne comme il le devrait et qu’il ne se brisera pas avec les changements futurs. Les tests automatisés sont en quelque sorte la pierre angulaire de l’écriture d’un code fiable et, bien que des tests approfondis aient été réalisés à un moment donné, ils n’ont pas fonctionné lorsque je me suis lancé dans le projet. C’est un autre exemple de la dette technique qui apparaît dans toute son horreur. Maintenir les tests, et en ajouter de nouveaux pour prendre en compte le nouveau code, est un travail supplémentaire ; ignorer ce travail est un raccourci.

Une fois les tests terminés, je me suis concentré sur le nettoyage du code lui-même. Je me suis occupé de réparer le code défectueux, d’en supprimer les parties inutilisée ou obsolète et de remanier le mauvais code pour en faire un légèrement moins mauvais. Un des changements qui a commencé à prendre forme a été de déplacer le code pour les coffres de téléportation dans un plugin séparé, hors de la base de code principale. Comme Clusterio est avant tout le logiciel serveur qui rend possible les interactions de mods entre serveurs, le fait que ces coffres de téléportation soient également appelés Clusterio est déroutant, alors que nous nous attendons de plus en plus à ce que Clusterio soit utilisé pour d’autres motifs que ces coffres. Nous avons donc également décidé de rebaptiser la fonctionnalité téléportation-d’objets-via-des-coffres-magiques en Stockage subspatial. Pendant que j’y étais, j’ai aussi décidé de remplacer ces graphismes de coffres célestes tordus et de filets de récupération par quelque chose de plus approprié au stockage subspatial.

Les graphismes pour les stockages subspatiaux
Nouveaux graphismes de remplacement pour les entrées et sorties d’objets, de fluides et d’électricité dans le mod Subspace Storage.

Mais ils servent ici plutôt de supports, car je ne suis pas vraiment un artiste 3D lorsqu’il s’agit de textures et de modélisation mécanique. J’ai pris le temps de mettre en place une chaîne d’outils automatisée avec Blender pour rendre, recadrer et sortir les graphismes dans le mod. Vous savez comment c’est avec les programmeurs : ils automatisent tout.

Corriger les sauvegardes

Comme mon travail progressait, la première grande amélioration sur laquelle j’ai travaillé a été la correction de sauvegarde, mais avant d’en parler, je voudrais situer le problème qu’il tente de résoudre dans son contexte. Le moteur de jeu permet de modifier le comportement du jeu avec du code Lua par le biais de mods et/ou de scénarios. Les mods sont chargés au démarrage du jeu et leur mise à jour en nécessite le redémarrage. Les scénarios sont des codes Lua intégrés aux sauvegardes du jeu et le passage à un autre code de scénario ne nécessite que le chargement d’une autre sauvegarde.

Quand un comportement qui change le jeu est intégré dans un code de scénario, on parle souvent de modding doux, car il n’est pas nécessaire de télécharger des mods et de redémarrer Factorio pour rejoindre un serveur utilisant un tel code de scénario. S’il est facile de mettre à jour un mod et de continuer une sauvegarde existante, ce n’est pas aussi simple avec un code de scénario. Il y a essentiellement trois façons de mettre à jour le code d’un scénario dans une sauvegarde, que je vais vous énumérer en gros dans l’ordre de difficulté de mise en œuvre :

  • Pour les scénarios distribués via un mod, il est possible d’ajouter un script de migration dans le mod qui met à jour le scénario lorsque le mod est mis à jour. Bien que cela soit assez simple à faire, cela présente l’inconvénient majeur d’exiger que le mod soit installé pour exécuter la migration.
  • Vous pouvez remplacer le code du scénario stocké dans la sauvegarde lorsque le jeu n’est pas en cours. C’est ce que j’appelle la correction de sauvegarde et c’est relativement simple à faire car les sauvegardes sont des fichiers zip ordinaires et le code Lua y est stocké sous forme de fichiers texte ordinaires.
  • Vous pouvez également utiliser la nature dynamique de Lua pour charger et exécuter un nouveau code pendant le déroulement du jeu et du scénario. Cette option est de loin la plus compliquée, mais elle offre la possibilité d’appliquer des corrections au jeu pendant qu’une carte est en cours d’exécution. L’inconvénient est qu’elle est compliquée à mettre en œuvre et à corriger, ce qui augmente les chances que quelque chose tourne mal. De plus, la seule façon d’envoyer des données au jeu en cours d’exécution passe par des commandes, ce qui devient problématique lorsqu’elles sont longues.

Pour Gridlock Cluster, la troisième option a été réalisée via un scénario appelé Hotpatch (également connu sous le nom de scénario multi-mod côté serveur). Conceptuellement, Hotpatch est une chose très cool ; il vous permet de charger du code de type mod pendant que le jeu est démarré, et il exécutera ce code dans un environnement qui émule l’environnement de mod de Factorio. Mais l’utilisation de Hotpatch posait des problèmes majeurs : il est mal documenté, ce qui le rend difficile à utiliser correctement ; l’implémentation était incomplète et buggée ; et le problème le plus gênant était que le code du scénario mis à jour était envoyé sous forme de longues commandes au démarrage. Cela signifie que si des joueurs rejoignaient un serveur au démarrage, pendant le processus d’envoi de ces longues commandes pour mettre à jour le scénario, les choses dérapaient, ce qui n’est qu’une des nombreuses causes pour lesquelles les serveurs du Gridlock se sont plantés.

Si de nombreux problèmes liés au Hotpatch ont été résolus, la complexité et les difficultés de son utilisation m’ont appris une leçon précieuse : le fait de disposer de capacités avancées, comme la possibilité de réparer du code pendant l’exécution, ou de prodiges techniques de toute sorte d’ailleurs, ne justifie pas toujours la complexité et les problèmes auxquels ces systèmes avancés sont confrontés. J’en ai fait l’expérience en essayant de résoudre des problèmes auxquels Hotpatch a participé : tous les membres de l’équipe (moi y compris) ont eu du mal à comprendre le système et la façon de résoudre les problèmes avec lui.

Pour cette raison, j’ai décidé de remplacer le rôle joué par Hotpatch dans Clusterio par quelque chose de plus simple : la correction de sauvegarde. C’est une solution moins performante, avec plus de limitations sur la façon dont le code est écrit, mais la simplicité de son fonctionnement compense largement.

Tout casser

Après avoir mis en place la correction de sauvegarde, il est devenu évident qu’une révision majeure du code était nécessaire. Un point particulièrement douloureux de Clusterio était l’absence totale de gestion à distance. Si vous souhaitez démarrer un serveur Factorio qui fait partie du groupe, vous devez vous connecter à l’ordinateur qui l’héberge et le démarrer manuellement par le biais du gestionnaire de processus que vous avez choisi d’utiliser. Il en va de même si vous souhaitez modifier les réglages de ce serveur. Gérer ainsi un groupe est douloureux et c’est une leçon que nous avons apprise à la dure lors de l’événement Clusterio 60k.

Dans le cadre du Gridlock, le module de gestion des serveurs de jeu Pterodactyl a été utilisé pour gérer les serveurs à distance ; une bonne idée qui s’est avérée être la cause de nombreux problèmes. Mais je garde cette histoire pour une autre fois.

La possibilité de gérer à distance les serveurs Factorio dans Clusterio est une fonctionnalité souhaitée depuis longtemps et des tentatives ont été faites pour la mettre en œuvre. Cependant, ces tentatives ont été faites après coup et, en raison de la structure du code (un seul serveur Factorio par processus Node.js), il est devenu très difficile de mettre en œuvre une gestion à distance efficace sans procéder à une refonte majeure du code et sans tout casser dans le processus.

J’ai donc naturellement tout cassé et mis en place une gestion à distance.

La façon dont Clusterio 2.0 fonctionne est qu’un processus esclave est exécuté sur chaque ordinateur sur lequel vous voulez héberger des serveurs Factorio. Ces serveurs Factorio sont appelés des instances dans Clusterio et le processus esclave se connecte au serveur maître et écoute les commandes pour créer et démarrer des instances. Plusieurs instances peuvent fonctionner en même temps sur un esclave, ce qui signifie qu’il suffit de configurer un esclave pour chaque ordinateur sur lequel vous souhaitez héberger des serveurs Factorio, et qu’il n’y a qu’un seul processus Node.js à lancer sur ces ordinateurs.

Une autre chose qu’il fallait changer, c’était la façon dont Clusterio communique entre les ordinateurs. Dans la version 1, cette communication est assurée en grande partie par le serveur maître qui héberge un serveur HTTP et répond aux demandes qui lui sont adressées. Le problème est que le serveur maître ne peut pas envoyer de messages à d’autres ordinateurs, mais seulement répondre aux demandes qui lui sont envoyées par d’autres ordinateurs ; c’est ainsi que fonctionne le protocole HTTP. Pour contourner ce problème, j’ai remplacé le HTTP par un simple protocole basé sur WebSocket, utilisant les ressources JSON. WebSocket, contrairement à HTTP, permet aux deux parties de la connexion de s’envoyer des messages à tout moment.

Vu que tout était maintenant chamboulé, c’est en quelque sorte le point où le développement de la version 2.0 a réellement commencé. J’ai profité de cette occasion pour repartir de zéro sur beaucoup de choses dans les mois qui ont suivi.

J’espère que vous avez aimé cet aperçu du développement de Clusterio 2.0. Comme vous pouvez l’imaginer, il y a eu bien d’autres choses à propos de la version 2.0 au cours des 16 derniers mois, certainement assez pour d’autres articles sur le sujet. Veuillez noter que la version 2.0 n’est pas encore prête pour une utilisation en production, mais si vous êtes intéressé par le développement et souhaitez le tester, consultez notre serveur Discord et le répertoire GitHub.

Moddabilité : La naissance d’un mod DedlySpyer

Quelque chose que kovarex a dit dans le FFF-363 restera gravé dans ma mémoire :

C’est un exemple de fonctionnalité, que je DEVAIS absolument faire, car une fois que j’ai réalisé que la fonctionnalité pouvait être disponible, j’ai presque essayé de l’utiliser et j’ai été ennuyé par le fait qu’elle n’était pas présente.

This is an example of a feature, that I just HAD TO DO, because once I realised that the feature could be there, I was almost trying to use it and was annoyed by the fact that it wasn’t there.

— kovarex

Je joue régulièrement à Factorio depuis environ six ans, mais depuis que j’ai commencé à faire des mods, j’ai toujours aimé bricoler le jeu. Parfois, quand je joue, je finis par voir quelque chose de nouveau qui me dérange un peu et pour lequel il n’y a pas de mod pour le corriger. J’en arriverai à un point où je finirai par le modifier moi-même. Normalement, cela m’amène à abandonner ma partie de Factorio en cours, principalement parce que, pour moi, le fait de modder des correctifs me démange autant que le fait de jouer au jeu.

Peu après la sortie de la version 1.0, cela m’est de nouveau arrivé. J’ai récupéré la dernière version de Krastorio 2, je suis arrivé au moment où l’armure de puissance arrive dans le jeu, et je me suis demandé pourquoi je ne pouvais pas faire tourner les équipements. Bien sûr, je pourrais probablement tout remanier dans mon armure, mais parfois, j’ai juste envie de taper sur le R et de mettre ce truc correctement avec un minimum d’effort. Une recherche rapide sur le portail des mods m’a montré qu’il y existait Rotatable Batteries par GotLag; c’était donc possible, mais ce n’était pas été implémenté pour tout.

Faire fonctionner un mod en toutes circonstances peut parfois représenter une tonne de travail. La façon la plus sûre de traiter chaque cas est de coder en dur vos modifications pour chaque situation. Cela fonctionnera certainement, mais nécessite un suivi constant. Ayant déjà fait quelque chose de ce genre dans le passé, je sais que cela peut devenir assez lourd et difficile à lire. De plus, si l’un de ces autres mods change quelque chose, mon implémentation est soit carrément interrompue, soit incompatible avec le mod dit “supporté”. Je suis donc récemment devenu un grand adepte des tentatives pour rendre mes mods aussi dynamiques que possible pour éviter cela. Cela devrait, en théorie, faire gagner beaucoup de temps, mais ça ne fonctionne pas toujours.

L’automatisation selon xkcd
Origine : xkcd #1319

Cette réalité est pourtant ce que j’apprécie dans Factorio ; le “Oh, mais il faut que je fasse ce truc.” Ce n’est pas amusant si c’est juste ajouter une autre dépendance de mod en ajoutant une chaîne de caractères à une liste. Donc, pour lancer un nouveau mod avec pour objectif de pouvoir faire tourner n’importe quel équipement, sans avoir besoin de le maintenir constamment, j’avais besoin de m’appuyer sur la façon dont Factorio charge les mods.

Dans d’autres jeux où vous souhaitez ajouter des mods, vous disposez d’une sorte de liste de commandes de mods. Vous, le joueur, ou un programme créé par les moddeurs, devez indiquer au jeu dans quel ordre les mods doivent être chargés, pour vous assurer que tout s’imbrique suffisamment bien pour ne pas exploser. Factorio réalise cette commande via des dépendances de mod, mais il va aussi un peu plus loin. Factorio ne se contente pas de charger tous les mods dans l’ordre une fois, mais les charge dans l’ordre trois fois.

Trois fois ? Cela semble excessif, n’est-ce pas ? En fait, c’est une idée fantastique. Le wiki explique cela de manière beaucoup plus détaillée, mais je vais l’expliquer rapidement ici. Chaque mod, dans l’ordre de chargement, a une étape des réglages, puis une étape des données. L’étape des réglages est assez explicite, et l’étape des données concerne les prototypes, tels que les objets, les entités et les recettes. Ce cycle se répète ensuite deux autres fois. Les mods précisent ce qu’il faut charger à chaque itération du cycle. Les conventions de modding recommandent que tous les prototypes soient ajoutés le plus tôt possible dans ce processus. Cela permet aux mods qui veulent s’appuyer implicitement sur d’autres mods de le faire sans avoir besoin de connaître leur existence. Par exemple, le mod de base Factorio le fait pour le transport de liquides en barils.

C’est ainsi que la communauté dispose de mods de refonte géants qui modifient entièrement toutes les recettes ; ils le font simplement dans une phase des données ultérieure pour chaque recette du jeu. Pas de grandes listes de mods qui ont besoin de maintenance, pas de “Ce mod doit être chargé en dernier.”

C’est ainsi que je suis en mesure de rendre mon mod capable de faire tourner n’importe quel équipement. Il suffit simplement de déplacer à un stade des données ultérieur mes vérifications sur les équipements qui ont besoin d’une version pivotée, et cela devrait implicitement couvrir tous les équipements du jeu. Pas besoin pour moi de nommer les mods X, Y et Z comme des dépendances, ni pour le joueur de gérer quoi que ce soit de son côté ; ça marche tout simplement. Pas de gestion constante des changements de noms, à moins qu’il y ait un problème plus complexe que j’aurai plaisir à traquer.

Avec tout cela à l’esprit, quelques jours, et un Krastorio abandonné à moitié terminé, Rotatable Equipment est né.

Équipements tournés
Équipements du jeu de base et variantes tournées.

Contribuer

Comme toujours, nous attendons vos contributions pour les Alt-F4, que cela soit par la soumission d’un article ou en aidant pour les traductions. Si vous avez quelque chose d’intéressant en tête que vous souhaitez partager avec la communauté, vous êtes au bon endroit. Si vous n’êtes pas sûr, nous serons heureux de vous aider en discutant structure, contenu et idées. Donc si vous voulez vous impliquer dans les Alt-F4, rejoignez-le Discord pour ne rien rater !

Comme vendredi prochain tombe le jour de Noël, nous avons décidé de ne pas publier de numéro cette semaine-là, ce qui signifie que c’est le dernier numéro des Alt-F4 pour cette année ! Nous ferons notre retour en fanfare le 1er janvier avec un épisode spécial qui retracera l’évolution du projet jusqu’à présent, avec les points de vue des différents membres de l’équipe sur le travail qu’ils ont accompli. Cela devrait être amusant.