Dominando o WebRTC com PeerJS: criando um jogo Web P2P simples
Publicados: 2022-03-11WebRTC é uma tecnologia que permite a comunicação em tempo real entre navegadores da web. É relativamente novo e a definição da API ainda é considerada um rascunho. Juntamente com o fato de que o WebRTC ainda não é suportado por todos os principais navegadores da Web (e entre os que o fazem, alguns deles não suportam todos os recursos dessa tecnologia), isso torna relativamente difícil usar o WebRTC para qualquer aplicativo de missão crítica . Ou assim você pensaria!
Desde que foi introduzido pela primeira vez pelo Google em maio de 2011, o WebRTC tem sido usado em muitos aplicativos da web modernos. Sendo um recurso central de muitos navegadores da Web modernos, os aplicativos da Web podem aproveitar perfeitamente essa tecnologia para oferecer uma experiência de usuário aprimorada de várias maneiras. Aplicativos de streaming de vídeo ou conferência que não exigem plug-ins de navegador inchados e podem tirar proveito de redes peer-to-peer (P2P) (embora não transmitam todos os bits de dados por meio de algum servidor) é apenas uma parte de todas as coisas incríveis que pode ser alcançado com WebRTC.
Neste artigo, veremos como o WebRTC pode ser usado para criar um jogo web P2P simples do Connect Four. Para contornar as várias arestas e diferenças de implementação do WebRTC, usaremos uma incrível biblioteca JavaScript: PeerJS.
Dados sobre WebRTC
Antes de começarmos, é importante entender que o WebRTC não se trata apenas de transmitir fluxos de áudio e vídeo. Ele também fornece suporte para canais de dados P2P. Esses canais vêm em duas variações: confiáveis e não confiáveis. Como se pode imaginar, canais de dados confiáveis garantem que as mensagens sejam entregues e entregues em ordem, enquanto canais não confiáveis não oferecem tais garantias.
Além disso, os canais de dados WebRTC não requerem configuração de infraestrutura especial, além do que é necessário para uma conexão típica de peers WebRTC: um servidor de sinalização para coordenar a conexão entre os peers, um servidor STUN para descobrir a identidade pública dos peers e, opcionalmente, um servidor TURN para rotear mensagens entre peers se uma conexão direta entre peers não puder ser estabelecida (por exemplo, quando ambos os peers estiverem atrás de NATs). Se esses acrônimos soam familiares, é porque o WebRTC reaproveita as tecnologias existentes sempre que possível.
Isso abre as portas para muito mais casos de uso do WebRTC, incluindo, entre outros, jogos multiplayer, entrega de conteúdo e compartilhamento de arquivos. Novamente, tudo sem a necessidade de nenhum servidor intermediário e, portanto, com latências mais baixas.
Em nosso jogo web simples, usaremos um canal de dados entre dois navegadores da web para comunicar os movimentos do jogador para frente e para trás.
Conheça o PeerJS
O PeerJS pega a implementação do WebRTC em seu navegador e envolve uma API simples, consistente e elegante em torno dele. Ele tapa vários buracos na implementação do WebRTC de navegadores anteriores. Por exemplo, no Chrome 30 ou anterior, apenas canais de dados não confiáveis estavam disponíveis. O PeerJS, se configurado para usar canais de dados confiáveis, usaria um shim para esses navegadores mais antigos. Embora isso não fosse tão eficiente quanto a implementação nativa de canais confiáveis, ainda funcionaria.
Com o PeerJS, identificar peers é ainda mais simples. Cada peer é identificado usando nada além de um ID. Uma string que o peer pode escolher ou fazer com que um servidor gere uma. Embora o WebRTC prometa comunicação ponto a ponto, você ainda precisa de um servidor para atuar como um agente de conexão e lidar com a sinalização. O PeerJS fornece uma implementação de código aberto deste servidor do agente de conexão PeerJS Server (escrito em Node.js), caso você não queira usar sua versão hospedada na nuvem (que é gratuita no momento e vem com algumas limitações).
Conecte Quatro Goes P2P
Agora que temos uma fonte de confiança para trabalhar com WebRTC, ou seja, PeerJS, vamos começar criando um aplicativo Node.js/Express simples.
npm init npm install express --save npm install jade --save npm install peer --save
Usaremos isso apenas para hospedar o PeerJS Server e servir uma página e recursos de front-end. Precisaremos servir apenas uma única página, e isso conterá duas seções: um menu principal simples e uma grade Connect Four de 7 por 6.
Servidor PeerJS
Hospedar nosso próprio servidor PeerJS é muito fácil. O repositório oficial no GitHub tem até um botão de um clique para implantar uma instância do PeerJS Server no Heroku.
No nosso caso, queremos apenas criar uma instância do ExpressPeerServer em nosso aplicativo Node.js e servi-la em “/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
Com o PeerJS Server funcionando, passamos para o lado do cliente. Conforme discutido anteriormente, o PeerJS identifica os pares com IDs exclusivos. Esses IDs podem ser gerados pelo PeerServer para cada peer automaticamente, ou podemos escolher um para cada peer enquanto instanciamos os objetos Peer .
var peer = new Peer(id, options)
Aqui, id pode ser omitido completamente se quisermos que o servidor gere um para nós. No nosso caso, é isso que vamos querer fazer. O PeerServer garantirá que os IDs fornecidos sejam exclusivos. O segundo argumento, options , geralmente é um objeto que contém a chave (a chave da API, se você estiver usando o PeerServer hospedado na nuvem, ou host , port , path , etc, caso você mesmo esteja hospedando o PeerServer).
var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })
Para estabelecer uma conexão entre dois peers PeerJS, um dos peers deve conhecer o ID do outro peer. Para manter as coisas simples, em nossa implementação do Connect Four sobre WebRTC, exigiremos que o jogador que inicia o jogo compartilhe seu ID de par com seu oponente. Com o ID do peer de destino conhecido, uma simples chamada para peer.connect(destId) é tudo o que precisamos:

var conn = peer.connect(destId)
Tanto o objeto Peer quanto o objeto DataConnection retornados por peer.connect(destId) emitem alguns eventos realmente úteis que valem a pena ouvir. Para os propósitos deste tutorial, estamos particularmente interessados no evento 'data' do objeto DataConnection e eventos 'error' de ambos os objetos.
Para enviar dados para a outra extremidade da conexão, basta invocar conn.send(data) :
conn.send('hello')
Embora um pouco exagerado para nossas necessidades aqui, o PeerJS transmite dados entre os pares depois de codificá-los no formato BinaryPack. Isso permite que os pares comuniquem strings, números, arrays, objetos e até blobs.
Para receber dados de entrada, basta ouvir o evento 'data' em conn :
conn.on('data', function(data) { // data === 'hello' })
E isso é praticamente tudo o que precisamos!
Lógica do jogo
O primeiro jogador, aquele que inicia um jogo, recebe seu ID de par, gerado pelo PeerJS, que ele pode compartilhar com seu oponente. Uma vez que um oponente entra no jogo usando o ID de par do primeiro jogador, o primeiro jogador pode fazer um movimento.
Connect Four, sendo um jogo de regras e mecânicas simples, tem apenas um tipo de movimento: cada jogador, por sua vez, deve escolher uma coluna e colocar um disco nela. Isso significa que tudo o que um par precisa para se comunicar é o número da coluna na qual o jogador atual escolheu colocar seu disco. Transmitiremos essas informações como um array com dois elementos: uma string 'move' e um número - 0- índice baseado da coluna da esquerda.
Toda vez que um jogador clica em uma coluna:
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])
Depois de enviar esses dados de movimento para o oponente, atualizamos o estado do jogo localmente. Isso inclui determinar se o jogador atual ganhou ou se o jogo terminou empatado.
Na extremidade receptora desses dados de movimentação:
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
E, naturalmente, depois disso, atualizamos o estado do jogo localmente, determinamos se o oponente venceu ou se o jogo terminou empatado.
Observe como precisamos realizar verificações de sanidade nos dados recebidos. Isso é importante, pois com jogos baseados em WebRTC, não temos servidor intermediário e lógica de jogo baseada em servidor validando os dados de movimentação.
Para manter os snippets simples, as linhas de código que atualizam a interface do usuário foram omitidas. Você pode encontrar o código-fonte completo para o JavaScript do lado do cliente aqui.
Conectando tudo
Para combinar tudo, criamos uma página simples com duas seções. No carregamento da página, a seção que contém o menu principal é mostrada, a seção que contém a grade do jogo é mantida 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 | 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
Fazer com que esses elementos DOM pareçam bonitos está além do escopo deste tutorial. Por isso, recorreremos ao nosso confiável companheiro Bootstrap e faremos um estilo leve sobre ele.
Quando o primeiro jogador clica no botão “Iniciar”, a grade do jogo é revelada junto com o ID do jogador. O jogador pode então compartilhar este ID de par com seu oponente.
O segundo jogador pode clicar e depois clicar no botão “Join”, digitar o ID do par do primeiro jogador e começar o jogo.
Experimentando
Você pode experimentar este aplicativo de exemplo em https://arteegee.herokuapp.com.
Ou você pode clonar o repositório do GitHub, instalar dependências do NPM e testá-lo localmente:
git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm start
Assim que o servidor estiver em execução, você pode apontar seu navegador da Web para http://localhost:5000, iniciar um jogo em uma guia e ingressar em outra guia (ou até mesmo em um navegador da Web compatível com WebRTC diferente) usando o ID do par.
Você pode abrir o console do seu navegador da Web para ver algumas informações de depuração, pois neste aplicativo de exemplo, o cliente PeerJS foi configurado para realizar o log detalhado.
Mas não funciona para mim!
Existem duas razões principais pelas quais este jogo pode não funcionar no seu computador.
É possível que você esteja usando um navegador da Web que ainda não suporta as APIs WebRTC necessárias. Se for esse o caso, você pode tentar um navegador diferente - um que suporte WebRTC e canais de dados.
Se você estiver usando um navegador da Web moderno com suporte a WebRTC, existe a chance de estar por trás de alguma infraestrutura de rede na qual o WebRTC não pode penetrar. Idealmente, esse problema pode ser facilmente resolvido com um servidor TURN, mas como o aplicativo de exemplo não está usando um, não funcionará quando você e seu oponente estiverem por trás de NATs.
Conclusão
WebRTC é uma tecnologia nova, e suas implementações estão muito longe de serem maduras. Isso geralmente causa alguns desafios exclusivos para os desenvolvedores. No entanto, com a disponibilidade de bibliotecas como o PeerJS, que abstrai perfeitamente as APIs brutas, a tecnologia já está se tornando bastante acessível.
Espero que este breve tutorial para construir um jogo baseado em PeerJS o ajude a começar com o WebRTC e a construir alguns incríveis aplicativos da Web ponto a ponto em tempo real.