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.

Publié par