Attention : Ce tuto nécessite de bonne connaissance en programmation Javascript et en réseau.


I - Présentation

I.a - Serveur

Le serveur sera une application node.js, utilisant les modules http et socket.io. En fonction de la plateforme que vous utilisez, le mode d'installation de node.js peut varier de tout au tout. Autant, pour une installation windows, ça se fait en 5 min, autant pour une installation sur un raspberry pi 2, j'ai grave galéré. Maintenant, ça fonctionne très bien, mais ça m'a pris quelques heures.

Pour faire simple, node.js permet de lire du code Javascript, mais en le compilant au fur et à mesure de la lecture de façon optimisée (c'est géré par le moteur V8 de google), ce qui permet une exécution très rapide du code. En utilisant conjointement les modules http et socket.io, on peut réaliser un serveur WebSocket sur lequel on peut venir se connecter en lien permanent.

Attention, il y a un point qui mérite d'être abordé, c'est qu'un serveur node.js, ne permet pas "nativement" le multithreading pour chaque connexion cliente. Les messages de tous les clients connectés sont gérés les uns à la suite des autres. La question légitime que l'on peut se poser, c'est comment gérer les traitements qui peuvent être très long? Du genre les appels à une base de données, ou un webservice externe, etc... Et bien, Javascript dispose de librairie permettant de faire des appels asynchrone sur ces type de traitements, ce qui évite de bloquer en attente le déroulement du traitement principale, et qui appellera une fonction quant le traitement secondaire sera terminé en arrière plan.


I.b - Client

Pour avoir une sorte de parallélisme entre le code serveur et le code client, on va également utiliser socket.io sur un projet RpgMaker MV. Certes, il faudra ajouter un fichier dans les librairies Javascript et modifier l'index.html, mais ça se fait très bien. Et puis, socket.io assure une meilleure compatibilité d'utilisation des WebSockets selon les navigateurs, et utilise l'API native websocket quant c'est possible.

Il y'a aussi le fait que l'on n'est pas contraint d'utiliser le handle 'message', qui déclenche l'event onmessage. Avec socket.io, on peut définir nos propres handle, ce qui peut être pratique dans certain cas.


II - Installation

II.a - Serveur

Comme je le disais précédemment, selon votre système d'exploitation, c'est plus ou moins laborieux, surtout sur linux, ce qui est fort dommage, vu qu'il est préférable d'avoir un serveur linux. On va donc supposer que vous êtes sur mac ou Windows, ce qui est plus simple pour faire du développement avec RpgMaker MV, il suffit donc de se rendre sur la page officiel de node.js : https://nodejs.org/en/ et de télécharger la dernière version stable. Une fois téléchargé, vous l'installez en gardant les options par défaut.

A partir de maintenant, j'expliquerai les manipulations sur windows, ceux qui utilisent Mac, n'auront pas de difficulté à faire la transcription.

Pour vérifier que node.js se soit bien installé, il faut ouvrir une fenêtre de commande (Touche Windows + R, tapez cmd, puis appuyez sur entrée) :

cmd:

Microsoft Windows [version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Tous droits réservés.

C:\Users\tonyryu>node -v
v4.4.3

C:\Users\tonyryu>

Le numéro de version peut varier selon le moment où vous suivez ce tuto. Si cette commande retourne un numéro de version, c'est tout bon, on va pouvoir commencer à s'amuser.

On va maintenant réaliser un test simple, créez un dossier node directement à la racine c:, et créez dans celui-ci un fichier test.js, dans lequel vous allez mettre ce code :

Code:
console.log('hello world!!');

Enregistrez le fichier, puis retourner sur la fenêtre de commande :

cmd :

C:\Users\tonyryu>cd c:\node

C:\node>node test.js
hello world!!

C:\node>


node.js est fourni avec un très grand nombre de module standard, tel que http pour permettre la gestion d'un serveur http, sauf que pour faire du WebSocket, il nous faut un module supplémentaire, qui est socket.io.
Ce qui est également pratique avec node.js, c'est qu'il fourni un outil de gestionnaire de paquet, Node Package Manager : npm. npm est un outil très puissant permettant d'installer des packages de façon local ou global, de mettre à jour ces packages en fonction de fichier de configuration, etc... si vous êtes curieux, vous trouverez pleins de site web détaillant les fonctionnalité de npm.
Pour ce tuto, on va simplement installer socket.io en global, on retourne donc sur notre fenêtre de commande :

cmd :

C:\node>npm install -g socket.io
socket.io@1.4.5 C:\Users\Tonyryu\AppData\Roaming\npm\node_modules\socket.io
├── has-binary@0.1.7 (isarray@0.0.1)
├── debug@2.2.0 (ms@0.7.1)
├── socket.io-parser@2.2.6 (isarray@0.0.1, json3@3.3.2, component-emitter@1.1.2, benchmark@1.0.0)
├── socket.io-adapter@0.4.0 (socket.io-parser@2.2.2)
├── engine.io@1.6.8 (base64id@0.1.0, ws@1.0.1, engine.io-parser@1.2.4, accepts@1.1.4)
└── socket.io-client@1.4.5 (to-array@0.1.4, indexof@0.0.1, component-emitter@1.2.0, object-component@0.0.3, backo2@1.0.2, component-bind@1.0.0, parseuri@0.0.4, engine.io-client@1.6.8)

C:\node>


Cela permet d'installer socket.io et ses dépendances, dans un dossier accessible directement par toutes les applications lancées avec node.js.

Créons un nouveau dossier serveurRMMV dans le dossier node, et ajoutons le fichier serveurRMMV.js, dans lequel on va y mettre le code suivant :

Code :
var http = require('http');
var server = http.createServer();

var io = require('socket.io').listen(server);

io.sockets.on('connect'function (socket) {
  console.log('Un client se connecte, socket.id = ' + socket.id);
  // code à lancer quant un client se connecte

  // Quand le serveur reçoit un signal de type "message" du client    
  socket.on('message'function (message) {
    console.log('Un client me parle ! Il me dit : ' + message);
    var tabMsg = message.split('|');
    if(tabMsg[0]){
      switch(tabMsg[0]) {   // Ce switch/case doit être modifié selon les commandes de votre netcode
        case 'CMD_1':
          // Code de la commande CMD_1

          break;
        case 'CMD_2':
          // Code de la commande CMD_2

          break;
        default:
          // Code lorsque commande inconnu

      }
    }

  });
  socket.on('disconnect'function () {
    console.log('Un client se déconnecte, socket.id = ' + socket.id);
    // code à lancer quant un client se déconnecte

  });
});

console.log('Lancement du serveur sur port 9000');
server.listen(9000);

Dans les grandes lignes, ça créé un serveur http qui ne fait rien a part écouter le port TCP 9000, on créé également un point d'entrée/sortie par socket (websocket) en écoutant le serveur http. Et l'on ajoute des fonctions de callback sur des événements du gestionnaire de socket (connect) et d'un socket (message, disconnect).
La fonction de callback sur laquelle on va principalement travailler et celle déclenchée lors de l'événement message sur un socket.

Vous sauvegardez et retournez sur la fenêtre de commande :

cmd :

C:\node>cd serveurRMMV

C:\node\serveurRMMV>node serveurRMMV.js
Lancement du serveur sur port 9000

Voila, le serveur est maintenant en attente de connexion entrante

II.b - Client

Créer un nouveau projet RMMV, nommez le de façon à le retrouver facilement dans les dossiers de votre ordinateur.

Contrairement au précédent tuto avec le serveur PHP, nous avons besoins d'ajouter la librairie socket.io dédiée au client au projet RMMV. Pour cela, il faut aller la chercher ici https://github.com/socketio/socket.io-client/tree/main/dist. Une fois sur la page github, cliquez sur le fichier socket.io.js qui se trouve dans la liste. Dans la page contenant le code du fichier, faites un clic droit sur le bouton [Raw], sélectionnez l'option 'enregistrer la cible du lien sous' ou 'Enregistrer le lien sous' et choisissez le dossier /js/libs/ de votre projet RMMV sur lequel vous allez réaliser le tuto. Ce dossier contient déjà quelque librairie js, tel que pixi.js, fpsmeter.js, lz-string.js ou autre.
Il faut aussi ajouter le chargement de ce script lors du chargement de la page par le navigateur, il vous faut donc éditer le fichier index.html, et ajouter cette balise html juste en dessous des autres librairies et avant les script rpgMaker

Code :
<script type="text/javascript" src="js/libs/socket.io.js"></script>

En gros :

Code :
<!DOC id="GameCanvas"TYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
        <meta name="viewport" content="user-scalable=no">
        <link rel="icon" href="icon/icon.png" type="image/png">
        <link rel="apple-touch-icon" href="icon/icon.png">
        <link rel="stylesheet" type="text/css" href="fonts/gamefont.css">
        <title>Project1</title>
    </head>
    <body style="background-color: black">
        <script type="text/javascript" src="js/libs/pixi.js"></script>
        <script type="text/javascript" src="js/libs/fpsmeter.js"></script>
        <script type="text/javascript" src="js/libs/lz-string.js"></script>
        <script type="text/javascript" src="js/libs/socket.io.js"></script>
        <script type="text/javascript" src="js/rpg_core.js"></script>
        <script type="text/javascript" src="js/rpg_managers.js"></script>
        <script type="text/javascript" src="js/rpg_objects.js"></script>
        <script type="text/javascript" src="js/rpg_scenes.js"></script>
        <script type="text/javascript" src="js/rpg_sprites.js"></script>
        <script type="text/javascript" src="js/rpg_windows.js"></script>
        <script type="text/javascript" src="js/plugins.js"></script>
        <script type="text/javascript" src="js/main.js"></script>
        <br>
    </body>
</html>


Ensuite, on va y rajouter un plugin simple exploitant socket.io : Tonyryu_WebSocketio.js et ajoutez le dans votre projet RMMV. Laissez le paramètre Host tel qu'il est, localhost correspond à un alias de l'ip 127.0.0.1, qui est une adresse dédiée à soit-même (un pc tentant une connexion vers 127.0.0.1, tente en fait une connexion vers lui-même).

Sur la première Map, ajoutez un event contenant ceci :



Lancez le projet, et activez l'event avec le héro, puis consultez la fenêtre d'exécution du serveur, vous verrez cette nouvelle ligne (l'id peut  être différent, c'est normal) :

cmd :

Un client se connecte, socket.id = /#vDl1E2wlfUUaHgtqAAAA


Cela indique, qu'un client s'est connecté, qu'il est référencé par l'id /#vDl1E2wlfUUaHgtqAAAA. La connexion restera ouverte, jusqu'à ce qu'on fasse la demande de fermeture d'un coté ou de l'autre, ou de l'arrêt du client ou du serveur. Si vous fermez le jeu en cours, vous aurez ces nouvelles lignes sur la fenêtre d'exécution du serveur :

cmd :

Un client se déconnecte, socket.id = /#vDl1E2wlfUUaHgtqAAAA


III - Netcode

On va maintenant commencer à travailler sur le netcode, c'est à dire écrire comment doit réagir le client et le serveur quant il ya une action d'ouverture, de réception de message ou de fermeture.

Les endroits à modifier d'une partie comme de l'autre sont clairement identifiés par les commentaires, et la structure est quasiment la même, ce qui est un avantage certain.

On va donc implémenter des nouvelles fonctionnalités


III.a - Interrupteur 1 à ON quant connexion ouverte

Pour ceux qui avaient fait le premier tuto avec le serveur PHP, ils ne seront pas perdu, on fait la même chose.

Pour modifier un interrupteur RMMV en JS, c'est très simple, exemple interrupteur 1 à ON :

Code :
$gameSwitches.setValue(1true);


Donc le code du plugin, il faut modifier la fonction de callback lors de l'événement 'connect', comme ceci :

Code :
   $wsConnexion.on('connect'function() {
      // Code a lancer à la connexion
      $gameSwitches.setValue(1true);
    });


et la fonction de callback de l'événement 'disconnect' :

Code :
   $wsConnexion.on('disconnect'function() {
      // code à lancer à la deconnexion
      $gameSwitches.setValue(1false);
    });


Et l'on va ajouter une seconde page à notre event pour permettre d'arrêter le webSocket, comme-ceci :



voila maintenant, l'on peut facilement démarrer, arrêter le Websocket et en connaitre l'état.


III.b - Donner 1000 gold aux autres clients

On va enfin pouvoir réaliser une interaction multijoueur, pour cela il y a plusieurs choses à réaliser : 
1 : sur client : ajouter l'action d'ajout de 1000 gold quant le client reçoit le message GAIN_GOLD
2 : sur serveur : ajouter la gestion du message SEND_GOLD, permettant l'envoi du message GAIN_GOLD aux autres clients
3 : sur client : ajouter un event pour envoyer le message SEND_GOLD

Dans le plugin, dans la fonction de callback de l'événement message, on va remplacer le bloc case suivant :

Code :
         case 'CMD_1':
            // Code de la commande CMD_1
            break;

par

Code :
         case 'GAIN_GOLD':
            $gameParty.gainGold(1000);
            break;


sur le serveur, également dans la fonction callback à l'événement 'message', on va remplacer le bloc case suivant :

Code :
       case 'CMD_1':
          // Code de la commande CMD_1

          break;

par

Code :
       case 'SEND_GOLD':
          // Code de la commande SEND_GOLD
          socket.broadcast.emit('message''GAIN_GOLD');
          break;


socket.broadcast.emit('message', 'GAIN_GOLD'); Permet d'envoyer le message 'GAIN_GOLD' à tous les autres clients connectés.

Et l'on va ajouter un nouvel événement sur la carte, comme ceci :



Enregistrez le projet, réaliser un déploiement Windows, afin d'avoir un game.exe, que vous pourrez lancé en même temps que le test du projet. connectez vous au Websocket avec les 2 jeux, puis avec l'un des 2, actionner l'event permettant l'envoi de gold vers les autres joueurs connectés. Si tout a bien été fait, l'autre joueur à maintenant 1000 gold en plus.