URL:     https://linuxfr.org/news/gameshell-le-retour
Title:   GameShell, le retour
Authors: phyve
        palm123, bobble bubble et Benoît Sibaud
Date:    2021-06-25T15:43:22+02:00
License: CC By-SA
Tags:    jeu, sérieux et shell
Score:   3


Il y a quelques mois, je postais une [dépêche](http://https://linuxfr.org/news/gameshell-apprendre-les-rudiments-du-shell-en-s-amusant) décrivant “GameShell”, un jeu que j’avais développé pour enseigner les bases de la ligne de commandes. Cette dépêche avait provoqué discussions, corrections de bugs et suggestions intéressantes.



Trois mois et quelques centaines de commits plus tard, je me permets de faire une petite mise à jour.


Globalement (mais je suis un peu biaisé), le GameShell d’aujourd’hui est nettement mieux que le GameShell d’hier. Ceci a un coût : la taille d’une archive GameShell a été multipliée par 3. On est passé de 44kio à 140kio !
Je ne sais pas comment on traduit “bloat” en français (“boursouflage” ?), mais pour le moment, ça ne m’empêche pas de dormir !


La version précédente avait été testée par plusieurs générations d’étudiants, parfois très inventifs. Alors n’hésitez pas à vous mettre dans la peau d’un étudiant en faisant une partie avant de me faire un retour.
Je suis preneur de toute critique, suggestion, rapport de bug, ticket, contribution et j’en passe.



Si certains veulent créer des missions, je pourrais faire une petite description de l’architecture d’une mission type. Ce n’est pas très compliqué, et une année, j’ai même eu un étudiant qui en a ajouté une comme « question bonus » !

----

[GameShell sur Github](https://github.com/phyver/GameShell)
[README en français](https://github.com/phyver/GameShell/blob/master/README-fr.md)
[archive pour faire une partie](https://github.com/phyver/GameShell/releases/download/latest/gameshell.sh)

----

Principe
========



L’idée derrière GameShell n’a pas changé : il s’agit d’effectuer des « missions » qui correspondent en fait à des opérations usuelles de gestion d’un ordinateur. Par exemple, *« Cherchez le diamant dans le labyrinthe. »* reviendra à chercher l’unique fichier contenant la chaine `diamant` dans une arborescence.


Toutes les opérations se font dans un terminal en utilisant le shell (`bash` en l’occurrence) et les commandes usuelles.


On lance une partie typique avec



```
$ ./gameshell.sh

 ____                      ____  _          _ _
/ ___| __ _ _ __ ___   ___/ ___|| |__   ___| | |
| |  _ / _` | '_ ` _ \ / _ \___ \| '_ \ / _ \ | |
| |_| | (_| | | | | | |  __/___) | | | |  __/ | |
\____|\__,_|_| |_| |_|\___|____/|_| |_|\___|_|_|

                            _
                          _/ \
         _               /    \
        / \_   __       /\/\  /\
       /    \ /  \     /    \/  \
      /\/\  /\    `--./.'-    `-.\
     /    \/  ' ^ _    _  _
   /\ ____..      YY  Y     _   |~  _
  /._/  _ \_        Y  Y   [_]--'--[_]
 / / .'/#\_ `-.    Y  YY   |'|""`""|'|
   .-_/#@##\  `\"" ' Y     | | /^\ | |
  " "'"''"'"'''" '         |_|_|I|_|_|

[mission 1] $
```





et on se retrouve dans une session `bash` quasi normale.



Les seules opérations spécifiques sont celles relatives à la gestion des missions. Elles sont accessibles avec la commande spéciale `gsh` (anciennement `gash`, mais ce n’était pas approprié pour la version anglophone).



 - `gsh goal` pour afficher l’objectif de la mission courante (anciennement, `show`),
 - `gsh check` pour vérifier si la mission courante est finie,
 - `gsh reset` pour ré-initialiser la mission courante. (Pratique quand on a fait une bêtise…)



Il y a d’autres commandes (voir `gsh HELP` pour une liste complète), mais elles ne devraient pas servir au joueur en temps normal.



```
[mission 1] $ gsh goal

 ,------------------------------------------------------.
(_\                                                      \
   |  Objectif                                            |
   |  ========                                            |
   |                                                      |
   |  Allez tout en haut du donjon.                       |
   |                                                      |
   |                                                      |
   |  Commandes utiles                                    |
   |  ================                                    |
   |                                                      |
   |  cd LIEU                                             |
   |    Se déplace vers le lieu donné.                    |
   |    Note : ``cd`` est une abréviation pour "change    |
   |    directory".                                       |
   |                                                      |
   |  ls                                                  |
   |    Liste les lieux accessibles depuis la position    |
   |    actuelle.                                         |
   |    Note : ``ls`` est une abréviation pour "liste".   |
   |                                                      |
   |  pwd                                                 |
   |    Affiche la position actuelle.                     |
   |    Note : ``pwd`` est une abréviation pour "print    |
   |    working directory".                               |
   |                                                      |
   |  gsh reset                                           |
   |    Ré-initialise la mission au début.                |
   |                                                      |
   |                                                      |
   |  Remarque                                            |
   |  --------                                            |
   |                                                      |
   |  Les termes apparaissant en MAJUSCULES dans les      |
   |  commandes sont des méta-variables : vous devez les  |
   |  remplacer par des valeurs (chaines de caractères)   |
   |  appropriées.                                        |
  _|                                                      |
 (_/________________________________________________(*)___/
                                                     \
                                                      ))
                                                      ^

[mission 1] $ ls
Chateau  Echoppe  Foret  Jardin  Montagne
[mission 1] $ cd Chateau
[mission 1] $ ls
Batiment_principal  Cave  Donjon  Grande_salle  Observatoire
[mission 1] $ cd Donjon
[mission 1] $ ls
Premier_etage
[mission 1] $ cd Premier_etage
[mission 1] $ ls
Deuxieme_etage
[mission 1] $ cd Deuxieme_etage
[mission 1] $ ls
Haut_du_donjon
[mission 1] $ cd Haut_du_donjon
[mission 1] $ ls
[mission 1] $ gsh check

Bravo, vous avez réussi la mission 1 !

[mission 2] $
```





Il y a actuellement 42 missions. Pour vous donner une idée, mes bons étudiants arrivent autour de la mission 35 en 2h30.



Lancement d’une partie
======================



La distribution d’une archive GameShell (c’est-à-dire le code GameShell avec une liste de missions) a été simplifiée. Au lieu d’une archive usuelle au format `.tgz` (qu’il fallait donc désarchiver avant de pouvoir lancer une partie), j’utilise une *« archive auto-extractante »* : un fichier unique contenant un entête « script » et l’archive en binaire. L’entête extrait la partie « archive » dans un fichier temporaire, utilise `tar -xf` et lance le jeu. Lorsqu’on quitte, une archive est régénérée et intégrée au script, permettant de reprendre facilement la partie à la mission courante.


Une telle archive est générée automatiquement par github lors des commits sur la branche master:
 https://github.com/phyver/GameShell/releases/download/latest/gameshell.sh
Le code de GameShell contient un script `utils/archive.sh` pour générer ses propres archives si l’on souhaite supprimer des missions, en changer l’ordre, etc.



Internationalisation
====================



Un ticket ouvert sur github demandait si j’avais l’intention de traduire GameShell.



Ce n’était pas au programme, mais Rodolphe Lepigre (qui avait eu l’idée originale de GameShell) a fait une pull-request en disant : « regarde, c’est facile ».



Résultat, j’ai mis mes corrections de TP en suspens et me suis mis à bosser sur GameShell ! (Ne vous en faites pas, j’ai repris mes corrections par la suite...)



GameShell est donc maintenant officiellement disponible en anglais ou français. La langue dépend de votre locale, plus précisément de la variable `LC_MESSAGES`. Si c’est autre chose que `en_..` ou `fr_..`, c’est l’anglais qui sera utilisé.



Sur les systèmes GNU, on peut changer la langue simplement en donnant l’option `-L fr` ou `-L en` au lancement de GameShell. (On peut même donner plusieurs langues par ordre de préférence, style `-L it:fr:en`, mais ça ne sert pour le moment à rien vu que les seules langues supportées sont `fr` et `en` et que tous les messages sont traduits.)


Pour les systèmes non GNU (comme macOS), il faudra spécifier une locale valide à la main :



```
$ LC_MESSAGES=fr_FR.utf8 ./gameshell.sh
```





(Vous pouvez utiliser la commande `locale -a` pour obtenir la liste des locales supportées par votre système.)



Les messages sont choisis par `gettext` et les traductions sont donc indépendantes du code de GameShell ou des missions. Pour ceux qui ne connaissent pas, au lieu de



```sh
 echo "Error: wrong password."
```





le code de GameShell contient



```sh
 echo "$(gettext "Error: wrong password.")"
```





La commande `gettext` va chercher le message correspondant à `"Error: wrong password."` dans la langue courante. Si aucune traduction n’est trouvée, `gettext` renverra son entrée et le message sera donc affiché en anglais.
De la même manière,



```sh
 mkdir Castle
```





est remplacé par



```sh
 mkdir "$(gettext "Castle")"
```





etc.



Outre les fichiers texte donnant les descriptions des missions (ou les messages d’aide), les traducteurs doivent gérer des fichiers du style


```fr.po
..

msgid   "Error: wrong password."
msgstr  "Erreur : mauvais mot de passe."

msgid   "Error: you are not allowed to run this command."
msgstr  "Erreur : vous n'avez pas le droit d'exécuter cette commande."

msgid   "Is this information correct? [Y/n]"
msgstr  "Est-ce que ces informations sont correctes ? [O/n]"

..
```





Aucune connaissance en programmation shell n’est donc nécessaire pour faire ces traductions.



Dépendances
===========



Les versions initiales de GameShell avaient des dépendances surprenantes, notamment



 - Python3
 - `gcc`
 - `x11-utils`



`gcc` et `x11-utils` étaient en fait des dépendances sur des missions particulières, qu’on pouvait ne pas inclure dans une archive.



Les missions peuvent maintenant vérifier leurs dépendances. S’il manque quelque chose, la mission est simplement annulée.



La dépendance sur Python3 a été entièrement supprimée. Les scripts Python ont été, pour la plupart, ré-écrits en `awk`. Je ne dis pas que `awk` est mieux que Python, mais il a l’avantage d’être requis par la norme POSIX et il est installé par défaut sur les plateformes cibles de GameShell. (Linux, BSD, macOS)



Je ne connaissais pas beaucoup (à part l’occasionnel `... | awk '{print $4}'`), mais c’est finalement un langage assez marrant.



Au final, la seule dépendance "forte" pour GameShell est `bash` (mais on pourra bientôt utiliser `zsh` à la place), et si on veut jouer dans une autre langue que celle de Shakespeare, `gettext`. Il faut aussi avoir un système POSIX avec les utilitaires standards (`ls`, `cp`, `tail`, `grep`, etc.)



POSIX
=====



En interne, le code a été modifié pour favoriser les solutions POSIX. La raison est que cela simplifie la gestion des différentes plateformes cibles pour GameShell (Linux, BSD, macOS).



Dans un souci de simplification, la plupart des fonctions utilisées par GameShell sont maintenant externalisées dans des scripts indépendants. (cf dossier `bin/`)


Cela a été l’occasion d’apprendre pas mal de trucs et de voir les avantages (mais aussi les limites) de la norme POSIX.



Normalement, il n’y a plus beaucoup d’endroits où du code spécifique est nécessaire :


 - sauvegarde de l’environnement (obtenir la liste des variables d’environnement et des fonctions du shell de manière portable) : script `bin/save_environment.sh`,
 - gestion des processus (la commande `ps` utilise des options différentes sur les différentes plateformes, et même la partie standardisée n’est pas respectée) : missions du groupe `processes`,
 - des scripts de tests (`auto.sh` et `test.sh`) de missions qui insèrent des commandes dans l’historique à la main (`bash` sait faire ça).



PS
==



Pour ceux qui ont lu jusque-là et qui font une partie, les commandes « admin » (voir `gsh HELP`) ont besoin d’un mot de passe.
Par défaut, c’est « gsh » (comme la commande).
Ça peut servir en cas de bug qui ne permet plus de valider une mission. On peut utiliser `gsh skip` pour la sauter !