Blog<Sam>

copy(MyLife.begin(), MyLife.end(), ostream_iterator<string>(www, “\n”));

Archive pour mars, 2008

Exo²::Network, mes premiers pas dans la programmation réseau

Publié par Sam sur 6 mars, 2008

PaquetsAh, le réseau… Ma bête noire ! Je hais tout ce qui se rapporte au réseau. Souvent, lorsque quelque chose m’énerve dans un programme, c’est dû au réseau. Dans les jeux particulièrement, avec les problèmes de prédiction et le lag, sans compter les déconnexions. En plus, on ne peut souvent rien faire : soit ça marche, soit ça ne marche pas, mais ce n’est pas en bidouillant votre PC que vous allez avoir 5 de ping à votre jeu online. Cela crée un sentiment d’impuissance extrême, car même si vous avez une machine très puissante, vous ne pouvez rien faire, j’ai bien essayé de secouer les câbles RJ45 pour que les paquets avancent plus vite, mais rien n’y fait.

Niveau programmation aussi, c’est la galère : non seulement les paquets peuvent être perdus, mais en plus ils peuvent arriver dans le désordre. Utiliser TCP résout partiellement ce problème, mais TCP force les paquets à tous arriver et les met forcément dans le bon ordre, cela ralentit considérablement les performances (dans un jeu vidéo si un paquet est perdu on fait avec, on ne s’amuse pas à attendre qu’il soit renvoyé). Il faut donc utiliser UDP, qui souffre de ces problèmes. Les API, de plus, sont très très pauvres, même sous Windows dont les sockets sont basés sur ceux de BSD (on retrouve donc la plupart des fonctions BSD utilisable sous Linux comme Windows, avec quelques légères différences et ajouts). Cela peut paraître simple au début (peu de fonctions), mais en fait, il faut fournir un travail considérable pour écrire un système sécurisé, rapide, fiable, adaptable et souple, d’envoi/réception de paquets en UDP.

HouseEn parlant de sécurité et de fiabilité, si tout le monde ment est un principe important pour Dr. House (merci Raph pour l’analogie), il l’est tout autant pour le codage de la partie serveur : Il ne faut faire aucune supposition sur les clients, penser à tout ce qu’il peuvent faire, imaginer que tous les clients connectés vous en veulent à vous et vos enfants, pour espérer coder un serveur fiable qui ne va pas tomber en miettes à la première attaque ou au premier bug d’un client.

Depuis quelques jours donc, j’ai décidé de coder la partie réseau de mon projet de mini-moteur de jeu, Exo². Le premier problème est de choisir l’API ou la bibliothèque à utiliser. Notre ligne de conduite pour Exo² (nous sommes deux sur le projet, moi et Raph) est de ne rien utiliser qui ressemble à un moteur : uniquement des API (OpenAL, DirectX…), ou des bibliothèques réalisant des fonctions précises importantes, longues et compliquées à coder correctement, pour peu de valeur ajoutée si on l’avait fait nous-même (zlib, boost, …). De plus ces bibliothèques doivent être sous une licence non restrictive permettant de les utiliser dans un projet commercial fermé (licences de type LGPL, BSD, MIT, ZLIB, …), pour qu’on reste libre de passer d’un jour à l’autre en closed-source (pour le moment Exo² est sous GPL).

Pour le réseau, j’ai vite choisi enet : une librairie assez légère qui définit et implémente un protocole de transmission basé sur UDP, permettant entre autres l’envoi et la réception de paquets de manière à la fois fiable et non-fiable (on peut combiner les deux pour une même connection), c’est-à-dire en s’assurant que les paquets arrivent ou pas. Dans tous les cas, ils sont automatiquement fragmentés et rassemblés, et leur réception est assurée d’arriver dans le même ordre qu’à leur envoi. Elle est pas belle la vie ? Utiliser cette bibliothèque me permet de me concentrer sur le moteur réseau proprement dit, sans avoir à coder pendant des mois l’envoi et la réception de paquets UDP pour obtenir un résultat qui aura sans doutes plus de failles que enet.

J’ai décidé de commencer par faire une liste de commandes simples que je souhaite pouvoir réaliser :
- net_createserver pour créer un server
- net_join pour se connecter à un serveur
- net_disconnect pour se déconnecter
- net_kick pour déconnecter un client
- net_clientlist pour obtenir la liste des clients.

Ces objectifs permettent d’obtenir rapidement un début d’architecture réseau :
- CNetwork, la classe centrale du système
- CNetClient, la représentation d’un client coté client
- CNetPeer, la représentation d’un client coté serveur
- CNetServer, la représentation d’un serveur coté serveur

Fidèle depuis peu à certains principe d’eXtreme Programming (en particulier YAGNI), je ne code que ce dont j’ai besoin, c’est pourquoi il manque probablement de nombreuses classes qui verront le jour plus tard, quand j’en aurai besoin.

DoSEn regardant le tutoriel d’enet, cela n’a pas l’air très compliqué, mais en réalité on s’aperçoit vite que c’est largement insuffisant, comme la plupart des tutoriels pour n’importe quel sujet d’ailleurs : ils ne sont pas dans le contexte. Lorsque vous intégrez le code d’un tutoriel dans votre application, vous devez l’adapter au contexte, et vous vous rendez compte qu’il faut souvent faire de grandes modifications. Par exemple leurs demandes de connexions et déconnexions sont synchrones, c’est-à-dire qu’ils bloquent le programme. Bien sûr je n’ai pas envie que le jeu “freeze” à chaque fois qu’il essaie de se connecter à un serveur, et il faut donc que tout soit asynchrone.

Bref, après quelques jours j’ai réussi à avoir quelque chose d’à peu prêt correct qui ne fait aucune supposition sur le client, et permet au serveur d’accepter/refuser des connexions tout en maintenant une liste de clients (avec une limitation paramétrable), et au client de se connecter et se déconnecter. Le seul gros problème que je vois actuellement est l’attaque par déni de service puisque n’importe qui peut rester connecté au serveur et donc facilement atteindre le nombre maximum de clients grâce à des connexions multiples, mais je me pencherai là-dessus beaucoup plus tard.

Le code correspondant à ce que je viens de raconter est disponible sur Codeplex, version 9927 d’Exo², mais j’ai déjà modifié tout ça.

Exo²

Publié dans Programmation | Taggé: , , , , , | Aucun commentaire »

Intégrer l’aide d’autres produits et SDKs dans celle de Visual Studio

Publié par Sam sur 3 mars, 2008

L’aide de Visual Studio se base sur la plateforme d’aide Microsoft Help 2 utilisant le programme Document Explorer. Cette plateforme était à l’origine destinée uniquement à Visual Studio, mais en fait quelques autres produits commencent à l’utiliser comme successeur du bon vieux CHM comme par exemple les derniers IDE Borland.
Là où ça devient intéressant, c’est que l’aide de Visual Studio est en fait un ensemble de collections d’aides, et qu’il est possible d’ajouter à cet ensemble toute collection compatible avec le Document Explorer !

Voici la marche à suivre :
- Dans l’index de l’aide de Visual Studio, rechercher “Visual Studio 2008 Combined Help” (en remplaçant le 2008 par la version utilisée évidemment)
- cocher les produits à intégrer
- relancer Visual Studio

Cela m’a permi d’intégrer l’aide de DirectX à celle de Visual Studio, ainsi que l’aide d’OpenAL.
Maintenant je peux faire “F1″ dans Visual Studio sur une fonction de DirectX et avoir le résultat !

Aide de DirectX intégrée à celle de Visual Studio
Aide de DirectX intégrée à celle de Visual Studio

Publié dans Programmation | Taggé: , , , , , | Aucun commentaire »