Architecture d'un serveur de jeu avec des salles de discussions

a marqué ce sujet comme résolu.

Bonsoir. A dire vrai je n’ai pas vraiment de question précise sur un bout de code, je vous demande plutôt de l’aide pour concevoir l’architecture d’un projet personnel. À ce titre je n’ai pas de deadline comme pour un projet étudiant.

Mon projet consiste à réaliser un serveur de jeu pour jouer au loup garou en ligne. C’est exactement le même principe que wolfy.fr ou loup garou en ligne sauf que je tiens à en faire une solution opensource et totalement gratuite. J’ai choisi C++ car j’ai vraiment envie de faire du C++ (j’en ai un peu marre du Web car je travaille sur un projet de site qui me prends beaucoup de temps et d’énergie et j’ai besoin de découvrir d’autres choses). L’intérêt du serveur est qu’il soit totalement gratuit, opensource et donc que chacun puisse le télécharger, le personnaliser etc. Un peu comme pour un serveur de jeu (Minecraft, Counter Strike etc).

Pour ce qui est du serveur il faut le voir un peu comme un serveur discord. Le créateur du serveur peut créer des salles de jeu (chaque salle ayant sa propre configuration). Ces salles ne pourront être rejoint que dans le cas où sa partie n’est pas en cours (un état soit lobby, soit in progress).

Maintenant, d’un point de vue Client :

  1. Le client se connecte à un serveur en précisant l’adresse ip et le port ;
  2. Une fois connecté il a une interface similaire à discord ;
  3. En cliquant sur une salle il la rejoint.

Je fais abstraction de la partie métier car je m’y intéresserai plus tard. Pour le moment je tiens à mettre en place ce système de serveur et salles (de chat). J’ai commencé à réfléchir à l’architecture du projet avec ce diagramme de classe mais j’avoue bloquer…

Si vous avez des conseils je suis preneur ou sinon n’hésitez pas à me demander des précisions.

+0 -0

Pour l’instant il n’y a pas une grande difficulté.

Niveau code.

Le joueur se connecte au serveur => Il reçoit la liste des salles disponible. Il sélectionne une salle => Il reçoit la liste des messages de la salles.

Quand il envoie un message, celui-ci est lié à la salle. Toutes les personnes connectées à la même salle reçoive le message.

+0 -0

J’ai choisi C++ car j’ai vraiment envie de faire du C++ (j’en ai un peu marre du Web car je travaille sur un projet de site qui me prends beaucoup de temps et d’énergie et j’ai besoin de découvrir d’autres choses).

Tu as déjà fais de la partie réseau en C++ ?

Vu que c’est un jeu du loup-garou, il n’y a pas les mêmes contraintes de réseau que dans la plupart des jeux vidéo (qui requièrent du temps réel), ce qui fait qu’ici, on peut se contenter d’une techno qui fait les choses tranquilou /TCP plutôt qu’un protocole un peu hardcode /UDP.

En d’autres termes, ça peut être une bonne idée de réaliser les communications avec grpc, parce que celui-ci peut résoudre de très nombreux problèmes avant même qu’ils ne se posent, et ça va te permettre de te concentrer sur le plus important : qu’est-ce que le serveur et les clients vont se dire, plutôt que passer du temps à coder la façon dont ils se le disent. Et en bonus ça permet d’écrire plusieurs clients dans plusieurs technos différentes.

+0 -0

Sur ton diagramme :

Serveur

C’est quoi la classe "Serveur" ? J’ai l’impression que cela rassemble en fait 2 concepts différents : un serveur au sens de discord, c’est a dire un ensemble de rooms. Ou un serveur au sens de réseau. A mon sens, cela devrait être séparé dans 2 classes.

Tu as clairement 3 domaines fonctionnels différents : la structure de données de ton "serveur" (au sens discord) avec des rooms, la partie réseau et la partie file. En termes de dépendance, la première ne doit a priori pas dépendre des 2 autres et les 2 autres doivent être indépendantes entre elles et dépendre de la première.

Room

Tu es parti sur une structure avec interface et héritage, mais ce choix n’est compréhensible juste en regardant le diagramme.

En termes d’organisation, je te conseille de formaliser dans un premier temps de formaliser tes user cases, c’est a dire de détailler "untel veut faire cela, il fait ceci, cela, ceci, etc". Pour détailler chaque action possible.

Et ne pas non plus hésiter a écrire un document de design, pour décrire rapidement les choix de conception, pourquoi tu fais un héritage sur telle classe, pourquoi tu mets tel attribut dans une classe, etc.

Pour moi, une ChatRoom et une GameRoom ont les mêmes comportements (c’est a dire que le code est le même), seuls les valeurs changent a priori.

Player

Tu as des fonctions pour gérer l’était d’un joueur (sleep, dead, etc). Mais cela implique que si tu veux changer l’état ou les règles du jeu, tu as besoin de recompiler et redistribuer ton application, ce qui est lourd.

C’est pour ça que dans les jeux vidéos, on sépare souvent le gameplay (souvent fait dans des langages de scripts) et le moteur de jeu (avec des langages bas niveaux souvent).

A mon sens, tu devrais faire pareil. En pratique, cela veut dire par exemple que l’état soit codé dans une liste de chaines de caractères (par exemple dans un JSON) et ta classe ne contient qu’un attribut qui index cette liste. Et de la même façon, tu peux t’amuser si tu veux a chercher un moyen de coder les règles de transition entre états dans un JSON ou un langage de script rudimentaire.


Pour généralement, sur la démarche, en plus de la rédaction des user cases et du design document, je te conseille de travailler par une approche itérative de décomposition en sous problèmes.

Concrètement, cela veut dire par exemple de commencer par se poser la question du découpage au niveau de l’application et découper dans un premier temps en 3 parties (comme je l’ai fait avant) : la partie rooms, la partie réseau et la partie fichier. (Et tu pourras probablement ajouter une partie GUI).

Puis tu te poses la question du découpage de chacune de ces parties en sous parties. Puis ces sous-parties en sous-sous-parties. Et ainsi de suite. Et ton diagramme doit permettre de visualiser ce découpage.

Et a chaque niveau d’analyse, tu respectes au maximum les principes de conception : SOLID, demeter, découplage, KISS, etc. Et j’aime bien les approches Agile et utiliser GitHub.

+1 -0

Nos réponses ont plus ou moins de sens en fonction de :

  • Si tu connais le C++ ou pas
  • Si tu connais un langage de programmation ou pas
  • Si tu t’y connais un peu en reseau ou pas

Ce que je ne comprend pas, c’est où bloques-tu ?

PS: Ton diagrammes est illisible. Google fait n’importe-quoi. Mieux vaut que tu glisses l’image directement sur le forum. Le forum supporte l’upload d’image en Drap&Drop.

+0 -0

J’ai bien lu tout vos messages, merci pour vos retours.

Vu que c’est un jeu du loup-garou, il n’y a pas les mêmes contraintes de réseau que dans la plupart des jeux vidéo (qui requièrent du temps réel), ce qui fait qu’ici, on peut se contenter d’une techno qui fait les choses tranquilou /TCP plutôt qu’un protocole un peu hardcode /UDP.

Je pensais en effet partir sur quelque chose basé sur TCP. J’ai jeté un œil vite fait au Get Started, ça à l’air sympa. Cependant je n’en suis pas encore à désigner l’approche réseau :)

@gbdivers pour la classe Serverje sentais aussi que je sortais loin du cadre SRP. Qu’il y avait bien d’un côté une classe qui avait pour but la communication réseau, qui devrait s’appeler Server et qui serait responsable de la communication réseau (accepter de nouvelles connexions, recevoir (pas traiter) les données, terminer une connexion, envoyer des données). J’ai même des doutes sur le fait que ça soit SRP de recevoir/envoyer des données et gérer les connexions / déconnexion des clients. Après j’exagère peut-être un peu non ?

Pour la classe qui sonne comme Server Discord je ne sais pas du tout comment la nommer. Pour moi il s’agit d’une classe qui s’assimile à une collection. D’expérience je m’abstiens de la nommer RoomsManager car je me suis déjà fait taper sur les doigts pour avoir utilisé un terme si peu concret. En réalité, pour moi, l’ensemble des salles pourrait être contenues dans un std::vector<Room>. Dans ce cas je me pose la question suivante : de quoi serait responsable une telle classe ?

Room

Pour les classes room je ne sais pas trop si on essaiera de mettre en place des salles textuelles et des salles vocales. Dans l’optique de ne pas me bloquer je pensais faire une interface RoomInterface et l’implémentation de base TextRoom, laissant la possibilité plus tard d’ajouter VocalRoom. Ceci dit les noms choisis sur le diagramme sont mauvais et mon approche reste peut-être vaseuse.

Sinon, la responsabilité d’une classe Room c’est de lancer une partie pour un ensemble de joueurs (ceux qui ont rejoint la salle, la partie étant gérée par un script donc). À ce titre elle doit avoir une collection de joueurs et être capable de communiquer avec ses joueurs. Dans ce cas, est-ce que l’utilisation d’un singleton Server est appropriée (je peux appeler Server partout dans mon code, ce qui sonne moyennement bien et plutôt comme quelque chose de bizarre) ? Est-ce que la classe devrait avoir un pointeur/une référence vers le serveur dès sa création (dans ce cas, puisqu’il y a sûrement unicité du serveur, ça ressemble à du singleton caché) ? Il existe sûrement d’autres façon plus joli pour communiquer l’état de la partie à travers le réseau.

Le raisonnement est le même pour les données reçues, envoyées par les clients.

Player

Alors si je ne me trompe pas, on aborde là le code métier ? C’est la partie que je pensais attaquer vraiment plus tard. C’est vrai que de coder tout le gameplay en dur est une mauvaise idée je m’en rend compte. Il faudrait que je me renseigne sur comment écrire l’état d’une partie sur un fichier. Pour chaque partie j’essaierai d’ailleurs de garder un historique des états et des communications (enfin je dis ça mais je ne le ferais peut-être pas avant très longtemps).

Après, chaque partie suit un schéma constant normalement : la nuit, on appelle des rôles chacun leur tour (ordre non aléatoire) pour qu’il réalise une action (tuer un joueur, connaître le rôle d’un joueur etc). Le jour (une fois que tout les rôles de la nuit ont joué) on commence par regarder si certains rôles ont une action à réaliser (le dictateur peut déclencher la nuit un coup d’état ce qui lui permet de tuer un joueur au lever du jour, le chasseur tue un joueur de son choix le jour quand il meurt) puis on invite les joueurs à débattre puis à voter un joueur qui sera pendu. Finalement, j’ai l’impression que la seule partie variable de tout ce système ce sont les rôles. Est-ce que mettre en place un système de plugins pour les rôles ne serait pas suffisant finalement ?

Plus généralement je devrais donc :

  • essayer de retravailler en me basant sur les modules Network, Rooms, File (où file correspond en fait à la gestion des états d’une partie ?) ;
  • définir des cas d’utilisations clairs ;
  • penser à créer un système plus souple pour le gameplay.

Pour le moment, j’aimerai déjà réussir à faire l’aspect réseau et salles. Même pas de chat ou quoi, juste que les utilisateurs puissent se connecter à une salle, voir les autres utilisateurs connecté au serveur et ceux situés dans la même salle qu’eux.

EDIT : Essai d’un début de use case diagramm!

+0 -0

@gbdivers pour la classe Serverje sentais aussi que je sortais loin du cadre SRP. Qu’il y avait bien d’un côté une classe qui avait pour but la communication réseau, qui devrait s’appeler Server et qui serait responsable de la communication réseau (accepter de nouvelles connexions, recevoir (pas traiter) les données, terminer une connexion, envoyer des données). J’ai même des doutes sur le fait que ça soit SRP de recevoir/envoyer des données et gérer les connexions / déconnexion des clients. Après j’exagère peut-être un peu non ?

Oui, tu auras probablement plus de choses a mettre dans la partie réseau et tu devras découper en plusieurs classes. Mais il faut faire une analyse fonctionnelle plus détaillée de cette partie pour faire la conception.

Pour la classe qui sonne comme Server Discord je ne sais pas du tout comment la nommer. Pour moi il s’agit d’une classe qui s’assimile à une collection. D’expérience je m’abstiens de la nommer RoomsManager car je me suis déjà fait taper sur les doigts pour avoir utilisé un terme si peu concret. En réalité, pour moi, l’ensemble des salles pourrait être contenues dans un std::vector<Room>. Dans ce cas je me pose la question suivante : de quoi serait responsable une telle classe ?

Tu as aussi les fonctions createRoom et removeRoom. Tu auras probablement aussi besoin de chercher la liste des rooms, etc.

Donc tu as assez pour faire une classe indépendante.

Pour les classes room je ne sais pas trop si on essaiera de mettre en place des salles textuelles et des salles vocales. Dans l’optique de ne pas me bloquer je pensais faire une interface RoomInterface et l’implémentation de base TextRoom, laissant la possibilité plus tard d’ajouter VocalRoom. Ceci dit les noms choisis sur le diagramme sont mauvais et mon approche reste peut-être vaseuse.

Si tu design maintenant le support des canaux vocaux, integre ca dans le design.

Par contre, si tu ne fais pas l’analyse fonctionnelle maintenant, alors ne l’intègre pas "au cas où".

Tu as en gros 2 approches pour concevoir un programme : soit l’approche classique (cycle en V), soit Agile. (J’aime simplifier a l’extreme).

Dans le premier cas, tu dois faire complètement l’analyse fonctionnelle, donc cela n’a pas de sens de faire un truc "au cas où".

Dans le second cas, tu fais l’analyse au moment où tu en as besoin. Mais pour cela, il faut avoir un conception qui intègre le fait que la conception et l’implémentation va évoluer.

Pour être clair, cela veut dire qu’on ajoute pas dans la conception quelque chose d’inconnu (une fonctionnalité "au cas où"), mais on intègre une contrainte connue (le fait que la conception va évoluer). C’est tres different.

Sinon, la responsabilité d’une classe Room c’est de lancer une partie pour un ensemble de joueurs (ceux qui ont rejoint la salle, la partie étant gérée par un script donc). À ce titre elle doit avoir une collection de joueurs et être capable de communiquer avec ses joueurs. Dans ce cas, est-ce que l’utilisation d’un singleton Server est appropriée (je peux appeler Server partout dans mon code, ce qui sonne moyennement bien et plutôt comme quelque chose de bizarre) ? Est-ce que la classe devrait avoir un pointeur/une référence vers le serveur dès sa création (dans ce cas, puisqu’il y a sûrement unicité du serveur, ça ressemble à du singleton caché) ? Il existe sûrement d’autres façon plus joli pour communiquer l’état de la partie à travers le réseau.

Le raisonnement est le même pour les données reçues, envoyées par les clients.

C’est quelque chose qui doit ressortir de ton analyse. Il n’y a pas de réponse générique. Créer un singleton veut dire que tu pourras accéder à ta classe partout, mais cela veut dire aussi qu’il faudra un mecanisme de dispatch, pour envoyer les infos aux bons endroits. Au final, tu risques de regler un probleme (l’accès a cette classe partout) mais créer un autre problème (le dispatch des messages).

Une autre approche serait de voir les rooms comme des dispatcheurs en eux même, du type broadcast (communication un vers plusieurs) pour les membres de la room.

Alors si je ne me trompe pas, on aborde là le code métier ? C’est la partie que je pensais attaquer vraiment plus tard. C’est vrai que de coder tout le gameplay en dur est une mauvaise idée je m’en rend compte. Il faudrait que je me renseigne sur comment écrire l’état d’une partie sur un fichier. Pour chaque partie j’essaierai d’ailleurs de garder un historique des états et des communications (enfin je dis ça mais je ne le ferais peut-être pas avant très longtemps).

Si tu le mets dans le design, c’est que tu en a fait l’analyse. Si tu ne veux pas faire l’analyse de cette partie maintenant, ne la mets pas dans le design.

Mais essaie de travailler par partie. Si tu bosses sur l’architecture interne de tes rooms, oublie les fichiers, le reseau, le gameplay pour le moment. Essaie de garder cette indépendance dans ton analyse entre les parties (si tu n’arrives pas a analyser les parties indépendamment les une des autres, comment arriveras tu a avoir concevoir des modules independants au final ?)

Après, chaque partie suit un schéma constant normalement : la nuit, on appelle des rôles chacun leur tour (ordre non aléatoire) pour qu’il réalise une action (tuer un joueur, connaître le rôle d’un joueur etc). Le jour (une fois que tout les rôles de la nuit ont joué) on commence par regarder si certains rôles ont une action à réaliser (le dictateur peut déclencher la nuit un coup d’état ce qui lui permet de tuer un joueur au lever du jour, le chasseur tue un joueur de son choix le jour quand il meurt) puis on invite les joueurs à débattre puis à voter un joueur qui sera pendu. Finalement, j’ai l’impression que la seule partie variable de tout ce système ce sont les rôles. Est-ce que mettre en place un système de plugins pour les rôles ne serait pas suffisant finalement ?

Dans ce que tu viens de dire, tu fais clairement la distinction entre la partie "moteur de jeux" (le concept de role, les différentes fonctionnalités des rôles, etc) et la partie gameplay.

A mon avis, tu n’as pas besoin de créer des plugins, tu peux probablement mettre ca dans du JSON.

Pour le moment, j’aimerai déjà réussir à faire l’aspect réseau et salles. Même pas de chat ou quoi, juste que les utilisateurs puissent se connecter à une salle, voir les autres utilisateurs connecté au serveur et ceux situés dans la même salle qu’eux.

Je pense que tu es dans la bonne démarche pour ton analyse. Continue de bien séparer l’analyse des rooms et du réseau.

+1 -0

Bonjour :)

Pour la partie réseau je n’ai rien apporté de nouveau mais j’ai séparé au mieux les classes. J’ai une classe tcp_server qui est responsable d’accepter de nouveaux clients et de mettre fin à la connexion. La classe tcp_connection est responsable de l’envoi et de la réception de données vers/depuis un client. Je pense que j’ai bien respecté le SRP ici. Après je n’ai pas développé plus que ça, me concentrant d’abord sur l’aspect des rooms comme tu me l’as conseillé.

Comme je l’ai dit, je n’étais pas inspiré pour nommer la classe responsable de la gestion de la liste. Je l’ai pour l’instant nommer server_rooms. D’ailleurs je dis "responsable de la gestion de la liste" alors qu’on m’a, il me semble, dit que "gestion" était un terme trop générique. Pour le moment, son rôle est de pouvoir :

  • créer une nouvelle salle ;
  • supprimer une salle ;
  • enlever une salle (en fait je devrai peut-être renommer ça en hide…) ;
  • restaurer une salle précédemment enlevée.

Ensuite, vient nécessairement un moment où réseau a besoin de connaître room (ou l’inverse) pour que la salle puisse communiquer avec ses clients non ? Du moins c’est ce que je comprends dans

Tu as clairement 3 domaines fonctionnels différents : la structure de données de ton "serveur" (au sens discord) avec des rooms, la partie réseau et la partie file. En termes de dépendance, la première ne doit a priori pas dépendre des 2 autres et les 2 autres doivent être indépendantes entre elles et dépendre de la première.

Donc la partie room doit dépendre de network. Je ne sais pas trop comment modéliser cette dépendance à vrai dire. J’ai pensé à une classe de liaison member/room (avec des pointeurs ou des références) que j’ai nommée un peu au hasard (j’ai pas compris la partie sur le dispatcher et le broadcaster… d’où le nom de la classe pour que je pense à redemander). Elle aurait pour but de savoir quelle membre appartient à quelle salle. Je ne suis pas sûr que ce but soit bien formulé cependant.

Ensuite, pour la partie moteur de jeu/gameplay je ne suis pas sûr de suivre. Si le concept de rôles, de ses fonctionnalités etc fait partie du moteur de jeux, qu’est-ce qui fait partie de la partie gameplay ? Si je fais des hypothèses, je dirai que ce qui fait partie du moteur de jeu se code en dur dans le code C++, et ce qui fait partie du gameplay est géré par des états (fichiers) ou un langage de script. On pourra voir ça plus tard sinon…

WerewolfServer.png
WerewolfServer.png
+0 -0

Bonsoir,

J’aimerais apporter quelques points qui me paraissent importants et qui n’ont pas du tout été discutés:

Je suis le créateur d’une plate-forme de jeux de société en ligne, qui est basé exactement sur les mêmes principes que toi: on se connecte, on rejoint une salle (qui s’appelle "table" dans mon cas), on lance la partie, et on peut chatter (uniquement en texte pour le moment). A cela près que de mon côté, je n’ai absolument pas l’intention de faire de l’open source avec ce projet.

Un des principes que j’ai fixé très tôt et que j’estim, avec le recul, m’avoir particulièrement réussi, est le concept du client débile.

Le client n’est là que pour afficher ce que le serveur lui dit d’afficher, et n’envoi au serveur que les actions brutes effectuées par l’utilisateur, comme les touches appuyées par exemple. Rien d’autre. IL n’a aucune notion de jeu, de règle de jeu, et en fait pas même celle de salle ou de liste d’autres clients connectés. C’est le serveur qui gère entièrement les salles et le déroulement des jeux. LE client, lui, il se fiche totalement de tout ça.

Selon moi ça a beaucoup d’avantages:

  • Tu ne dupliques pas inutilement les logiques métier, et tu ne les éparpilles pas non plus un peu partout dans ton code (ça arrive plus vite qu’on ne le croit), tout est centralisé sur le serveur
  • Ca complique la triche et le piratage, car peu d’informations directement manipulables sont transmises
  • Si les règles du jeu évoluent, seul le serveur doit se mettre à jour
  • Sauf changement non rétrocompatible du protocole, les clients ne sont pas obligés de se mettre à jour
  • Le client étant minimal, il est aussi de fait plus robuste

Un autre point qui me fait un peu tiquer est celui-ci:

Le client se connecte à un serveur en précisant l’adresse ip et le port ;

Je ne sais pas quel public tu vises avec ton jeu, mais note que l’échange manuel de l’IP et du port n’est pas si simple pour ceux qui ne s’y connaissent pas très bien en informatique.

Si tu souhaites rester sur un fonctionnement globalement décentralisé, je pense que tu devrais te pencher sur la conception d’un méta-serveur. En très résumé, la première chose que devrait faire le client à mon avis est de télécharger une liste de serveurs ouverts. Plutôt que d’entrer manuellement une IP et un port, le joueur n’a plus qu’à sélectionner un serveur dans la liste pour s’y connecter. C’est beaucoup plus facile pour l’utilisateur lambda. Tous les joueurs ne sont vraiment pas des as en informatique, comme j’ai pu m’en rendre compte moi-même.

Note que, ce n’est pas si difficile de maintenir un méta-serveur fixe et centralisé (un petit service web suffit). C’est beaucoup plus difficile si tu veux quelque chose qui soit vraiment décentralisé jusqu’au bout, car dans ce cas tu vas devoir t’inspirer de ce que font les réseaux peer-to-peer. A te lire je n’ai pas l’impression que tu as le niveau requis; je ne suis même pas sûr que je l’aurais moi-même.

Pour le moment, j’aimerai déjà réussir à faire l’aspect réseau et salles. Même pas de chat ou quoi, juste que les utilisateurs puissent se connecter à une salle, voir les autres utilisateurs connecté au serveur et ceux situés dans la même salle qu’eux.

C’est une bonne première étape pour poser les bases de ton architecture à mon avis. Commence par introduire les premières briques du réseau et du serveur, et commence par quelqeu chose de très simple. Tu complèteras par la suite.

Clairement c’est ce qui est le plus difficile et probablement aussi ce qui est le moins fun, alors plus vite tu as une base minimale fonctionnelle, plus vite tu pourras vraiment t’amuser avec le métier (le jeu), et plus vite les idées d’amélioration se succèderont et te donneront envie d’avancer. De façon générale, mieux vaut commencer avec ce qui est le moins sympa et le plus compliqué, car à commencer par le facile, on bute quand on arrive au compliqué et on se démotive.

Enfin, étant donné que c’est un projet que tu fais pour ton plaisir personnel en-dehors de toute exigeance scolaire ou professionnelle, contrairement à la plupart des avis précédents, je pense qu’il ne sert à rien de trop se formaliser sur les schémas, usecases et autres diagrammes. Ca peut être très utile pour éclaircir tes idées, surtout au début, mais la diagrammite excessive peut aussi t’entraîner vers la démotivation. Pas seulement parce que c’est pas cool à faire, mais aussi et surtout parce que tu pourrais tout à coup être pris de vertige devant tout le travail que tu t’imagines avoir à faire en relisant tout tes papiers.

J’ai commencé des centaines de projets perso, très peu ont atteint le stade du vaguement montrable; et ceux qui ont le mieux abouti ne sont pas ceux où j’ai commencé par écrire plein de doc avant de coder. C’est ceux où, au contraire, j’ai commencé très tôt à coder, et où j’ai procédé par instinct, au fil des idées d’abord très très simples, améliorant le code au fur et à mesure. LE plus important est de se fixer des petits objectifs et d’avoir le plaisir de les voir réalisés rapidement; un pas après l’autre, sans se presser, et sans regarder l’horizon trop lointain, pour ne pas voir qu’il s’éloigne ene même temps qu’on avance.

C’est l’avantage des projets perso, de pouvoir procéder ainsi. Quand on a des clients qui ont des exigeances, évidemment, on ne peut pas, on doit nécessairement les formaliser pour qu’ils signent en confiance.

+0 -0

Merci pour ton retour et ton avis d’expérience.

Pour le moment j’avoue ne pas avoir trop réfléchi à la conception du client, la partie m’intéressant plus étant le serveur. C’est là qu’on conçoit le jeu finalement. On peut avoir plusieurs clients différents qui interagissent avec le même serveur. Certes je ne pense pas que mon projet rencontre un succès assez important pour pousser des développeurs à concevoir leur propre client… mais j’aime l’idée de projet open-source.

Ensuite, tu ne penses pas que je fasse le bon choix en choisissant de se connecter manuellement au serveur. Dans un premier temps je trouve ça plus simple déjà. Je n’ai pas envie de me plonger tout de suite dans la gestion d’un serveur central qui effectue une liste des serveurs ouverts. Surtout que ça casse un peu avec l’idée que j’ai de mon projet : permettre à qui le veut d’héberger des parties de loups garou personnalisable, sans à débourser d’argent. Je conçois qu’au début ça nuira à la popularité du projet et à sa facilité d’utilisation. Mais ce n’est pas la problématique majeure. A vrai dire, même si personne ne l’utilise, ce n’est pas très grave. Je sais que moi au moins j’aurai fait quelque chose qui correspond exactement à mes besoins les fois où je voudrais jouer avec mes amis :D

Mon intérêt dans ce projet c’est de progresser en architecture logicielle, apprendre à identifier les besoins du projet et à les formaliser, en déduire des classes qui répondent aux besoins etc. Je trouve alors que les diagrammes sont efficaces pour communiquer sur le forum. Contrairement à ce que tu pourrais croire, la partie technique (conception d’un moteur de jeu très simpliste, définition du protocole de communication etc) m’intéresse et me motive beaucoup plus que la partie concernant les rôles des joueurs etc. Et même dans cette partie, ce qui me stimule plus c’est la volonté de saisir les difficultés de la mise en place d’un tel système.

EDIT : Oui mais non… En fait le serveur qui liste les serveurs ouverts ne nuit pas à l’idée du projet puisque ça ne fait pas débourser d’argent au final à ceux qui hébergerait un serveur chez eux ou sur un VPS (enfin dans ce dernier cas j’entends argent supplémentaire quoi :) ). Mais dans ce cas ça viendra plus tard je pense.

+0 -0

Ensuite, tu ne penses pas que je fasse le bon choix en choisissant de se connecter manuellement au serveur. Dans un premier temps je trouve ça plus simple déjà. Je n’ai pas envie de me plonger tout de suite dans la gestion d’un serveur central qui effectue une liste des serveurs ouverts.

Si, dans un premier temps, c’est plus simple, et ce n’est effectivement de loin pas indispensable pour l’instant. Je mettais ça pour le jour où tu sortiras ton jeu. J’aurais peut-être dû mieux préciser, désolé.

Mon intérêt dans ce projet c’est de progresser en architecture logicielle, apprendre à identifier les besoins du projet et à les formaliser, en déduire des classes qui répondent aux besoins etc.

OK, c’était pas clair pour moi que tu tenais à la formalisation presque plus que la réalisation. J’ai pas l’impression que ce soit très commun.

Contrairement à ce que tu pourrais croire, la partie technique (conception d’un moteur de jeu très simpliste, définition du protocole de communication etc) m’intéresse et me motive beaucoup plus que la partie concernant les rôles des joueurs etc.

C’est vraiment bien de déjà savoir ça, et de nouveau je n’ai pas l’impression que ce soit si courant.

Personnellement, je ne compte plus le nombre de jeux que j’ai commencé pour ne jamais finir, me rendant compte après plus ou moins de temps et avoir codé une plus ou moins grande partie du moteur que c’est celui-là qui m’intéressait le plus, et qu’il restait encore un enrobage entier à élaborer pour que le jeu soit vaguement montrable même a des amis et qu’il soit un minimum jouable pendant plus que 5 minutes

+0 -0

Oui je vois au final ce que tu veux dire. C’est vrai que je ne suis pas forcément très clair dans ce que je demande.

@gbdivers pour la partie gameplay j’ai réfléchi. En fait, le moteur de jeu s’occupe de tout ce qui est autour du déroulement de la partie (gestion des connexions, gestion de la communication, des salles etc). Une fois qu’une salle lance une partie, le moteur ne fait que lancer un simple script qui gère les transitions d’états de la partie avec des fichiers. Ça me semble un peu plus clair que la dernière fois. En revanche avec cette architecture (en gros le moteur de jeu laisse la main au script) je vois pas comment le script peut communiquer avec le moteur de jeu (bloquer certaines personnes dans le chat car ils n’ont pas le droit de parler)… Bref, ça se précise un peu déjà.

Pour la partie room j’attends ton retour car je bloque sur sa liaison/dépendance du module réseau. :)

EDIT : Je pensais sinon à une classe association comme ceci. Par contre niveau implémentation je vois pas comment ça se ferait (quelle est alors sa responsabilité ? représenter l’association room/utilisateur ?).

+0 -0

En réalité j’ai l’impression que j’ai beaucoup de lacune dans le domaine de conception. Je n’avance pas beaucoup, même en me limitant à mon diagramme de cas d’utilisations ci-dessus. Je me demande vers quelles ressources me tourner actuellement pour essayer de réfléchir de façon un peu plus méthodique ou juste progresser. Je pensais faire des recherches autour de code design mais ça semble très lié à l’implémentation au final. Si vous savez quels mots clés je pourrai utiliser pour mieux définir les besoins du projet et pour commencer à le formaliser, je suis preneur :)

Je pense que je peux déjà essayer de me familiariser plus rigoureusement avec UML. Pour le reste je suis perdu ^^.

Si je devais faire une analogie je dirais que j’aime bien construire des cabanes mais maintenant j’aimerai apprendre comment dessiner les plans de ma maison avant de la construire.

Bonne soirée à vous :)

Il y a quelques mots clés à connaitre et a rechercher sur Google : SOLID, Demeter, design pattern, architectural pattern, etc. Il faut les étudier pour savoir les applications (donc savoir dans quelles conditions les utiliser, dans quelles conditions ne pas les utiliser, et dans quelles conditions les adapter à ses besoins).

Quelques liens

Il y aussi des livres de référence :

Tu as aussi la série dirigée par Martin Fowler https://www.informit.com/imprint/series_detail.aspx?ser=335487 et celle de Beck https://www.informit.com/imprint/series_detail.aspx?ser=4266306

Et pour la méthodologie, je te conseille de regarder Agile et TDD.

+0 -0

Merci pour ces liens. Pour la méthodologie je m’étais déjà renseigné, notamment sur les méthodes agiles. Concernant les TDD, je les applique désormais quand je fais du web donc je suis assez familiarisé avec le concept. J’ai cependant un peu de mal pour le moment à en faire avec C++ (je n’ai écrit qu’une classe pour le moment, la classe configuration, et toutes les opérations effectuent des modifications sur un membre… je ne vois pas comment la tester :ange: mais j’ai réussi à installer le google test framework) mais je pense qu’en se forçant à le faire ça viendra.

Quant aux designs pattern, je pense qu’ils sont en effet utiles à connaître, savoir quand on en a besoin, quand ils ne sont pas nécessaires ou contre-productifs (enfin l’exemple du singleton qu’il ne faut pas utiliser à tout va car il rend finalement beaucoup de code dépendant d’une classe…), mais ils ne me suffisent pas encore à définir l’architecture de mon projet.

Je pense donc lire les liens que tu m’as donné. Ensuite, je pense qu’il faut que je passe suffisamment de temps à me poser la question de ce que je veux faire concrètement. Ensuite le formaliser avec des schémas UML (use cases notamment). Au final c’est ce que je commence à faire avec ce projet. Pour le reste, je pense que je modélise mal mon diagramme de classe en UML. J’ai commencé à regarder des cours. Par exemple, on évite de représenter une dépendance entre deux classes (A possède un vector de B) avec un attribut (un membre) de classe mais plutôt en reliant les deux blocs classes par une relation (1 vers n, ou bien n vers n etc). Bien sûr, ce n’est pas nécessairement suffisant.

De mémoire, dans la méthodologie agile (je garde seulement l’idée importante) on procède par itérations plutôt que de développer le système d’une traite. Je pense que pour une première itération, le système de salles est suffisant. Ensuite développer la base du module réseau (de quoi créer le serveur TCP, accepter et fermer des connexions). Je pense que je reviendrai vers vous une fois que j’aurai déjà fait ça (ou bien si je me pose des questions ou que je bloque).

J’ai commencé à vouloir implémenter le module réseau mais c’est pas encore ça. Notamment, le module des salles dépend du module réseau comme recommandé par gbdivers. Ainsi la salle peut envoyer des données à tout ses clients en itérant sur l'unordered_set. En revanche, comment écouter les données des clients (dans la class tcp_connection) tout en sachant comment les interpréter (ie quelle salle est concernée). J’ai commencé à écrire une dépendance dans le module réseau mais au final ça va à l’encontre de ce que me recommande gbdivers. Je ne vois pas trop comment faire.

Sinon j’ai du mal avec Boost.Asio donc mon code est même pas encore utilisable :ange: . Voici la pull request pour le module réseau :)

Résumé

On a bien avancé je trouve sur le projet même si au final en prenant du recul il n’y a pas beaucoup de features. Un petit récapitulatif : je cherchais à faire un module réseau en respectant au maximum vos recommandations, notamment celle du respect SOLID (SRP, Open/Closed principle, Liskov’s substitution, Interfaces segregation, Demeter’s law) dont je comprends pas encore tout (le L et le D :innocent: ). Le module réseau est actuellement fonctionnel dans le sens où :

  • il permet d’accepter des connexions entrantes (tcp et websockets) ;
  • il permet d’envoyer des données aux connexions acceptées ;
  • il permet de recevoir les données envoyées par les connexions.

J’ai également implémenté un début de module pour les salles. Il manque encore la possibilité de sauvegarder l’ensemble des salles etc mais je n’en fais pas encore ma priorité. En réalité, ce que pense faire maintenant c’est implémenter un module de commandes qui sera utilisé pour interprété les données envoyées par les connections.

Module de commandes et pattern observer

Je n’ai pas une grande expérience en développement et surtout moins en conception de logiciel. Mon expérience se résume essentiellement au développement de plugin pour minecraft (bukkit/sponge), ou même un début de mod avec Forge, et de conception de sites web. Pour ces derniers je reste toujours sur des architectures de bases Modèle - Vue - Controlleur avec un framework (Laravel). Autant dire qu’il ne reste presque que la partie métier.

Ce qui a retenu mon attention ici c’est la façon de créer des commandes pour les plugins Minecraft et l’architecture utilisée derrière me semble adaptée à ce que je veux faire. Finalement, une commande c’est un nom, quelques attributs comme sa description etc, et un executor qui se charge de traiter les arguments de la commande et d’effectuer des actions sur un contexte qu’on lui passe en référence dans son constructeur.

J’aimerai donc vous demandez votre avis sur le fait de coupler ce pattern de commande avec le pattern observer. Ce dernier serait utilisé pour observer les connections. À chaque réception de données, on ferait un notify (TCP_SESSION_EVENTS::DATA_RECEIVED, data). Ainsi, un module de commande qui aurait un command_observer et auquel la connexion sera inscrite pourra recevoir cet événements et essayer d’appeler une commande correspondante en lui passant les arguments.

Pour ça il faut également créer un registre de commandes avec une unordered_map<string, command> notamment ainsi qu’une méthode register(command) et try_to_call(command).

J’ai commencé à modéliser cette partie en UML mais c’est encore un peu vide. Il manque encore pas mal de choses et je me pose encore beaucoup de question comme

  • comment faire une hiérarchie de commande ? (j’ai vite fait modéliser le truc en UML mais ça me parait plus compliqué) ;
  • comment gérer le cas où une commande prendrait du temps à s’exécuter ? C’est plus théorique car étant donné le type de serveur, ça ne devrait pas arrivé si souvent. Mais dans le cas où ça arriverait : on ne pourrait plus traiter les données entrante (impression de lag ?). Il faudrait sûrement créer un système de queue mais j’arrive pas encore à le modéliser ;

Par ailleurs j’ai bien conscience que ce que je dis peut ne pas être clair. Mais l’intérêt que je vois dans ce module, couplé à l’observer pattern, c’est son évolutivité. Seulement je manque d’expérience et ça peut peut-être être tout le contraire que je ne soupçonne même pas :( .

Comment je vois cette possibilité évolutive

Je voulais aussi détailler pourquoi et comment je vois cette structure évolutive.

Le fait d’avoir un sujet observable, la connexion, rend la classe presque indépendante du reste du code. En soit, le code interne de la classe ne sera modifié que pour ajouter une énumération d’évènements possibles (enfin je vois juste TCP_SESSION_EVENTS::DATA_RECEIVED pour le moment). Les méthodes pour ajouter/supprimer un observer venant d’une classe plus générique observable (j’aime pas trop le terme sujet car pas simple à resituer en dehors du contexte).

Par ailleurs, chaque partie du programme ayant des besoins spécifiques peut créer une instance de ce module de commande. Par exemple, le module de salles peut observer les connexions pour savoir quand un client essaie de changer de salle. Il revient au commmand_executor de faire les vérifications quant à la possible réalisation de l’action. De même, si on commence un jeu, il devra avoir son propre module de commande car on voudra lui faire passer une référence vers l’instance de la partie, pour que l’exécuteur de la commande puisse y accéder.

Voici un schéma actualisé de mon UML. Je vais d’ailleurs bientôt suivre les recommandations que l’on m’a faite et refaire un diagramme de classe par module. Je devrai modéliser autrement l’interaction et les dépendances entre modules non ? :P

Remerciements

Je tenais aussi à remercier les membres du Discord Not a Name qui peut-être ne sont pas présents ici mais qui m’aident énormément avec mes soucis d’implémentations, d’apprentissage etc. Merci à REMqb, Raz, GB, Arkena, Alairon, Praetonus, jo_link_noir et j’en oublie certainement d’autres.

Merci également à vous qui prenez le temps de me lire et de me répondre :)

EDIT : Ah et si vous voulez jeter un œil au code source c’est sur cette branche.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte