Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency

Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
Il cloud come servizio, l'infrastruttura come servizio, la piattaforma come servizio, la piattaforma di comunicazione come servizio, le videoconferenze come servizio, e che dire del cloud gaming come servizio? Sono già stati fatti diversi tentativi per creare giochi in cloud (Cloud Gaming), ad esempio Stadia, recentemente lanciata da Google. Stadia non è nuova in WebRTC, ma possono gli altri utilizzare WebRTC allo stesso modo?

Thanh Nguyen ha deciso di testare questa possibilità nel suo progetto open source CloudRetro. CloudRetro è basato su Pion, una popolare libreria WebRTC basata su Go (grazie a Sean del gruppo di sviluppatori Pion per l'aiuto nella preparazione di questo articolo). In questo articolo, Thanh fornisce una panoramica dell'architettura del suo progetto e condivide ciò che ha trovato utile e le sfide affrontate durante il lavoro.

Introduzione

L'anno scorso, quando Google ha annunciato Stadia, sono rimasto completamente sbalordito. L'idea è così unica e innovativa che mi sono costantemente chiesto come fosse possibile con le tecnologie attuali. Il desiderio di comprendere meglio questo tema mi ha spinto a creare la mia versione di un gioco in cloud open source. Il risultato è stato semplicemente fantastico. Vorrei condividere di seguito il processo di lavoro sul mio progetto annuale progetto.

TLDR: versione breve con i punti principali

Perché il futuro appartiene ai giochi in cloud

Credo che il Cloud Gaming diventerà presto una nuova generazione non solo di giochi, ma anche in altri ambiti dell'informatica. I giochi in cloud rappresentano il culmine del modello client/server. Questo modello massimizza la gestione del backend e minimizza il lavoro sul frontend, collocando la logica di gioco su un server remoto e trasmettendo le immagini/audio al cliente. Il server esegue l'elaborazione pesante, quindi il cliente non è più vincolato alle limitazioni hardware.

Google Stadia, in sostanza, permette di giocare a giochi AAA (cioè giochi blockbuster di alta qualità) su un'interfaccia simile a YouTube. La stessa metodologia può essere applicata ad altre applicazioni pesanti offline, come un sistema operativo o il design grafico 2D/3D, ecc., in modo che possiamo eseguirle stabilmente su dispositivi con caratteristiche tecniche basse su diverse piattaforme.

Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
Il futuro di questa tecnologia: immaginate se Microsoft Windows 10 funzionasse nel browser Chrome?

I giochi in cloud sono tecnicamente complessi

Il gaming è uno di quei rari settori in cui è necessaria una reazione rapida e costante da parte dell'utente. Se occasionalmente ci troviamo con un ritardo di 2 secondi al clic su una pagina, questo è accettabile. I flussi video in diretta tendono a ritardare di alcuni secondi, ma offrono comunque una comodità sufficiente. Tuttavia, se un gioco ha frequentemente un ritardo di 500 ms, giocare diventa semplicemente impossibile. Il nostro obiettivo è raggiungere una latenza estremamente bassa affinché la disconnessione tra l'input e i media sia la minore possibile. Pertanto, l'approccio tradizionale allo streaming video non è applicabile qui.

Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
Modello generale di gioco in cloud

Progetto open source CloudRetro

Ho deciso di creare un campione di prova di gioco in cloud per verificare se tutto ciò fosse possibile con restrizioni di rete così severe. Per testare il concetto, ho scelto Golang, poiché è il linguaggio a me più familiare e si adatta bene a questa implementazione per molti altri motivi, come scoperto in seguito. Go è semplice e si sviluppa molto rapidamente; i canali in Go sono ottimi per gestire la multithreading.

Progetto CloudRetro.io – servizio di gioco in cloud open source per giochi retro. L'obiettivo del progetto è portare nelle tradizionali esperienze di gioco retro la massima comodità possibile e aggiungere il multiplayer.
Puoi trovare ulteriori dettagli sul progetto qui: https://github.com/giongto35/cloud-game.

Funzionalità di CloudRetro

Per dimostrare tutta la potenza dei giochi in cloud, su CloudRetro vengono utilizzati giochi retro. Questo consente di ottenere molte esperienze di gioco uniche.

  • Portabilità del gioco
    • Riproduzione istantanea all'apertura della pagina; non è necessaria alcuna installazione o download
    • Funziona nel browser mobile, quindi non è necessario alcun software per avviarlo

  • Le sessioni di gioco possono essere condivise su più dispositivi e memorizzate nel cloud per accessi futuri
  • Il gioco può essere trasmesso in streaming ed essere giocato da più utenti contemporaneamente:
    • Crowdplay tipo TwitchPlayPokemon, solo più cross-platform e più in tempo reale
    • Gioco offline in online. Molti utenti possono giocare senza alcuna configurazione di rete. In Samurai Shodown è ora possibile giocare a 2 giocatori attraverso la rete CloudRetro

    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Demo di un gioco online multiplayer su dispositivi diversi

    Infrastruttura

    Requisiti e stack tecnologici

    Di seguito è riportato un elenco dei requisiti che ho stabilito prima di iniziare il progetto.

    1. Un giocatore
    Questa richiesta potrebbe sembrare non troppo importante e ovvia qui, ma è una delle mie conclusioni chiave, consente ai giochi cloud di rimanere il più lontano possibile dai tradizionali servizi di streaming. Se ci concentriamo sul gioco per un singolo giocatore, possiamo eliminare un server centralizzato o un CDN, poiché non abbiamo bisogno di trasmettere a masse. Invece di caricare i flussi su un server inghiottitore o inviare pacchetti a un server WebSocket centralizzato, i flussi di servizio vengono inviati direttamente all'utente tramite una connessione peer-to-peer WebRTC.

    2. Flusso media a bassa latenza
    Leggendo di Stadia, incontro spesso in alcuni articoli il riferimento a WebRTC. Ho capito che WebRTC è una tecnologia eccezionale, ed è perfetta per l'uso nei giochi cloud. WebRTC è un progetto che fornisce a browser web e applicazioni mobili comunicazione in tempo reale tramite un'interfaccia API semplice. Fornisce una connessione peer-to-peer, ottimizzata per i media e ha codec standard integrati, come VP8 e H264.

    Ho dato priorità a garantire il massimo comfort per gli utenti, piuttosto che mantenere un'alta qualità grafica. Nell'algoritmo sono ammesse alcune perdite. In Google Stadia c'è un passaggio aggiuntivo per ridurre le dimensioni dell'immagine sul server, e i frame vengono scalati a una qualità più alta prima di essere inviati ai nodi peer-to-peer.

    3. Infrastruttura distribuita con routing geografico
    Indipendentemente da quanto sia ottimizzato l'algoritmo di compressione e il codice, la rete rimane comunque un fattore determinante che contribuisce maggiormente alla latenza. L'architettura deve avere un meccanismo di accoppiamento del server più vicino all'utente per ridurre il tempo di andata e ritorno (RTT). L'architettura deve avere 1 coordinatore e diversi server di streaming distribuiti in tutto il mondo: Ovest degli Stati Uniti, Est degli Stati Uniti, Europa, Singapore, Cina. Tutti i server di streaming devono essere completamente isolati. Il sistema può regolare la sua distribuzione quando un server si unisce alla rete o ne esce. Così, con un alto traffico, l'aggiunta di server supplementari permette di realizzare una scalabilità orizzontale.

    4. Compatibilità del browser
    I giochi in cloud si presentano al meglio quando richiedono il minimo agli utenti. Questo significa che c'è la possibilità di avviarli nel browser. I browser aiutano a rendere il processo di gioco il più confortevole possibile per gli utenti, liberandoli dall'installazione di software e hardware. I browser aiutano anche a garantire la compatibilità cross-platform per le versioni mobili e desktop. Fortunatamente, WebRTC è ben supportato in vari browser.

    5. Chiarezza nella separazione dell'interfaccia di gioco e del servizio
    Considero il servizio di giochi in cloud come una piattaforma. Ognuno dovrebbe avere la possibilità di collegarsi alla piattaforma per qualsiasi cosa. Attualmente ho integrato LibRetro con il servizio di giochi in cloud, perché LibRetro offre una bella interfaccia di emulatore di gioco per giochi retro, come SNES, GBA, PS.

    6. Stanze per il multiplayer, crowd play e collegamento esterno (deep-link) con il gioco
    CloudRetro supporta molti nuovi stili di gioco, come CrowdPlay e Online MultiPlayer per giochi retro. Se più utenti aprono lo stesso deep-link su computer diversi, vedranno lo stesso gioco avviato e potranno persino unirsi a esso.

    Inoltre, gli stati di gioco vengono memorizzati nel cloud. Questo consente agli utenti di continuare a giocare in qualsiasi momento su qualsiasi altro dispositivo.

    7. Scalabilità orizzontale
    Come qualsiasi SAAS al giorno d'oggi, i giochi in cloud devono essere progettati per essere orizzontalmente scalabili. La struttura "coordinatore-lavoratore" consente di aggiungere più lavoratori per gestire un volume maggiore di traffico.

    8. Nessun vincolo a un solo cloud
    L'infrastruttura di CloudRetro è situata su diversi fornitori di cloud (Digital Ocean, Alibaba, fornitore personalizzato) per varie regioni. Attivo il lancio in contenitore Docker per l'infrastruttura e configuro i parametri di rete tramite uno script bash, per evitare dipendenze da un solo fornitore di cloud. Combinando ciò con il NAT Traversal in WebRTC, possiamo ottenere flessibilità per implementare CloudRetro su qualsiasi piattaforma cloud e anche su computer di qualsiasi utente.

    Progettazione architettonica

    Lavoratore: (o il server di streaming menzionato sopra) moltiplica i giochi, avvia la pipeline di codifica e trasmette media codificati agli utenti. Le istanze del worker sono distribuite in tutto il mondo e ogni worker può gestire più sessioni utente contemporaneamente.

    Coordinatore: si occupa di abbinare il nuovo utente con il worker più appropriato per lo streaming. Il coordinatore interagisce con i worker tramite WebSocket.

    Archiviazione degli stati di gioco: archivio remoto centrale per tutti gli stati di gioco. Questo archivio fornisce funzioni importanti come il salvataggio/caricamento remoto.

    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Architettura di alto livello di CloudRetro

    Scenario utente

    Quando un nuovo utente apre CloudRetro nei passaggi 1 e 2 mostrati nell'immagine sottostante, il coordinatore, insieme all'elenco dei worker disponibili, viene richiesto alla prima pagina. Successivamente, nel passo 3, il cliente calcola i ritardi per tutti i candidati tramite una richiesta HTTP ping. Questo elenco di ritardi viene quindi inviato di nuovo al coordinatore, che può determinare il worker più adatto per assistere l'utente. Nel passo 4 sottostante viene creata una partita. Viene stabilita una connessione streaming WebRTC tra l'utente e il worker assegnato.
    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Scenario utente dopo aver ottenuto accesso

    Cosa c'è dentro il worker

    Le pipeline di gioco e streaming sono archiviate all'interno del worker in modo isolato e scambiano informazioni lì tramite un'interfaccia. Attualmente, questa comunicazione avviene mediante il trasferimento di dati in memoria attraverso canali Golang nello stesso processo. Il prossimo obiettivo è la segregazione, ovvero l'esecuzione indipendente del gioco in un altro processo.

    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Interazione dei componenti del worker

    Componenti principali:

    • WebRTC: componente client, che riceve l'input dell'utente e restituisce media codificati dal server.
    • Emulatore di gioco: componente di gioco. Grazie alla libreria Libretro, il sistema è in grado di eseguire il gioco all'interno dello stesso processo e di intercettare internamente media e flussi di input.
    • Le immagini/il video vengono catturati e inviati al codificatore.
    • Codificatore di immagini/audio: pipeline di codifica che riceve i frame media, li codifica in background e restituisce immagini/audio codificati.

    Implementazione

    CloudRetro si basa su WebRTC come tecnologia principale, quindi prima di addentrarmi nei dettagli dell'implementazione in Golang, ho deciso di parlare di WebRTC stesso. È una tecnologia straordinaria che mi ha molto aiutato a raggiungere un ritardo nella trasmissione dei dati pari a solo una frazione di secondo.

    WebRTC

    WebRTC è progettato per garantire connessioni peer-to-peer di alta qualità su applicazioni mobili native e browser tramite semplici API.

    NAT Traversal

    WebRTC è noto per la sua funzionalità di NAT Traversal. WebRTC è progettato per la comunicazione peer-to-peer. Il suo obiettivo è trovare il percorso diretto più adatto evitando gateway NAT e firewall per la comunicazione peer-to-peer attraverso un processo chiamato ICE. Nell'ambito di questo processo, le API di WebRTC trovano il tuo indirizzo IP pubblico utilizzando server STUN e lo reindirizzano a un server di relay (TURN), quando non è possibile stabilire una connessione diretta.

    Tuttavia, CloudRetro non sfrutta completamente questa possibilità. Le sue connessioni peer-to-peer non esistono tra utenti, ma tra utenti e server cloud. La parte server del modello ha meno restrizioni sulla connessione diretta rispetto a un normale dispositivo utente. Ciò consente l'apertura anticipata delle porte in entrata o l'uso diretto di indirizzi IP pubblici, poiché il server non si trova dietro un NAT.

    In passato volevo trasformare il progetto in una piattaforma di distribuzione giochi per Cloud Gaming. L'idea era di permettere ai creatori di giochi di fornire giochi e risorse di streaming. E gli utenti interagirebbero direttamente con i fornitori. In questo modo decentralizzato, CloudRetro è semplicemente un ambiente per collegare risorse di streaming di terze parti agli utenti, rendendolo più scalabile, poiché non è più vincolato all'hosting. Il ruolo del NAT Traversal di WebRTC è qui molto importante per facilitare l'inizializzazione di una connessione peer-to-peer su risorse di streaming di terze parti, semplificando la connessione tra il creatore e la rete.

    Compressione video

    La compressione video è una parte indispensabile del pipeline, che contribuisce in modo significativo alla fluidità del flusso. Sebbene non sia necessario conoscere tutti i dettagli della codifica video in VP8/H264, comprendere il concetto aiuta a gestire i parametri di bitrate del video in streaming, a debuggare comportamenti imprevisti e a regolare la latenza.

    La compressione video per un servizio di streaming è un compito complesso, perché l'algoritmo deve garantire che il tempo totale di codifica + tempo di trasmissione in rete + tempo di dekodifica sia il più ridotto possibile. Inoltre, il processo di codifica deve essere coerente e continuo. Alcuni compromessi nel processo di codifica non sono applicabili: ad esempio, non possiamo preferire un lungo tempo di codifica a un file di dimensioni ridotte e a un tempo di dekodifica minore, o utilizzare una compressione incoerente.

    L'idea della compressione video consiste nell'escludere i bit di informazioni non necessari, mantenendo al contempo un livello accettabile di accuratezza per gli utenti. Oltre a codificare singoli fotogrammi statici, l'algoritmo deduce il fotogramma corrente da quello precedente e successivo, quindi invia solo la loro differenza. Come si può vedere nell'esempio con Pacman, vengono trasmesse solo le differenze.

    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Confronto dei fotogrammi video con Pacman

    Compressione audio

    In modo analogo, l'algoritmo di compressione audio elimina i dati che non possono essere percepiti dall'orecchio umano. Attualmente, Opus è il codec audio con le migliori prestazioni. È stato progettato per trasmettere onde audio tramite protocolli di datagramma ordinati, come RTP (Real Time Transport Protocol – protocollo di trasporto in tempo reale). La sua latenza è inferiore a quella di mp3 e aac, e la qualità è superiore. La latenza è di solito di circa 5~66,5 ms.

    Pion, WebRTC in Golang

    Pion è un progetto open source che porta WebRTC in Golang. Invece di avvolgere le librerie C++ native di WebRTC, Pion è un'implementazione nativa di WebRTC in Golang che offre migliori prestazioni, integrazione con Go e anche un controllo delle versioni sui protocolli WebRTC.

    La libreria offre anche uno streaming di dati con un gran numero di eccellenti moduli integrati con una latenza di meno di un secondo. Ha la propria implementazione di STUN, DTLS, SCTP, ecc. e alcuni esperimenti con QUIC e WebAssembly. Questa libreria open source è davvero una buona fonte didattica con ottima documentazione, implementazione di protocolli di rete e casi di studio interessanti.

    La comunità Pion, guidata da un creatore molto appassionato, è piuttosto vivace, con molte discussioni di qualità su WebRTC. Se sei interessato a questa tecnologia, unisciti a http://pion.ly/slack – imparerai molte cose nuove.

    Scrivere CloudRetro in Golang

    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Implementazione di un worker in Go

    Canali Go in azione

    Grazie al design elegante dei canali Go, le problematiche di streaming di eventi e parallelismo sono notevolmente semplificate. Come mostrato nel diagramma, diversi componenti lavorano in parallelo in diverse GoRoutines. Ogni componente gestisce il proprio stato e comunica attraverso i canali. La selezione condizionale di Golang richiede di trattare un singolo evento atomico alla volta durante ogni momento del gioco (game tick). Questo significa che per un tale design non è necessaria alcuna sincronizzazione. Ad esempio, quando un utente salva, è necessario avere un snapshot completo dello stato del gioco. Questo stato deve rimanere continuo mentre il login è attivo fino al completamento del salvataggio. Durante ciascun game tick, il backend può gestire solo l'operazione di salvataggio o di input, rendendo il processo thread-safe.

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

    Fan-in / Fan-out

    Questo modello Golang si adatta perfettamente al mio caso d'uso di CrowdPlay e Multiplayer. Seguendo questo modello, tutti gli input degli utenti in una stanza vengono incanalati in un canale di ingresso centrale. I media di gioco vengono quindi distribuiti a tutti gli utenti nella stessa stanza. In questo modo, ci si assicura di condividere lo stato del gioco tra diverse sessioni di gioco degli utenti.

    Gaming cloud open source su WebRTC: p2p, multiplayer, zero latency
    Sincronizzazione tra diverse sessioni

    Svantaggi di Golang

    Golang non è perfetto. I canali sono lenti. Rispetto alla sincronizzazione, il canale Go è semplicemente un modo più semplice di gestire eventi paralleli e in streaming, ma il canale non offre le migliori prestazioni. Sotto il canale c'è una logica complessa di sincronizzazione. Pertanto, ho apportato alcune modifiche all'implementazione, riutilizzando le sincronizzazioni e i valori atomici per sostituire i canali al fine di ottimizzare le prestazioni.

    Inoltre, il garbage collector in Golang è non gestito, il che provoca talvolta sospette lunghe pause. Questo ostacola notevolmente il funzionamento delle applicazioni in tempo reale.

    CGO

    Il progetto utilizza una libreria esistente VP8/H264 open source in Golang per la compressione dei media e Libretro per gli emulatori di giochi. Tutte queste librerie sono semplicemente dei wrapper della libreria C in Go utilizzando CGO. Alcuni dei difetti sono elencati in questo post di Dave Cheney. I problemi che ho affrontato:

    • impossibilità di catturare un crash in CGO, anche utilizzando Golang RecoveryCrash;
    • impossibilità di identificare il collo di bottiglia delle performance quando non si riesce a rilevare problemi dettagliati in CGO.

    Conclusione

    Ho raggiunto il mio obiettivo: ho compreso i servizi di gioco cloud e ho creato una piattaforma che aiuta a giocare a giochi retro nostalgici con i miei amici online. La creazione di questo progetto sarebbe stata impossibile senza la libreria Pion e il supporto della comunità Pion. Sono estremamente grato per il suo intenso sviluppo. Le semplici API fornite da WebRTC e Pion hanno reso l'integrazione fluida. La mia prima prova di concetto è stata rilasciata nella stessa settimana, anche se non sapevo in anticipo della connessione peer-to-peer (P2P).

    Nonostante la semplicità di integrazione, lo streaming P2P è davvero un'area molto complessa nell'informatica. Deve affrontare la complessità delle architetture di rete pluriennali, come IP e NAT, per creare una sessione peer-to-peer. Durante il lavoro su questo progetto ho accumulato molte conoscenze preziose su reti e ottimizzazione delle prestazioni, quindi consiglio a tutti di provare a costruire prodotti P2P utilizzando WebRTC.

    CloudRetro soddisfa tutti gli scenari d'uso che mi aspettavo, dal mio punto di vista come retro gamer. Tuttavia, penso che ci siano molte aree nel progetto che posso migliorare, come rendere la rete più affidabile e performante, garantire una qualità grafica più elevata dei giochi o la possibilità di condividere giochi tra gli utenti. Sto lavorando duramente per questo. Per favore, seguite progetto e supportatelo se vi piace.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster