14 ans de Guerre Tribale

Maintenir et moderniser un ancien jeu de navigateur

InnoGames revient sur le développement et les mises à jour de leur premier succès : Guerre Tribale.

2 commentaires Donner une note à l'article (5)

Article lu   fois.

Les deux auteur et traducteur

Traducteur : Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Image non disponible

Guerre Tribale (Tribal Wars), est un jeu de stratégie classique en ligne dans le navigateur et en développement actif depuis 2003.

Lorsque le jeu a été mis en ligne pour la première fois en 2003, le monde du développement Web était très différent. Internet Explorer avait 95 % des parts de marché, le HTML5 n'avait pas encore été pensé et les médias interactifs en ligne étaient très limités. Après avoir travaillé pendant 10 ans sur Guerre Tribale, je vais retracer l'historique du développement et décrire certains défis que nous avons rencontrés lors de la récente modernisation du jeu. Je vais aussi mentionner les efforts qui ont été nécessaires pour accompagner la croissance du jeu fonctionnant sur un simple serveur à un jeu ayant plusieurs millions d'utilisateurs actifs par mois. Finalement, je vais résumer les leçons apprises en travaillant sur un ancien produit.

II. Une passion

Les versions initiales du moteur du jeu (2003-2006) ont été développées avec PHP 4. La majorité de la logique de jeu était définie par des fonctions groupées dans des fichiers suivant les domaines du jeu concernés. Les interactions avec le joueur étaient implémentées en utilisant des classes simples pour le contrôleur et Smarty pour les vues. Il y avait trois tests : un pour s'assurer que le résultat d'une bataille entre deux joueurs était correct, un pour tester que l'envoi d'e-mails fonctionnait et le dernier pour tester la localisation (avec Gettext). Toutes les données étaient stockées dans une base de données MySQL 3.23.

Une des fonctionnalités principales du jeu est que les actions sont réalisées en temps réel, même lorsque vous ne jouiez pas. Un worker PHP passait sur toutes les actions en attente (telle une attaque contre un joueur) toutes les secondes afin de les traiter.

Image non disponible
L'écran de classement du joueur dans une des premières versions du jeu.

L'interface du jeu était simple et totalement générée par le serveur. Elle était principalement composée de tableaux de données avec des graphismes limités. Le seul contenu interactif était regroupé dans un fichier JavaScript de 500 lignes contenant la logique pour des choses tels les compteurs.

Du côté de l'infrastructure, le jeu a démarré sur un serveur installé dans la chambre d'un des fondateurs. Rapidement, il a été migré sur un petit nombre de serveurs loués à une entreprise locale, chacun ayant un processeur Pentium 4 et 2 Go de mémoire. Nous avons démarré avec Apache pour ensuite passer à lighttpd pour son aspect haute performance et sa légèreté. Grâce à la simplicité du jeu, les requêtes mettaient moins de 10 ms pour être traitées.

Image non disponible
Un des premiers serveurs à faire fonctionner Guerre Tribale.Il est maintenant utilisé comme cale par un de nos administrateurs système.

À ce moment, le jeu n'était développé que par un développeur, un artiste et un designer.

III. Transition vers une petite startup

En trois ans, Guerre Tribale est devenu un jeu par navigateur à succès et en 2007, l'entreprise InnoGames GmbH a été créée pour continuer le développement. C'est à partir de ce moment que nous avons un historique complet du projet grâce à notre migration de CVS vers Subversion au début de 2007.

Image non disponible

La qualité du code du moteur a reçu de nombreuses améliorations grâce aux nouvelles fonctionnalités orientées objet introduites dans PHP 5. La priorité a été d'améliorer la stabilité du produit tout en s'assurant qu'il pouvait fonctionner de manière fluide sur différents marchés. À ce moment, le jeu est disponible dans 15 langues/pays différents. Pour améliorer les performances, nous avons commencé à utiliser memcache pour la mise en cache des opérations lourdes.

Aussi, plusieurs « microservices » ont été développés pour aider le fonctionnement :

  • un service « d'aide » indépendant a été utilisé comme un CMS pour développer les manuels/guides du jeu, ensuite transmis au serveur de jeux grâce à XMLRPC ;
  • un outil de localisation web pour permettre aux gestionnaires communautaires de facilement traduire le jeu. Cela fonctionnait en récupérant du dépôt les fichiers de langues de Gettext, en extraire une liste de phrases, puis en renvoyant les fichiers dans le dépôt pour le prochain déploiement. Une version améliorée du produit est toujours utilisée 10 ans après ;
  • un service de collection de statistiques rudimentaires a été utilisé pour récupérer toutes les informations du jeu provenant de tous les serveurs et pour les agréger permettant ainsi un affichage détaillé du nombre de joueurs, des tendances, etc.

Pour l'interface, le jeu n'a pas changé de technologie. De façon éparse, nous commencions à utiliser XMLHttpRequest (AJAX) pour améliorer la prise en main du jeu, mais jamais comme un prérequis pour jouer. La première utilisation a permis aux joueurs de renommer leur village sans subir un rechargement de la page.

Le 7 mars 2007, la première version de Mootools, un framework JavaScript orienté objet léger a vu le jour. Moins de deux mois après, il a été ajouté au jeu pour aider le développement de l'interface. Cela a aidé l'intégration de nouvelles fonctionnalités telles que le glisser-déposer et des tutoriels interactifs pour accueillir les joueurs.

L'équipe de développement est restée petite, avec seulement un couple de développeurs actifs, un artiste et un administrateur système.

Pour supporter la croissance du jeu, nous avons hébergé le jeu sur nos propres serveurs placés dans un datacenter proche. Les serveurs de bases de données étaient équipés de deux unités embarquant deux processeurs Xeon (53xx) avec 16 Go de mémoire et huit disques de 74 Go en RAID 1 et le serveur web était équipé d'un Xeon 3000 quadricœurs avec 4 Go de mémoire et un disque dur.

IV. Croissance du jeu

En 2009, l'équipe derrière Guerre Tribale a atteint la taille record de six développeurs. Tous les développements sont réalisés avec une approche orientée objet et nous avons migré vers JIRA pour une gestion adéquate des bogues, des nouvelles fonctionnalités et dans l'ensemble de la gestion du projet.

À ce moment, le jeu a atteint des millions de joueurs actifs par mois et cela a entraîné des problèmes de performances. Nous avons utilisé un mélange de croissance horizontale et verticale en lançant de nouvelles instances de jeu régulièrement. Celles-ci étaient sous la forme d'un serveur de bases de données et de plusieurs serveurs Web. La configuration était répartie inégalement, car les joueurs se jetaient sur les dernières instances mises en ligne laissant peu de joueurs sur les anciennes. Nos instances luttaient pour surmonter la grosse charge sur la base de données avec autant d'utilisateurs simultanés.

Pour essayer de résoudre cela, des parties du jeu ont été déplacées sur une seconde base de données afin que chaque instance soit gérée par deux serveurs. Pour être précis, ce n'était pas une réplication, mais le stockage de différentes tables. Dans le cas où le jeu devait faire une jointure entre les tables des deux serveurs, le moteur réalisait la jointure manuellement basée sur le résultat de deux requêtes.

Cela nous a permis de continuer à exécuter le jeu sur le matériel existant pendant presque deux ans. Une fois que du matériel plus puissant a été disponible, nous avons pu revenir au modèle où le jeu est entièrement géré par une unique base de données. Avec du recul, il aurait peut-être été meilleur d'investir dans du matériel plus puissant plutôt que de séparer les bases de cette façon. La séparation des bases de données et la maintenance du code ont vraiment été compliquées pour l'équipe de développement.

Au début de 2010, nous avons décidé de retirer Mootools de l'interface et de passer à jQuery. Le jeu a commencé à être plus compliqué avec l'arrivée de nouvelles fonctionnalités interactives. À ce moment, la création de tâches automatisées telles que Grunt ou Gulp n'existaient pas et nous avons donc développé des mécanismes de fusion et de réduction de code en PHP. Cela peut vous sembler très archaïque pour vous, mais gardez à l'esprit qu'Internet Explorer avait toujours 60 % des parts du marché et le support de IE6 était encore nécessaire.

V. Passer au mobile

En 2011, le marché des jeux par navigateur a commencé à évoluer vers les jeux hautement graphiques et nous avons atteint notre nombre record de joueurs actifs par mois. À ce moment, peu de choses avaient changé technologiquement dans le moteur du jeu. PHP est resté inchangé pendant plusieurs années en attendant une version 6 qui a été finalement ratée. Le jeu possédait une petite suite de tests unitaires pour les fonctionnalités principales, mais beaucoup de choses ne pouvaient pas facilement être testées à cause du vieux design du code.

Notre infrastructure est passée aux machines virtuelles exécutées sur des serveurs blades, au début en conservant la séparation entre le serveur de bases de données (attaché à un Storage Area Network (SAN) et le serveur Web (avec un disque local). Finalement, le SAN a été retiré et maintenant tous les serveurs ont des disques SSD. Nous continuons d'utiliser la même configuration à ce jour, même si nous avons fait des améliorations dans l'infrastructure qui sont en dehors de la portée de cet article.

Nous avons réalisé une grande modification dans notre processus en remplaçant lighttpd avec nginx pour notre serveur Web. Nous avons observé un développement ralenti du projet lighttpd et nous avons donc migré vers quelque chose de plus commun.

Pendant plusieurs années, nous avons offert une version réduite du jeu pour les navigateurs mobiles et en 2011, nous avons décidé qu'il était temps d'offrir notre propre application native pour iOS et Android. Cela a été un des plus grands défis jusqu'à présent : comment adapter un jeu par navigateur qui n'a pas d'API définie et offrir une expérience mobile fluide sans revoir complètement le moteur ou agrandir l'équipe de développement ?

Image non disponible

La première version de notre application iOS a consisté en un processus d'enregistrement/authentification, un menu sympa, une vue Web, les notifications push et bien sûr, les paiements. À ce moment, nous avions décidé de créer une nouvelle API - uniquement pour les applications mobiles - qui agirait comme une interface compatible avec le reste du jeu. Avec du recul, il aurait peut-être été une meilleure idée d'investir plus de temps dans le refactoring de nos systèmes afin de mieux définir l'API dans le but de l'utiliser pour les navigateurs et les mobiles, mais cela aurait pris plus de temps. Nous étions particulièrement intéressés dans l'établissement d'une présence sur les magasins d'applications pour voir où cela allait nous mener.

Les notifications push et les paiements étaient gérés par des microservices indépendants qui par la suite ont été capables de prendre en charge nos autres jeux. Lorsque notre première application a été lancée, nous envoyions plusieurs centaines de notifications par semaine. Aujourd'hui, nous en envoyons plus de 100 millions à travers le même microservice.

Au fur et à mesure que le temps passe, nous avons ajouté des fonctionnalités à l'application iOS pour arriver à une solution hybride où quelques morceaux du jeu sont générés par le moteur comme le contenu HTML puis affichés dans une vue Web et d'autres fonctionnalités « cœur » telles que la carte, la vue du village et des quartiers généraux et de la messagerie de jeu sont implémentées nativement. L'application peut passer sans accroches entre les deux types d'écrans et communiquer avec la vue Web grâce à JavaScript afin de s'assurer que l'application native et les pages Web sont synchronisées.

Un avantage de notre couche de compatibilité mobile sur mesure et que malgré une première version vieille de six ans, vous êtes toujours en mesure de l'utiliser pour vous connecter et jouer.

Grâce à notre présence sur les magasins d'applications, plus d'un tiers de nos nouveaux joueurs proviennent de ces applications.

VI. Les jours modernes de Guerre Tribale

Même si l'équipe derrière Guerre Tribale est maintenant plus petite qu'à son top (nous avons deux développeurs, un artiste, un ingénieur qualité, un game designer, un chef de projet, un gestionnaire communautaire en chef et un administrateur système) nous avons réalisé plusieurs modernisations au cours des dernières années malgré les défis liés au vieux code.

Voici un résumé des technologies actuelles :

  • la majorité des fonctionnalités sont écrites en PHP (avec les nouvelles fonctionnalités du PHP 7 telles que les types scalaires). Nous avons des tests unitaires et d'intégration là où cela est possible et ils sont définis au travers d'applications composées d'une petite page pour l'interface ;
  • nous utilisons redis pour la mise en cache et la sauvegarde de certaines données (comme le classement du joueur) avec environ 13 Go de données ;
  • nous utilisons Node.js et socket.io (avec redis) pour permettre les mises à jour à la volée de notre moteur vers le joueur et pour faciliter notre système de chat ;
  • nous utilisons RabbitMQ pour les événements qui n'ont pas besoin d'être exécutés en temps réel ;
  • nous utilisons toujours un démon PHP pour gérer en arrière-plan les événements, même si maintenant nous en exécutons quatre en parallèle pour une instance standard ;
  • nous utilisons toujours MySQL (en version 5.6) comme base de données primaire avec environ 1.7 To de données ;
  • nous envoyons plus de 100 millions d'événements par jour au cluster Hadoop pour les analyser ;
  • nous avons forcé HTTPS partout depuis 2015 ;
  • nous avons activé HTTP2 début 2016 ;
  • nous utilisons grafana pour créer des tableaux de bord et des graphiques afin d'avoir un aperçu rapide de la santé du jeu et des nouvelles fonctionnalités ;
  • le support d'IPv6 arrive prochainement :) ;
  • nous avons une flotte d'environ 1150 machines virtuelles pour 330 instances (toutes sous Debian Jessie) ;
  • nous pouvons déployer une nouvelle version du jeu sur tous les serveurs en moins de cinq minutes et nous essayons de déployer du nouveau contenu au moins une fois par semaine. Nous réalisons souvent plusieurs déploiements par jour.

Nous avons moins de joueurs qu'il y a cinq ans, mais nous avons une infrastructure plus complexe et nous devons toujours être prudents pour ne pas rencontrer des problèmes de performances et de répartition de charge. Notre plus grand défi d'expansion reste que la plupart des joueurs jouent sur l'instance la plus récente (90 % des joueurs sont sur les dix plus grandes instances). Nous atteignons quelquefois les limites verticales de notre stratégie d'expansion. Plus récemment, nous avons eu une instance avec plus de 120 attaques de joueurs toutes les secondes devant être traitées sans délai en tâche de fond, nécessitant plus de 20 workers pour surmonter la charge.

Une des plus grandes modernisations que nous ayons faites a été l'introduction de socket.io pour permettre les notifications de notre moteur vers le joueur. Nous n'étions plus contraints de régulièrement chercher de nouvelles données ou d'attendre que le navigateur du joueur rafraîchisse la page afin de s'assurer que de nouvelles données soient disponibles. Nous avons actuellement quatre instances de notre serveur pour chaque monde, où les joueurs sont distribués aléatoirement sur chaque instance. Si l'instance A doit communiquer à l'instance B (c'est-à-dire lorsqu'un joueur est en train d'écrire à un autre joueur dans le système de chat), redis est utilisé comme relais pour les messages. Nous envoyons plus de 150 000 messages chaque minute via socket.io.

Après la mise à jour de notre moteur en PHP 7, nous avons vu une diminution de 40 % du temps passé dans les requêtes serveur. Bien que toutes ces améliorations ont été réalisées, certaines parties de la logique du jeu sont toujours contenues dans des fonctions diverses. Il existe quelques parties du jeu où le code n'a pas été retouché depuis le commit initial sur Subversion (oui, nous utilisons toujours cela).

Sachant la petite taille de notre équipe, nous avons préféré, dans plusieurs cas, ne pas toucher quelque chose s'il n'est pas cassé et si cela ne nécessite pas de changement dans le système annexe. Dans d'autres cas, nous avons complètement supprimé des fonctionnalités pour les recréer à partir de zéro.

Le système d'e-mails de Guerre Tribale est l'une des plus vieilles fonctionnalités et cela depuis une dizaine d'années. Elle est toujours constituée d'appels de fonction avec de la logique de base de données mélangés dans des contrôleurs. Pendant longtemps nous n'avons appliqué que des changements mineurs. Lorsque le chat a été implémenté dans le jeu, l'idée était que le moteur devait être le même pour le chat et la messagerie interne au jeu. De cette façon, vous auriez pu utiliser le chat dans le navigateur pour parler à un joueur sur mobile qui aurait vu le message dans sa liste d'e-mails. Grâce à un refactoring complet de notre système de messagerie, nous pouvions facilement intégrer un pont entre le serveur socket.io et le moteur pour permettre un chat sans accroche entre les joueurs ou des groupes de joueurs.

Grâce à la nature hybride de nos applications mobiles, nous pouvons introduire de nouvelles fonctionnalités sans pour autant avoir une quelconque mise à jour côté client. Nous introduisons régulièrement de nouveaux événements temporaires au jeu et grâce aux incroyables capacités du HTML 5 nous pouvons offrir une version attrayante à travers la vue Web embarquée.

Vous pouvez trouver des défis dans les endroits les plus inattendus lorsque vous travaillez sur un si vieux projet. Au fil de la croissance de Guerre Tribale, les joueurs ont développé des scripts d'aide et des outils eux-mêmes fonctionnant dans le contexte du jeu : soit un système rudimentaire de module. Malheureusement, la plupart de ces scripts sont diffusés en externe au travers d'une connexion non sécurisée et ne marchèrent donc plus lorsque le jeu a été chargé de manière sécurisée. Pour éviter cela, nous avons développé et exécuté un reverse proxy temporairement qui récupère le contenu de ces scripts afin de les fournir au travers de nos propres serveurs par HTTPS. Nous avons averti les joueurs de cela et finalement nous avons pu les retirer.

VII. Apprentissages d'un travail sur un produit ancien

Pour avoir personnellement travaillé sur Guerre Tribale pendant presque dix ans en tant que développeur généraliste (moteur, interface, mobile et opérations basiques sur le serveur), un de mes apprentissages principaux a été que vous ne devez pas toujours refactoriser un vieux code afin d'ajouter des améliorations à un ancien jeu. Il y a eu plusieurs moments où nous aurions pu arrêter ce que nous faisions pour nous concentrer sur l'amélioration de la qualité du code et ainsi passer plusieurs mois sur quelque chose qui n'aurait pas apporté de vraies améliorations à nos joueurs.

Je pense aussi que travailler sur un code ancien ne signifie pas que vous devez avoir peur des nouvelles technologies. Ne soyez pas effrayé de commencer des petites expériences comme l'implémentation des notifications dans le navigateur. Essayez des projets plus grands comme la création de notre moteur en Node.js. Adoptez de nouvelles technologies comme HTTP2 aussi tôt que possible.

Il est préférable d'avoir quelque chose qui fonctionne que quelque chose de parfait. Vous pouvez publier rapidement, au plus tôt et recevoir des retours pour de futures améliorations au lieu de travailler indéfiniment sur quelque chose qui peut être la meilleure implémentation du moteur, mais qui est détesté par vos clients.

Mettez à jour votre environnement aussi tôt que possible et utilisez les nouvelles fonctionnalités disponibles afin d'apporter de nouvelles choses sympas à vos utilisateurs finals. Assurez-vous que vous comprenez tous les composants de votre plateforme. Ne vous restreignez pas à une unique spécialité (par exemple, le développement de l'interface) lorsque vous devez au moins comprendre le fonctionnement basique du moteur, quelle est l'infrastructure du serveur, etc.

Et finalement, soyez fier de votre travail et n'utilisez pas son âge comme une excuse pour prendre des raccourcis ou diminuer la qualité.

VIII. Historique

2003 : lancement du jeu, avec une centaine de joueurs et un serveur dans la chambre du fondateur.

2004 : passage à MySQL 4.

2005 : passage à PHP 5, MySQL 5.

2006 : lancement de la version traduite du jeu et expansion vers plusieurs mondes.

2007 : fondation de l'entreprise et expansion sur de multiples marchés. Première équipe officielle travaillant sur le jeu.

2008 : premier contenu interactif avec AJAX.

2009 : le jeu est disponible dans 25 langues, 6 développeurs travaillant sur le jeu, des millions de joueurs actifs.

2010 : lancement de la version optimisée pour mobile.

2011 : premier essai dans les magasins d'applications avec le lancement de la version iOS.

2012 : lancement de la version Android.

2013 : transition de lighttpd vers nginx, utilisation d'un gestionnaire de paquets PHP. Transition vers un planning hebdomadaire et par publication.

2014 : première implémentation du moteur socket.io dans NodeJS pour permettre les mises à jour du jeu en temps réel.

2015 : utilisation de RabbitMQ pour les queues.

2016 : passage à PHP 7, transition de memcached vers redis, activation de HTTP2.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 InnoGames. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.