Les coulisses de Citadelle Lab

Les coulisses de Citadelle Lab

Citadelle Lab est une association loi 1901 qui a pour but d'organiser des événements autour du jeu Minecraft. Nous proposons principalement des événements de type « Cités » : un mode de jeu où les joueurs, répartis en équipes, s'affrontent en récoltant des ressources pour les vendre. L'objectif final est simple : accumuler le plus d'argent possible face aux équipes adverses.


L'infrastructure réseau : Proxy et Serveurs de jeu

Pour pouvoir accueillir les joueurs sur un réseau de plusieurs serveurs Minecraft de manière transparente, on utilise un Proxy. C'est le point d'entrée unique auquel les joueurs vont se connecter. C'est lui qui gère leur connexion et leur répartition sur les différentes machines du réseau.

Les softwares les plus connus pour gérer les proxys sont Velocity et BungeeCord. BungeeCord étant aujourd'hui un peu vieillissant, nous utilisons Velocity, qui offre de meilleures performances et une sécurité accrue.

Côté serveur de jeu (les mondes où les joueurs évoluent), nous utilisons le software Paper. Tout comme Velocity, c'est l'outil le plus à jour, le plus optimisé et le mieux maintenu par la communauté, bien qu'il existe des alternatives comme Spigot ou Purpur.

Schéma d'une infrastructure classique d'un serveur Minecraft

Répartition de la charge (Load Balancing) et Performances

Dans le cadre de nos événements « Cités », les joueurs n'ont pas besoin d'être tous sur le même serveur en même temps, à l'exception du Spawn. Nous allons donc répartir la charge de calcul sur différents serveurs en plaçant 6 équipes de 5 joueurs* sur chacun d'eux.

Nous aurons ainsi un maximum de 30 joueurs par serveur**. Cette limite n'est pas choisie au hasard : elle permet de maintenir un TPS (Ticks Per Second) compris entre 19,50 et 20. Sur Minecraft, le TPS est l'indicateur de performance absolu. La valeur idéale est de 20 TPS (le serveur calcule 20 actions par seconde). Plus ce nombre diminue, plus le serveur peine à suivre et risque de faire subir des ralentissements (lags) aux joueurs.

*Nous sommes encore en train d'étudier le nombre de teams par serveur que nous souhaitons mettre pour garder des serveurs stables
**Ici quand nous parlons de serveur, nous parlons de serveur sur Pterodactyl (cf. Hébergement et Déploiement)
Schéma global de l'infrastructure de Citadelle Lab

Le parcours d'un joueur est le suivant : il se connecte au proxy qui le dirige vers le serveur Spawn, point de passage obligatoire. Depuis ce Spawn, il peut rejoindre directement le serveur attribué à son équipe via une commande ou en interagissant avec un PNJ (Personnage Non Joueur).

Gérer les pics d'affluence et optimiser l'attribution

Les serveurs Spawn et Event ont une particularité : ils doivent pouvoir accueillir l'ensemble des joueurs simultanément lors de rassemblements. Ils consomment donc beaucoup plus de ressources (CPU et RAM) lors des pics de connexion.

Dans notre exemple actuel, nous avons 18 équipes (soit 90 joueurs) réparties sur 3 serveurs de jeu. Nous pourrions augmenter ce nombre en ajoutant de nouvelles machines, ou en augmentant le nombre d'équipes par serveur. Toutefois, il est crucial de trouver le point d'équilibre parfait pour déterminer la limite de joueurs par machine tout en garantissant des performances optimales.

Pour aider les serveurs à tenir la charge, nous procédons à un équilibrage stratégique lors des inscriptions. Les plus grosses équipes (les joueurs très compétitifs habitués aux Cités), qui construisent souvent des farms massives très gourmandes en ressources serveur, sont identifiées. Nous les séparons volontairement sur des serveurs de jeu différents, aux côtés d'équipes plus modestes, afin d'éviter qu'un seul serveur ne subisse toute la charge de calcul de ces usines géantes.


Hébergement et Déploiement : L'outil Pterodactyl

Pour héberger et gérer facilement l'ensemble de nos serveurs, nous utilisons Pterodactyl. Cet outil s'installe sur une machine Linux et se charge de créer un conteneur Docker isolé pour chaque serveur Minecraft.

Ce choix technique nous offre une interface Web très puissante. Elle nous permet de déployer de nouvelles instances en quelques clics, de surveiller la console en direct, de monitorer la consommation des ressources (RAM et CPU) en temps réel, mais aussi d'éditer nos fichiers de configuration de manière collaborative directement depuis notre navigateur.

Serveurs sur Pterodactyl

Serveur Paper

Du côté des serveurs Paper, l'expérience de jeu est propulsée par différents plugins entièrement développés en interne par l'équipe de Citadelle Lab. Voici les principaux outils qui composent notre écosystème :

  • Citadelle-Moderation : La boîte à outils de notre équipe de modération (commandes de Vanish pour se rendre invisible, téléportation, Freeze pour immobiliser un joueur suspect, Invsee pour inspecter un inventaire, etc.).
  • Citadelle-Redis : Notre librairie de communication pour transmettre des informations entre les différents serveurs de jeu et le proxy.
  • Citadelle-Core : Le cœur du système. Il gère la création des équipes (teams), l'économie de la partie, et agit comme une API centrale à laquelle les autres plugins viennent se greffer.
  • Citadelle-Chat : Le système de chat qui gère le chat inter-serveurs, ainsi que les canaux privés pour les équipes et le staff.
  • Citadelle-Shop : L'économie du jeu, avec un shop rotatif et un système de paliers évolutifs pour la revente des items.
  • Citadelle-ClearLag : Notre outil d'optimisation automatisé qui supprime régulièrement les entités au sol et les mobs (monstres/animaux) en surplus pour soulager le serveur.
  • Citadelle-QOL (Quality of Life) : Une alternative "version light" à des softwares massifs comme EssentialsX, conçue uniquement pour les commandes de confort essentielles.

Tous ces plugins forment un écosystème où beaucoup sont interdépendants. Par exemple, Citadelle-Core est la fondation indispensable pour faire fonctionner le Chat, le Shop et Redis. À l'inverse, Citadelle-Redis est vital pour le Core et les outils de Modération.

Pour mieux comprendre la logique technique derrière tout cela, prenons le temps de détailler le fonctionnement de notre outil de communication : Citadelle-Redis.


Zoom sur Citadelle-Redis : La passerelle de notre réseau

Par nature, un serveur Minecraft est isolé : il ne sait absolument pas ce qu'il se passe sur les autres serveurs du réseau. Si un joueur n'est pas connecté sur "son" serveur, il le considère comme déconnecté. Citadelle-Redis fait office de passerelle pour briser cette isolation.

C’est là que la technologie Redis intervient. Il s'agit d'une base de données fonctionnant entièrement dans la mémoire vive (RAM), ce qui la rend extrêmement rapide. En y stockant les données de session des joueurs en temps réel (pseudo, identifiant unique UUID, et serveur de jeu actuel), Redis offre une vision globale de notre réseau.

Grâce à cela, si un administrateur veut savoir où se trouve un joueur, ou si le système d'auto-complétion du chat (la touche Tab) doit proposer les pseudos de joueurs situés sur d'autres serveurs, le plugin va piocher ces informations dans Redis de manière quasi instantanée. De la même façon, lorsqu'un joueur rejoint le jeu, le plugin récupère son équipe via Citadelle-Core et met à jour sa session sur Redis. Ainsi, le serveur de Chat et les autres machines savent immédiatement à quelle équipe il appartient.

Performances et Asynchronisme : Protéger les TPS

Côté technique, l'une de nos plus grandes priorités lors du développement a été la performance. Sur Minecraft, si nous faisions des requêtes réseau directes à chaque fois qu’un joueur tape une commande ou cherche un pseudo, le processus principal du serveur (Main Thread) serait mis en pause le temps de recevoir la réponse. Ces attentes créeraient des micro-lags, ce qui ferait chuter nos les TPS.

Pour éviter cela, Citadelle-Redis utilise un cache local. Il conserve une copie de l'état global du réseau dans la mémoire du serveur de jeu, et vient la mettre à jour de manière asynchrone (en arrière-plan, sans bloquer le jeu) toutes les secondes.

De plus, pour ne pas saturer notre bande passante avec de multiples petites requêtes, nous utilisons une méthode appelée « pipelining » : lors du rafraîchissement des données, le plugin groupe ses demandes et récupère toutes les informations des joueurs en un seul gros paquet, plutôt que de les interroger un par un.

Architecture et Résilience : Le couplage lâche

Enfin, pour rendre notre infrastructure plus flexible et à l'épreuve des pannes, Citadelle-Redis est branché à Citadelle-Core grâce à une technique de développement appelée « couplage lâche » (via la réflexion Java).

En termes simples, cela signifie que le plugin ne présume pas que le Core est toujours présent. Il va d'abord "détecter" si Citadelle-Core est installé avant de tenter de synchroniser les équipes. L'avantage ? Si nous décidons de démarrer un petit serveur de test sans le Core, le plugin s'adaptera intelligemment et fonctionnera de manière autonome sans faire planter (crash) l'instance.

Il en va de même pour la tolérance aux pannes : si la base de données Redis subit une coupure momentanée, le plugin continue de fonctionner en distribuant les dernières données connues dans son cache. Le réseau reste fluide, le serveur ne bloque pas, et nous garantissons une stabilité maximale lors de nos événements.

Serveur Velocity

Tout comme sur nos serveurs Paper, nous avons développé en interne une suite de plugins dédiés spécifiquement à notre proxy Velocity. C'est ici que s'effectue le premier filtre avant même que les joueurs ne posent le pied en jeu :

  • Citadelle-TeamLimit : L'outil qui limite automatiquement le nombre de joueurs d'une même équipe connectés simultanément.
  • Citadelle-Redis : La version "proxy" de notre librairie pour échanger les données avec les autres serveurs.
  • Citadelle-Moderation : Le gestionnaire de sanctions global du réseau (commandes de Ban, Mute, Warn, et vérification d'IP).
  • Citadelle-Maintenance : Un système de liste d'accès (whitelist) dynamique avec un système de rôles, directement relié aux rôles Discord de nos joueurs.

Contrairement aux serveurs de jeu où les dépendances sont nombreuses, ici, seule une véritable dépendance existe : Citadelle-TeamLimit a besoin de Citadelle-Redis. Découvrons plus en détail la mécanique ingénieuse de ce système de limite d'équipe.


Zoom sur Citadelle-TeamLimit : Gérer les remplaçants

Lors de nos événements, le format de jeu impose des équipes de 5 joueurs actifs sur le terrain. Cependant, les équipes inscrivent en réalité 6 joueurs (incluant un remplaçant) afin de maximiser leur temps de jeu et pallier d'éventuelles absences.

Puisque l'ensemble de ces 6 joueurs est inscrit sur notre whitelist pour pouvoir accéder au proxy, il est indispensable de restreindre de manière automatisée le nombre de connexions simultanées pour une seule et même équipe.

C’est précisément là que Citadelle-TeamLimit intervient. En interceptant les tentatives de connexion des joueurs directement sur le proxy (avant même qu'ils ne soient envoyés vers un monde Minecraft), le plugin va effectuer une double vérification :

  1. Il interroge la base de données PostgreSQL (gérée par le Core) pour obtenir le team_id (l'identifiant de l'équipe) du joueur qui tente de se connecter.
  2. Une fois ce team_id récupéré, il demande à Citadelle-Redis de compter combien de joueurs possédant ce même identifiant sont actuellement en ligne.

Si le système détecte que la limite autorisée de 5 joueurs est déjà atteinte par ses coéquipiers, la tentative de connexion du 6ème joueur est proprement refusée avec un message d’erreur personnalisé.

Performances : L'art de ne pas bloquer la file d'attente

Côté technique, l'une de nos plus grandes priorités a été, encore une fois, la performance. Effectuer une requête vers une base de données à chaque tentative de connexion peut s'avérer lent. Si nous faisions cela de manière classique (synchrone), cela mettrait en pause le processus principal de Velocity (Main Thread), impactant ainsi la connexion de tous les autres joueurs.

Pour éviter ce goulot d'étranglement, le plugin utilise un système d'exécution asynchrone grâce à des tâches adaptées sur Velocity (EventTask.withContinuation). Il est couplé à un système appelé HikariCP, un outil qui crée un pool de connexions (il maintient plusieurs connexions à la base de données ouvertes et prêtes à l'emploi en permanence). Ainsi, la vérification s'exécute entièrement en arrière-plan en une fraction de seconde, sans jamais perturber la fluidité du proxy.

Vérification continue et équité de jeu

Il fallait également parer aux situations exceptionnelles, comme des erreurs de synchronisation ou des transferts d'équipe réalisés en plein événement. Pour cela, une tâche de vérification supplémentaire s'exécute silencieusement en arrière-plan toutes les 3 secondes.

Pour éviter de saturer (et de faire planter) la base de données PostgreSQL à chaque cycle, cette tâche collabore avec Citadelle-Redis pour interroger l'état global du réseau directement dans la mémoire vive, ce qui est quasi instantané.

En parallèle, Citadelle-TeamLimit garde un historique local des heures de connexion des joueurs (joinTimes). Pourquoi ? Car si une équipe dépasse accidentellement la limite autorisée à cause d'un bug ou d'un changement en direct, le plugin va trier les joueurs de l'équipe du plus ancien au plus récent, et déconnecter automatiquement le dernier arrivé. Cette logique stricte mais équitable permet de préserver l'expérience de jeu des joueurs qui étaient déjà en pleine action, sans punir l'équipe entière.

Déploiement

Lors de nos derniers événements, nous faisions face à un problème logistique majeur : le déploiement. Lorsqu'une correction de bug critique ou une nouvelle fonctionnalité (feature) devait être appliquée en plein événement, il fallait compiler (build) le plugin et l'envoyer (l'upload) manuellement sur l'ensemble de nos serveurs.

Avec 8 ou 9 serveurs de jeu différents, cela impliquait de supprimer l'ancien fichier, de télécharger le nouveau, et de relancer la machine pour chaque instance. C'était une tâche répétitive et terriblement chronophage, un luxe que l'on ne peut pas se permettre quand on doit agir dans l'urgence.

Pour pallier ce problème et moderniser notre chaîne de CI/CD, j'ai développé une plateforme interne baptisée Hermes.

Voici comment fonctionne ce nouveau pipeline de déploiement, de la ligne de code jusqu'au serveur :

  1. Le code et la vérification : Lorsqu'un développeur modifie un plugin et envoie son code (commit) sur notre dépôt GitHub, nous utilisons les GitHub Actions. Ces scripts vont automatiquement build (compiler) le plugin et vérifier en même temps qu'il ne contient aucune erreur.
  2. Le stockage en ligne : Une fois le plugin compilé avec succès, l'outil GitHub l'envoie de manière sécurisée sur un Bucket R2 (un espace de stockage cloud fourni par Cloudflare), en le rangeant dans le bon dossier (Velocity ou Paper).
  3. L'interface Hermes : Notre plateforme web Hermes se connecte à ce Bucket R2, récupère la liste des plugins à jour et l'affiche sur son tableau de bord.
  4. Le déploiement en un clic : Depuis l'interface, il nous suffit de cliquer sur un plugin et de cocher les serveurs sur lesquels nous voulons l'installer. (La liste des serveurs est récupérée en temps réel grâce à l'API de Pterodactyl). Hermes se charge alors d'upload les fichiers simultanément sur tous les serveurs cibles via cette même API.

Une plateforme en constante évolution

D'ailleurs, pendant la rédaction de cet article, j'ai profité de l'occasion pour refaire entièrement le frontend (l'interface visuelle) et une partie du backend (le moteur caché) d'Hermes, afin d'obtenir un design et une ergonomie qui correspondent parfaitement à nos exigences.

Cette nouvelle version apporte son lot de nouveautés : elle conserve désormais un historique des versions pour chaque plugin, et permet surtout de mettre à jour automatiquement les plugins sur nos serveurs lorsqu'une nouvelle version est détectée sur notre espace de stockage.

Côté technique, la stack d'Hermes s'appuie sur le framework AdonisJS v6 pour le backend, et sur React propulsé par Inertia.js pour la partie frontend.

À terme, ce projet de déploiement est destiné à être rendu open source ! L'objectif est de permettre à d'autres administrateurs de serveur Minecraft de pouvoir l'utiliser et contribuer à son code.


Subscribe to Noah LEBRETON

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe