Est-ce la bonne architecture pour notre jeu mobile MMORPG?


14

Ces jours-ci, j'essaie de concevoir l'architecture d'un nouveau jeu mobile MMORPG pour mon entreprise. Ce jeu est similaire à Mafia Wars, iMobsters ou RISK. L'idée de base est de préparer une armée pour combattre vos adversaires (utilisateurs en ligne).

Bien que j'aie déjà travaillé sur plusieurs applications mobiles, c'est quelque chose de nouveau pour moi. Après beaucoup de lutte, j'ai trouvé une architecture qui est illustrée à l'aide d'un organigramme de haut niveau:

Diagramme de flux de haut niveau

Nous avons décidé d'opter pour le modèle client-serveur. Il y aura une base de données centralisée sur le serveur. Chaque client aura sa propre base de données locale qui restera synchronisée avec le serveur. Cette base de données sert de cache pour stocker des éléments qui ne changent pas fréquemment, par exemple des cartes, des produits, des stocks, etc.

Avec ce modèle en place, je ne sais pas comment résoudre les problèmes suivants:

  • Quelle serait la meilleure façon de synchroniser les bases de données serveur et client?
  • Un événement doit-il être enregistré dans la base de données locale avant de le mettre à jour sur le serveur? Que se passe-t-il si l'application se termine pour une raison quelconque avant d'enregistrer les modifications dans la base de données centralisée?
  • Les requêtes HTTP simples serviront-elles à la synchronisation?
  • Comment savoir quels utilisateurs sont actuellement connectés? (Une façon pourrait être de demander au client d'envoyer une demande au serveur toutes les x minutes pour signaler qu'il est actif. Sinon, considérez un client comme inactif).
  • Les validations côté client sont-elles suffisantes? Sinon, comment annuler une action si le serveur ne valide pas quelque chose?

Je ne sais pas si c'est une solution efficace et comment elle évoluera. J'apprécierais vraiment que les personnes qui ont déjà travaillé sur de telles applications puissent partager leurs expériences qui pourraient m'aider à trouver quelque chose de mieux. Merci d'avance.

Information additionnelle:

Le côté client est implémenté dans le moteur de jeu C ++ appelé marmalade. Il s'agit d'un moteur de jeu multiplateforme qui signifie que vous pouvez exécuter votre application sur tous les principaux systèmes d'exploitation mobiles. Nous pouvons certainement réaliser le filetage et qui est également illustré dans mon organigramme. Je prévois d'utiliser MySQL pour le serveur et SQLite pour le client.

Ce n'est pas un jeu au tour par tour donc il n'y a pas beaucoup d'interaction avec les autres joueurs. Le serveur fournira une liste de joueurs en ligne et vous pouvez les combattre en cliquant sur le bouton de bataille et après une certaine animation, le résultat sera annoncé.

Pour la synchronisation de la base de données, j'ai deux solutions en tête:

  1. Stockez l'horodatage de chaque enregistrement. Gardez également une trace de la dernière mise à jour de la base de données locale. Lors de la synchronisation, sélectionnez uniquement les lignes qui ont un horodatage supérieur et envoyez-les à la base de données locale. Conservez un indicateur isDeleted pour les lignes supprimées afin que chaque suppression se comporte simplement comme une mise à jour. Mais j'ai de sérieux doutes sur les performances car pour chaque demande de synchronisation, nous devrons analyser la base de données complète et rechercher des lignes mises à jour.
  2. Une autre technique peut consister à conserver un journal de chaque insertion ou mise à jour effectuée sur un utilisateur. Lorsque l'application cliente demande une synchronisation, accédez à ce tableau et découvrez quelles lignes de quel tableau ont été mises à jour ou insérées. Une fois ces lignes transférées avec succès au client, supprimez ce journal. Mais je pense à ce qui se passe si un utilisateur utilise un autre appareil. Selon le tableau des journaux, toutes les mises à jour ont été transférées pour cet utilisateur, mais en réalité cela a été fait sur un autre appareil. Il nous faudra donc peut-être aussi garder une trace de l'appareil. La mise en œuvre de cette technique prend plus de temps, mais ne sait pas si elle exécute la première.

2
Je voudrais prendre une évaluation de MsSQL et jouer avec - je ne suis pas sûr de ce que MySQL fournit en termes de réplication, mais MsSQL 2008 R2 vous donne une CHARGE de techniques de réplication (5-6) à choisir. Juste une pensée, ne détestant pas MySQL ou quoi que ce soit, mais Microsoft est vraiment bon en clustering.
Jonathan Dickinson

1
@Jonathan Dickinson: Il y a un cluster MySQL: P
Coyote

1
Regardez également PostgreSQL - la version 9 a une technologie de réplication, PostgreSQL a une excellente réputation de longue date pour gérer très bien les charges lourdes, de nombreux développeurs semblent être des fous d'optimisation, et surtout c'est open source et complètement gratuit pour toutes les utilisations (y compris commercial).
Randolf Richardson

le fait est que nous ne pouvons pas utiliser la même base de données côté client et côté serveur. Je pense que la base de données la plus appropriée pour le client serait SQLite afin qu'elle ne consomme pas beaucoup de ressources car l'application s'exécuterait sur des appareils mobiles. Alors que la personne travaillant sur la partie serveur ne connaît que MySQL, c'est pourquoi nous l'avons choisi. J'ai également entendu dire que la plupart des jeux MMO utilisent la base de données MySQL.
umair

1
@umair: Le côté client peut être SQLite (sur l'iPhone par exemple, il est déjà disponible). Mais vous pouvez choisir de simplement stocker les données pertinentes dont vous avez besoin dans un fichier (ce que fait SQLite de toute façon) et ne pas vous soucier de la base de données côté client du tout comme vous le feriez avec une application Javascript.
Coyote

Réponses:


15

Si ce n'est pas un jeu "en temps réel" dans le sens où les joueurs n'ont pas besoin de voir le résultat immédiat des actions d'un autre joueur sur une scène de jeu, alors vous devriez être d'accord avec les requêtes HTTP. Mais gardez à l'esprit les frais généraux de HTTP.

Cela dit, l'utilisation de HTTP ne vous évitera pas de concevoir votre protocole de communication avec soin. Mais si vous êtes en charge à la fois du côté serveur et côté client, vous avez de la chance car vous pouvez ajuster le protocole quand vous en avez besoin.

Pour synchroniser entre la base de données maître et la base de données client, vous pouvez utiliser le protocole de transport auquel vous avez accès, HTTP ou autres. La partie importante est la logique derrière la synchronisation. Pour une approche simple, regroupez simplement à partir du serveur toutes les dernières modifications nécessaires au client depuis le dernier horodatage dans la base de données client. Appliquez-le à la base de données client et continuez avec cela. Si vous avez plus de changements côté client, téléchargez-le s'il est toujours pertinent, sinon jetez-le.

Certains jeux n'utilisent même pas de base de données locale, ils gardent simplement une trace de l'état en regroupant les informations pertinentes du serveur en cas de besoin.

Si la perte d'événements locaux n'est pas acceptable, alors oui, vous devez disposer d'un stockage local et enregistrer sur ce stockage aussi souvent que possible. Vous pouvez essayer de le faire avant chaque envoi réseau.

Pour vérifier les utilisateurs actifs, nous avons utilisé pour ping avec HTTP toutes les 20 secondes sur un jeu réussi ... Cette valeur a augmenté immédiatement car les serveurs ont été surchargés :( L'équipe du serveur n'a pas pensé au succès. Je vous dirais donc d'ajouter un message ou une sorte d'en-tête spécial dans votre protocole de communication qui vous permettra de reconfigurer vos clients (pour l'équilibrage de charge de fréquence ping et d'autres valeurs liées à la communication).

Les validations côté client suffisent si cela ne vous dérange pas les tricheurs, les pirates et autres scripts automatisés attaquant votre jeu. Le serveur peut simplement refuser votre action et envoyer un message d'erreur avec quelques détails. Vous gérez ce message d'erreur en annulant les modifications locales ou en ne les appliquant pas à votre stockage local si vous pouvez attendre que le serveur réponde pour enregistrer réellement les modifications.

Nous avons utilisé le modèle de commande dans notre jeu pour permettre une restauration simple et efficace des actions utilisateur ayant échoué ou non valides. Les commandes ont été jouées localement envoyées aux pairs ou traduites dans le message du serveur, puis appliquées aux pairs ou vérifiées sur le serveur, en cas de problème, les commandes n'étaient pas lues et la scène du jeu revenait à l'état initial avec une notification.

Utiliser HTTP n'est pas une mauvaise idée car, plus tard, il vous permettra de vous intégrer très facilement avec les clients Flash ou HTML5, il est flexible, vous pouvez utiliser n'importe quel type de langage de script de serveur et avec des techniques de base d'équilibrage de charge, ajoutez plus de serveurs plus tard sans trop d'effort pour faire évoluer votre backend.

Vous avez beaucoup à faire mais c'est un travail amusant ... Alors profitez-en!


3
+1 pour 'HTML étant probablement assez bon' car c'est probablement :).
Jonathan Dickinson

1
@Coyote: merci d'avoir pris du temps et d'avoir fourni une réponse aussi détaillée. Aimez vraiment votre idée HTTP pour fournir un support à d'autres clients.
umair

1
Que la force soit avec vous :)
Coyote

5

Quelle serait la meilleure façon de synchroniser les bases de données serveur et client?

Le moyen le plus simple consiste à implémenter la base de données en tant que fichier unique que vous pouvez transférer. Si vous essayez de comparer les différences entre les bases de données, c'est un monde difficile, et je parle d'expérience.

Notez que votre serveur ne doit pas faire confiance aux décisions prises par le client sur la base de cette base de données locale, car le client peut la modifier. Il doit exister uniquement pour les détails de présentation.

Un événement doit-il être enregistré dans la base de données locale avant de le mettre à jour sur le serveur?

Et si le serveur décide que l'événement ne s'est jamais produit? Le client ne devrait de toute façon pas prendre de décisions concernant les événements, car il ne peut pas faire confiance au client.

Je remarque que vous parlez également de la base de données locale de deux manières: une, pour "les choses qui ne changent pas fréquemment" et deux, pour les événements. Ceux-ci ne devraient pas vraiment être dans la même base de données, pour les raisons ci-dessus - vous ne voulez pas commencer à essayer de fusionner ou de différencier des lignes de données individuelles dans des bases de données. Par exemple, l'intégrité référentielle devient un problème lorsqu'un client a une référence à un élément que vous décidez de supprimer du serveur. Ou si le client change une ligne et que le serveur change une ligne, quelle modification est prioritaire et pourquoi?

Les requêtes HTTP simples serviront-elles à la synchronisation?

Oui, à condition qu'ils soient assez rares ou petits. HTTP n'est pas économe en bande passante, alors gardez cela à l'esprit.

Comment savoir quels utilisateurs sont actuellement connectés? (Une façon pourrait être de demander au client d'envoyer une demande au serveur toutes les x minutes pour signaler qu'il est actif. Sinon, considérez un client comme inactif).

Si vous utilisez un protocole transitoire comme HTTP, alors c'est une idée raisonnable. Lorsque le serveur reçoit un message d'un client, vous pouvez mettre à jour l'heure de la «dernière vue» pour ce client.

Les validations côté client sont-elles suffisantes? Sinon, comment annuler une action si le serveur ne valide pas quelque chose?

Non pas du tout. Le client est entre les mains de l'ennemi. Comment annuler une action dépend entièrement de ce que vous considérez comme une action et de ses effets. L'itinéraire le plus simple consiste à ne pas mettre en œuvre l'action du tout jusqu'à ce que le serveur réponde pour l'autoriser. Une route légèrement plus délicate consiste à s'assurer que chaque action a une capacité de restauration pour annuler l'action et mettre en cache toutes les actions non acquittées sur le client. Lorsque le serveur les reconnaît, supprimez-les du cache. Si une action est refusée, annulez chaque action dans l'ordre inverse jusqu'à et y compris celle refusée.


1
Vous avez réussi. Vous avez raison de synchroniser les bases de données +1 Le client ne doit jamais modifier la base de données par lui-même ... Appelez simplement les fonctions qui s'exécutent sur le serveur et mettez à jour la base de données en utilisant les logiques du jeu, puis récupérez les résultats.
Coyote

@Kylotan: merci d'avoir signalé les flux dans mon modèle
umair

@Kylotan: "Le moyen le plus simple est d'implémenter la base de données en tant que fichier unique que vous pouvez transférer. Si vous essayez de comparer les différences entre les bases de données, c'est un monde difficile, et je parle d'expérience." cela signifie que toutes les données sont téléchargées à chaque lancement de l'application. Il y a des choses qui ne changeront pas très fréquemment et il peut donc ne pas être acceptable de les télécharger à chaque fois. Cela augmentera également considérablement le temps de lancement de l'application. Des pensées?
umair

Si les données sont petites, le problème disparaît. Je serais surpris si votre base de données centralisée de données statiques était très volumineuse. Mais de toute façon, si vous divisez les données statiques en parties plus petites, vous n'avez qu'à envoyer celles qui ont changé depuis la dernière fois. Bien sûr, vous pouvez le faire sur une base par ligne, si vous souhaitez conserver une heure de modification dans chaque ligne. En général, je préfère avoir des données statiques en dehors de la base de données relationnelle, pour me permettre de les écraser facilement.
Kylotan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.