Domar WebRTC con PeerJS: hacer un juego web P2P simple

Publicado: 2022-03-11

WebRTC es una tecnología que permite la comunicación en tiempo real entre navegadores web. Es relativamente nuevo y la definición de la API todavía se considera un borrador. Junto con el hecho de que WebRTC aún no es compatible con todos los principales navegadores web (y entre los que sí lo son, algunos de ellos no son compatibles con todas las características de esta tecnología), esto hace que sea relativamente difícil usar WebRTC para cualquier aplicación de misión crítica. . ¡O al menos eso pensarías!

Conecta Cuatro a través de WebRTC usando PeerJS: ¡Mira mamá, no hay servidor!

Conecta Cuatro a través de WebRTC usando PeerJS: ¡Mira mamá, no hay servidor!
Pío

Desde que Google lo introdujo por primera vez en mayo de 2011, WebRTC se ha utilizado en muchas aplicaciones web modernas. Al ser una característica central de muchos navegadores web modernos, las aplicaciones web pueden aprovechar esta tecnología sin problemas para brindar una experiencia de usuario mejorada de muchas maneras. Las aplicaciones de transmisión de video o conferencias que no requieren complementos de navegador inflados y pueden aprovechar las redes punto a punto (P2P) (sin transmitir cada bit de datos a través de algún servidor) son solo una parte de todas las cosas increíbles que se puede lograr con WebRTC.

En este artículo, veremos cómo se puede usar WebRTC para hacer un juego web P2P simple de Connect Four. Para solucionar los diversos aspectos básicos y las diferencias de implementación de WebRTC, utilizaremos una increíble biblioteca de JavaScript: PeerJS.

Datos sobre WebRTC

Antes de comenzar, es importante comprender que WebRTC no se trata solo de transmitir transmisiones de audio y video. También proporciona soporte para canales de datos P2P. Estos canales vienen en dos variaciones: confiables y no confiables. Como se puede adivinar, los canales de datos confiables garantizan que los mensajes se entreguen y se entreguen en orden, mientras que los canales no confiables no brindan tales garantías.

Infraestructura WebRTC: un océano de siglas

Infraestructura WebRTC: un océano de siglas
Pío

Además, los canales de datos de WebRTC no requieren una configuración de infraestructura especial, aparte de lo que necesita una conexión de par WebRTC típica: un servidor de señalización para coordinar la conexión entre pares, un servidor STUN para averiguar la identidad pública de los pares y, opcionalmente, un servidor TURN para enrutar mensajes entre pares si no se puede establecer una conexión directa entre pares (por ejemplo, cuando ambos pares están detrás de NAT). Si estos acrónimos le suenan familiares, es porque WebRTC reutiliza las tecnologías existentes siempre que sea posible.

Esto abre la puerta a muchos más casos de uso de WebRTC, incluidos, entre otros, juegos multijugador, entrega de contenido y uso compartido de archivos. Nuevamente, todo sin la necesidad de ningún servidor intermediario y, por lo tanto, con latencias más bajas.

En nuestro sencillo juego web, utilizaremos un canal de datos entre dos navegadores web para comunicar los movimientos de los jugadores de un lado a otro.

Conoce a PeerJS

PeerJS toma la implementación de WebRTC en su navegador y lo envuelve en una API simple, consistente y elegante. Tapa varios agujeros en la implementación de WebRTC de navegadores anteriores. Por ejemplo, en Chrome 30 o anterior, solo estaban disponibles los canales de datos no confiables. PeerJS, si está configurado para usar canales de datos confiables, usaría una corrección para esos navegadores más antiguos. Aunque esto no sería tan eficaz como la implementación nativa de canales confiables, aún funcionaría.

Con PeerJS, identificar pares es aún más simple. Cada par se identifica usando nada más que una identificación. Una cadena que el par puede elegir por sí mismo, o hacer que un servidor genere una. Aunque WebRTC promete comunicación de igual a igual, aún necesita un servidor para actuar como intermediario de conexión y manejar la señalización. PeerJS proporciona una implementación de código abierto de este servidor de intermediario de conexión PeerJS Server (escrito en Node.js), en caso de que no desee utilizar su versión alojada en la nube (que es gratuita en este momento y viene con algunas limitaciones).

Conectar cuatro va P2P

Ahora que tenemos una fuente de confianza para trabajar con WebRTC, es decir, PeerJS, comencemos creando una aplicación simple de Node.js/Express.

 npm init npm install express --save npm install jade --save npm install peer --save

Usaremos esto solo para alojar el servidor PeerJS y servir una página y activos front-end. Necesitaremos servir solo una página, y esta contendrá dos secciones: un menú principal simple y una cuadrícula Connect Four de 7 por 6.

Servidor PeerJS

Alojar nuestro propio servidor PeerJS es realmente fácil. El repositorio oficial en GitHub incluso tiene un botón de un solo clic para implementar una instancia de PeerJS Server en Heroku.

En nuestro caso, solo queremos crear una instancia de ExpressPeerServer en nuestra aplicación Node.js y servirla en "/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 }))

Cliente PeerJS

Con PeerJS Server en funcionamiento, pasamos al lado del cliente. Como se discutió anteriormente, PeerJS identifica a los pares con ID únicos. Estos ID pueden ser generados por PeerServer para cada par automáticamente, o podemos elegir uno para cada par al instanciar los objetos Peer .

 var peer = new Peer(id, options)

Aquí, la identificación se puede omitir por completo si queremos que el servidor genere una para nosotros. En nuestro caso, eso es lo que querremos hacer. PeerServer se asegurará de que las identificaciones que proporcione sean únicas. El segundo argumento, options , suele ser un objeto que contiene una clave (la clave API, si está utilizando PeerServer alojado en la nube, o host , puerto , ruta , etc. en caso de que esté alojando PeerServer usted mismo).

 var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })

Para establecer una conexión entre dos pares de PeerJS, uno de los pares debe conocer la ID del otro par. En aras de mantener las cosas simples, en nuestra implementación de Connect Four sobre WebRTC, requeriremos que el jugador que inicia el juego comparta su ID de compañero con su oponente. Con el ID del par de destino conocido, una simple llamada a peer.connect(destId) es todo lo que necesitaremos:

 var conn = peer.connect(destId)

Tanto el objeto Peer como el objeto DataConnection devueltos por peer.connect(destId) emiten algunos eventos realmente útiles que vale la pena escuchar. Para los propósitos de este tutorial, estamos particularmente interesados ​​en el evento de 'datos' del objeto DataConnection y los eventos de 'error' de ambos objetos.

Para enviar datos al otro extremo de la conexión, simplemente invoque conn.send(data) :

 conn.send('hello')

Aunque es un poco exagerado para nuestras necesidades aquí, PeerJS transmite datos entre pares después de codificarlos en formato BinaryPack. Esto permite que los compañeros comuniquen cadenas, números, matrices, objetos e incluso blobs.

Para recibir datos entrantes, simplemente escuche el evento 'data' en conn :

 conn.on('data', function(data) { // data === 'hello' })

¡Y eso es prácticamente todo lo que necesitamos!

Lógica del juego

Al primer jugador, el que comienza un juego, se le muestra su ID de compañero generado por PeerJS, que puede compartir con su oponente. Una vez que un oponente se une al juego usando la ID de compañero del primer jugador, el primer jugador puede hacer un movimiento.

Connect Four, al ser un juego de reglas y mecánicas simples, solo tiene un tipo de movimiento: cada jugador, por turno, debe elegir una columna y dejar caer un disco en ella. Esto significa que todo lo que un compañero necesita comunicar es el número de columna en el que el jugador actual ha elegido colocar su disco. Transmitiremos esta información como una matriz con dos elementos: una cadena 'mover' y un número - 0- índice basado en la columna de la izquierda.

Cada vez que un jugador hace clic en una columna:

 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])

Después de enviar estos datos de movimiento al oponente, actualizamos el estado del juego localmente. Esto incluye determinar si el jugador actual ha ganado o si el juego ha terminado en empate.

En el extremo receptor de estos datos de movimiento:

 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

Y naturalmente, después de esto actualizamos el estado del juego localmente, determinamos si el oponente ha ganado o si el juego ha terminado en empate.

Observe cómo debemos realizar controles de cordura en los datos entrantes. Esto es importante ya que con los juegos basados ​​en WebRTC, no tenemos un servidor intermediario ni una lógica de juego basada en servidor que valide los datos de movimiento.

Para simplificar los fragmentos, se han omitido las líneas de código que actualizan la interfaz de usuario. Puede encontrar el código fuente completo para el JavaScript del lado del cliente aquí.

Conectándolo todo

Para combinarlo todo, creamos una página simple con dos secciones. Al cargar la página, se muestra la sección que contiene el menú principal, la sección que contiene la cuadrícula del juego se mantiene oculta.

 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 | &nbsp; 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

Hacer que estos elementos DOM se vean bonitos está más allá del alcance de este tutorial. Por lo tanto, recurriremos a nuestro compañero de confianza Bootstrap y le haremos un poco de estilo.

Cuando el primer jugador hace clic en el botón "Iniciar", se revela la cuadrícula del juego junto con la identificación del compañero del jugador. Luego, el jugador puede compartir esta ID de compañero con su oponente.

Inicie un nuevo juego y comparta su ID de compañero generado por PeerJS

El segundo jugador puede hacer clic y luego hacer clic en el botón "Unirse", ingresar la ID de compañero del primer jugador y comenzar el juego.

Use la ID de compañero de su oponente para unirse a un juego

probandolo

Puede probar esta aplicación de ejemplo en https://arteegee.herokuapp.com.

O bien, puede clonar el repositorio desde GitHub, instalar las dependencias de NPM y probarlo localmente:

 git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm start

Una vez que el servidor se está ejecutando, puede apuntar su navegador web a http://localhost:5000, iniciar un juego desde una pestaña y unirse desde otra pestaña (o incluso un navegador web compatible con WebRTC diferente) utilizando la ID de compañero.

Puede abrir la consola de su navegador web para ver información de depuración, ya que en esta aplicación de ejemplo, el cliente PeerJS se configuró para realizar un registro detallado.

¡Pero no funciona para mí!

Hay dos razones principales por las que este juego puede no funcionar en tu computadora.

Es posible que esté utilizando un navegador web que aún no es compatible con las API WebRTC necesarias. Si ese es el caso, es posible que desee probar con un navegador diferente, uno que admita WebRTC y canales de datos.

Si está utilizando un navegador web moderno compatible con WebRTC, existe la posibilidad de que esté detrás de alguna infraestructura de red que WebRTC no puede penetrar. Idealmente, este problema se puede abordar fácilmente con un servidor TURN, pero dado que la aplicación de ejemplo no usa uno, no funcionará cuando tanto usted como su oponente estén detrás de NAT.

Conclusión

WebRTC es una tecnología nueva y sus implementaciones están bastante lejos de ser maduras. Estos a menudo causan algunos desafíos únicos para los desarrolladores. Sin embargo, con la disponibilidad de bibliotecas como PeerJS que abstraen perfectamente las API sin procesar, la tecnología ya se está volviendo bastante accesible.

Espero que este breve tutorial para crear un juego basado en PeerJS lo ayude a comenzar con WebRTC y a crear algunas aplicaciones web increíbles en tiempo real entre pares.