Je vais aborder cela à partir d'une discussion de haut niveau et ensuite travailler vers vos questions. Dans un souci de divulgation, je n'ai aucune expérience personnelle de l'utilisation de socket.io mais beaucoup d'exposition à l'espace problématique en ce qui concerne les MMORPG.
La conception de l'architecture réseau d'un moteur MMORPG et / ou la sélection d'un middleware ou d'un projet open source pour fournir la fonctionnalité est l'une des décisions les plus difficiles influencée par la conception du jeu, le budget et l'expertise technique de l'équipe. Le choix ultime influencera d'autres décisions architecturales (et parfois aussi des décisions de conception).
En tant que développeurs de MMORPG, nous prévoyons un grand succès (souvent également connu sous le nom de succès catastrophique) où un grand nombre déclenche des voyants et des sirènes. L'un des grands nombres effrayants qui survient se trouve dans des algorithmes qui sont N-carrés (N ^ 2 ci-après), dans votre question, la première chose qui m'a sauté aux yeux est que cela ressemblait à la conception appelée pour qu'une entité diffuse des informations à toutes les autres entités connectées. Ceci est l'exemple classique d'un problème N ^ 2.
Les MMO abordent généralement les problèmes de N ^ 2 en s'attaquant au problème de plusieurs manières différentes; systèmes de conscience (situationnels, spatiaux, etc.) où une entité est au courant d'un sous-ensemble de toutes les autres entités, partitionnant les joueurs en différents "fragments", partitionnant les joueurs en "zones" et / ou instanciant, mettant en œuvre des mécanismes de jeu qui découragent trop les joueurs de se rassembler (tempêtes de téléportation d'Asheron Call), etc.
La plupart des MMORPG et de nombreux moteurs FPS ont des architectures de réseau assez sophistiquées qui prennent en charge une variété de fonctionnalités, notamment:
- voies de communication fiables et non fiables (TCP, implémentations personnalisées de paquets UDP et UDP fiables)
- mise en forme de la bande passante (priorisation, durée de vie, etc.)
- réplication automatique des données de terrain / viables et des appels de fonction
- ensembles atomiques de données (c'est-à-dire données communiquées ensemble)
- mises à jour discrètes (c'est-à-dire où chaque transition est importante)
- correction de latence
- une variété d'astuces pour que le client se sente réactif
Je trouve que la documentation de mise en réseau Unreal et la documentation de mise en réseau de valve fournissent une bonne introduction sur une variété de problèmes.
Alors, maintenant, abordons les questions.
Serait-ce une meilleure idée de les «collecter» et de les diffuser, disons, une fois par 1/10 de seconde?
Il est difficile de fournir une réponse simple oui ou non ici ... car cela dépend de l'échelle (nombre d'entités observatrices), de la fréquence des mises à jour et de la taille des mises à jour. Par exemple, les collecter tous pourrait être terriblement faux si la taille des mises à jour pouvait faire exploser un tampon quelque part.
Le client pour les jeux MMORPG et FPS est généralement conçu de telle sorte qu'il visualise quelque chose qui "semble" correct même s'il ne reçoit pas de mise à jour pour beaucoup plus de cadres de mise à jour que ce qui est "normal". Lorsque vous utilisez une communication non fiable (UDP), vous pouvez vous attendre à perdre un certain nombre de mises à jour dans le vide, les clients peuvent compenser cela en envoyant des mises à jour plus fréquentes que celles qui pourraient être utilisées avec un transport fiable.
D'après un examen rapide de la documentation socket.io, il semble qu'il prend en charge les voies de communication à la fois fiables et non fiables (volatiles dans sa terminologie).
J'aborderais cela en abordant, à quelle fréquence les mises à jour sont-elles nécessaires ...
Si un joueur se déplace en ligne droite à un taux constant, une fréquence de mise à jour inférieure est très bien car les clients observateurs peuvent prédire avec une grande précision où le joueur sera à tout moment. Lorsqu'un joueur tourne dans un cercle serré ou fait des changements de direction rapides, des mises à jour beaucoup plus fréquentes sont nécessaires. Inversement, lorsqu'un joueur ne bouge pas du tout, il n'y a aucune raison d'envoyer des mises à jour de mouvement.
Quoi qu'il en soit, il n'est probablement pas (généralement) nécessaire d'envoyer des mises à jour de chaque trame du client au serveur. Le serveur lui-même peut choisir d'envoyer des messages à chaque trame où il se trouve, ou de les retarder (voir mise en forme de la bande passante, hiérarchisation et mise à jour de la durée de vie).
D'autres types de mises à jour ont des caractéristiques différentes ... par exemple, considérez un champ "santé" qui est modifié lorsqu'un joueur ou une créature est endommagé. Une façon de l'implémenter consiste à diffuser chaque modification immédiatement lorsqu'elle se produit, mais cela entraîne un gaspillage de traitement et de bande passante si la valeur est modifiée plusieurs fois dans une trame ou des trames consécutives (les architectures de réseau qui implémentent la mise en forme de la bande passante résolvent ce problème en fusionnant les mises à jour vers seule la plus récente est envoyée à un client observateur lorsqu'il dispose d'une bande passante disponible).
le client doit-il envoyer de nombreux messages différents (exp acquis, cliqué sur l'élément) dès qu'ils se produisent ou plutôt un seul collecté?
Encore une fois, aucune réponse simple oui ou non ne fonctionnera ici. Selon ce que vous entendez précisément par collecté ... les deux peuvent être corrects dans des circonstances différentes et dépendent également de la mise en œuvre de la couche réseau.
La collecte de messages pour une entité spécifique à envoyer comme un seul message peut (en fonction de l'implémentation) réduire la surcharge de bande passante pour l'envoi d'un message (en réduisant vos coûts) à l'inverse (en fonction de l'implémentation, comme les mappages de champs / valeurs communiqués par des chaînes) peut augmenter la bande passante par rapport à un type de message spécifique plus simple.
En examinant la documentation socket.io, il me semble que la surcharge des messages est à l'extrémité supérieure du spectre, ce qui favoriserait la collecte de mises à jour à envoyer sous forme de message agrégé plutôt que de nombreuses mises à jour uniques.
Je recommanderais de revoir toutes les mises à jour que vous envisagez de répliquer, par exemple la plupart des MMORPG et FPS ne prennent pas la peine d'envoyer le joueur cliqué sur X événements aux clients observateurs, sauf si cela entraînerait un changement d'état pour un objet dont ils étaient également au courant. .
Voici un exemple concret : RuneScape "coche" une fois toutes les ~ 0,6 seconde. Vous pouvez en lire plus ici . J'imagine que cela simplifie les choses de leur point de vue, car dans leurs scripts, ils spécifient probablement les délais et les retards en ticks au lieu de millisecondes. Et bien sûr, avec un taux de tick aussi élevé / lent, les utilisateurs avec des connexions lentes ne sont pas très désavantagés par rapport aux autres joueurs.
la source
Une façon de travailler consiste à dissocier les changements de données réels de la diffusion de ces changements. Lorsqu'un objet change, effectuez la modification et définissez un indicateur «sale». Lorsque vient le temps de diffuser les modifications, n'envoyez que des données pour les objets marqués et effacez l'indicateur. Les valeurs qui sont modifiées plusieurs fois sont automatiquement fusionnées ici, car votre mise à jour envoie uniquement l'état au moment de la diffusion. Vous pouvez également marquer des sous-objets ou des propriétés individuelles afin de ne diffuser que ce qui a changé.
Oh, et généralement, vous n'envoyez pas les images d'animation au serveur ou aux autres clients. L'état d'animation précis est généralement considéré comme un détail de présentation et laissé à chaque client à résoudre, et à la place, assurez-vous simplement que chaque client sait quelle animation est en cours de lecture.
la source