Protocole pour mmorpg

Bonjour,

C’est mon retour d’expérience dans le domaine de la création d’un protocole pour un MMORPG (pokecraft). Mon 1er protocole étant dans la transmission de liste de copies un interne d’un Pc, il n’a que peu de rapport. Le protocole n’est pas fini ni définitif, mais ça fait plusieurs mois que je le travaille.

Cryptage et compression

Crypter la connexion ne sert théoriquement à rien (comme le http à ses débuts). D’après ce qu’ont m’a dit, une technique avec des hashs permet de transmettre le login/pass en hash sans possibilités de l’intercepter. J’ai donc choisi de la rendre optionnelle (je sais pas ce qui va passer par la dans le futur, et que les hardcores gamers soient protégés pour jouer 24h/24)
Le découpage des paquets, le flux non constant des données, le traitement rapide de bloque de données ne permettent pas une compression naturelle et transparente comme le ferait le http avec le gzip. Rien que l’entête de compression ajoute au moins 8 bits à chaque transmission, et la compression n’est efficace qu’a parti de 128 octets (données majoritairement non compressible, compression mixte). Il a donc été choisi de bien écrire le protocole pour éviter la redondance de données similaires. Mais pour les données arbitraires une compression fixe a été choisie: xz pour les éléments qui peuvent être mis en cache (soit dans le data pack directement, soit dans un cache sur le disque), Zlib pour la compression/décompression dynamique.

Minimisation des latences

Un point important appliqué dans pas mal de jeu, la minimisation des latences. Ici le jeu s’y prête grace à un gameplay principalement mono-joueur (multi-joueur fait pas les clans, leagues, …). Donc cela se résume à envoyer des données groupées, et de préférence avant que le client en as besoin. Par exemple, une liste de nombre aléatoire est envoyé pour que le client puisse dedans (liste gardé côté serveur pour faire la même chose). Ça évite le trafic réseau et les lenteurs pour tous les éléments aléatoires spécifiques au joueur.
Quand le joueur va vraiment avoir besoin d’attendre (attente du choix d’une attaque d’un autre joueur), alors c’est au client de le faire attendre (message d’attente).

Séparation du statique et du dynamique

Les données sont séparées en statique (le datapack) et dynamique (envoyée par le protocole). Cela permet de juste faire un rsync pour envoyé le datapack quand il y a un changement, histoire de minimiser les données envoyées, car les données ne changent que très peu (majoritairement statique). Et les données dynamiques sont transmises à la volée en fonction de leur type.

Protocole adpaté pour étre réparti

Le protocole est fait pour être découpé côté serveur. Les paquets de gestion de chat, de gestion de map, gestion des données locales au joueur (combat, inventaire…), sont faits pour être réparti sur plusieurs threads avec une bande passante minimale et sans latence (prêt pour un cluster?). Coté serveur le tout est réparti par des messages asynchrones pour ne jamais bloquer les différents éléments.

Attention, ici l’utilisation de threads est la pour éviter qu’une tache bloquante ne bloque les autres. Je pense à la répartition des déplacements des joueurs sur les autres joueurs, qui a une complexité carré et donc une fâcheuse tendance à mettre le cpu à 100% (surtout que c’est une partie trés solicité). Mais il y a aussi les accès disque ou les accès bloquant à la base de données, qui s’il ne serai pas mis dans un thread à part, bloquerai les joueurs, ou induirai des latences.

Les taches comme les combats, utilisation des objects ont une compléxité fixe, donc en plus d’étre très rapide, les répartir sur plusieurs thread/serveur ne servirai à rien (pose de mutex, induction de lenteur réseau, passage d’un traitement de 1µs à 1ms, soit 1000x plus lent). Et ils sont bien plus rapides que par exemple l’est la répartition des déplacements des joueurs sur les autres joueurs. Les traiter sur 1 cpu permet de ne pas rajouter de couche pour traiter ces données qui ce compte en centaines d’octects.

Datapack

Comment apporter de la souplesse pour ne pas obliger tout le monde à avoir la même histoire, même monstres à combattre, mêmes maps, …? C’est simple, il suffit de faire varier le datapack en function du serveur. Donc ce dernier doit contenir le contenu du jeu, graphisme, audio, données diverses, objets de l’inventaire, … Le client se comporte donc comme un navigateur (lecture avec des codecs déjà connus), les formats supportés sont donc multi-platforme comme: png, xml, ogg, …

Conculsion

L’écriture d’un protocole demande du temps, surtout de ce genre. Il faut bien veiller à l’asynchronisme (pour de pas avoir d’influence des latences), la minimisation de bande passante, et la fléxibilité. Ne rajoutez pas de compression, au d’autre couche du genre car ça vas être mieux, calculer les coups de ces couches (en terme de bande passante, cpu, latences, …). Vous devez aussi trouver quel sont les packets/messages/ordres qui vont étre appeller tout le temps, pour concentrer vos effort et minimiser encore la bande passante.

Qt, select(), poll() et epoll()

Bonjour, je viens vous parler d’un problème récurent avec Qt. La boucle d’évènement, c’est la base de Qt, car c’est grâce à elle que Qt fait ces signaux/slots, les divers events, le réseau, et bien d’autre chose.

Problème

Le problème est le suivant. Ce système s’appuie sur les mécanismes interne de l’os, select() est utilisé sur les systèmes unix. Hors ce procédé est obsoléte, en plus, plus il as de chose utilisé ou non, plus il vas être lent (complexité n(x)). Exemple:

  • En programmation, ça veux dire que si vous avec 30 sockets tcp d’ouvert (ça arrive souvent avec un navigateur moderne), 40 dossiers surveillés (pour les changements), bref une bonne partie des choses basé sur les events, et qui utilise un nombre minimal de connexion signals/slots (les programmes en compte je pense 150 pour les plus petit, 500 pour les moyens). Alors l’application vas ralentir car elle vas regarder dans une liste des choses ouverte celle qui corresponds à la votre
  • Coté utilisateur: Les applications comme Ultracopier, ktorrent, dolphin (explorateur de fichiers), … vont ralentir petit à petit. ktorrent vas être beaucoup plus lent qu’il ne le devrai juste à cause de cette histoire (d’un ordre de 10x d’après l’auteur)

Solution

 

L’utilisation d’epoll() permet d’avoir une complexité fixe n(1), ça veux dire que le temps de traitement est fixe, et donc ne dépends pas du nombre de fichier affiché à l’écran, du nombre de torrent ouvert, du nombre de socket tcp ouvert.

Qt dit que c’est pour l’utilisation mémoire, hors sur les petites applications 10Ko de mémoire, c’est pas ce qui vas changer grands choses, surtout que chez moi (mesuré avec l’outils massif de valgrind), le système signals/slots + les autres truc de Qt consomme 90% de la mémoire prise par mon application. Et les grosses application deviennes impraticable ou trés lent. Et les gros bureaux comme KDE, qui ont des dizaines de milliers de descripteur ouvert, j’en parle même pas.

Conclusion

Hors il n’existe pour l’instant aucun event dispatcher fini/stable avec epoll pour Qt que j’ai pu trouver. Il serai utile de trouver ce genre d’event dispatcher, pour avoir accès à ces méthodes même si Qt ne veux pas y passer.

N’étant pas un expert sur ce que je vous ai dit plus haut, il ce peu qu’il y ai des erreurs.

Création de mmorpg

Bonjour, voilà mon retour d’expérience comme joueur/administrateur système et GM de mmorpg, cet avis sera peut-être déjà acquis pour la plus part:
Point gameplay:
– Le jeu doit être amusant en solo comme un multi, pour ne pas abandonner si peu de personnes viennent
Seulement recopier un gameplay ne sert à rien, leurs joueurs irons toujours à gameplay égale là où il y a d’autres joueurs. Inovez!
Avoir un datapack/rates personnalisable, histoire que chaque personne y trouve son compte (histoire spécifique, augmentation des levels à son rythmes)
– Avoir des extensions de protocoles avec une bonne base, pour étendre le gameplay.
Partie performance:
– Beaucoup de dev font du: multiplayer online role-playing game (MORPG) pas du _Massively_ multiplayer online role-playing game (_M_MORPG), il prévoit pour 50 joueurs (pour eux c’est _Massively_, et se plante quand ils ont plus de joueurs)
– Ce qui en résulte, que le petit GM qui veut ouvrir son petit serveur le ferme rapidement car tous les mois il doit claquer des sommes folles en dédié (60€/mois pour avoir une machine correcte chez ovh).
– La personne qui veut mettre son serveur sur sa ligne ADSL ne peuvent en général pas. Car le débit est trop faible (personne ne fait de compression de la connexion tcp, ni ne prévois des options pour minimisé le débit), et à cause des pings. On peut pour la plupart des jeux, faire des protocoles d’auto-correction des latences qui permettent d’éviter que les pings n’ont trop d’importance. Pourtant, héberger chez soit, ou sur un trés petit hébergement est très utile pour démarrer.
– Calcule de complexité, rare sont ceux qui le font, cela ce pose surtout sur les joueurs qui se vois mutuellement, c’est en général: bande passant + cpu = nombre de joueur qui se vois mutuellement ^ 2

– Tout le monde se dit: on verra après pour les performances, hors une fois que tu as 1000 personnes en ligne et un serveur/client complet, personne n’a le courage de le refaire, surtout si il est crade. Résultat, tout le monde ralle car sa rame.

– Les pc ont une puissance qui augmente exponentiellement et le serveur sont exponentiellement plus lent (ce qui fait que depuis des années le nombre général maximum de joueurs reste fixe). Idiot vous direz. La plupart des serveurs ne supportent que <70 joueurs visibles mutuellement et 1000 connectés. Ce qui est très petit (j’ai fait sans problème 500 visibles et 100 000 connectés).

– Pour un petit nombre de joueur (<20), un simple mono coeur atom/arm, quelque centaine de mega de mémoire, une connexion adsl doivent suffire et pour les larges échelles (>10 000), un bon serveur (quad core, 8Go de mémoire, 1000Mbps) doit aussi suffire.
J’ai déjà vu:
– L’utilisation de tous les coeurs en multi-thread avec tellement de mutex qui se gène mutuellement. Cela fait des temps système énorme car sur certain cpu il n’y a pas d’optimisation hardware des mutex. Et qu’un seul cpu était utilisé en gros au final. (Je parle de l2jfree comparé au serveur propriétaire).
– Des serveur qui prennent 10% du cpu à vide sans aucun joueur…(minecraft, l2jfree, …), soit des fois c’est utile (timer et autre), mais pas avec 10% du cpu!
– Blockage, les joueurs s’en fiches que vous utilisez vos 56 coeurs si ça rame, ils veulent que ça ne rame jamais. Le plus efficace est donc de travailler en event, d’isoler les complexités exponentielles et les accés db/disk dans un thread. Idem pour les parties lentes. Dans le multi-thread avec event, essayer de libérer les boucles d’event+thread critique d’un maximum de choses pour gagner en latence.
– Ne sauvegardez pas en continu dans la base de données, faites le à la déconnexion des joueurs, pour les données continues (tel que les déplacements), à intervalle régulier. Bien sûr, les choses ponctuels peuvent être backupé en instantané (mais dans un thread à part).

 

 

Donc pour tout le monde (hébergeur, celui qui paye le dédié, les joueurs avec compte payant pour payer le dédié), il est indispensable de soigner certain point tel que les performances, le protocole (ça permet aussi de faire d’autres implémentations), la sécurité, la prévention de bug (personne n’aime quand tout le serveur crash et ça re-roll avec les données d’il y as 1h).
Je ne conseille à personne de faire le noob et de se lancer dans un jeu sans avoir déjà une bonne expérience en programmation (et avec un projet clair et réaliste). Les joueurs font partie des utilisateurs les plus exigeants et ce segment est là où il y a le plus de concurrence. Cela se traduit par déjà un grand projet public ou une solide motivation + y jouer même tout seul.

 

 

Les graphismes (je ne suis pas graphiste, mais j’ai quand même un peu d’expérience, manga, pixel art):
– Il ne faut pas les bâcler, car comme moi, beaucoup de joueurs regardent ça pour trier les bons jeux des petits jeux de merde. Donc très important pour avoir de nouveaux joueurs, bien plus que pour les logiciels. Il vaut mieux en avoir peu et bien, que beaucoup et mal. (J’aime bien mars space shooter pour ça)
– Les graphismes personnalisables dans le datapack sont importants.

 

 

Les fonctionnalités des bases importantes:
– Toujours prévoir l’envoie du pass en hash (sécurité), beaucoup utilise le même passe partout (dons les visites qu’il visites)
– L’update du datapack (voir client) dans le protocole, c’est assez crade d’avoir un updater à côté de son jeu
– Datapack par serveur, pour avoir des données par serveur et charger soit en fixe (pour les données les – dynamiques) soit à chaud (pour les données les + dynamiques). Important pour la concurrence et minimiser les données transmises et ne pas charger tout le temps les données dynamiques identiques (ratio read/write correcte pour la partie dynamique).
– Définir le nombre max de joueurs en ligne et en db (définir sur 16Bits, 32Bits ou 64bits), et bien proportionner/optimiser son serveur/client.
– Ne pas exclure de joueurs (multi-platforme, ou au moins support sous wine). C’est toujours frustrant (surtout quand on a acheté le jeu), de passer sous windows pour jouer (ou d’être privé du jeu si on a pas windows 😉 ).

– Visibilité correcte, faite en sorte que le joueur voit toujours une proportion correcte de l’écran (zoom adaptatif, …). Quitte à réduire la distance de visibilité si le nombre de joueur est trop grand. Et à ne rien afficher si les calcules à faire sont exagérés (le joueurs préfèrent ne rien voir, que de tout voir et que ça lag tellement qu’ils ne peuvent pas jouer). N’oubliez pas de mettre des hystérésis pour éviter de lag/prendre trop de bande passante près de la limite du serveur.