RMIGame est programmé entièrement en JAVA, il utilise la technologie RMI. Il dispose d'une architecture 3-tiers, composée d'une partie client, une partie serveur et une dernière base de données. Nous allons décrire l'architecture du système distribué que forme ce logiciel, ci-dessous. Nous n'entrerons pas dans les détails, nous ne décrirons que l'architecture permettant les communications entre les différents tiers de l'application. Une description un peu plus précise de chaque tiers se trouve dans leur description respective.
 
 
 

Architecture des jeux
 
 
     La description des jeux est effectuée dans la section leur étant destinée. Ici, nous allons parler de l'architecture mise en oeuvre lors de l'exécution d'une partie d'un jeu. Les jeux fonctionnent en mode client-serveur, la particularité vient du fait que le client ne dispose pas du jeu. Le tiers serveur de RMI Game dispose du client et du serveur des jeux. La partie client, est envoyée dynamiquement sous forme sérialisée au client désireux de l'utiliser.
   Les jeux de RMI Game sont composés de deux parties:
       - la partie Controller exécutée par le serveur
       - la partie Client destinée au client


   La partie Controller contient une classe qui implémente l'interface GameController. Cette classe est instanciée par le serveur pour créer une partie (demandée par un client). L'interface GameController permet d'obtenir les caractéristiques du jeu, d'y ajouter des joueurs et de le démarrer.
     La partie Client contient une classe qui implémente l'interface GameClient (connue par le serveur le client). Cette classe est instanciée par le Controller du jeu (côté serveur) pour être envoyée au client par un appel RMI. Cette classe et tous ses attributs doivent donc être sérializables. Comme le client n'a pas les fichiers de cette partie Client, le bytecode de cette classe et de celles qui y sont référencées est téléchargé par le serveur de fichiers HTTP (ClassFileServer). L'interface GameClient permet au client qui a reçu ce jeu de lui donner une instance de javax.swing.JPanel pour que le jeu puisse s'y afficher, elle permet aussi de signaler un événement clavier et de quitter le jeu.
         Communications Client-Controller
   La communication entre le Controller et les clients se fait par appels RMI, donc les parties Controller et Client doivent chacun posséder une classe héritant de java.rmi.server.UnicastRemoteObject et implémentant l'interface java.rmi.Remote. Ces deux classes instanciées permettent ainsi de faire des appels distants spécifiques au jeu entre la partie Controller et les parties Client.
   Nous allons étudier plus en détail ces mécanismes en prenant l'exemple du fonctionnement de JPong, en n'oubliant pas que tous les jeux supportés par le serveur (maintenant ou dans le futur) suivent le même schéma de fonctionnement.
 
 
 

Fonctionnement de JPong
 
 
  Dans un premier temps voici un récapitulatif des classes utilisées par JPong, elles se situent toutes dans le paquetage fr.unice.RMIGame.games.jpong:


Côté controleur (serveur):
     JPongController - controleur du jeu, implémente GameController
     RemoteJPongController - hérite de java.rmi.Remote, partie visible par le client
     RemoteJPongControllerImpl - hérite de UnicastRemoteObject, implemente RemoteJPongController
     Ball - la balle du ping-pong
     Player - représente les joueurs
     Calculation - calcul les rebonds de la balle
     Shuffle - détermine des trajectoire et des nombres aléatoires

Côté client:
     JPongClient - partie Client du jeu (implémente GameClient, Serializable), contient les classes internes Player, Ball, JPongTable.
     RemoteJPongClient - hérite de java.rmi.Remote, partie visible par le serveur
     RemoteJPongClientImpl - hérite de UnicastRemoteObject, implemente RemoteJPongClient
 
     Lorsqu'un client choisit de créer une partie de JPong, le serveur instancie la classe JPongController et ajoute ce client comme joueur "maître". Son constructeur instancie RemoteJPongControllerImpl pour les communications du client vers le controleur, puis crée la table (classes interne JPongTable) et la balle.
   Lorsqu'un client est ajouté, le controleur instancie JPongClient en y intégrant l'instance de RemoteJPongController et l'envoie au client avec la fonction receiveGame() de RemoteRMIGClient. Le client qui reçoit le jeu l'affiche avec la fonction setDisplay(JPanel) de GameClient et demande au jeu de créer un objet remote (RemoteJPongClient) et de l'envoyer au controleur avec la fonction join() de RemoteJPongController.
   Lorsque le controleur reçoit le remote du client, il lui crée un joueur et renvoie ses caractéristiques à tous les clients inscrits à cette partie. Lorsque le jeu a démarré les clients peuvent déplacer leur joueur par les touches du clavier, c'est le controleur qui calcul la position des joueurs. Par exemple, si un client veut aller à droite, JPongClient appelle la fonction goRIGHT() sur RemoteJPongController qui avertit le controleur, celui-ci calcule la nouvelle position (en testant s'il y a collision avec les bords de la table) et renvoie cette position à tous les clients. La position de la balle est calculée par le serveur à intervalles réguliers et envoyée aux clients.
   Cette architecture et son fonctionnement sont représentés, en résumé, sur le schéma ci-dessous:



 
   1- Le serveur crée une partie en instanciant JPongController, c'est lui également qui demandera l'ajout d'un nouveau joueur à cette partie (demande effectuée par le client lui-même, qui est traîtée par RMIGameServer.
   2- Lorsqu'un nouveau joueur entre dans la partie, JPongController crée une instance de JPongClient et l'envoie au client demandeur grâce à la méthode receiveGame() de RemoteRMIGClient.
   3- JPongClient et tous ses attributs étant sérialisables, ils sont transmis automatiquement au serveur grâce au mécanisme de l'appel RMI.
   4- JPongClient maintenant du côté du client envoie à son controleur, son RemoteJPongClient, à fin d'être atteint par le controleur lors du déroulement du jeu. Cet envoie s'effectue par la fonction join() de RemoteJPongController.


   Nous avons étudié d'autres solutions pour la conception du jeu. Les clients auraient, par exemple, pu calculer eux-même la trajectoire de la balle, et n'envoyer que leur changement de localisation. Cette solution aurait permis de rendre le serveur beaucoup moins sensible à une augmentation de charge. Dans notre conception le serveur calcule tout pour tout le monde...
   Cette solution n'a pas que des avantages, car si les clients ne vont pas à la même vitesse, il risque d'y avoir une désynchronisation des jeux. Ce problème peut être résolu par un mécanisme de synchronisation avec le serveur, mais selon le type de jeux il peut devenir plus complexe. Ici, la gestion des collisions entre joueurs et la balle, complique un peu plus les choses. Nous pensons qu'aucune solution n'est optimale, et toutes contiennent des avantages et des inconvénients. Cependant, il serait intérssant de concevoir plusieurs architectures différentes pour le même jeu, pour voir laquelle est optimale.