Oswajanie WebRTC z PeerJS: tworzenie prostej gry internetowej P2P

Opublikowany: 2022-03-11

WebRTC to technologia umożliwiająca komunikację w czasie rzeczywistym między przeglądarkami internetowymi. Jest stosunkowo nowy, a definicja interfejsu API jest nadal uważana za wersję roboczą. W połączeniu z faktem, że WebRTC nie jest jeszcze obsługiwany przez wszystkie główne przeglądarki internetowe (a wśród tych, które obsługują, niektóre z nich nie obsługują wszystkich funkcji tej technologii), sprawia to, że korzystanie z WebRTC w aplikacjach o znaczeniu krytycznym jest stosunkowo trudne. . A przynajmniej tak myślisz!

Połącz cztery przez WebRTC za pomocą PeerJS: Patrz ma, nie ma serwera!

Połącz cztery przez WebRTC za pomocą PeerJS: Patrz ma, nie ma serwera!
Ćwierkać

Od czasu wprowadzenia go po raz pierwszy przez Google w maju 2011 r., WebRTC był używany w wielu nowoczesnych aplikacjach internetowych. Będąc podstawową funkcją wielu nowoczesnych przeglądarek internetowych, aplikacje internetowe mogą bezproblemowo korzystać z tej technologii, aby zapewnić użytkownikom lepsze wrażenia na wiele sposobów. Aplikacje do strumieniowego przesyłania wideo lub konferencji, które nie wymagają nadętych wtyczek do przeglądarek i mogą korzystać z sieci peer-to-peer (P2P) (nie przesyłając każdego bitu danych przez jakiś serwer), to tylko część wszystkich niesamowitych rzeczy, które można osiągnąć dzięki WebRTC.

W tym artykule przyjrzymy się, jak można wykorzystać WebRTC do stworzenia prostej gry internetowej P2P Connect Four. Aby obejść różne ostre krawędzie i różnice w implementacji WebRTC, użyjemy niesamowitej biblioteki JavaScript: PeerJS.

Dane przez WebRTC

Zanim zaczniemy, ważne jest, aby zrozumieć, że WebRTC to nie tylko przesyłanie strumieni audio i wideo. Zapewnia również obsługę kanałów danych P2P. Kanały te występują w dwóch odmianach: niezawodnym i niewiarygodnym. Jak można się domyślać, niezawodne kanały danych gwarantują, że wiadomości są dostarczane i dostarczane w kolejności, podczas gdy nierzetelne kanały takich gwarancji nie dają.

Infrastruktura WebRTC - ocean akronimów

Infrastruktura WebRTC - ocean akronimów
Ćwierkać

Co więcej, kanały danych WebRTC nie wymagają specjalnej konfiguracji infrastruktury, poza tym, co jest wymagane przez typowe połączenie równorzędne WebRTC: serwer sygnalizacyjny do koordynowania połączenia między równorzędnymi, serwer STUN do określania publicznej tożsamości równorzędnych i opcjonalnie serwer TURN do kierowania wiadomości między peerami, jeśli nie można nawiązać bezpośredniego połączenia między peerami (na przykład, gdy obaj peery są za NATami). Jeśli te akronimy brzmią znajomo, to dlatego, że WebRTC zmienia przeznaczenie istniejących technologii tam, gdzie to możliwe.

Otwiera to drzwi do znacznie większej liczby przypadków użycia WebRTC, w tym między innymi do gier wieloosobowych, dostarczania treści i udostępniania plików. Ponownie, wszystko bez potrzeby jakiegokolwiek serwera pośredniczącego, a co za tym idzie z mniejszymi opóźnieniami.

W naszej prostej grze internetowej użyjemy kanału danych między dwiema przeglądarkami internetowymi, aby przekazywać ruchy gracza w tę i z powrotem.

Poznaj PeerJS

PeerJS przejmuje implementację WebRTC w Twojej przeglądarce i otacza go prostym, spójnym i eleganckim interfejsem API. Zatyka różne dziury w implementacji WebRTC we wcześniejszych przeglądarkach. Na przykład w Chrome 30 lub starszym dostępne były tylko niewiarygodne kanały danych. PeerJS, jeśli jest skonfigurowany do korzystania z niezawodnych kanałów danych, użyje podkładki dla tych starszych przeglądarek. Chociaż nie byłoby to tak wydajne, jak natywna implementacja niezawodnych kanałów, nadal działałoby.

Dzięki PeerJS identyfikacja peerów jest jeszcze prostsza. Każdy peer jest identyfikowany za pomocą tylko identyfikatora. Ciąg, który peer może sam wybrać lub wygenerować go przez serwer. Chociaż WebRTC obiecuje komunikację peer-to-peer, nadal potrzebujesz serwera, który będzie działał jako broker połączeń i obsługiwał sygnalizację. PeerJS zapewnia implementację open source tego serwera brokera połączeń PeerJS Server (napisany w Node.js), na wypadek, gdybyś nie chciał korzystać z jego wersji hostowanej w chmurze (która jest teraz bezpłatna i ma pewne ograniczenia).

Połącz cztery idzie P2P

Teraz, gdy mamy już źródło zaufania do pracy z WebRTC, czyli PeerJS, zacznijmy od stworzenia prostej aplikacji Node.js/Express.

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

Użyjemy tego tylko do hostowania PeerJS Server i obsługi strony i zasobów frontonu. Będziemy musieli wyświetlać tylko jedną stronę, która będzie zawierać dwie sekcje: zwykłe menu główne i siatkę Connect Four 7 na 6.

Serwer PeerJS

Hosting naszego własnego serwera PeerJS jest naprawdę łatwy. Oficjalne repozytorium na GitHub ma nawet przycisk do wdrożenia instancji PeerJS Server w Heroku za pomocą jednego kliknięcia.

W naszym przypadku chcemy po prostu stworzyć instancję ExpressPeerServer w naszej aplikacji Node.js i obsłużyć ją w „/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 }))

Klient PeerJS

Po uruchomieniu PeerJS Server przechodzimy do strony klienta. Jak omówiono wcześniej, PeerJS identyfikuje peerów z unikalnymi identyfikatorami. Identyfikatory te mogą być generowane przez PeerServer automatycznie dla każdego peera lub możemy wybrać jeden dla każdego peera podczas tworzenia instancji obiektów Peer .

 var peer = new Peer(id, options)

Tutaj id można całkowicie pominąć, jeśli chcemy, aby serwer go dla nas wygenerował. W naszym przypadku właśnie to będziemy chcieli zrobić. PeerServer zapewni, że identyfikatory, które rozdaje, będą unikalne. Drugi argument, options , jest zwykle obiektem zawierającym klucz (klucz API, jeśli korzystasz z PeerServer hostowanego w chmurze lub host , port , path , itp. w przypadku, gdy sam hostujesz PeerServer).

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

Aby nawiązać połączenie między dwoma peerami PeerJS, jeden z peerów musi znać ID drugiego peera. Aby uprościć sprawę, w naszej implementacji Connect Four przez WebRTC będziemy wymagać od gracza rozpoczynającego grę, aby udostępnił swój identyfikator równorzędny swojemu przeciwnikowi. Gdy znany jest identyfikator docelowego peera, wystarczy proste wywołanie peer.connect(destId) :

 var conn = peer.connect(destId)

Zarówno obiekt Peer , jak i obiekt DataConnection zwrócony przez peer.connect(destId) emitują kilka naprawdę przydatnych zdarzeń, na których warto posłuchać. Na potrzeby tego samouczka szczególnie interesuje nas zdarzenie „data” obiektu DataConnection oraz zdarzenia „błąd” obu obiektów.

Aby przesłać dane na drugi koniec połączenia, po prostu wywołaj conn.send(data) :

 conn.send('hello')

Chociaż trochę przesada jak na nasze potrzeby, PeerJS przesyła dane między peerami po zakodowaniu ich w formacie BinaryPack. Dzięki temu peery mogą komunikować się ciągami, liczbami, tablicami, obiektami, a nawet blobami.

Aby otrzymać przychodzące dane, po prostu nasłuchuj zdarzenia „data” na conn :

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

I to prawie wszystko, czego potrzebujemy!

Logika gry

Pierwszemu graczowi, który rozpoczyna grę, pokazywany jest identyfikator partnera wygenerowany przez PeerJS, który może udostępnić swojemu przeciwnikowi. Gdy przeciwnik dołączy do gry używając identyfikatora pierwszego gracza, pierwszy gracz może wykonać ruch.

Connect Four, będąca grą o prostych zasadach i mechanice, ma tylko jeden rodzaj ruchu: każdy gracz po kolei musi wybrać kolumnę i wrzucić do niej dysk. Oznacza to, że wszystko, co peer musi się komunikować, to numer kolumny, w której bieżący gracz zdecydował się wrzucić swój dysk. Prześlemy te informacje jako tablicę z dwoma elementami: ciągiem „ruch” i liczbą - 0- na podstawie indeksu kolumny od lewej.

Za każdym razem, gdy gracz kliknie kolumnę:

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

Po wysłaniu tych danych ruchu do przeciwnika, aktualizujemy stan gry lokalnie. Obejmuje to określenie, czy aktualny gracz wygrał, czy też gra zakończyła się remisem.

Po otrzymaniu tego ruchu dane:

 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

I oczywiście, po tym lokalnie aktualizujemy stan gry, ustalamy, czy przeciwnik wygrał, czy gra zakończyła się remisem.

Zwróć uwagę, jak musimy sprawdzać poprawność przychodzących danych. Jest to ważne, ponieważ w przypadku gier opartych na WebRTC nie mamy serwera pośredniczącego ani logiki gry opartej na serwerze, weryfikującej dane ruchu.

Aby zachować prostotę fragmentów, pominięto wiersze kodu, które aktualizują interfejs użytkownika. Pełny kod źródłowy JavaScript po stronie klienta można znaleźć tutaj.

Łącząc to wszystko

Aby to wszystko połączyć tworzymy prostą stronę z dwoma sekcjami. Podczas ładowania strony wyświetlana jest sekcja zawierająca menu główne, sekcja zawierająca siatkę gry jest ukryta.

 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

Sprawienie, by te elementy DOM wyglądały ładnie, wykracza poza zakres tego samouczka. Dlatego uciekniemy się do naszego zaufanego towarzysza Bootstrap i wykonamy nad nim lekką stylizację.

Gdy pierwszy gracz kliknie przycisk „Start”, zostanie odsłonięta siatka gry wraz z identyfikatorem gracza. Gracz może następnie udostępnić ten identyfikator równorzędny swojemu przeciwnikowi.

Rozpocznij nową grę i udostępnij swój identyfikator równorzędny wygenerowany przez PeerJS

Drugi gracz może kliknąć, a następnie kliknąć przycisk „Dołącz”, wprowadzić identyfikator pierwszego gracza i rozpocząć grę.

Użyj identyfikatora równorzędnego przeciwnika, aby dołączyć do gry

Wypróbowanie

Możesz wypróbować tę przykładową aplikację na https://arteegee.herokuapp.com.

Możesz też sklonować repozytorium z GitHub, zainstalować zależności NPM i wypróbować je lokalnie:

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

Gdy serwer jest uruchomiony, możesz skierować swoją przeglądarkę internetową na http://localhost:5000, rozpocząć grę z jednej karty i dołączyć z innej karty (lub nawet innej przeglądarki obsługującej WebRTC) przy użyciu identyfikatora równorzędnego.

Możesz otworzyć konsolę przeglądarki internetowej, aby zobaczyć niektóre informacje debugowania, ponieważ w tej przykładowej aplikacji klient PeerJS został skonfigurowany do wykonywania pełnego rejestrowania.

Ale to nie działa dla mnie!

Istnieją dwa główne powody, dla których ta gra może nie działać na twoim komputerze.

Możliwe, że używasz przeglądarki internetowej, która nie obsługuje jeszcze niezbędnych interfejsów API WebRTC. W takim przypadku możesz wypróbować inną przeglądarkę — taką, która obsługuje WebRTC i kanały danych.

Jeśli korzystasz z nowoczesnej przeglądarki internetowej z obsługą WebRTC, istnieje szansa, że ​​stoisz za infrastrukturą sieciową, której WebRTC nie może przeniknąć. Idealnie ten problem można łatwo rozwiązać za pomocą serwera TURN, ale ponieważ przykładowa aplikacja go nie używa, nie będzie działać, gdy zarówno ty, jak i twój przeciwnik jesteście za NATami.

Wniosek

WebRTC to nowa technologia, a jej implementacje nie są jeszcze dojrzałe. Często stawiają one programistom pewne wyjątkowe wyzwania. Jednak dzięki dostępności bibliotek, takich jak PeerJS, które zgrabnie oddzielają surowe API, technologia staje się już całkiem dostępna.

Mam nadzieję, że ten krótki samouczek dotyczący budowania gry opartej na PeerJS pomoże Ci rozpocząć pracę z WebRTC i zbudować niesamowite aplikacje internetowe typu peer-to-peer działające w czasie rzeczywistym.