Existe-t-il un moyen de rendre un monde dynamique tel qu'un MMORPG évolutif horizontalement?

11

Imaginez un monde ouvert de plus de 500 joueurs avec des données changeant aussi rapidement que 20 mises à jour / joueur / seconde. La dernière fois que j'ai travaillé dans un MMORPG similaire, il utilisait SQL, donc il était évident qu'il ne pouvait pas interroger la base de données tout le temps. Au lieu de cela, il a chargé tous les lecteurs de la base de données en mémoire en tant qu'objets C ++ et les a utilisés. Autrement dit, il a été mis à l'échelle verticalement. Serait-il possible de rendre ce serveur évolutif horizontalement à la place? Existe-t-il une base de données conçue pour prendre en charge ce nombre de mises à jour simultanément?

MaiaVictor
la source
Pourquoi voulez-vous mettre à jour le lecteur dans la base de données 20 fois / seconde?
Balon
@Balon, c'est là que je suis confus. Si je ne le mets pas à jour dans la base de données, juste dans la mémoire, alors j'aurai différents états entre différentes machines. Mais je suppose que les mises à jour de la base de données ont des frais généraux énormes, donc cela ne fonctionnera pas vraiment pour ce nombre de mises à jour?
MaiaVictor
2
Si vous pensez vraiment, vraiment que différentes machines (ou même processus) ont besoin de mises à jour à 20 Hz sur des centaines d'objets, contournez complètement la base de données et utilisez directement un système de messagerie. Mais ce que vous pensez vraiment, vraiment que vous voulez n'est pas ce que vous voulez vraiment. Ce que vous voulez, c'est avoir une portée saine de qui a besoin de savoir quoi, puis avoir un moyen de transporter proprement les objets entre les étendues en plus de cela. Vous devriez répondre à la question de savoir pourquoi vous avez besoin de mises à jour à 20 Hz entre différentes machines pour obtenir d'excellentes réponses, quelqu'un pourrait penser à une nouvelle façon d'aborder le problème.
Patrick Hughes
@PatrickHughes Je ne sais pas de quoi j'ai besoin, j'explique simplement le fonctionnement du jeu. Les personnages se déplacent de 2 à 3 tuiles / seconde. Un joueur qui chasse peut être entouré de quelques monstres, donc au moins 10 tuiles / joueur / seconde. Ensuite, il y a des objets en décomposition sur le sol, sur le sac à dos du joueur. Il y a des attaques en direction du joueur, il y a des attaques en direction du monstre. Il y a la santé qui se dégrade, le mana utilisé, les minuteries qui infligent des dégâts de poison au joueur. Donc, les choses changent très vite. Ceci est la conception du jeu. Comment une telle conception pourrait-elle être mise à l'échelle verticalement?
MaiaVictor
1
J'ai vu cela sur HackerNews il y a quelque temps: paralleluniverse.co Ils travaillent sur une base de données qui fait tout le travail de segmentation / distribution spatiale pour vous. Je suppose que sous le capot, ils font toutes les choses dans les réponses ci-dessous.
tire

Réponses:

17

Cas de test de 500 joueurs communiquant tous, soit 250 000 flux d'informations volant à 20 Hz. La bande passante interne pour cela serait, en supposant 100 octets par message, environ 500 Mo / s. Cela semble ambitieux. Surtout entre les processus.

Si vous séparez les joueurs en groupes de 100, cela diminue à 20 Mo / s, etc. C'est pourquoi les MMO ont des zones, et dans ces zones de petites bulles d'influence, et ainsi de suite jusqu'à ce que la bande passante devienne raisonnable.

Le problème d'origine peut être dit que si 10 personnes partagent toutes des informations en temps réel, mais que vous voulez 500 toutes , c'est une croissance exponentielle des liens de communication et comment pouvons-nous contourner cela . J'ai peur qu'il n'y ait aucune balle magique dont j'aie jamais entendu parler qui puisse magiquement faire disparaître la progression géométrique.

N'utilisez pas de base de données pour communiquer, c'est à cela que sert la messagerie. Utilisez la base de données pour appliquer les transactions et stocker les informations que vous ne voulez pas que les joueurs perdent. La plupart des MMO que je connais ne mettent à jour la base de données qu'avec des informations dynamiques sur le joueur toutes les 1 à 10 minutes, ou à des endroits pratiques comme des transitions de zone ou l'entrée dans des zones "sûres" dans la conception.

Vous devrez peut-être repenser les besoins du jeu pour chaque joueur, peu importe la distance, pour avoir des mises à jour en temps réel du contenu du sac à dos de chaque autre joueur.

Modifiez également le modèle de mise à jour de 20 Hz à une vitesse basée sur la distance, une personne à 1 mile de distance n'a pas besoin de savoir que vous vous êtes déplacé d'un pied à exactement 230,6 secondes, puis d'un autre pied à 231,4 secondes, ils peuvent gérer le déplacement de 15 pieds tous les 10 secondes.

Patrick Hughes
la source
Réponse impressionnante et informative, merci. Mais je pourrais ajouter que tandis que le monde change à un rythme très rapide, un joueur ne peut voir que les autres joueurs juste à côté de lui. Je ne le vois pas comme géométrique - 500 joueurs envoient des informations au serveur; le serveur envoie périodiquement des informations à ces 500 joueurs. C'est linéaire, comme je vois. Mais le point principal est sur le 4ème paragraphe: si j'utilise uniquement la base de données pour le stockage, alors je charge les données en mémoire. Si je charge des données en mémoire dans une machine, je crée une version désynchronisée du monde. Voilà ce que je ne comprends pas.
MaiaVictor
Pour 1 client: 1 msg out + 1 msg in = 2. Pour 2 clients: 2 msg out, 2 msg in = 4. Pour 3 clients: 3 msg out, 3 msg in = 9. Et c'est ainsi. C'est comme ça: envoyez un message de statut, le serveur m'envoie le résultat et les 2 autres clients (1 entrée, 3 sorties) et 3 clients qui font tout cela (1 entrée 9 sorties). Bien qu'il semble linéaire pour un seul client sur les 3, vous pouvez le multiplier par tous les clients pour le débit total du système. En ce qui concerne la désynchronisation, même les processus sur la même boîte physique sont désynchronisés jusqu'à ce que le message d'état soit créé et envoyé, c'est juste une question d'où le tuyau se vide, RAM locale ou net.
Patrick Hughes
5

Utilisez le filtrage des zones d'intérêt. Si un monde est divisé en 3 serveurs et que la zone sur le serveur 1 est loin de la zone du serveur 3, il n'y a aucune raison pour qu'ils partagent des informations sur les entités.

De même, sur un seul serveur, envoyez uniquement les informations pertinentes aux clients. Si le joueur A se trouve à l'extrémité totalement opposée de la carte du joueur B, il n'y a aucune raison d'envoyer des mises à jour sur B à A, ou vice versa.

Lorsque vous avez plusieurs serveurs dans un monde continu, vous aurez des entités proches d'un bord sur le serveur 2 qui sont proches d'entités sur le serveur 1. Vous pouvez envoyer des mises à jour du serveur "faisant autorité" pour une entité à l'autre serveur (le cas échéant) et transférez également tous les messages au serveur faisant autorité, le cas échéant.

Oui, dans ce cas, un serveur sera légèrement obsolète pour des entités particulières. N'essayez pas de résoudre cela. Débrouille toi avec. Supposons que les entités soient un peu obsolètes. Effectuez toute logique nécessitant des informations à jour uniquement sur le serveur propriétaire des entités. Lorsqu'une entité en affecte une autre, envoyez un message et supposez que cela peut prendre plusieurs ticks de logique de jeu avant d'être traité et que votre vue soit mise à jour.

Cette conception facilite également le thread d'un seul serveur. Aucune entité ne doit en modifier directement une autre, envoyer uniquement des messages, et les caches proxy locaux par serveur / par thread doivent être supposés être légèrement obsolètes.

Par exemple, si l'entité A attaque l'entité B, ne vérifiez pas la durée de vie de B et n'envoyez pas de message de mort s'il atteint 0. Envoyez simplement un message "endommagé", laissez le serveur faisant autorité pour B le gérer, puis gérez tout message "entité morte" envoyé ultérieurement par le serveur B si l'entité A s'en soucie.

Il en va de même pour toute application non ludique de grande taille. Une base de données centrale n'est pas une technologie magique de partage instantané. Deux serveurs doivent communiquer avec les messages, de manière asynchrone, par lots, afin de maintenir un débit élevé. D'où la popularité de technologies comme AMPQ et similaires. Les bases de données sont destinées au stockage et prennent en charge la synchronisation par nécessité, ce qui leur permet d'être utilisées pour les communications, non pas parce qu'elles sont elles-mêmes destinées à la synchronisation ou à la communication.

Sean Middleditch
la source
Merci, cela a mis fin à la plupart de mes doutes. Vous m'avez également donné l'idée de séparer les serveurs par les joueurs, pas les zones - cela aurait l'air plus fluide. Chaque serveur prend en charge x joueurs. J'aime vraiment ça! Est-ce utilisé? Et aussi, il y a encore une chose. Comme je l'ai demandé ci-dessus, je viens d'apprendre une nouvelle base de données NoSQL, Couchbase. Il est censé être exactement comme CouchDB, sauf avec des vitesses d'écriture / lecture très rapides: jusqu'à 200 000 mises à jour par seconde! Peut-être que cela pourrait réellement fonctionner comme un "modèle de monde partagé en temps réel", ou pas encore?
MaiaVictor
Je n'ai aucune idée si cette technique est utilisée dans la nature en dehors du "sharding" grossier habituel des serveurs. Le simple fait de le faire par les joueurs et la zone géographique signifie que chaque serveur peut avoir besoin de connaître un très grand nombre d'entités dans un ensemble diversifié de zones, augmentant la charge du serveur et augmentant considérablement la communication inter-serveurs. Le faire par zone signifie que votre serveur peut être surchargé sur les zones surpeuplées (bien que vous puissiez diviser et joindre dynamiquement les zones dans ce cas), mais signifie que chaque serveur a un ensemble plus petit d'entités non-joueurs pertinentes et de la géométrie pour garder une trace de .
Sean Middleditch
@Dokkat: Il pourrait être possible d'avoir une sorte de "zones douces" où chaque serveur manipule principalement des joueurs dans une partie particulière du monde du jeu, mais demandez-leur de transférer le joueur de manière transparente à un autre serveur s'ils s'éloignent trop de la région de leur serveur d'origine. Il vous suffit de vous assurer que le transfert est suffisamment fluide pour que les joueurs ne le remarquent pas vraiment. Vous pouvez même essayer d'utiliser des techniques adaptatives sophistiquées pour conserver des groupes de joueurs en interaction sur le même serveur, même s'ils se trouvent juste à la limite d'une région.
Ilmari Karonen
3

Vous serez probablement intéressé par cet article sur Gamasutra , où les développeurs d' Eve Online expliquent comment il est possible de réussir un jeu avec 400 000 joueurs actifs ... dans une base de données SQL.

Liosan
la source
2

Ne pensez pas à la base de données comme une sorte de modèle mondial partagé en temps réel stockant tout sur tout à tout moment - comme vous l'avez remarqué, cela ne peut pas fonctionner.

Au lieu de cela, traitez la base de données plus comme un fichier de sauvegarde mis à jour automatiquement: vous ne mettez à jour la base de données qu'occasionnellement, comme lorsque les joueurs se connectent ou se déconnectent ou se déplacent d'une zone à une autre, ou chaque fois qu'un événement important se produit que vous ne voulez pas être perdu en cas de panne du serveur.

L'état réel du monde en temps réel doit être conservé par les serveurs de jeu, en mémoire, comme dans votre exemple d'origine. Maintenant, l'astuce pour la mise à l'échelle horizontale est que tous les serveurs n'ont pas besoin de tout savoir à chaque instant . Par exemple, si le joueur A joue dans la zone A sur le serveur A, zone B serveur exécutant B n'a généralement pas besoin de savoir ce que le joueur A a dans son sac à dos - et, si elle ne le besoin de savoir que pour une raison quelconque ( par exemple, parce que le joueur B dans la zone B lance une sorte de sort d'espionnage à distance sur A) il peut simplement demander cette information à l'autre serveur .

Cela vous oblige à attribuer des responsabilités claires aux serveurs, de sorte que lorsque le serveur B veut connaître le sac à dos du joueur A, il saura quel serveur dispose des informations faisant autorité à ce sujet. Vous voudrez probablement également inclure une sorte de mécanisme d'abonnement aux mises à jour , de sorte que, par exemple, le serveur B puisse simplement dire au serveur A " J'ai quelqu'un qui espionne le joueur A, tenez-moi au courant de tout ce qu'ils font jusqu'à ce que je vous dise le contraire. " veulent également inclure une sorte de système de diffusion mondial pour les événements mondiaux importants que les joueurs peuvent avoir besoin de savoir, peu importe où ils se trouvent; bien sûr, ces événements doivent également être enregistrés dans la base de données, mais leur diffusion active sur tous les serveurs signifie que les serveurs n'auront pas à continuer d'interroger la base de données pour les mises à jour.

Ilmari Karonen
la source
Réponse géniale! C'est précisément ce que je demandais, merci. Alors peut-être que la clé est de diviser le serveur en zones, en gardant la logique en mémoire. Pourrais-je ajouter, cependant: je viens d'apprendre une nouvelle base de données NoSQL, Couchbase. Il est censé être exactement comme CouchDB, sauf avec des vitesses d'écriture / lecture très rapides: jusqu'à 200 000 mises à jour par seconde! Peut-être que cela pourrait réellement fonctionner comme un "modèle de monde partagé en temps réel", ou pas encore?
MaiaVictor
@Dokkat non, ce ne sera pas le cas. Couchbase n'est pas magique.
Philipp
2

D'autres réponses ont fait un bon travail en indiquant comment utiliser une base de données et non pas utiliser une base de données pour la communication. Un autre aspect que vous pourriez examiner est de classer vos mises à jour en fonction de la façon dont les informations doivent être communiquées à d'autres entités. Plutôt que d'étendre la communication aux serveurs, vous pouvez distribuer votre messagerie et utiliser des mécanismes pubsub pour communiquer les mises à jour entre les entités. Par exemple, vous pouvez traiter l'emplacement différemment en fonction de vos proches:

  • Une localisation précise en temps réel peut être utile dans le rayon R
  • Des mises à jour de localisation moins précises et moins fréquentes pourraient être utiles dans le rayon 2 * R
  • Aucune information de localisation peut être nécessaire au-delà du rayon 2 * R

Vous pouvez communiquer des informations de localisation pour une entité en recherchant périodiquement des entités dans le rayon 2 * R (ou un multiple de celui-ci en fonction du taux de mise à jour et de la vitesse maximale d'une entité), et en abonnant l'entité au flux de localisation précis ou imprécis de l'autre entité.

Vous pouvez avoir différentes stratégies pour différents types d'informations, regrouper les éléments communs dans les mêmes files d'attente de messages, ou avoir différentes files d'attente pour les messages qui doivent aller à différentes entités (ou simplement les envoyer à l'ensemble d'entités le plus large et faire supprimer les messages s'ils ne sont pas utiles).

David N
la source