Apprivoiser WebRTC avec PeerJS : créer un jeu Web P2P simple
Publié: 2022-03-11WebRTC est une technologie qui permet une communication en temps réel entre les navigateurs Web. Il est relativement nouveau et la définition de l'API est toujours considérée comme un brouillon. Couplé au fait que WebRTC n'est pas encore pris en charge par tous les principaux navigateurs Web (et parmi ceux qui le font, certains d'entre eux ne prennent pas en charge toutes les fonctionnalités de cette technologie), cela rend relativement difficile l'utilisation de WebRTC pour toutes les applications critiques. . Ou alors vous penseriez!
Depuis son introduction par Google en mai 2011, WebRTC a été utilisé dans de nombreuses applications Web modernes. En tant que fonctionnalité essentielle de nombreux navigateurs Web modernes, les applications Web peuvent tirer parti de cette technologie de manière transparente pour offrir une expérience utilisateur améliorée de plusieurs façons. Les applications de streaming vidéo ou de conférence qui ne nécessitent pas de plugins de navigateur gonflés et peuvent tirer parti des réseaux peer-to-peer (P2P) (sans transmettre chaque bit de données via un serveur) ne sont qu'une partie de toutes les choses étonnantes qui peut être réalisé avec WebRTC.
Dans cet article, nous verrons comment WebRTC peut être utilisé pour créer un simple jeu Web P2P de Connect Four. Pour contourner les différents aspérités et différences d'implémentation de WebRTC, nous utiliserons une bibliothèque JavaScript étonnante : PeerJS.
Données sur WebRTC
Avant de commencer, il est important de comprendre que WebRTC ne consiste pas uniquement à transmettre des flux audio et vidéo. Il prend également en charge les canaux de données P2P. Ces canaux existent en deux variantes : fiables et non fiables. Comme on peut le deviner, des canaux de données fiables garantissent que les messages sont livrés et qu'ils sont livrés dans l'ordre, tandis que des canaux non fiables ne fournissent pas de telles garanties.
De plus, les canaux de données WebRTC ne nécessitent aucune configuration d'infrastructure spéciale, autre que ce qui est nécessaire pour une connexion de pair WebRTC typique : un serveur de signalisation pour coordonner la connexion entre les pairs, un serveur STUN pour déterminer l'identité publique des pairs et éventuellement un serveur TURN. pour acheminer les messages entre pairs si une connexion directe entre pairs ne peut pas être établie (par exemple lorsque les deux pairs sont derrière des NAT). Si ces acronymes vous semblent familiers, c'est parce que WebRTC réutilise les technologies existantes dans la mesure du possible.
Cela ouvre la porte à beaucoup plus de cas d'utilisation de WebRTC, y compris, mais sans s'y limiter, les jeux multijoueurs, la diffusion de contenu et le partage de fichiers. Encore une fois, le tout sans avoir besoin d'un serveur intermédiaire et donc avec des latences plus faibles.
Dans notre jeu Web simple, nous utiliserons un canal de données entre deux navigateurs Web pour communiquer les mouvements des joueurs dans les deux sens.
Rencontrez PeerJS
PeerJS prend l'implémentation de WebRTC dans votre navigateur et l'entoure d'une API simple, cohérente et élégante. Il comble divers trous dans l'implémentation WebRTC des navigateurs précédents. Par exemple, dans Chrome 30 ou une version antérieure, seuls les canaux de données non fiables étaient disponibles. PeerJS, s'il était configuré pour utiliser des canaux de données fiables, utiliserait un shim pour ces anciens navigateurs. Bien que cela ne soit pas aussi performant que l'implémentation native de canaux fiables, cela fonctionnerait toujours.
Avec PeerJS, l'identification des pairs est encore plus simple. Chaque pair est identifié en utilisant rien d'autre qu'un identifiant. Une chaîne que le pair peut choisir lui-même ou demander à un serveur d'en générer une. Bien que WebRTC promette une communication peer-to-peer, vous avez quand même besoin d'un serveur pour agir en tant que courtier de connexion et gérer la signalisation. PeerJS fournit une implémentation open source de ce serveur de courtier de connexion PeerJS Server (écrit en Node.js), au cas où vous ne voudriez pas utiliser leur version hébergée dans le cloud (qui est gratuite pour le moment et comporte certaines limitations).
Connect Four Goes P2P
Maintenant que nous avons une source de confiance pour travailler avec WebRTC, c'est-à-dire PeerJS, commençons par créer une simple application Node.js/Express.
npm init npm install express --save npm install jade --save npm install peer --save
Nous l'utiliserons uniquement pour héberger PeerJS Server et servir une page et des ressources frontales. Nous n'aurons besoin de servir qu'une seule page, et celle-ci contiendra deux sections : un menu principal simple et une grille Connect Four 7 par 6.
Serveur PeerJS
Héberger notre propre serveur PeerJS est vraiment facile. Le référentiel officiel sur GitHub dispose même d'un bouton en un clic pour déployer une instance de PeerJS Server sur Heroku.
Dans notre cas, nous voulons simplement créer une instance d' ExpressPeerServer dans notre application Node.js, et la servir à "/peerjs":
var express = require('express') var app = express() // … Configure Express, and register necessary route handlers srv = app.listen(process.env.PORT) app.use('/peerjs', require('peer').ExpressPeerServer(srv, { debug: true }))
Client PeerJS
Avec PeerJS Server opérationnel, nous passons du côté client. Comme indiqué précédemment, PeerJS identifie les pairs avec des identifiants uniques. Ces identifiants peuvent être générés automatiquement par PeerServer pour chaque pair, ou nous pouvons en choisir un pour chaque pair lors de l'instanciation des objets Peer .
var peer = new Peer(id, options)
Ici, id peut être complètement omis si nous voulons que le serveur en génère un pour nous. Dans notre cas, c'est ce que nous voudrons faire. PeerServer s'assurera que les identifiants qu'il donne sont uniques. Le deuxième argument, options , est généralement un objet contenant une clé (la clé API, si vous utilisez un PeerServer hébergé dans le cloud, ou host , port , path , etc. si vous hébergez vous-même le PeerServer).
var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })
Afin d'établir une connexion entre deux pairs PeerJS, l'un des pairs doit connaître l'ID de l'autre pair. Dans un souci de simplicité, dans notre implémentation de Connect Four sur WebRTC, nous demanderons au joueur commençant le jeu de partager son ID de pair avec son adversaire. Avec l'ID du pair de destination connu, un simple appel à peer.connect(destId) est tout ce dont nous aurons besoin :

var conn = peer.connect(destId)
L'objet Peer et l'objet DataConnection renvoyés par peer.connect(destId) émettent des événements vraiment utiles qui valent la peine d'être écoutés. Pour les besoins de ce didacticiel, nous nous intéressons particulièrement à l'événement 'data' de l'objet DataConnection et aux événements 'error' des deux objets.
Pour envoyer des données à l'autre extrémité de la connexion, invoquez simplement conn.send(data) :
conn.send('hello')
Bien qu'un peu exagéré pour nos besoins ici, PeerJS transmet les données entre pairs après les avoir encodées au format BinaryPack. Cela permet aux pairs de communiquer des chaînes, des nombres, des tableaux, des objets et même des blobs.
Pour recevoir des données entrantes, écoutez simplement l'événement 'data' sur conn :
conn.on('data', function(data) { // data === 'hello' })
Et c'est à peu près tout ce dont nous avons besoin !
Logique du jeu
Le premier joueur, celui qui commence une partie, voit son ID de pair généré par PeerJS qu'il peut partager avec son adversaire. Une fois qu'un adversaire rejoint le jeu en utilisant l'identifiant de pair du premier joueur, le premier joueur est autorisé à faire un mouvement.
Connect Four, étant un jeu de règles et de mécanismes simples, n'a qu'un seul type de mouvement : chaque joueur, à son tour, doit choisir une colonne et y déposer un disque. Cela signifie que tout ce qu'un pair doit communiquer est le numéro de colonne dans laquelle le joueur actuel a choisi de déposer son disque. Nous transmettrons cette information sous la forme d'un tableau à deux éléments : une chaîne 'move' et un nombre - 0- index basé sur la colonne à partir de la gauche.
Chaque fois qu'un joueur clique sur une colonne :
if(!turn) { // it is not the current player's turn return } var i // i = chosen column index if(grid[i].length == 6) { // the column doesn't have any more space available return } // track player's move locally grid[i].push(peerId) // end current player's turn turn = false conn.send(['move', i])
Après avoir envoyé ces données de mouvement à l'adversaire, nous mettons à jour l'état du jeu localement. Cela inclut de déterminer si le joueur actuel a gagné ou si le jeu s'est terminé par un match nul.
À la réception de ces données de déplacement :
if(turn) { // ignore incoming move data when it is the current player's turn return } var i = data[1] if(grid[i].length == 6) { // ignore incoming move data when it is invalid return } // track opponent's move locally grid[i].push(opponent.peerId) // activate current player's turn turn = true
Et naturellement, après cela, nous mettons à jour l'état du jeu localement, déterminons si l'adversaire a gagné ou si le jeu s'est terminé par un match nul.
Remarquez comment nous devons effectuer des vérifications d'intégrité sur les données entrantes. Ceci est important car avec les jeux basés sur WebRTC, nous n'avons pas de serveur intermédiaire et de logique de jeu basée sur le serveur validant les données de mouvement.
Pour garder les extraits simples, les lignes de code qui mettent à jour l'interface utilisateur ont été omises. Vous pouvez trouver le code source complet du JavaScript côté client ici.
Tout connecter
Pour combiner le tout, nous créons une page simple avec deux sections. Au chargement de la page, la section contenant le menu principal est affichée, la section contenant la grille de jeu est gardée cachée.
section#menu div.animated.bounceIn div h1 Connect Four br div.no-support() div.alert.alert-warning p Unfortunately, your web browser does not <a href="http://iswebrtcreadyyet.com">support WebRTC</a> div a.btn.btn-primary.btn-lg(href='#start') Start | a.btn.btn-default.btn-lg(href='#join') Join section#game() div div h1 Connect Four br table.table.grid tbody for i in [0, 1, 2, 3, 4, 5] tr for j in [0, 1, 2, 3, 4, 5, 6] td div.slot br div.alert.alert-info p
Rendre ces éléments DOM jolis dépasse le cadre de ce didacticiel. Par conséquent, nous allons recourir à notre compagnon de confiance Bootstrap et lui appliquer un style léger.
Lorsque le premier joueur clique sur le bouton "Démarrer", la grille de jeu est révélée avec l'identifiant du pair du joueur. Le joueur peut alors partager cet ID de pair avec son adversaire.
Le deuxième joueur peut cliquer puis cliquer sur le bouton "Rejoindre", entrer l'identifiant du premier joueur et commencer la partie.
Essayer
Vous pouvez essayer cet exemple d'application sur https://arteegee.herokuapp.com.
Vous pouvez également cloner le référentiel à partir de GitHub, installer les dépendances NPM et l'essayer localement :
git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm start
Une fois le serveur en cours d'exécution, vous pouvez pointer votre navigateur Web sur http://localhost:5000, démarrer un jeu à partir d'un onglet et le rejoindre à partir d'un autre onglet (ou même d'un autre navigateur Web compatible WebRTC) à l'aide de l'ID de pair.
Vous pouvez ouvrir la console de votre navigateur Web pour voir certaines informations de débogage, car dans cet exemple d'application, le client PeerJS a été configuré pour effectuer une journalisation détaillée.
Mais ça ne marche pas pour moi !
Il y a deux raisons principales pour lesquelles ce jeu peut ne pas fonctionner sur votre ordinateur.
Il est possible que vous utilisiez un navigateur Web qui ne prend pas encore en charge les API WebRTC nécessaires. Si tel est le cas, vous pouvez essayer un autre navigateur - un navigateur qui prend en charge WebRTC et les canaux de données.
Si vous utilisez un navigateur Web moderne avec prise en charge de WebRTC, il est possible que vous soyez derrière une infrastructure réseau que WebRTC ne peut pas pénétrer. Idéalement, ce problème peut être facilement résolu avec un serveur TURN, mais comme l'exemple d'application n'en utilise pas, cela ne fonctionnera pas lorsque vous et votre adversaire êtes derrière des NAT.
Conclusion
WebRTC est une nouvelle technologie, et ses implémentations sont assez loin d'être matures. Ceux-ci posent souvent des défis uniques aux développeurs. Cependant, avec la disponibilité de bibliothèques comme PeerJS qui résument parfaitement les API brutes brutes, la technologie devient déjà assez accessible.
J'espère que ce bref tutoriel pour créer un jeu basé sur PeerJS vous aidera à démarrer avec WebRTC et à créer d'incroyables applications Web peer-to-peer en temps réel.