Net DroîdsHome | BeOS | PowerPulsar | Ballades | Photos | Tourbillon | Favourites | Bookmarks | |
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
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.
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.
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.)
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:
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
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.
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".
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:
Dernière idée [16 Août 1995]:
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.
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).
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.
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.
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.
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.
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++).
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).
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:
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.
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.
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:
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:
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).
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" --------------------*/
R'alf
Le 02/02/94
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).
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.
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).
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.
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.
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
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.]
[...]
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.
[...]
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 [ ;-) ].
[... ]
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 */
|
|
counts WebCounter.