Quelles sont les meilleures façons de sérialiser et de désérialiser les messages réseau pour le jeu multijoueur C / C ++?

11

Nous utilisons JSON en ce moment et voulons passer à un format binaire pour certains types de messages entre le client et le serveur.

Dois-je simplement lire les structures dans le socket? Utilisez des tampons / épargnes proticol?

Comment représenter des tableaux de données?

À quoi devrait ressembler l'interface pour emballer / déballer les données?

HaltingState
la source

Réponses:

12

En supposant...

  1. vous parlez de convertir en un tampon d'octets
  2. Vous utilisez UDP et les performances sont une préoccupation

Essayez d'éviter de gaspiller de l'espace dans votre paquet pour définir la structure. IE envoie, au minimum, un octet pour indiquer le type de paquet, puis suppose simplement que chaque paquet reçu suit la structure prédéfinie pour ce type de paquet

Dois-je simplement lire les structures dans le socket? Utilisez des tampons / épargnes proticol?

  • Oui, lisez la structure entière SI vous avez besoin de la structure entière
  • Non, faites vous-même la structure du paquet, ce sera sûrement plus petit que la sérialisation en utilisant ces méthodes; vous devez savoir exactement quelles données le paquet doit inclure

Comment représenter des tableaux de données?

  • En tant que tableaux de données. Lors de la réception, continuez à lire le tampon jusqu'à la fin des données pour éviter d'envoyer un décompte des éléments du tableau

À quoi devrait ressembler l'interface pour emballer / déballer les données?

  • Vous pouvez facilement configurer un tas de méthodes pour convertir des types de base en octets, à partir de là, vous pouvez également utiliser ces méthodes pour convertir des types personnalisés. Les détails sur la façon de le faire peuvent être trouvés presque partout, j'en suis sûr (j'utilise C # personnellement)

Une dernière chose, la taille des paquets est une préoccupation, en particulier pour un instantané: size = packetSize x entités x connectedPlayers; Vous pouvez donc avoir 60 x 10 x 16 = 9 600 octets par paquet. Ensuite, envoyez ceci 20 fois par seconde: = 192 000 bps = 187 Kbps. Il s'agit évidemment d'une vitesse de téléchargement de bande passante élevée. D'où la nécessité de minimiser chacun des facteurs contribuant à la taille des paquets lorsque c'est possible.

Cet article m'a énormément aidé: Valve Multiplayer Networking

Indeed005
la source
Un autre article que j'ai découvert en lisant diverses questions de sérialisation et de mise en réseau d'objets il y a quelques semaines était celui-ci qui décrit comment le moteur Unreal le fait. Un bon point de comparaison pour la source Valve.
Martin Foot
1
Votre méthode de tableau ne fonctionnera pas dans le cas général - où est la «fin des données»? Même si vos messages sont délimités, cela signifie que vous ne pouvez pas avoir plus d'un tableau par structure. Pour résoudre ce problème, l'affiche originale peut soit rester sur des tableaux de longueur fixe, soit s'assurer qu'il n'y a qu'un seul tableau par structure (à la fin de la structure), ou envoyer une valeur de comptage au début du tableau.
Kylotan
Encore une astuce: n'oubliez pas de traiter l'endianisme, cela peut être très ennuyeux si vous ne savez pas qu'une telle chose existe.
Bon point @Kylotan, convenez que dans certains cas, ces données supplémentaires ne peuvent pas être évitées; mais si je me retrouve à ajouter plusieurs tableaux à un seul paquet,
j'envisagerais
1

Ce problème a été résolu par Google et Facebook:

  1. Tampons de protocole de Google - Google est un grand utilisateur de C ++:

    Les tampons de protocole sont un moyen de coder des données structurées dans un format efficace mais extensible. Google utilise des tampons de protocole pour presque tous ses protocoles RPC internes et formats de fichiers.

  2. Apache Thrift (anciennement par Facebook):

    Thrift est un cadre logiciel pour le développement évolutif de services multilingues. Il combine une pile logicielle avec un moteur de génération de code pour créer des services qui fonctionnent de manière efficace et transparente entre C ++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C #, Cocoa, JavaScript, Node.js, Smalltalk et OCaml.

un nerd payé
la source
Les tampons de protocole de Google sont trop lents pour les jeux en temps réel à plus grande échelle. Cependant, je les ai trouvés assez sympas pour le prototypage et les petits numéros de joueurs à cause du versioning. Comme d'habitude, votre profileur racontera la vraie histoire.
Patrick Hughes
Eh bien, ils sont assez bons pour Google, et Google évolue assez bien, et ils ont bien fonctionné lorsque je les ai utilisés. C'est pourquoi je les ai recommandés.
un nerd payé le
Google ne nécessite pas de performances en temps réel. Google a besoin de fiabilité et de disponibilité, les deux étant bien servis par les tampons de protocole. La complexité de toutes ces versions de secours et de génération de code passe-partout ajoute des frais généraux et lorsque vous envoyez et recevez 1000 mises à jour à des intervalles de 50 à 100 ms, cela s'ajoute. Profil d'un tampon de protocole plusieurs versions anciennes contre un sérialiseur codé spécifique aux données à portée de main. @ Indeed005 a l'essentiel.
Patrick Hughes
+1, car bien que ces formats soient un peu trop grands et lents pour la plupart des jeux en temps réel ou à large bande passante (en raison de la présence d'informations supplémentaires qui vous permettent de reconstruire des paquets arbitrairement complexes), cela ne veut pas dire qu'ils ne sont pas utiles dans certains jeux, par exemple. au tour par tour. Si l'optimisation de chaque ressource n'est pas nécessaire, ces formats peuvent vous faire gagner beaucoup de temps et ils sont certainement plus efficaces que JSON.
Kylotan