dimanche 27 mai 2012

Programmation réseau - et l'aléatoire ?

          Programmer un jeu en réseau, c'est se placer d'emblée dans une optique très différente.
C'est toute la conception du programme qui change ! C'est tellement nouveau qu'il faut plus qu'un cours théorique pour se préparer à la montagne de questions et de problèmes qui apparaissent lors de la mise en pratique.

          En sommes, rien ne vaut l'expérience pratique dans ce domaine. C'est à la fois passionnant et très frustrant, surtout pour un programmeur comme moi, totalement amateur et sans bases théoriques solides. Une question qui s'est rapidement soulevée pendant la programmation de Strategos est celle de l'aléatoire.

          Voici le problème : pendant une partie en réseau, le joueur 1 attaque le joueur 2. Pendant le combat, la créature du joueur 1 va générer un nombre aléatoire qui représentera son attaque. En mode solo, aucune difficulté. Mais comment partager ce nombre aléatoire en multijoueur sur deux applications différentes ?!


J'ai envisagé 3 possibilités :

          1. Faire que le serveur soit "lourd", c'est à dire qu'il gère toutes les variables et fonctions (et les clients ne feraient qu'afficher et lui envoyer les inputs). Ainsi, l'aléatoire n'aurait à être généré qu'une seule fois, comme en mode solo, et sera sans peine affiché par les deux clients.
Cette solution est sans doute très efficace, mais j'ai opté pour une architecture tout autre pour Strategos (clients riches, et serveur qui ne sert que de relais). J'ai donc cherché d'autres moyens :

          2. Générer le nombre aléatoire chez un client, et l'envoyer à l'autre client pendant l'attaque. Trop compliqué pour moi, et trop risqué à mettre en œuvre. Imaginons que la connexion rencontre des problèmes à ce moment précis, et les deux clients sont désynchronisés à jamais.
Reste la troisième méthode, celle que j'ai choisie :

          3. Jouer sur le pseudo-aléatoire. En utilisant les congruences linéaires (Wikipédia en parle bien) pour générer son nombre aléatoire, on peut très facilement partir d'une même seed et obtenir des séries "aléatoires" identiques. Ainsi, les deux applications produisent de façon autonome... les mêmes nombres aléatoires !
Cela peut paraître déroutant dans la mesure où le propre de l'aléatoire est d'être imprévisible.


          Le résultat me satisfait vraiment : on continue à faire un simple appel à une génération aléatoire locale (sans s'embêter avec le réseau ou en multipliant les échanges donc), tout en sachant que les clients resteront synchronisés.
"Mais, me direz-vous, si les deux clients partagent la même seed, ils produiront toujours la même série !"
Pour pallier ce risque de monotonie, il suffit que le serveur, à son ouverture, génère un nombre aléatoire (à partir de l'heure par exemple), pour ensuite l'envoyer aux deux clients. Ce nombre sera leur seed qui servira à la génération de leur série !

          On a donc un aléatoire commun et facile à utiliser, qui se renouvelle à chaque lancement du jeu. J'ai résumé ça par un petit schéma en deux temps (je commence à bien aimer faire mes petits schéma, même s'ils sont assez peu utiles !) :