R'alf Home Page

Net Droîds


Home | BeOS | PowerPulsar | Ballades | Photos | Tourbillon | Favourites | Bookmarks |

Net Droîds

(Spécifications informelles et incomplètes)

1. Sommaire

1. Sommaire
2. Première partie
2.1 Résumé
2.2 Principe
2.3 Les droîdes
2.4 Salles et cartes
2.5 Le déroulement du jeu
2.5.1 Distributeur Eternel d'Energie (DEE)
2.5.2 Téléporteur
2.5.3 Kit de réparation
2.5.4 La perforatrice
2.5.5 Armes
2.5.6 Armes exotiques
2.5.7 Objets sophistiqués
2.6 Implémentation
2.6.1 Petit scénario
2.6.2 Problème de l'interruption
3. Seconde partie
3.1 Avant Propos
3.2 Contenu
3.3 Réseau maître/clients
3.3.1 Des sockets pour communiquer
3.3.2 Deux couches pour le réseau
3.3.3 Couche de réordonnement
3.3.4 Couche de routage
3.3.5 Structure des messages
3.3.6 Plusieurs maîtres
3.3.7 Section Finale


2. Première partie

R'alf, Saint Maur, le 30 Janvier 1994

Salut Francis !

Voilà, j'ai redéveloppé ton idée des droîdes sous une autre perspective et je m'en vais te conter ce que ça donne, tu verras ça n'est ni triste ni simple... Mais il subsiste quelques insuffisances dans le scénario et c'est pour ça que j'ai besoin de toi.

2.1. Résumé

Le principe du jeu est fort simple: on a des droîdes (ou des robots, peu importe l'appellation car son origine n'est pas contrôlée) que l'on peut programmer. Que l'on peut ? Non, en fait on est obligé de les programmer.
On dispose aussi de salles et de couloirs afin de se créer une petite forteresse. Et lorsque le jouer est suffisamment développé, il part attaquer ses voisins.

2.2. Principe

Le principe de base est que le jeu se déroule sur une ou des stations, chaque joueur derrière son terminal (graphique, bien entendu). Avec ses droîdes, le joueur va construire des salles et les relier par des couloirs selon sa propre volonté: il se construit une forteresse et c'est lui qui en crée le plan. Cette forteresse étant construite à partir de salles reliées par des couloirs, elle ressemblera (et sera donc gérée) comme un graphe quelconque (mais sans intersections: un couloir ne peut en couper un autre).

Le jeu est interactif orienté temps-réel. Pas question de dire qu'on agit puis on attend qu'un tour passe: tout ce qu'on fait prend action immédiatement.

Supposons deux joueurs: A et B. Chaque joueur se crée sa forteresse dans son coin et (par construction), le joueur A n'a aucune chance, en perçant un couloir, de tomber par hasard sur une salle du joueur B (et inversement): les deux mondes sont complètement séparés.
Alors, demandes tu, s'ils sont séparés, comment A va-t-il attaquer B ?
Et bien simplement en utilisant le téléporteur qui est fourni à chaque équipe au départ. Mais c'est un modèle peu puissant qui est fourni d'office: il ne peut téléporter qu'une personne à la fois, et il faut lui donner une destination. Cette destination est une "adresse" dont un champ est le numéro de forteresse et l'autre champ une position dans cette forteresse. Du coup, au départ, le joueur A connaît sont adresse de forteresse mais il ne connaît pas les adresses des autres: il est donc obligé de tâtonner pour savoir où aller. On considérera, au début, que le fait d'enclencher le téléporteur sur une adresse inexistante le désactive automatiquement, sans créer de dégâts.
Une fois le téléporteur enclenché sur une adresse valide, le droîde qui manipule le téléporteur acquiert une vision limitée de la salle où l'appareil à atterrit (nota: le fait d'enclencher le téléporteur ne téléporte pas le porteur de l'appareil; en fait le téléporteur est une "porte" dans le mur qui mène à une autre salle.)

2.3. Les droîdes

Bon, tout ça c'est bien joli mais ça reste hyper-vague (hyper-ion ?).
Voici donc comment je conçois les droîdes.
Tout d'abord un droîde est un bonhomme qui a une certaine énergie électrique et une certaine force. Il peut également subir des dégâts et se faire réparer.
Un droîde porte un numéro d'ordre d'acquisition (c'est le 5ième droîde du joueur A), ainsi qu'un nom (une chaîne quelconque), une classe et une structure de données (au sens C du terme) quelconque, définie par le joueur. La classe du droîde sert à indiquer ce qu'il fait: mécano, combattant, reprogrammeur, éboueur, etc.
Le droîde peut manipuler des objets (physiques) ou des messages (chaînes de caractères ou données ou structures quelconques) et les transmettre. Les objets ne peuvent pas être dupliqués (mais peuvent être détruits), les messages peuvent être dupliqués et/ou détruits.

Ensuite un droîde est pour moi un truc qui communique avec les autres droîdes présents, à vue uniquement ; un droîde peut:

  1. échanger de l'énergie électrique avec un autre droîde selon un protocole de dialogue: l'autre veut-il distribuer de l'énergie, combien en dispose-t-il et combien en donne-t-il ?
  2. échanger des messages (éventuellement en garder une copie ou pas), ou échanger des objets (on ne garde pas de copie, l'objet étant par définition atomique sans pourtant être nucléaire).
  3. échanger des programmes (des fonctions, i.e. des bouts de programmes). Un droîde peut ainsi donner des ordres à un autre en lui transmettant des programmes. Cette reprogrammation peut être forcée (le droîde A reprogramme complètement le droîde B sans lui demander son avis) ou alors volontaire (A demande l'avis de B avant). Le cas volontaire peut être: A veut reprogrammer B mais B fait un truc et, "attends, j'en ai pas pour longtemps, tu me reprogrammera dès que j'aurais fini".
  4. demander des choses (objets ou messages) ou chercher qui les possède.
  5. utiliser une console d'ordinateur (il y en aura au moins 1 dès le départ) qui permet de savoir l'état des troupes, le plan de la forteresse, etc. [On remarquera que le plan, par exemple, est un message: un droîde A peut donner le plan au droîde B. Mais si le plan sur l'ordinateur évolue, le droîde B n'en sera toutefois pas au courant car sa copie du plan est plus ancienne. Il conviendra donc à B de demander aux autres s'ils possèdent une copie plus récente du plan.]
  6. la force du droîde est égale à son énergie moins ses dégâts. Pour augmenter l'énergie il suffit d'en prendre un peu dans un distributeur ou auprès d'un autre droîde. Pour supprimer les dégâts, il suffit de consulter un droîde mécano (il fait que ça et ne peut donc pas se défendre). Se faire réparer prend du temps et immobilise à la fois le mécano et le droîde réparé.
  7. par défaut, un droîde n'est pas armé mais comme c'est un "être de métal", il est très fort ("ouuuh, comme il est beau le corps de mon droîde", sic la tapette - this was a gag from the R'alf, bad hein ? heu, bad eau ?). Et donc deux droîdes qui se battent entre eux, et bah c'est un peu monotone car c'est le plus fort qui gagne. Donc pour que ce soit moins monotone, le droîde que se bat le plus gagne des points d'expérience et après, même s'il est moins fort, il peut vaincre son adversaire qui est moins expérimenté. Mais comme tout ça c'est un peu trop fair-play, il faudra mettre des armes.
Interlude Interlude Interlude Interlude Interlude Interlude Interlude Interl...
Cet interlude a été réalisé 100% nature, sans couper/coller.
Ce petit coin d'interlude est l'endroit idéal pour indiquer que PPR disposait de la version totale de DOOM et donc des deux armes qui ne sont accessibles que dans la version totale (de DOOM, faut-il le préciser ? Oui.) Et donc notamment, il y a une arme qui se nomme la "BKG 9000", qui graphiquement, occupe un quart de l'écran en bas au milieu. Quand on tire, au début il ne se passe rien, ça se charge, puis vient une énorme étincelle (genre la moitié de l'écran) qui part vers le milieu de la salle où ça éclate en forme d'explosion (un truc du style l'explosion du Nostromo à la fin de Alien 1). L'intérêt de la chose, c'est que s'il y avait 20 mecs dans la salle, après il n'y en plus aucun: les mecs sont littéralement éclatés contre la paroi. Il n'y a même pas de déchets, simplement la paroi prend un aspect un peu plus rouge... Bizarrement ce truc là ne fait aucun dégât au porteur de l'instrument (même quand ça t'explose dans la tronche parce qu'un imbécile de monstre imbuttable te colle à la peau, l'idiot !)
de l'interlude Fin de l'interlude Fin de l'interlude Fin de l'interlude Fin de
  1. un droîde est suffisamment fort pour en porter un autre, mais il est toutefois plus vulnérable. D'une manière générale, un droîde doit avoir un quota de poids de charge, afin d'éviter que le droîde ne se transforme en "bombe". Je ne prévoie pas la possibilité que deux ou trois droîdes se réunissent pour porter une charge trop lourde, ça serait trop bordélique. Par contre je prévoie que plus un droîde est chargé, et moins il est efficace au combat ou en vitesse de déplacement (un droîde peut courir).
  2. initialement, un droîde n'appartient à personne: c'est le premier qui le programme qui en devient propriétaire.
  3. par conséquent, si l'on rencontre un droîde adverse, on se bat avec jusqu'à ce qu'il se rende ou soit mort (énergie et force nulles). Si le droîde n'est pas trop endommagé (dégâts - à la limite on peut le réparer mais il se peut que ça prenne trop de temps), il suffit de le reprogrammer et il nous appartient. Nota: si on capture un droîde adverse, ses programmes sont encore fonctionnels. Il convient donc de le reprogrammer avant de le mettre en activité.
  4. un droîde est soit en état actif, soit en état passif (inactif). Un droîde actif devient passif lorsqu'il n'a plus d'énergie ou qu'il est trop endommagé (force=énergie-dégat): sa force doit être positive non nulle. On admettra qu'un droîde nu (sans aucune charge: armes, etc.) avec une force nulle ou négative mais une énergie positive peut marcher (à vitesse réduite de moitié). Toutefois il ne peut plus lutter.
  5. un droîde qui ne peut lutter au combat (force <= 0) est obligé de se rendre à l'ennemi (uniquement si celui-ci tente de le capturer).

Au niveau de la programmation, les droîdes seront programmés dans un langage interprété. On définira un langage simple, de niveau plutôt élevé et structuré. Ce qui est sûr c'est qu'on doit disposer:

Les fonctions proposées d'office seront des trucs du genre:

Le coup d'envoyer un mail, c'est vaguement sérieux: on peut très bien imaginer que l'on programme ses robots sur la défensive puis on passe le programme en background, voir carrément on se déloggue et le programme continue à tourner dans son coin. Du coup, hop, on va bouffer au RU et puis on revient et on regarde son mail: un des robots t'envoie un mail pour te dire qu'ils sont attaqués et qu'il faut que tu ailles voir. Et la tu relance l'interface graphique du programme et tu regarde la situation sur le plan -- voir plus loin sur l'implémentation. Eventuellement, le robot qui a considéré que l'attaque était trop violente peut également mettre le jeu en pause de son coté (il a le droit car il n'y a personne pour superviser derrière).

Autre possibilités: se rendre (en cas d'attaque), consulter la carte (chaque droîde à un plan en mémoire pour aller d'un endroit x1,y1 à un autre endroit x2,y2), voire demander à un autre droîde le chemin à suivre.

Il faut également étudier la possibilité pour un droîde d'aller se faire capturer par un autre de façon à l'infiltrer. L'idée serait que le droîde, lorsqu'il se fera reprogrammer par l'ennemi, garde un bout de programme qui pourrait être déclenché par un droîde ami ou un événement extérieur.

Pour faire cela, on pourrait également autoriser un droîde à masquer son champ de joueur et à le modifier par le nom d'un autre joueur: il garderait son programme et pourrait ainsi s'infiltrer chez l'ennemi. Une parade consiste, pour un joueur, à placer une donnée clef dans ses droîdes et à obliger chaque droîde à vérifier si ses congénères possèdent la même. Un tel droîde est encore détectable par le fait que ses fonctions ne possèdent pas le même nom que celles des droîdes du joueur ennemi. Or un droîde peut demander à un autre la liste de ses fonctions ou le buter s'il ne veut pas les donner.

2.4. Salles et cartes

Au niveau des cartes des forteresses, un droîde doit pouvoir ajouter un fragment de carte à sa propre carte. Une fonction sera prévue pour ça. A chaque carte de chaque droîde sera associée une date de création. Un droîde pourra ainsi savoir si la carte d'un autre droîde est plus récente ou pas et fusionner automatiquement les cartes pour les mettre à jour.

Quand aux salles, elles se verront attribuer une adresse. Chaque forteresse disposera d'un numéro unique et du nom de son joueur. Les deux informations formeront une paire unique (le numéro pourra être distribué par le programme maître, cf. implémentation). Chaque salle, au sein d'une forteresse, disposera d'un numéro (ordonné selon la création et unique) ainsi qu'un nom de salle, une classe de fonction (par exemple cette salle s'appelle "médor" et est de classe "stock d'armes"). Intérêt: pouvoir ordonner à un droîde de se rendre dans la salle la plus proche de type "stock d'armes", sans se préoccuper de laquelle il s'agit.

Chaque salle contiendra des objets.

Mais il serait également intéressant de doter chaque salle d'une file de messages que chaque droîde peut laisser, consulter ou prendre (lire puis effacer). Ces messages devraient pouvoir comprendre un bout de programme. Exemple: si la salle d'armes "médor" est vide, laisser un message qui envoie tout droîde cherchant des armes sur la salle "méveille".

2.5. Le déroulement du jeu

C'est un point que je n'arrive pas encore complètement à formaliser, quoique j'ai une vague idée de ce que je veux.
On a déjà parlé de l'énergie des droîdes. D'où vient-elle ? D'où viennent les droîdes. Voici ce que j'en pense.
Tout d'abord, le jeu commence avec une salle, un droîde bourré d'énergie et de force, un téléporteur et un objet que l'on nommera un "distributeur éternel d'énergie". On pourrait également le nommer "Générateur d'Energie Infinie" mais ça fait un peu Ford Escort.
On va également dire que le lots d'appareils de départ comporte une perforatrice. C'est pas pour faire des cartes perforées mais pour creuser des couloirs et des salles.
La première chose à faire sera donc de programmer notre unique droîde afin qu'il prenne la perforatrice et creuse un trou dans le mur (un couloir) puis y colle une salle. Ca pourrait être un programme comme ca:

> mode immédiat:
> faire
> chercher: quoi=perforatrice / où=ici
> tant que chercher==pasTrouvé
> prendre: quoi=perforatrice / où=ici
> creuser: avec=perforatrice / quoi=couloir / direction=NE / distance=5m
> creuser: avec=perforatrice / quoi=salle / direction=NE / taille=10m
> appeler: qui=joueur / dire="pfff, c'est fini"

Le problème qui me vient à l'esprit, c'est comment faire apparaître au cours du jeu, selon un mode progressif, des armes et des objets de plus en plus sophistiqués.

L'idée qui m'est venue à l'esprit consiste à dire que:

  1. quand on creuse une salle, celle-ci se trouve automatique dotée d'un nouvel objet.
  2. en 2026, il y a eu une guerre termos-de-café-nucléaire, suivie d'un tremblement de terre, d'une poussée hitech-tonique, d'un retournement d'ongle et tous les combattants sont morts en laissant derrière eux des salles bourrées d'armes. Résultat: il y des salles bourrées d'armes et d'objets, et en creusant des couloirs, on peut tomber dessus par inadvertance (sur les salles bourrées d'armes, compris ?)
  3. on fixe une somme initiale et on donne d'office un objet qui sert à fabriquer des objets. Seulement, il ne peut en faire qu'un à la fois, ça coûte de l'argent et en plus ça prend du temps. Cette méthode ne résout pas le problème puisqu'elle ajoute celui de savoir comment trouver cet argent.

Dernière idée [16 Août 1995]:

  1. certains droîdes ont pour rôle de développer le niveau du joueur dans certains domaines : ce sont des chercheurs. Classiquement (cf. jeux de stratégie), le développement pourrait se faire selon les axes industrie (capacité de production), science (type d'armes et puissance) et politique (capacité à convaincre d'autres droîdes à changer de camps).

Parmi les objets dont on devrait disposer, j'en vois un certains nombre qu'il faut classer selon le développement de la forteresse, sinon ça n'a aucun intérêt.
Cela signifie qu'au cour de je ne sais pas quel événement (i.e.; idée n°4 ci-dessus), les joueurs vont prendre du grade et ainsi accéder à des objets/armes plus puissants.
Cela pose encore un problème: un joueur novice peut-il s'attaquer à un joueur confirmé ? En se basant sur le principe des serveurs (voir l'implémentation juste après), un joueur peut très bien se retirer du jeu, sauver puis reprendre la partie en cours.
Donc un nouveau joueur, ou commençant une partie à zéro, peut très bien voir surgir un téléporteur en provenance d'un joueur confirmé. Ou alors il peut se téléporter chez un joueur confirmé. Ca revient au même.
Il y a deux solutions; la première: on passe un paramètre au téléporteur pour lui indiquer de ne choisir, aléatoirement, qu'un joueur de même niveau ou inférieur ou supérieur. L'autre solution, c'est que le programme n'autorise les accès que des joueurs de même niveau. Si ce n'est pas possible (pas de joueur de niveau similaire), on en prend un au hasard dans les niveaux les plus proches.

Reste à définir ce qu'est ce niveau de développement et comment il augmente: par le combat, le temps passé, etc.

Voici une liste d'objets, donnée en vrac. Chaque objet sera simplement décrit comme appartenant à un mode "d'office" (on en a un au départ), ou "novice" (ou les acquiert très tôt) ou "moyen" ou "confirmé". Je ne sais pas encore comment on trouve ces objets.

2.5.1. Distributeur Eternel d'Energie (DEE)

Objet d'office.
L'objet Distributeur d'Energie peut être accédé par n'importe quel droîde. Si celui-ci lui demande de l'énergie, l'autre lui en fourni, quelque soit la couleur, le joueur auquel appartient le droîde.

Si on veut, on pourra déplacer le DEE mais il restera unique.
Supposons un droîde qui se trouve à l'autre bout de la forteresse. Une alarme se déclenche car son énergie est inférieure à 25%. La solution pour ce droîde est donc de retourner au DEE s'alimenter en énergie. Mais si c'est un combattant ou autre, il a une tache a accomplir et celle-ci se trouve donc inutilement interrompue.
L'idée est donc de programmer un droîde qui ne sert à rien (ou à la limite qu'à ça) pour aller au DEE, se charger, aller à l'autre bout de la forteresse et demander si certains on besoin d'énergie (et l'échange). C'est là l'utilité de l'échange d'énergie et notre petit droîde est donc un porteur d'énergie.

Dans la foulée, on vient d'inventer le besoin de batteries, objets se trouvant au niveau moyen. Deux classes de batteries: portables (ne s'use que si l'on s'en sert) et fixes (une fois rechargées au DEE, on les porte dans une salle éloignée et puis quand elles sont vides, on les rapporte temporairement au DEE - on peut bien sur créer un canal téléporteur pour les transporter, ça va plus vite).

2.5.2. Téléporteur

Objet novice (il y a en un d'office).
C'est la pièce centrale, fournie d'office. Il fonctionne selon deux modes: mode connu ou mode hasard. Le mode connu implique de posséder un plan de l'endroit où l'on veut aller, même partiel (on ne pourra aller que dans les endroits définis). Le mode hasard choisi le joueur au hasard et la position au hasard.
Il est a remarquer que le mode connu du téléporteur peut s'appliquer sur sa propre forteresse: on peut créer un canal de téléportation dans sa propre forteresse pour joindre deux coins éloignés (même proche, à la limite).
Enfin, point important, un canal téléporteur est bidirectionnel. Donc lorsque l'on ouvre un canal chez un ennemi, cet ennemi peut s'y engouffrer s'il a été programmé pour : utiliser le téléporteur se fait comme prendre un couloir dans une certaine direction mais nécessite une instruction spéciale.
Un droîde qui passe à coté d'un téléporteur (i.e. se trouve dans la même salle) disposera d'une instruction spéciale pour regarder à travers le téléporteur. Cela lui permettra de voir la salle où est atterri le téléporteur mais également de voir la salle qui est à l'origine d'un téléporteur qui surgit brusquement. La fonction renverra un flag indiquant si le droîde connaissait, oui ou non, la salle qu'il vient de voir. Si ce n'est pas le cas, il disposera dès lors d'un fragment de carte qu'il pourra transmettre.

2.5.3. Kit de réparation

Objet novice mais pas d'office.
Le droîde qui porte cet objet peut réparer d'autres droîdes. Mais cela l'immobilise complètement, lui et le droîde à réparer. Ce droîde peut porter des armes mais ne peut pas les utiliser pendant qu'il répare. Il peut interrompre sa réparation pour se battre.

2.5.4. La perforatrice

Objet novice. Il y en a un d'office.
C'est un objet assez lourd et peut maniable. Il est tellement peut maniable qu'il ne peut creuser que dans 8 directions: N, NE, E, SE, S, SO, O et NO. En plus cet objet a besoin d'être alimenté en énergie (auprès du DEE ou d'un autre droîde ou une batterie). Par contre, la construction est assez rapide.

2.5.5. Armes

Des armes de novices: pistolet, fusil au plomb, escopette, arbalète (??!)
Des armes moyennes: un fusil moyen, un fusil plus efficace, une mitraillette (fusil à tir en rafale). Egalement une mine peu efficace (ne fait que blesser par exemple), bombes à retardement (une mine est invisible, une bombe ne l'est pas), grenades (plus petit qu'une bombe et on la jette).
Des armes confirmées: lance-missiles, mine à portée moyenne, mine à portée longue, mine sélective (programmable suivant la couleur du droîde), oxydant métalliques, etc.

2.5.6. Armes exotiques

De la glue, des aimants, des électro-aimants (les droîdes sont composés d'une structure métallique en fer doux, dirons nous). Des allumettes, de l'essence, sans parler des lance-flammes (j'ai dit sans parler des lance-flammes). Des peaux de bananes (ça gliIIIIiisssssss'aaAAAAAAAaaargh! booum!), des cordes en Nylon très fin (je les mets en plein passage, boum il tombe et ça casse : degats++).

2.5.7. Objets sophistiqués

Objet confirmé: un radar très longue portée qui donne une estimations de l'existence des autres joueurs, leur niveau, et indique les limites de leurs adresses (nombre de salles).
Objet confirmé: radar courte portée pour voir à travers les murs. On voit le plan et la position/couleur des droîdes. On ne voit pas comment il sont armés ni ce qu'ils portent. Portée limitée quand même.
Objets confirmés: détecteur de mines (elles sont totalement invisibles), antioxydant, obscurateur de salle (on n'y voit plus rien or les droîdes communiquent à vue !), anti-obscurateur de salle (on passe en vision infrabidulle et du coup on y voit mieux).

Autres objets confirmés: déprogrammateur de droîde à portée [très] courte ou [très] longue (comme un téléporteur, sauf qu'on indique la forteresse, la salle et le nom du droîde et plouf! il se trouve déprogrammé).

Egalement, non pas un anti-déprogrammateur, mais, plus fort!, un anti-actions à distance: empêche et informe que quelqu'un tente de faire quelque chose à distance sur le droîde (déprogrammer ou autre) et en plus indique précisément qui (mais ne l'indique que si on avait connaissance de la carte de l'endroit où se trouve l'autre droîde).

2.6. Implémentation

Chaque joueur se trouvera sur une machine UNIX. Il est clair, dans mon idée, que c'est sous Unix qu'il faut développer la chose.
Coté graphique, on arrive ainsi naturellement à du X11 mais pas question de sprites qui s'affichent dans la VBL, du coup. Il faudrait en fait séparer le jeu en deux parties, indépendantes:

  1. une partie interface, mi-graphique, mi-texte. Le graphique servirait à définir/voir les plans de la forteresse, l'état des droîdes et leur déplacement sur la carte (en temps réel, si possible). Le tout avec une vue de dessus serait déjà suffisant. On range la 3D dans le placard, ou alors on compile le programme sur une Silicon Graphics (et on colle un patch dans le programme pour lui interdire de tourner sur une STAFx). Quand à la programmation, on pourrait se contenter d'appeler VI ou EMACS (ou Textedit) en background pour éditer un fichier. L'idéal serait un éditeur/déboggueur du programme dans le jeu, mais c'est pas simple à faire (le déboggueur, si, ça va encore, mais l'éditeur resterait forcement un peu frustre face à un Textedit ou un EMACS -- le plus simple étant de pouvoir définir son éditeur, au choix). Un éditeur interne devrait être du genre GFA-Basic: indentation automatique et vérification des phrases lors de la frappe. Avec une aide (même sommaire) en ligne sur les fonctions intégrées et une liste déroulante de fonctions soit intégrées soit définies: l'utilisateur devrait pouvoir se créer des bibliothèques de fonctions types et les coller sur un droîde (pour les modifier ensuite, selon les circonstances). Ce programme sera ensuite nommé le client graphique. On devrait pouvoir lancer plusieurs clients graphiques pour le même client de calcul (voir (b) ci-dessous). Dans un but de supervision, il devrait exister un client graphique non-graphique, i.e. en mode texte, juste histoire d'avoir un oeil sur le truc, un historique, etc.
  2. ensuite j'imagine bien le programme lui-même tournant dans un process bien séparé de l'interface: les deux programmes peuvent communiquer part des envois de messages ou une socket (UDP ou TCP, au choix). Cela permet ainsi de faire disparaître l'interface tout en autorisant le jeu à continuer. Ce programme sera nommé le client, tout court.

Un programme client peut se trouver dans quatre états (un seul à la fois):

Nota: le cas où le joueur arrête la partie sans sauver est équivalent au cas interrompu.

  1. quelque part sur le réseau, il doit y avoir un maître: un programme qui reçoit toutes les requêtes des clients (les joueurs) afin de les coordonner et de les renseigner. Ce programme serait également basé sur une socket. Le plus fiable est ici une socket UDP, afin de pouvoir sortir des limites du système. Ce programme n'a aucune interface (tout au plus du texte de débug).

Intérêt du programme maître me semble évident: on lui donne une adresse IP connue (par exemple un serveur sur un réseau). Chaque client (le programme qui exécute les calculs d'un joueur) va se manifester, lors de son lancement ou de sa mort, auprès du maître. Ce dernier va en contrepartie lui indiquer quels sont les joueurs actifs.

2.6.1. Petit scénario

Le joueur A veut se téléporter sur le joueur B.
Le client A commence par demander au maître si le client B se trouve encore actif.
Si ce n'est pas la cas, la téléportation échoue. (On prévoira une fonction spéciale demandant si de nouveau joueurs sont apparus).
Si le client B existe mais se trouve en mode pause, alors le téléporteur l'indique: le client n'est pas mort, il est simplement en attente (parce que le joueur est parti en cours, probablement). J'en discute plus loin.
Donc le client B est actif, pas en pause ni rien. Le téléporteur se met donc en place: le client A envoie un message au client B (directement, sans passer par le programme maître, à grand coups de sockets UDP) et, en cas d'adresse random, le client B choisi une salle au pif et l'indique au client A. Le téléporteur est désormais actif, le plan de la salle choisie est transmis au droîde ayant initialisé le téléporteur.
Maintenant, si le joueur A envoie un droîde à travers le téléporteur, que se passe-t-il ? On dispose de deux possibilités: le code du droîde A va soit s'exécuter sur le client A, soit s'exécuter sur le client B.
S'il s'exécute chez A, le droîde se trouvant dans l'environnement de B, il va falloir une intense communication (en termes de messages/sockets) entre les deux clients pour que le client A puisse faire évoluer le droîde A chez le client B. En plus le client B a besoin de la position et des paramètres du droîde A pour ses propres calculs. Cela surcharge quelque peu le réseau.
Donc supposons que le droîde A s'exécute chez le client B: lors de la téléportation, le programme et les caractéristiques du droîde sont figées chez le client A puis transmises chez le client B: le code ira donc s'exécuter physiquement chez B. Ca devrait aller plus vite et supprimer les échanges de messages. On remarquera que le client A ignore alors tout de son droîde, sauf qu'il a été téléporté chez B. Ca me parait d'autant plus raisonnable et vraisemblable (au niveau du jeu). C'est ce que je prendrais comme solution.

Petit détail: si le droîde A meurt chez B (ou est récupéré par le joueur B), le client B en informe-t'il le client A ? Oui car le client A garde une copie des droîdes téléportés (ainsi que où ils le sont). Si le droîde A meurt, alors la copie du droîde A chez le client A disparaît.
Autre détail amusant: le client B disparaît (core dump par exemple). Tous les droîdes de A sont-ils donc perdus ? Non, simplement, on dira qu'ils reviennent chez A.
Pourquoi ? Simplement parce que le maître envoie des requêtes aux clients reconnus de temps à autre. Le client B répond au maître, qui sait alors que le client B est encore vivant (ceci est, à mon humble avis, plus simple à dire qu'à implémenter puisqu'il faut tenir compte des éventuelles pertes de messages sous UDP). Lorsqu'un client B est reconnu comme mort par le maître, ce dernier va consulter la liste des droîdes des autres clients (le client A, en l'occurrence) qui se trouve chez le client B mort (nota: cette liste nécessite simplement une entrée: pour chaque client B, on veut savoir si tel ou tel autre client A à téléporté au moins un droîde, sans savoir combien ni lesquels). Le maître informe dès lors le client A que le client B est mort. Le client A peut dès lors récupérer ses droîdes (car il garde sur sa liste les droîdes téléportés, simplement il place un flag "est téléporté chez B").
Remarque: le maître n'a pas vraiment à savoir qui a téléporté quoi chez qui. Il suffit que le maître, lorsqu'il constate que le client B est mort, envoie une requête à tous les autres clients.

Reste le problème des requêtes, d'une manière générale: la communication sous UDP est non fiable. Le problème est que si l'on considère un réseau normal, par exemple le réseau SUN/VAX de l'UTC, alors ce réseau est fiable du point de vue UDP. Mais, en supposant que je place un client sur 'suna' et un maître sur 'sunser', si 'sunser' tombe en rade, le client 'suna' ne le saura jamais (puisque c'est du UDP). L'avantage c'est qu'une communication UDP est plus "universelle" qu'une communication TCP. Bref, c'est typiquement là un problème que j'aurais aimé voir résolu de façon simple et efficace en LO05.

Enfin, dernière précision: si un partie du réseau tombe en rade et que le client B ne peut plus communiquer avec le maître, le maître va le croire mort. Or il n'est pas vraiment mort, simplement (temporairement ?) inaccessible.
Résultat: le maître va demander au client A de récupérer ses droîdes, ce que A va s'empêcher de faire.
Mais que se passe-t-il si B revient sur le réseau ? Il dispose toujours des copies des droîdes actifs de A dans sa forteresse, alors que A utilise pendant ce temps la copie des droîdes qu'il venait de récupérer.
La solution me semble être la suivante: si le maître constate la mort puis la renaissance d'un client B, il indique à B qu'il la considéré comme mort -- et donc que les droîdes A chez B sont annulées.
En fait, ce n'est pas une super solution car le joueur A perd les droîdes qu'il a eu tant de mal à placer chez B. D'où...

Autre solution:

Si le client A téléporte des droîdes A chez le client B mais que le client B meurt ou devient inaccessible (c'est presque pareil), le maître en informe le client A qui décide de prendre:

  1. soit la solution précédante (on récupère les droîdes A),
  2. soit on fige les droîdes A chez B. On les considère comme étant toujours téléportés. Ainsi, lorsque B revient à la vie, les droîdes de A sont tjrs chez B.

De plus, si le client B se trouve coupé du maître, il peut éventuellement toujours arriver à communiquer avec A ? Hummm, non. Car en UDP, si B peut aller vers A et que A peut aller vers le maître, alors B peut aller vers le maître aussi.
Bon, OK, mais si le client B est coupé du reste du réseau, il le sait car il ne reçoit plus les requêtes actif/mort du maître (par timeout). Il doit donc pouvoir décider s'il veut:

  1. figer (et sauver) l'état de sa partie,
  2. continuer à se battre dans sa forteresse si des ennemis y ont pénétré. Maintenant, cette solution (b) n'est pas fortement fair-play car le joueur A ne peut modifier les algos de ses droîdes.

Au fait, je considère qu'un joueur A à un droit de vue sur ses droîdes lorsqu'ils sont téléportés chez le joueur B: il ne voit le plan que du point de vue du droîde (un droîde voit par défaut la salle où il se trouve et se construit "mentalement" le plan lorsqu'il parcoure les salles). De plus le joueur A peut suivre l'évolution de son droîde (état, énergie) et le reprogrammer à tout instant. Simplement, au niveau du plan de la forteresse B: ce plan n'est connu que du droîde A qui se trouve chez B. Les autres droîdes de A ne le connaissent pas, à moins que le droîde A ne rencontre un autre droîde A et ne lui transmette le message.

Pour éviter les "abus", on dira que le téléporteur ne peut être lancé que sur un terrain connu à partir d'une carte ou alors au hasard. Je pense au fait que le joueur A envoie un droîde chez B. Le droîde A repère une position intéressante pour y placer la sortie du téléporteur. Le joueur humain pourrait utiliser (frauduleusement) le fait qu'il supervise le tout pour donner les bonnes coordonnées au droîde qui manipule le téléporteur -- alors qu'il n'a pas le droit car ce droîde là ne connaît pas le plan de la forteresse B. Conclusion: il faut que le droîde A qui se trouve chez B revienne dans la forteresse de A, transmette le plan de B au droîde qui porte le téléporteur A et ensuite on peut y aller précisément. Sinon, un téléporteur qui ne connaît pas la position d'arrivée va atterrir complètement au hasard chez B (et s'il y a du monde, c'est dommage).

2.6.2. Problème de l'interruption

Lorsque le joueur A veut quitter la partie pour la reprendre plus tard mais qu'il a envoyé des droîdes chez B et C, comment s'y prendre ? En effet, B et C doivent pouvoir continuer à jouer même en l'absence de A.
Ce problème est nommé "problème de la sauvegarde asynchrone".
Comme le code des droîdes de A se trouve chez B, on peut dire que:

La dernière solution me semble élégante mais pas satisfaisante: si B connaît la position des droîdes figés de A, il peut les entourer d'une armée bien solide (par exemple 1 droîde A pour 10 de B) et lorsque A reprendra son activité, il aura du mal à assurer le coup.
On peut également forcer B à mettre au plus autant de droîdes B dans les salles où se trouve un droîde A qu'avant l'interruption. Je m'explique: si le droîde A se trouve dans une salle B avec 2 droîdes B au moment de l'interruption, alors, pour la reprise, il faut 2 droîdes B au même endroit et dans les mêmes conditions (armement et force). Et aucun des droîdes ne doit être en combat avec A. Si B ne peut plus mettre autant de droîdes en position, alors il peut en mettre moins, mais pas plus.

Sauvegarde synchrone

Tout cela n'empêche pas qu'on doive disposer d'une méthode de sauvegarde synchrone qui permette de détecter que:

Le problème est de savoir si une sauvegarde synchrone doit donner lieu à un chargement de partie synchrone (toutes les parties présentes). Non: les parties absentes seront gérées comme dans la sauvegarde asynchrone.

/*------------------------ fin du texte "NETDROID.TXT" --------------------*/


3. Seconde partie

R'alf

Le 02/02/94

3.1. Avant Propos

Ce fichier la ne devra pas contenir d'accents. Ca me permet de l'éditer même quand je ne suis pas sous DOS (i.e. Linux ou OpenWindows, qui ne les gèrent pas tout a fait de la même façon).
Entre les deux plates-formes (DOS/Linux), j'ai un gros problème avec les retour chariots (car le ^M n'est pas utilisé‚ sous Unix). Mais bon, c'est pas grave.
Au fait, plus j'utilise EMACS, et plus je le trouve génial. Je parle bien sur de la version complète d'EMACS et non pas de MicroEmacs (que je considère comme aussi agréable a utiliser que Windows Write, c'est dire -- en tout cas c'est tout autant utile).

3.2. Contenu

Je n'ai pas créé ce fichier pour dire que EMACS vaincra, non. Je ne suis pas un Amigaiste, moi, et je ne me bats pas pour les causes méconnues (mais pas perdues).
Ce fichier s'appelle NETDROI2.TXT. Son nom complet serait plutôt NETDROID2 mais je n'ai pas assez de place sous DOS.
Le but de ce fichier est d'expliquer une éventuelle implémentation de NETDROID. Il s'agit la plutôt d'un coup d'essai littéraire. Pas question d'écrire du code là-dessous, ou alors pas de façon formelle.
Ainsi, dans un premier temps, je pense que je parlerais de quelques réflexions que j'ai eu au niveau du réseau, histoire de dire que LO05 est une UV qui peut servir a quelque chose.
Dans un second temps, je pense que je me pencherais sur un langage simple a utiliser pour programmer les droîdes. L'ébauche que j'avais faite dans le fichier NETDROID me plaît. J'attends l'avis du Fakir la dessus.

3.3. Réseau maître/clients

Je ne rappellerais pas ce que je désigne par maître et client: le fichier NETDROID l'explique suffisamment bien. Ici, j'aimerais tout d'abord traiter du problème de UDP comme protocole de connexion et j'aimerais ensuite discuter de la façon dont j'implementrai l'échange de messages entre clients et maîtres via UDP. (Au fait, j'ai écrit "maîtres" au pluriel, ce n'est pas une erreur, j'y reviendrais après).

3.3.1. Des sockets pour communiquer

Bon, tout d'abord, mon idée est de me baser sur des sockets UDP. Je rappelle pour les novices qu'il y a principalement deux types de sockets...
Hum, avant tout, qu'est-ce qu'une socket (un socket ?) C'est un moyen de communication dont dispose UNIX (enfin, ceux qui implémentent les IPC, c'est a dire pas tous, mais maintenant ca devient standard: SUN, Linux, etc. le font donc ca me suffit amplement. Même VMS en propose, c'est dire.) Avec une socket, deux programmes peuvent s'échanger des messages (des paquets de caractères). Ces programmes peuvent se trouver sur la même machine ou sur des machines différentes, voir éventuellement sur des réseaux différents (via Internet).
Avec ca, on a deux types de connections: mode connecte ou non connecte.
Le mode connecte correspond a des sockets de TCP: les deux programmes ouvrent leur socket (l'un en serveur, l'autre en client) et la connexion marche, du point de vue du programme, comme un fichier (un "stream"); on est garanti par le système que les octets (on compte ici en octets) arrivent dans l'ordre d'émission et qu'il n'y a pas de perte ni d'altération.
L'autre mode, non connecte, est quelque peu diffèrent: le serveur du socket "écoute". Lorsqu'un client se connecte, il envoie un message de taille fixe au serveur; le serveur le traite et peut renvoyer un message dans le sens inverse. Ca c'est UDP (D comme Datagram). Ici, on ne sait pas si un paquet émis par un client arrivera réellement a destination (il peut se perdre), et on ne sait pas au bout de combien de temps. Deux paquets peuvent arriver dans un ordre diffèrent de l'ordre d'émission.
UDP est donc clairement non fiable, au contraire de TCP. Alors pourquoi le choisir ? Simplement parce qu'une connexion UDP a plus de possibilités d'être "universelle". C'est a dire qu'entre deux sites différents (par exemple, Orsay et l'UTC), on aura plus de facilite a mettre en oeuvre UDP que TCP: il n'est pas dit que la socket TCP veuille bien se connecter, alors que la socket UDP y arrivera plus facilement. D'autre part, en cas de rupture de liaison, la connexion TCP est rompue: la connexion s'établit un chemin fixe dans le réseau pendant tout le temps ou elle est active. Par contre, avec UDP, un message peut suivre un chemin diffèrent du message précèdent ou suivant -- tout dépends des sites traverses qui vont router le paquet sur la voie la moins chargée et la plus accessible, mais pas forcément la plus rapide. Ceci explique que des paquets UDP puissent se perdre ou être inverses.
Ceci dit, je ne désespère pas: UDP est non fiable et non ordonne, mais je ne pense que seuls une minorité des paquets transitants sont affectes. Toujours est-il que l'usage de UDP implique l'usage de protocoles vérifiant l'arrivée des paquets, et ce dans le bon ordre. C'est pour cela que j'ai fait LO05 et je vais tenter de m'en servir.

3.3.2. Deux couches pour le réseau

Pour utiliser UDP, je vais transmettre des messages de taille fixe. Les fonctions de messagerie d'UNIX permettent de traiter facilement des structures de taille connue dont le premier mot long indique le type. Ces fonctions sont msgrcv, msgsnd, etc. Elles permettent également de vérifier qu'un message est arrive ou de filtrer les entrées pour ne lire que les messages de tel type. J'ai déjà utilise des files de messages en LO05 et je dois dire que c'est ce qu'il y a de plus simple, fiable et rapide a mettre en oeuvre pour établir une communication père/fils (plus simple que les pipes, je trouve).
Ici on transmettra donc des messages, que j'appelle indifféremment messages ou paquets, au fait. Il est a noter qu'il faudra tenir compte de l'architecture différentes des machines, i.e. Big-Endian ou Little-Endian. J'en reparlerais plus tard.
C'est messages vont être traites par deux couches bien distinctes: une première couche de fonctions qui permettront de s'assurer que les paquets sont bien transmis et dans le bon ordre, et une seconde couche de fonctions qui supposent le réseau fiable et ordonne (fifo) et qui effectueront un routage automatique entre les maîtres et clients.
Je les appelle respectivement couche de réordonnement et couche de routage.

3.3.3. Couche de réordonnement

Elle sera base sur un algo brut, en plomb massif dirais-je, vu en LO05: l'algo (très) bourrin du send-and-wait. Le principe est simple: on donne un numéro a chaque paquet. Quand un site A veut communiquer avec le site B, il lui envoie un paquet numérote 0 qui contient des donnes, par exemple. Lorsque B renvoie le paquet, il doit renvoyer a A un paquet numéro 1 mais qui ne contient qu'un acknoledge. Si A ne reçoit pas le paquet 1 au bout d'un certains temps, il réémet le paquet 0.
Avec ce principe simple, les paquets de données ont tous des numéros pairs et les paquets d'acknoledge des numéros impairs.
On remarquera que ce protocole est classe comme très mauvais au niveau de l'optimisation de la largeur de bande du réseau: elle est clairement sous exploitée. Le but est ici de faire simple mais fonctionnel.
Comme je le disais précédemment, je compte sur la fiabilité des connexions Internet pour que ce protocole ne soit pas trop souvent mis a l'épreuve.
Problème a régler: la désynchronisation d'un des sites. Si B ne reçoit pas le paquet de données 0 et envoie un paquet de données a lui, alors il va attendre un packet ack et le site A fera de même !
Pour résoudre le problème, on place une horloge dans les paquets et le paquet le plus ancien est le plus crédible. Ainsi, si A envoie ses données avant celles de B, lorsque B reçoit les données de A, il considère que ses données sont moins prioritaires: il envoie l'ack puis réémet sont paquet de données. Je voie déjà le problème: si l'ack se perd, faut-il mettre un ack sur l'ack ? Je m'en vais consulter le poly de LO05 pour vérifier mais je ne sais pas si ca a été prévu.
[...] <-- to be continued

3.3.4. Couche de routage

La, c'est très simple puisqu'on considère que le réseau est fiable et fifo (i.e. ordonne). Je dispose d'un super protocole de routage que j'ai mis en oeuvre dans le TP3 de LO05 et qui marche nickel (avec des files de messages et des forks). Il suffit juste de l'adapter un peu.
[a dire: lorsqu'un client veut parler a un autre client, il forke un process qui se trouve être la couche de réordonnement et le client et le sousprocess communiquent avec une file de messages. Ca permet a la couche de réordonnement être bloquée en attente de messages.]
[...]

3.3.5. Structure des messages

Et j'explique qu'un message c'est un type (mot long 32 bits) suivi d'octets de données. Je formate le tout en Big-Endian.
Pour le mot long de type de msg, est-il automatiquement converti en Big-Endian/Little-Endian lorsqu'il est reçu par une machine ? Je ne pense pas. Idée est alors de ne mettre que des mots longs du style XYXYXYXY (hexa bien sur) comme identifieurs. Ca laisse quand même 256 types de messages différents et ca devrait suffire.
[...]

3.3.6. Plusieurs maîtres

Il y a un maître par site et chaque maître gère plusieurs clients. Par exemple on colle un maître sur "sunser" et trois clients sur "suna", "sunb" et "sunc".
Supposons qu'un ami a moi se trouve a Orsay. S'il lance un client, ce dernier peut tenter de se connecter sur "sunser" -- il y arrivera si FTP est accessible sur sunser, i.e. que le réseau SUN de l'UTC est ouvert sur l'extérieur. Mais maintenant, si ce n'est pas le cas ?
Et bien la solution c'est de lancer un programme maître sur le réseau VAX, qui connaît a la fois l'extérieur et le réseau SUN. Ce programme serait également un maître qui aurait un client se trouvant a Orsay.
Idée c'est que les maîtres forment un ensemble de sites de routage: si le client d'Orsay veut "parler" avec le client de "suna", il envoie le paquet a son maître qui va router le paquet sur le maître de sunser qui va lui même donner le paquet a "suna".

La question est de savoir si les clients doivent uniquement traiter avec leur maître ou s'il peut y avoir des communications client-client directes. Je pense que le plus simple est de dire que les clients communiquent a travers eux VIA les maîtres et ce uniquement. Ensuite les maîtres peuvent fonctionner comme selon l'algo de routage vu en LO05 -- qui marche très bien lui, puisque je l'ai mis en oeuvre dans le TP3 avec un franc succès.
Cet algo trouve toujours un chemin minimal dans le graphe de connexion et, avantage suprême, on peut insérer ou supprimer dynamiquement des noeuds pendant qu'il tourne: chaque site remet ses tables a jour comme il le faut. L'unique inconvénient c'est qu'il est base sur l'usage de tableaux et donc nécessite en théorie de savoir combien de sites max. il y a. En fait on détourne le problème en disant que ce tableau peut dynamiquement croître (a grand coup de realloc) et que sa taille max. est de 2^31, ce qui est probablement suffisant pour ce que je veux en faire [ ;-) ].
[... ]

3.3.7. Section Finale

Et voici quelques définitions pour EMACS (elles doivent se trouver a la fin du fichier texte, il les charge automatiquement lorsqu'il charge le fichier) :
< Local Variables: >
< mode: text >
< fill-column: 78 >
< End: >
Voila, c'est fini.

/* fin du fichier NETDROID2.TXT */


Home | BeOS | PowerPulsar | Ballades | Photos | Tourbillon | Favourites | Bookmarks |

[webcounter] counts WebCounter.


Last update : 12/07/1999