Zähmen von WebRTC mit PeerJS: Erstellen eines einfachen P2P-Webspiels
Veröffentlicht: 2022-03-11WebRTC ist eine Technologie, die Echtzeitkommunikation zwischen Webbrowsern ermöglicht. Sie ist relativ neu und die API-Definition gilt noch als Entwurf. Zusammen mit der Tatsache, dass WebRTC noch nicht von allen gängigen Webbrowsern unterstützt wird (und von denen, die dies tun, unterstützen einige nicht alle Funktionen dieser Technologie), macht es dies relativ schwierig, WebRTC für unternehmenskritische Anwendungen zu verwenden . Oder so würde man meinen!
Seit seiner Einführung durch Google im Mai 2011 wird WebRTC in vielen modernen Webanwendungen verwendet. Als Kernfunktion vieler moderner Webbrowser können Webanwendungen diese Technologie nahtlos nutzen, um in vielerlei Hinsicht ein verbessertes Benutzererlebnis zu bieten. Video-Streaming- oder Konferenzanwendungen, die keine aufgeblähten Browser-Plug-ins benötigen und Peer-to-Peer-Netzwerke (P2P) nutzen können (während nicht jedes Datenbit über einen Server übertragen wird), sind nur ein Teil all der erstaunlichen Dinge, die das tun kann mit WebRTC erreicht werden.
In diesem Artikel werfen wir einen Blick darauf, wie WebRTC verwendet werden kann, um ein einfaches P2P-Webspiel von Connect Four zu erstellen. Um die verschiedenen Ecken und Kanten und Implementierungsunterschiede von WebRTC zu umgehen, verwenden wir eine erstaunliche JavaScript-Bibliothek: PeerJS.
Daten über WebRTC
Bevor wir beginnen, ist es wichtig zu verstehen, dass es bei WebRTC nicht nur um die Übertragung von Audio- und Videostreams geht. Es bietet auch Unterstützung für P2P-Datenkanäle. Diese Kanäle gibt es in zwei Varianten: zuverlässig und unzuverlässig. Wie man sich vorstellen kann, garantieren zuverlässige Datenkanäle, dass Nachrichten zugestellt werden und dass sie der Reihe nach zugestellt werden, während unzuverlässige Kanäle keine solchen Garantien bieten.
Darüber hinaus erfordern WebRTC-Datenkanäle keine spezielle Infrastruktureinrichtung, abgesehen von dem, was für eine typische WebRTC-Peer-Verbindung erforderlich ist: ein Signalisierungsserver, um die Verbindung zwischen Peers zu koordinieren, ein STUN-Server, um die öffentliche Identität der Peers herauszufinden, und optional ein TURN-Server um Nachrichten zwischen Peers weiterzuleiten, wenn keine direkte Verbindung zwischen Peers hergestellt werden kann (z. B. wenn sich beide Peers hinter NATs befinden). Wenn Ihnen diese Akronyme bekannt vorkommen, liegt das daran, dass WebRTC vorhandene Technologien wo immer möglich umfunktioniert.
Dies öffnet die Tür zu viel mehr Anwendungsfällen von WebRTC, einschließlich, aber nicht beschränkt auf Multiplayer-Spiele, Bereitstellung von Inhalten und Dateifreigabe. Auch hier wieder alles ohne die Notwendigkeit eines zwischengeschalteten Servers und damit mit geringeren Latenzen.
In unserem einfachen Webspiel verwenden wir einen Datenkanal zwischen zwei Webbrowsern, um Spielerbewegungen hin und her zu kommunizieren.
Lernen Sie PeerJS kennen
PeerJS übernimmt die Implementierung von WebRTC in Ihrem Browser und umschließt sie mit einer einfachen, konsistenten und eleganten API. Es stopft verschiedene Lücken in der WebRTC-Implementierung früherer Browser. Beispielsweise waren in Chrome 30 oder älter nur unzuverlässige Datenkanäle verfügbar. PeerJS würde, wenn es für die Verwendung zuverlässiger Datenkanäle konfiguriert ist, einen Shim für diese älteren Browser verwenden. Obwohl dies nicht so leistungsfähig wäre wie die native Implementierung zuverlässiger Kanäle, würde es dennoch funktionieren.
Mit PeerJS ist die Identifizierung von Peers noch einfacher. Jeder Peer wird nur über eine ID identifiziert. Eine Zeichenfolge, die der Peer selbst auswählen oder von einem Server generieren lassen kann. Obwohl WebRTC Peer-to-Peer-Kommunikation verspricht, benötigen Sie trotzdem einen Server, der als Verbindungsvermittler fungiert und die Signalisierung abwickelt. PeerJS bietet eine Open-Source-Implementierung dieses Verbindungsbroker-Servers PeerJS Server (geschrieben in Node.js), falls Sie die in der Cloud gehostete Version nicht verwenden möchten (die derzeit kostenlos ist und einige Einschränkungen aufweist).
Verbinden Sie vier Gos P2P
Nachdem wir nun eine Vertrauensquelle für die Arbeit mit WebRTC, dh PeerJS, haben, beginnen wir mit der Erstellung einer einfachen Node.js/Express-Anwendung.
npm init npm install express --save npm install jade --save npm install peer --saveWir werden dies nur verwenden, um den PeerJS-Server zu hosten und eine Seite und Front-End-Assets bereitzustellen. Wir müssen nur eine einzige Seite bereitstellen, und diese enthält zwei Abschnitte: ein einfaches Hauptmenü und ein 7-mal-6-Vier-verbinden-Raster.
PeerJS-Server
Das Hosten unseres eigenen PeerJS-Servers ist wirklich einfach. Das offizielle Repository auf GitHub verfügt sogar über eine Ein-Klick-Schaltfläche, um eine Instanz von PeerJS Server für Heroku bereitzustellen.
In unserem Fall möchten wir nur eine Instanz von ExpressPeerServer in unserer Node.js-Anwendung erstellen und unter „/peerjs“ bereitstellen:
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 }))PeerJS-Client
Wenn der PeerJS-Server eingerichtet ist und läuft, gehen wir zur Client-Seite über. Wie bereits erwähnt, identifiziert PeerJS Peers mit eindeutigen IDs. Diese IDs können von PeerServer automatisch für jeden Peer generiert werden, oder wir können eine für jeden Peer auswählen, während wir Peer -Objekte instanziieren.
var peer = new Peer(id, options)Hier kann id ganz weggelassen werden, wenn wir möchten, dass der Server eine für uns generiert. In unserem Fall wollen wir das tun. PeerServer stellt sicher, dass die ausgegebenen IDs eindeutig sind. Das zweite Argument, options , ist normalerweise ein Objekt, das einen Schlüssel enthält (der API-Schlüssel, wenn Sie einen in der Cloud gehosteten PeerServer verwenden, oder host , port , path usw., falls Sie den PeerServer selbst hosten).
var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })Um eine Verbindung zwischen zwei PeerJS-Peers aufzubauen, muss einer der Peers die ID des anderen Peers kennen. Um die Dinge einfach zu halten, verlangen wir in unserer Implementierung von Connect Four über WebRTC, dass der Spieler, der das Spiel startet, seine Peer-ID mit seinem Gegner teilt. Da die Ziel-Peer-ID bekannt ist, genügt ein einfacher Aufruf von peer.connect(destId) :

var conn = peer.connect(destId)Sowohl das Peer -Objekt als auch das von peer.connect(destId) zurückgegebene DataConnection- Objekt geben einige wirklich nützliche Ereignisse aus, auf die es sich zu hören lohnt. Für die Zwecke dieses Tutorials interessieren uns besonders das 'data'-Ereignis des DataConnection -Objekts und die 'error'-Ereignisse beider Objekte.
Um Daten an das andere Ende der Verbindung zu senden, rufen Sie einfach conn.send(data) auf:
conn.send('hello')Obwohl es hier für unsere Bedürfnisse etwas übertrieben ist, überträgt PeerJS Daten zwischen Peers, nachdem sie im BinaryPack-Format codiert wurden. Dadurch können Peers Zeichenfolgen, Zahlen, Arrays, Objekte und sogar Blobs kommunizieren.
Um eingehende Daten zu empfangen, hören Sie einfach auf conn auf das Ereignis „data“:
conn.on('data', function(data) { // data === 'hello' })Und das ist so ziemlich alles, was wir brauchen!
Spiellogik
Dem ersten Spieler, der ein Spiel startet, wird seine von PeerJS generierte Peer-ID angezeigt, die er mit seinem Gegner teilen kann. Sobald ein Gegner dem Spiel unter Verwendung der Peer-ID des ersten Spielers beitritt, darf der erste Spieler einen Zug machen.
Connect Four, ein Spiel mit einfachen Regeln und Mechaniken, hat nur eine Art von Zug: Jeder Spieler muss der Reihe nach eine Spalte auswählen und eine Scheibe hineinlegen. Das bedeutet, dass alles, was ein Peer kommunizieren muss, die Spaltennummer ist, in der der aktuelle Spieler seine Scheibe abgelegt hat. Wir übertragen diese Informationen als Array mit zwei Elementen: einer Zeichenfolge „move“ und einer Zahl – 0 – basierender Index der Spalte von links.
Jedes Mal, wenn ein Spieler auf eine Spalte klickt:
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])Nachdem wir diese Zugdaten an den Gegner gesendet haben, aktualisieren wir den Status des Spiels lokal. Dazu gehört die Feststellung, ob der aktuelle Spieler gewonnen hat oder ob das Spiel unentschieden endete.
Auf der Empfängerseite dieser Bewegungsdaten:
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 = trueUnd natürlich aktualisieren wir danach lokal den Status des Spiels, stellen fest, ob der Gegner gewonnen hat oder ob das Spiel unentschieden endete.
Beachten Sie, wie wir Plausibilitätsprüfungen für eingehende Daten durchführen müssen. Dies ist wichtig, da wir bei WebRTC-basierten Spielen keinen zwischengeschalteten Server und serverbasierte Spiellogik haben, die die Bewegungsdaten validieren.
Um die Snippets einfach zu halten, wurden Codezeilen weggelassen, die die Benutzeroberfläche aktualisieren. Den vollständigen Quellcode für das clientseitige JavaScript finden Sie hier.
Alles verbinden
Um alles zu kombinieren, erstellen wir eine einfache Seite mit zwei Abschnitten. Beim Laden der Seite wird der Abschnitt mit dem Hauptmenü angezeigt, der Abschnitt mit dem Spielraster bleibt verborgen.
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 pDiese DOM-Elemente hübsch aussehen zu lassen, würde den Rahmen dieses Tutorials sprengen. Daher greifen wir auf unseren bewährten Begleiter Bootstrap zurück und machen ein leichtes Styling darüber.
Wenn der erste Spieler auf die Schaltfläche „Start“ klickt, wird das Spielraster zusammen mit der Peer-ID des Spielers angezeigt. Diese Peer-ID kann der Spieler dann mit seinem Gegner teilen.
Der zweite Spieler kann klicken und dann auf die Schaltfläche „Join“ klicken, die Peer-ID des ersten Spielers eingeben und das Spiel beginnen.
Ausprobieren
Sie können diese Beispielanwendung unter https://arteegee.herokuapp.com ausprobieren.
Oder Sie können das Repository von GitHub klonen, NPM-Abhängigkeiten installieren und es lokal ausprobieren:
git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm startSobald der Server läuft, können Sie Ihren Webbrowser auf http://localhost:5000 verweisen, ein Spiel von einer Registerkarte aus starten und über eine andere Registerkarte (oder sogar einen anderen WebRTC-fähigen Webbrowser) mit der Peer-ID beitreten.
Sie können die Konsole Ihres Webbrowsers öffnen, um einige Debug-Informationen anzuzeigen, da der PeerJS-Client in dieser Beispielanwendung so konfiguriert wurde, dass er eine ausführliche Protokollierung durchführt.
Aber es funktioniert nicht für mich!
Es gibt zwei Hauptgründe, warum dieses Spiel auf Ihrem Computer möglicherweise nicht funktioniert.
Möglicherweise verwenden Sie einen Webbrowser, der die erforderlichen WebRTC-APIs noch nicht unterstützt. Wenn das der Fall ist, sollten Sie einen anderen Browser ausprobieren – einen, der WebRTC und Datenkanäle unterstützt.
Wenn Sie einen modernen Webbrowser mit WebRTC-Unterstützung verwenden, besteht die Möglichkeit, dass Sie sich hinter einer Netzwerkinfrastruktur befinden, die WebRTC nicht durchdringen kann. Idealerweise lässt sich dieses Problem einfach mit einem TURN-Server lösen, aber da die Beispielanwendung keinen verwendet, funktioniert es nicht, wenn sowohl Sie als auch Ihr Gegner hinter NATs stehen.
Fazit
WebRTC ist eine neue Technologie, und ihre Implementierungen sind noch lange nicht ausgereift. Diese verursachen oft einige einzigartige Herausforderungen für Entwickler. Mit der Verfügbarkeit von Bibliotheken wie PeerJS, die die groben Roh-APIs sauber abstrahieren, wird die Technologie jedoch bereits recht zugänglich.
Ich hoffe, dieses kurze Tutorial zum Erstellen eines PeerJS-basierten Spiels hilft Ihnen beim Einstieg in WebRTC und beim Erstellen einiger erstaunlicher Peer-to-Peer-Webanwendungen in Echtzeit.
