Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień

Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
Oprogramowanie jako usługa, infrastruktura jako usługa, platforma jako usługa, platforma komunikacyjna jako usługa, wideokonferencje jako usługa, a co z grami w chmurze jako usługą? Podjęto już kilka prób stworzenia gier w chmurze (Cloud Gaming), takich jak Stadia, niedawno uruchomiona przez Google. Stadiony nie jest nowością w WebRTC, ale czy inni mogą korzystać z WebRTC w ten sam sposób?

Thanh Nguyen postanowił przetestować tę możliwość w swoim projekcie open source CloudRetro. CloudRetro bazuje na Pionie, popularny Biblioteka WebRTC oparta na Go (dzięki Pokaż od zespołu programistów Pion za pomoc w przygotowaniu tego artykułu). W tym artykule Thanh przedstawia przegląd architektury swojego projektu, a także opowiada o tym, jakich przydatnych rzeczy się nauczył i jakie wyzwania napotkał podczas swojej pracy.

Wejście

Kiedy w zeszłym roku Google ogłosiło Stadię, oszołomiło mnie to. Pomysł jest tak wyjątkowy i innowacyjny, że nieustannie zastanawiałem się, jak to w ogóle było możliwe przy istniejącej technologii. Chęć lepszego zrozumienia tego tematu skłoniła mnie do stworzenia własnej wersji gry chmurowej o otwartym kodzie źródłowym. Rezultat był po prostu fantastyczny. Poniżej chciałbym podzielić się procesem pracy nad moim rokiem projekt.

TLDR: wersja z krótkim slajdem z zaznaczonymi fragmentami

Dlaczego gry w chmurze są przyszłością

Wierzę, że Cloud Gaming wkrótce stanie się kolejną generacją nie tylko gamingu, ale także innych dziedzin informatyki. Gry w chmurze to szczyt modelu klient/serwer. Model ten maksymalizuje zarządzanie backendem i minimalizuje pracę frontendu, hostując logikę gry na zdalnym serwerze i przesyłając strumieniowo obrazy/audio do klienta. Serwer wykonuje intensywne przetwarzanie, więc klient nie jest już zdany na łaskę ograniczeń sprzętowych.

Google Stadia zasadniczo pozwala grać Gry AAA (tj. wysokiej klasy, hitowe gry) w interfejsie takim jak YouTube. Tę samą metodologię można zastosować w przypadku innych wymagających aplikacji offline, takich jak system operacyjny lub projekty graficzne 2D/3D itp. abyśmy mogli je konsekwentnie uruchamiać na urządzeniach o niskiej specyfikacji i na wielu platformach.

Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
Przyszłość tej technologii: Wyobraź sobie, że Microsoft Windows 10 działał w przeglądarce Chrome?

Granie w chmurze jest wyzwaniem technicznym

Gry to jeden z tych rzadkich obszarów, w których wymagana jest stała i szybka reakcja użytkownika. Jeśli czasami podczas klikania strony napotykamy 2-sekundowe opóźnienie, jest to dopuszczalne. Transmisje wideo na żywo mają tendencję do opóźnień o kilka sekund, ale nadal zapewniają rozsądną użyteczność. Jeśli jednak gra często opóźnia się o 500 ms, po prostu nie da się w nią grać. Naszym celem jest osiągnięcie wyjątkowo niskiego opóźnienia, aby różnica między sygnałem wejściowym a multimediami była jak najmniejsza. Dlatego tradycyjne podejście do strumieniowego przesyłania wideo nie ma tutaj zastosowania.

Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
Ogólny szablon gry w chmurze

Projekt open source CloudRetro

Postanowiłem stworzyć próbkę testową gry w chmurze, aby sprawdzić, czy to wszystko jest możliwe przy tak rygorystycznych ograniczeniach sieci. Wybrałem Golang jako dowód słuszności koncepcji, ponieważ był to język, który znałem najlepiej i który, jak później odkryłem, dobrze nadawał się do tej implementacji z wielu innych powodów. Go jest proste i rozwija się bardzo szybko; Kanały w Go doskonale nadają się do zarządzania wielowątkowością.

Projekt CloudRetro.io to usługa gier w chmurze typu open source przeznaczona do gier retro. Celem projektu jest zapewnienie jak najwygodniejszego doświadczenia w grach w tradycyjnych grach retro oraz dodanie trybu wieloosobowego.
Więcej o projekcie można dowiedzieć się tutaj: https://github.com/giongto35/cloud-game.

Funkcjonalność CloudRetro

CloudRetro wykorzystuje gry retro, aby zademonstrować moc gier w chmurze. Co pozwala uzyskać wiele wyjątkowych wrażeń z gry.

  • Przenośność gry
    • Natychmiastowe odtwarzanie po otwarciu strony; nie wymaga pobierania ani instalacji
    • Działa w przeglądarce mobilnej, więc do jego uruchomienia nie jest potrzebne żadne oprogramowanie

  • Sesje gier można udostępniać na wielu urządzeniach i przechowywać w chmurze do wykorzystania przy następnym logowaniu
  • Grę można transmitować strumieniowo lub grać w nią kilku użytkowników jednocześnie:
    • Gra społecznościowa, taka jak TwitchPlayPokemon, tylko bardziej międzyplatformowa i bardziej w czasie rzeczywistym
    • Gry offline online. Wielu użytkowników może grać bez konfigurowania sieci. W grę Samurai Shodown może teraz grać 2 graczy za pośrednictwem sieci CloudRetro

    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Wersja demonstracyjna gry online dla wielu graczy na różnych urządzeniach

    Infrastruktura

    Wymagania i stos technologii

    Poniżej znajduje się lista wymagań, które postawiłem przed rozpoczęciem projektu.

    1. Jeden gracz
    Wymóg ten może nie wydawać się tutaj zbyt ważny ani oczywisty, ale jest to jeden z moich najważniejszych wniosków, ponieważ pozwala grach w chmurze trzymać się jak najdalej od tradycyjnych usług przesyłania strumieniowego. Jeśli skupimy się na grze dla jednego gracza, możemy pozbyć się scentralizowanego serwera lub CDN, ponieważ nie musimy przesyłać strumieniowo do mas. Zamiast przesyłać strumienie do serwera ujścia lub przekazywać pakiety do scentralizowanego serwera WebSocket, strumienie usług są dostarczane bezpośrednio do użytkownika za pośrednictwem połączenia WebRTC typu peer-to-peer.

    2. Strumień multimediów o niskim opóźnieniu
    Czytając o Stadia, często w niektórych artykułach widzę wzmiankę o WebRTC. Zdałem sobie sprawę, że WebRTC to wyjątkowa technologia, idealna do wykorzystania w grach w chmurze. WebRTC to projekt zapewniający przeglądarkom internetowym i aplikacjom mobilnym komunikację w czasie rzeczywistym poprzez proste API. Zapewnia łączność peer-to-peer, jest zoptymalizowany pod kątem multimediów i ma wbudowane standardowe kodeki, takie jak VP8 i H264.

    Priorytetem było dla mnie zapewnienie możliwie najlepszego doświadczenia użytkownika nad utrzymaniem wysokiej jakości grafiki. Niektóre straty są dopuszczalne w algorytmie. Google Stadia ma dodatkowy etap polegający na zmniejszeniu rozmiaru obrazu na serwerze, a ramki są skalowane do wyższej jakości przed przesłaniem do równorzędnych urządzeń.

    3. Infrastruktura rozproszona z routingiem geograficznym
    Niezależnie od tego, jak zoptymalizowany jest algorytm i kod kompresji, decydującym czynnikiem mającym największy wpływ na opóźnienia jest sieć. Architektura musi posiadać mechanizm parowania serwera znajdującego się najbliżej użytkownika, aby skrócić czas podróży w obie strony (RTT). Architektura musi mieć 1 koordynatora i kilka serwerów przesyłania strumieniowego rozproszonych po całym świecie: Zachód Stanów Zjednoczonych, Wschód Stanów Zjednoczonych, Europa, Singapur, Chiny. Wszystkie serwery przesyłania strumieniowego muszą być całkowicie odizolowane. System może dostosować swoją dystrybucję, gdy serwer przyłącza się do sieci lub ją opuszcza. Zatem przy dużym ruchu dodanie dodatkowych serwerów pozwala na skalowanie poziome.

    4. Kompatybilność z przeglądarką
    Gry w chmurze sprawdzają się najlepiej, gdy wymagają od użytkowników jak najmniej. Oznacza to, że możliwe jest uruchomienie w przeglądarce. Przeglądarki pomagają użytkownikom zapewnić jak największy komfort gry, oszczędzając im konieczności instalowania oprogramowania i sprzętu. Przeglądarki pomagają także zapewnić funkcjonalność międzyplatformową pomiędzy wersjami mobilnymi i stacjonarnymi. Na szczęście WebRTC jest dobrze obsługiwany w różnych przeglądarkach.

    5. Wyraźne oddzielenie interfejsu gry i usługi
    Postrzegam usługę gier w chmurze jako platformę. Każdy powinien mieć możliwość podłączenia czegokolwiek do platformy. Teraz się zintegrowałem LibRetro z usługą gier w chmurze, ponieważ LibRetro oferuje piękny interfejs emulatora gier dla gier retro, takich jak SNES, GBA, PS.

    6. Pokoje do gry wieloosobowej, gry społecznościowej i zewnętrznego łączenia (głębokiego łącza) z grą
    CloudRetro obsługuje wiele nowych rozgrywki, takich jak CrowdPlay i Online MultiPlayer dla gier retro. Jeśli kilku użytkowników otworzy ten sam głęboki link na różnych komputerach, zobaczą uruchomioną tę samą grę i będą mogli nawet do niej dołączyć.

    Co więcej, stany gier są przechowywane w chmurze. Dzięki temu użytkownicy mogą kontynuować grę w dowolnym momencie na dowolnym innym urządzeniu.

    7. Skalowanie poziome
    Jak każdy obecnie SAAS, gry w chmurze muszą być zaprojektowane tak, aby były skalowalne w poziomie. Konstrukcja koordynator-pracownik umożliwia dodanie większej liczby pracowników w celu obsługi większego ruchu.

    8. Brak połączenia z jedną chmurą
    Infrastruktura CloudRetro jest hostowana u różnych dostawców usług w chmurze (Digital Ocean, Alibaba, dostawca niestandardowy) dla różnych regionów. Włączam uruchamianie infrastruktury w kontenerze Docker i konfiguruję ustawienia sieciowe za pomocą skryptu bash, aby uniknąć zamknięcia w jednym dostawcy chmury. Łącząc to z NAT Traversal w WebRTC, możemy mieć elastyczność wdrażania CloudRetro na dowolnej platformie chmurowej, a nawet na komputerach dowolnego użytkownika.

    Styl architektoniczny

    Pracownik: (lub wspomniany powyżej serwer przesyłania strumieniowego) zwielokrotnia gry, uruchamia potok kodowania i przesyła strumieniowo zakodowane multimedia do użytkowników. Instancje procesów roboczych są rozproszone po całym świecie, a każdy proces roboczy może jednocześnie obsługiwać wiele sesji użytkowników.

    Koordynator: jest odpowiedzialny za powiązanie nowego użytkownika z najbardziej odpowiednim pracownikiem do przesyłania strumieniowego. Koordynator komunikuje się z pracownikami za pośrednictwem protokołu WebSocket.

    Pamięć stanu gry: centralne zdalne przechowywanie wszystkich stanów gry. Ta pamięć zapewnia ważne funkcje, takie jak zdalne zapisywanie/ładowanie.

    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Architektura najwyższego poziomu CloudRetro

    Skrypt niestandardowy

    Gdy nowy użytkownik otworzy CloudRetro w krokach 1 i 2 pokazanych na poniższym rysunku, koordynator wraz z listą dostępnych pracowników zostaje przeniesiony na pierwszą stronę. Następnie w kroku 3 klient oblicza opóźnienia dla wszystkich kandydatów, korzystając z żądania ping HTTP. Lista opóźnień jest następnie odsyłana do koordynatora, aby mógł on wybrać najodpowiedniejszego pracownika do obsługi użytkownika. Krok 4 poniżej tworzy grę. Pomiędzy użytkownikiem a przypisanym pracownikiem zostaje nawiązane połączenie strumieniowe WebRTC.
    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Skrypt użytkownika po uzyskaniu dostępu

    Co kryje się w pracowniku

    Potoki gier i transmisji strumieniowych są przechowywane w izolacji wewnątrz pracownika i wymieniają tam informacje za pośrednictwem interfejsu. Obecnie komunikacja ta odbywa się poprzez przesyłanie danych do pamięci poprzez Kanały Golanga w tym samym procesie. Kolejnym celem jest segregacja, czyli tzw. niezależne uruchomienie gry w innym procesie.

    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Interakcja komponentów roboczych

    Główne składniki:

    • WebRTC: komponent klienta, który akceptuje dane wejściowe użytkownika i wysyła zakodowane multimedia z serwera.
    • Emulator gry: element gry. Dzięki bibliotece Libretro system jest w stanie uruchomić grę w tym samym procesie i wewnętrznie przechwycić multimedia i strumień wejściowy.
    • Ramki w grze są przechwytywane i wysyłane do kodera.
    • Koder obrazu/dźwięku: potok kodowania, który pobiera ramki multimedialne, koduje je w tle i generuje zakodowane obrazy/dźwięk.

    realizacja

    CloudRetro opiera się na WebRTC jako swojej technologii szkieletowej, więc zanim zagłębię się w szczegóły implementacji Golang, zdecydowałem się porozmawiać o samym WebRTC. To niesamowita technologia, która bardzo mi pomogła w osiągnięciu opóźnienia poniżej sekundy podczas przesyłania strumieniowego danych.

    WebRTC

    WebRTC został zaprojektowany w celu zapewnienia wysokiej jakości połączeń peer-to-peer w natywnych aplikacjach mobilnych i przeglądarkach przy użyciu prostych interfejsów API.

    Przechodzenie przez NAT

    WebRTC jest znane z funkcjonalności NAT Traversal. WebRTC jest przeznaczony do komunikacji typu peer-to-peer. Jego celem jest znalezienie najodpowiedniejszej trasy bezpośredniej, omijając bramy NAT i zapory ogniowe w komunikacji peer-to-peer w procesie zwanym ICE. W ramach tego procesu interfejsy API WebRTC znajdują Twój publiczny adres IP za pomocą serwerów STUN i przekazują go do serwera przekazującego (SKRĘCAĆ), gdy nie można nawiązać bezpośredniego połączenia.

    Jednak CloudRetro nie wykorzystuje w pełni tej funkcji. Połączenia peer-to-peer nie istnieją między użytkownikami, ale między użytkownikami a serwerami w chmurze. Strona serwerowa modelu ma mniej ograniczeń w bezpośredniej komunikacji niż typowe urządzenie użytkownika. Umożliwia to wstępne otwarcie portów przychodzących lub bezpośrednie korzystanie z publicznych adresów IP, ponieważ serwer nie znajduje się za NAT.

    Wcześniej chciałem przekształcić projekt w platformę dystrybucji gier dla Cloud Gaming. Pomysł polegał na umożliwieniu twórcom gier udostępniania gier i zasobów strumieniowych. Użytkownicy będą wchodzić w interakcje bezpośrednio z dostawcami. W ten zdecentralizowany sposób CloudRetro stanowi jedynie platformę do łączenia zasobów strumieniowych stron trzecich z użytkownikami, dzięki czemu jest bardziej skalowalny, gdy nie jest już hostowany. Rola WebRTC NAT Traversal jest tutaj bardzo ważna, aby ułatwić inicjalizację połączenia peer-to-peer z zasobami strumieniowymi stron trzecich, ułatwiając twórcy połączenie z siecią.

    Kompresja wideo

    Kompresja wideo jest nieodzowną częścią potoku i w znacznym stopniu przyczynia się do płynnego przepływu. Chociaż nie jest konieczna znajomość wszystkich szczegółów kodowania wideo VP8/H264, zrozumienie tych pojęć może pomóc w zrozumieniu opcji szybkości przesyłania strumieniowego wideo, debugowaniu nieoczekiwanych zachowań i dostosowaniu opóźnień.

    Kompresja wideo na potrzeby usługi przesyłania strumieniowego stanowi wyzwanie, ponieważ algorytm musi zapewnić, że całkowity czas kodowania + czas transmisji sieciowej + czas dekodowania są możliwie najkrótsze. Ponadto proces kodowania musi być spójny i ciągły. Niektóre kompromisy w zakresie kodowania nie mają zastosowania — na przykład nie możemy faworyzować długich czasów kodowania zamiast mniejszych rozmiarów plików i czasów dekodowania lub stosować niespójną kompresję.

    Ideą kompresji wideo jest wyeliminowanie niepotrzebnych fragmentów informacji przy jednoczesnym zachowaniu poziomu dokładności akceptowalnego dla użytkowników. Oprócz kodowania poszczególnych klatek obrazu statycznego, algorytm wnioskuje bieżącą klatkę z poprzedniej i następnej, dzięki czemu wysyłana jest tylko ich różnica. Jak widać na przykładzie Pacmana, przesyłane są tylko punkty różnicowe.

    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Porównanie klatek wideo na przykładzie Pacmana

    Kompresja dźwięku

    Podobnie algorytm kompresji dźwięku pomija dane, które nie mogą być postrzegane przez człowieka. Opus jest obecnie najskuteczniejszym kodekiem audio. Jest przeznaczony do przesyłania fali dźwiękowej za pośrednictwem uporządkowanego protokołu datagramowego, takiego jak RTP (Real Time Transport Protocol). Jego opóźnienie jest mniejsze niż w przypadku plików mp3 i aac, a jakość jest wyższa. Opóźnienie wynosi zwykle około 5 ~ 66,5 ms.

    Pion, WebRTC w Golang

    Pionek to projekt open source, który przenosi WebRTC do Golang. Zamiast zwykłego opakowania natywnych bibliotek WebRTC C++, Pion jest natywną implementacją WebRTC firmy Golang z lepszą wydajnością, integracją z Go i kontrolą wersji w protokołach WebRTC.

    Biblioteka umożliwia także przesyłanie strumieniowe z wieloma świetnymi wbudowanymi funkcjami z opóźnieniem poniżej sekundy. Ma własną implementację STUN, DTLS, SCTP itp. i kilka eksperymentów z QUIC i WebAssembly. Ta biblioteka open source sama w sobie jest naprawdę dobrym źródłem wiedzy z doskonałą dokumentacją, implementacjami protokołów sieciowych i fajnymi przykładami.

    Społeczność Pion, prowadzona przez twórcę z pasją, jest dość ożywiona i toczy się wiele wartościowych dyskusji na temat WebRTC. Jeżeli interesuje Cię ta technologia dołącz http://pion.ly/slack – dowiesz się wielu nowych rzeczy.

    Pisanie CloudRetro w Golang

    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Implementacja workera w Go

    Przejdź do kanałów w akcji

    Dzięki pięknemu projektowi kanału Go problemy związane ze strumieniowaniem wydarzeń i współbieżnością są znacznie uproszczone. Jak na diagramie, różne GoRoutines mają wiele komponentów działających równolegle. Każdy komponent zarządza swoim stanem i komunikuje się kanałami. Wybiórcze twierdzenie Golanga wymusza przetworzenie jednego zdarzenia atomowego za każdym razem w grze (zaznaczenie gry). Oznacza to, że w przypadku tego projektu nie jest potrzebne żadne blokowanie. Na przykład, gdy użytkownik zapisuje, wymagana jest pełna migawka stanu gry. Stan ten powinien pozostać ciągły, logując się do momentu zakończenia zapisu. Podczas każdego taktu gry backend może obsłużyć jedynie operację zapisywania lub wprowadzania danych, dzięki czemu wątek procesu jest bezpieczny.

    func (e *gameEmulator) gameUpdate() {
    for {
    	select {
    		case <-e.saveOperation:
    			e.saveGameState()
    		case key := <-e.input:
    			e.updateGameState(key)
    		case <-e.done:
    			e.close()
    			return
    	}
        }
    }

    Wentylacja/Wentylacja

    Ten szablon Golang idealnie pasuje do mojego przypadku użycia CrowdPlay i wielu graczy. Zgodnie z tym schematem wszystkie wejścia użytkowników w jednym pomieszczeniu są wbudowane w centralny kanał wejściowy. Następnie multimedia z grami są udostępniane wszystkim użytkownikom w tym samym pokoju. W ten sposób osiągamy podział stanu gry pomiędzy kilka sesji gry różnych użytkowników.

    Gry w chmurze typu open source w WebRTC: p2p, tryb wieloosobowy, zero opóźnień
    Synchronizacja pomiędzy różnymi sesjami

    Wady Golanga

    Golang nie jest doskonały. Kanał jest powolny. W porównaniu z blokowaniem kanał Go jest po prostu łatwiejszym sposobem obsługi zdarzeń współbieżnych i wielowątkowych, ale kanał nie zapewnia najlepszej wydajności. Pod kanałem znajduje się złożona logika blokowania. Wprowadziłem więc pewne zmiany w implementacji, ponownie stosując blokady i wartości atomowe podczas wymiany kanałów, aby zoptymalizować wydajność.

    Dodatkowo śmieciarz w Golang jest niezarządzany, co czasem powoduje podejrzanie długie przerwy. To znacznie zakłóca działanie aplikacji do przesyłania strumieniowego w czasie rzeczywistym.

    Cgo

    Projekt wykorzystuje istniejącą bibliotekę open source Golang VP8/H264 do kompresji multimediów oraz Libretro dla emulatorów gier. Wszystkie te biblioteki są po prostu opakowaniami biblioteki C w Go Cgo. Niektóre wady są wymienione w ten post autorstwa Dave'a Cheneya. Problemy, które napotkałem:

    • niemożność wyłapania awarii w CGO, nawet z Golang RecoveryCrash;
    • brak identyfikacji wąskich gardeł wydajnościowych, gdy nie jesteśmy w stanie wykryć szczegółowych problemów w CGO.

    wniosek

    Osiągnąłem swój cel, jakim było zrozumienie usług gier w chmurze i stworzenie platformy, która pomaga mi grać w nostalgiczne gry retro ze znajomymi online. Ten projekt nie byłby możliwy bez biblioteki Pion i wsparcia społeczności Pion. Jestem niezmiernie wdzięczny za jego intensywny rozwój. Proste interfejsy API dostarczane przez WebRTC i Pion zapewniły bezproblemową integrację. Mój pierwszy dowód koncepcji został opublikowany w tym samym tygodniu, mimo że nie miałem wcześniejszej wiedzy na temat komunikacji peer-to-peer (P2P).

    Pomimo łatwości integracji, przesyłanie strumieniowe P2P jest rzeczywiście bardzo złożonym obszarem informatyki. Aby stworzyć sesję peer-to-peer, musi uporać się ze złożonością długotrwałych architektur sieciowych, takich jak IP i NAT. Pracując nad tym projektem, zdobyłem wiele cennej wiedzy na temat optymalizacji sieci i wydajności, dlatego zachęcam wszystkich do próbowania budowania produktów P2P przy użyciu WebRTC.

    CloudRetro obsługuje wszystkie przypadki użycia, których oczekiwałem z mojej perspektywy jako gracza retro. Myślę jednak, że w projekcie jest wiele obszarów, które mogę ulepszyć, np. zwiększenie niezawodności i wydajności sieci, zapewnienie wyższej jakości grafiki w grach czy możliwość udostępniania gier pomiędzy użytkownikami. Ciężko nad tym pracuję. Podążaj proszę projekt i wspieraj, jeśli Ci się podoba.

Źródło: www.habr.com

Dodaj komentarz