Software come servizio, infrastruttura come servizio, piattaforma come servizio, piattaforma di comunicazione come servizio, videoconferenze come servizio, e cosa dire del gaming in cloud come servizio? Ci sono state diverse iniziative per creare giochi in cloud (Cloud Gaming), come Stadia, recentemente lanciato da Google. Stadia , ma possono gli altri utilizzare WebRTC allo stesso modo?
Thanh Nguyen ha deciso di esplorare questa possibilità nel suo progetto open source CloudRetro. CloudRetro si basa su Pion, libreria WebRTC basata su Go (grazie a del team di sviluppo Pion per l'assistenza nella scrittura di questo articolo). In questo articolo, Thanh esamina l'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 continuamente chiesto come sia possibile con le tecnologie attuali. Il desiderio di capire meglio questo argomento mi ha spinto a creare la mia versione di un gioco cloud open source. Il risultato è stato fantastico. Di seguito, vorrei condividere il processo di lavoro sul mio progetto annuale .
TLDR: versione breve con i punti salienti
Perché il futuro è nei giochi cloud
Credo che il Cloud Gaming presto diventerà la nuova generazione non solo di giochi, ma anche di altri settori informatici. I giochi cloud rappresentano l'apice del modello client/server. Questo modello massimizza la gestione del back-end e minimizza il lavoro del front-end, posizionando la logica di gioco su un server remoto e trasmettendo in streaming immagini/audio al client. Il server gestisce l'elaborazione intensa, quindi il client non è più vincolato dalle limitazioni hardware.
Google Stadia, in sostanza, consente di giocare a (ovvero giochi blockbuster di alta qualità) su un'interfaccia simile a YouTube. La stessa metodologia può essere applicata anche ad altre applicazioni pesanti offline, come il sistema operativo o il design grafico 2D/3D, ecc., in modo da poterle eseguire in modo stabile su dispositivi con specifiche tecniche inferiori su diverse piattaforme.

Il futuro di questa tecnologia: immaginate se Microsoft Windows 10 funzionasse nel browser Chrome?
I giochi cloud sono tecnicamente complessi
Il gaming è uno di quei rari ambiti in cui è necessaria una reazione rapida costante da parte dell'utente. Se occasionalmente ci troviamo di fronte a un ritardo di 2 secondi durante un clic sulla pagina, ciò è accettabile. I flussi video in diretta tendono a subire un ritardo di alcuni secondi, ma offrono comunque un uso ragionevole. Tuttavia, se un gioco ha frequentemente un ritardo di 500 ms, giocare diventa semplicemente impossibile. Il nostro obiettivo è raggiungere un ritardo estremamente basso, in modo che il divario tra input e media sia il più ridotto possibile. Pertanto, l'approccio tradizionale al video in streaming qui non è applicabile.

Modello generale di gioco cloud
Progetto open source CloudRetro
Ho deciso di creare un campione test di gioco in cloud per verificare se tutto questo sia possibile con così rigide limitazioni di rete. 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 successivamente. Go è semplice e si sviluppa molto rapidamente; i canali in Go sono perfetti per la gestione della multithreading.
Progetto – un servizio di gioco in cloud con codice sorgente aperto per giochi retro. L'obiettivo del progetto è portare nelle tradizionali esperienze di gioco retro il massimo del comfort e aggiungere il multiplayer.
È possibile approfondire il progetto qui: .
Funzionalità di CloudRetro
Per dimostrare tutta la potenza dei giochi in cloud, CloudRetro utilizza giochi retro. Questo consente di ottenere una serie di esperienze di gioco uniche.
- Portabilità del gioco
- Riproduzione istantanea all'apertura della pagina; non è necessaria né il caricamento né l'installazione
- Funziona nel browser mobile, quindi non è necessario alcun software per avviare
- Le sessioni di gioco possono essere condivise su più dispositivi e memorizzate nel cloud per il prossimo accesso.
- Il gioco può essere trasmesso in streaming e giocato da più utenti contemporaneamente:
- Crowdplay tipo TwitchPlayPokemon, ma più cross-platform e in tempo reale.
- Giochi offline online. Molti utenti possono giocare senza configurare la rete. In Samurai Shodown ora possono giocare 2 giocatori tramite CloudRetro.

Versione demo di un gioco multiplayer online su diversi dispositivi.Infrastruttura
Requisiti e stack tecnologico.
Di seguito è riportato un elenco di requisiti che ho impostato prima di iniziare il progetto.
1. Un giocatore.
Questo requisito potrebbe sembrare non così importante e ovvio qui, ma è una delle mie conclusioni chiave; consente ai giochi in cloud di stare il più lontano possibile dai tradizionali servizi di streaming. Se ci concentriamo sui giochi per un solo giocatore, possiamo eliminare il server centralizzato o il CDN, poiché non abbiamo bisogno di trasmettere a masse. Invece di caricare i flussi su un server dedicato o inviare pacchetti a un server WebSocket centralizzato, i flussi di servizio vengono inviati all'utente direttamente attraverso una connessione peer-to-peer WebRTC.2. Flusso multimediale a bassa latenza
Leggendo di Stadia, incontro spesso in alcuni articoli un riferimento a WebRTC. Ho capito che WebRTC è una tecnologia eccezionale e si adatta perfettamente all'uso nei giochi in cloud. WebRTC è un progetto che fornisce ai browser web e alle app mobili comunicazione in tempo reale attraverso una semplice API. Fornisce una connessione peer-to-peer, ottimizzata per i media, con codec standard integrati come VP8 e H264.Ho dato priorità a garantire il massimo comfort agli utenti piuttosto che mantenere un'elevata qualità grafica. L'algoritmo ammette 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à superiore prima di essere trasmessi ai nodi peer-to-peer.
3. Infrastruttura distribuita con instradamento geografico
Indipendentemente da quanto sia ottimizzato l'algoritmo di compressione e il codice, la rete rimane il fattore cruciale che contribuisce maggiormente alla latenza. L'architettura deve prevedere 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 o si disconnette dalla rete. Pertanto, in caso di elevato traffico, l'aggiunta di server aggiuntivi consente di effettuare scalabilità orizzontale.4. Compatibilità del browser
I giochi in cloud brillano al meglio quando richiedono un minimo ai giocatori. Questo significa che possono essere avviati direttamente nel browser. I browser rendono l'esperienza di gioco il più confortevole possibile per gli utenti, liberandoli dall'installazione di software e hardware. Inoltre, i browser garantiscono la compatibilità tra piattaforme per le versioni mobile e desktop. Fortunatamente, WebRTC è ben supportato in vari browser.5. Chiara separazione tra l'interfaccia di gioco e il servizio
Considero il servizio di giochi in cloud come una piattaforma. Ognuno dovrebbe avere la possibilità di collegare qualsiasi cosa alla piattaforma. Attualmente ho integrato con il servizio di giochi in cloud, poiché LibRetro offre una bellissima interfaccia di emulatore per giochi retro come SNES, GBA e PS.6. Stanze per multiplayer, crowd play e deep linking con il gioco
CloudRetro supporta molte nuove modalità di gioco, come CrowdPlay e Multiplayer Online per i giochi retro. Se più utenti aprono lo stesso deep link su computer diversi, vedranno lo stesso gioco avviato e potranno anche unirsi.Inoltre, gli stati di gioco sono 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 scalabili orizzontalmente. La struttura "coordinatore-lavoratore" consente di aggiungere più lavoratori per gestire un traffico maggiore.8. Nessun legame con un solo cloud
L'infrastruttura CloudRetro è ospitata su diversi fornitori di cloud (Digital Ocean, Alibaba, fornitore personalizzato) per diverse regioni. Attivo l'avvio in un contenitore Docker per l'infrastruttura e configuro i parametri di rete utilizzando uno script bash, per evitare la dipendenza da un unico fornitore di cloud. Combinando questo con NAT Traversal in WebRTC, possiamo ottenere flessibilità per distribuire CloudRetro su qualsiasi piattaforma cloud e persino su macchine di qualsiasi utente.Design architettonico
Lavoratore: (o server di streaming menzionato sopra) moltiplica i giochi, lancia il pipeline di codifica e trasmette i 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 un nuovo utente al worker più adatto per lo streaming. Il coordinatore interagisce con i worker tramite WebSocket.
Archivio degli stati di gioco: un archivio remoto centrale per tutti gli stati del gioco. Questo archivio offre funzioni importanti come il salvataggio e il caricamento remoto.

Architettura di alto livello di CloudRetroCaso d'uso
Quando un nuovo utente apre CloudRetro nei passaggi 1 e 2 mostrati nell'immagine qui sotto, il coordinatore richiede la prima pagina insieme all'elenco dei lavoratori disponibili. Successivamente, nel passaggio 3, il cliente calcola i ritardi per tutti i candidati utilizzando una richiesta HTTP ping. Questo elenco di ritardi viene quindi inviato nuovamente al coordinatore, in modo che possa determinare il lavoratore più adatto per assistere l'utente. Nel passaggio 4 qui sotto viene creata una partita. Viene stabilita una connessione in streaming WebRTC tra l'utente e il lavoratore assegnato.

Flusso utente dopo aver ottenuto l'accessoCosa c'è all'interno del lavoratore
I pipeline di gioco e streaming sono conservati all'interno del lavoratore in modo isolato e scambiano informazioni tramite un'interfaccia. Attualmente, questa connessione avviene tramite il trasferimento dati in memoria attraverso nello stesso processo. Il prossimo obiettivo è la segregazione, ossia l'esecuzione indipendente della partita in un altro processo.

Interazione tra i componenti del lavoratoreComponenti principali:
- WebRTC: componente client che riceve l'input dell'utente ed emette media codificato 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.
- I fotogrammi in-game vengono catturati e inviati al codificatore.
- Codificatore di immagine/audio: pipeline di codifica che riceve i fotogrammi multimediali, li codifica in background e produce immagini/audio codificati.
Implementazione
CloudRetro fa affidamento su WebRTC come tecnologia di trasporto principale, quindi prima di approfondire i dettagli dell'implementazione in Golang, ho deciso di parlare di WebRTC stesso. È una tecnologia straordinaria che mi ha aiutato a raggiungere una latenza della trasmissione dei dati di solo una frazione di secondo.
WebRTC
WebRTC è progettato per fornire connessioni peer-to-peer di alta qualità su app mobili native e nei browser tramite API semplici.
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ù appropriato, evitando gateway NAT e firewall per le comunicazioni peer-to-peer attraverso un processo chiamato . Nell'ambito di questo processo, l'API WebRTC rileva il tuo indirizzo IP pubblico utilizzando i server STUN e lo reindirizza a un server di retransmission (), quando non può essere stabilita una connessione diretta.
Tuttavia, CloudRetro non sfrutta appieno questa capacità. Le sue connessioni peer-to-peer non avvengono tra utenti, ma tra utenti e server cloud. La parte server del modello ha meno vincoli sulla connessione diretta rispetto ai normali dispositivi degli utenti. Ciò consente di effettuare l'apertura preliminare delle porte in ingresso o di utilizzare indirizzi IP pubblici direttamente, poiché il server non è dietro un NAT.
In passato volevo trasformare il progetto in una piattaforma per la distribuzione di giochi nel Cloud Gaming. L'idea era di consentire ai creatori di giochi di fornire giochi e risorse di streaming. Gli utenti avrebbero interagito direttamente con i fornitori. In questo modo decentralizzato, CloudRetro è solo un ambiente per collegare risorse di streaming di terze parti agli utenti, rendendolo più scalabile poiché il servizio di hosting non è più un ostacolo. Il ruolo del WebRTC NAT Traversal è fondamentale per facilitare l'inizializzazione di una connessione peer-to-peer su risorse di streaming di terze parti, semplificando il collegamento tra il creatore e la rete.
Compressione video
La compressione video è una parte essenziale del pipeline che contribuisce in larga misura alla fluidità dello streaming. Anche se non è necessario conoscere tutti i dettagli della codifica video in VP8/H264, comprendere il concetto aiuta a navigare i parametri di bitrate dello streaming video, risolvere comportamenti inaspettati e ottimizzare la latenza.
La compressione video per lo streaming è una sfida complessa, poiché l'algoritmo deve garantire che il tempo totale di codifica + tempo di trasmissione in rete + tempo di decodifica sia il più ridotto possibile. Inoltre, il processo di codifica deve essere coerente e continuo. Alcuni compromessi durante la codifica non sono accettabili: ad esempio, non possiamo privilegiare un tempo di codifica più lungo a favore di una dimensione del file più piccola e di un tempo di decodifica inferiore, né utilizzare una compressione incoerente.
L'idea alla base della compressione video è quella di escludere i bit di informazione non necessari, mantenendo però un livello di precisione accettabile per gli utenti. Oltre a codificare singoli fotogrammi statici, l'algoritmo trae conclusioni per il fotogramma corrente dai fotogrammi precedenti e successivi, inviando solo le loro differenze. Come si può vedere dall'esempio con Pacman, vengono trasmesse solo le differenze.

Confronto dei fotogrammi video con l'esempio di PacmanCompressione audio
Similarmente, l'algoritmo di compressione audio esclude i dati che non possono essere percepiti dall'orecchio umano. Opus è attualmente il codec audio con le migliori prestazioni. È progettato per trasmettere onde sonore attraverso un protocollo di datagramma ordinato, 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 è normalmente di circa 5~66,5 ms.
Pion, WebRTC in Golang
è un progetto open source che porta WebRTC su Golang. Invece di avvolgere le librerie C++ native di WebRTC, Pion è una realizzazione nativa di WebRTC in Golang con prestazioni superiori, integrazione con Go e controllo delle versioni sui protocolli WebRTC.
La libreria offre anche lo streaming con molti moduli integrati eccellenti, con una latenza inferiore a un secondo. Dispone di una propria implementazione di STUN, DTLS, SCTP, ecc. e ha fatto alcune sperimentazioni con QUIC e WebAssembly. Questa libreria open source è davvero una buona risorsa per l'apprendimento, con un'ottima documentazione, implementazione di protocolli di rete e interessanti esempi.
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 – imparerai molte cose nuove.
Scrivere CloudRetro in Golang

Implementazione di un worker in GoCanali Go in azione
Grazie al design intuitivo dei canali Go, le problematiche di streaming degli eventi e di parallelismo sono notevolmente semplificate. Come illustrato nel diagramma, più componenti operano in parallelo all'interno di diverse GoRoutines. Ogni componente gestisce il proprio stato e comunica tramite i canali. L'affermazione selettiva di Golang obbliga a trattare un solo evento atomico per ogni tick di gioco. Questo implica che per tale design non è necessaria alcuna esclusione. Ad esempio, quando un giocatore salva, è necessario un snapshot completo dello stato di gioco. Questo stato deve rimanere coerente, eseguendo input fino a quando il salvataggio non è completo. Durante ogni tick di gioco, il backend può gestire solo l'operazione di salvataggio o l'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 template Golang si adatta perfettamente al mio caso d'uso di CrowdPlay e Multiplayer. Seguendo questo schema, tutti gli input degli utenti in una stanza vengono incorporati nel canale centrale di ingresso. I media di gioco vengono quindi distribuiti a tutti gli utenti in una stanza. In questo modo, otteniamo una separazione dello stato di gioco tra più sessioni di gioco di utenti diversi.

Sincronizzazione tra diverse sessioniSvantaggi di Golang
Golang non è perfetto. Il canale è lento. Rispetto ai blocchi, il canale Go è semplicemente un modo più semplice per gestire eventi paralleli e in streaming, ma il canale non fornisce le migliori prestazioni. Dietro il canale c'è una logica complessa di blocco. Pertanto, ho apportato alcune modifiche all'implementazione, riapplicando i blocchi e i valori atomici per sostituire i canali e ottimizzare le prestazioni.
Inoltre, il garbage collector in Golang è incontrollato, il che porta a pause sospette e lunghe in alcuni casi. Questo interferisce notevolmente con le prestazioni delle applicazioni in tempo reale.
CGO
Il progetto utilizza la libreria open source VP8/H264 di Golang per la compressione dei media e Libretro per gli emulatori di giochi. Tutte queste librerie sono semplici wrapper della libreria C in Go, utilizzando . Alcuni dei difetti sono elencati in . I problemi che ho incontrato sono stati:
- impossibilità di catturare crash in CGO, anche con Golang RecoveryCrash;
- impossibilità di identificare il collo di bottiglia delle prestazioni, quando non possiamo rilevare problemi dettagliati in CGO.
Conclusione
Ho raggiunto il mio obiettivo: ho esplorato i servizi di gioco cloud e creato una piattaforma che permette di giocare a giochi retro nostalgici con i miei amici online. Creare questo progetto sarebbe stato 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 consentito un'integrazione fluida. Il mio primo proof of concept è stato rilasciato nella stessa settimana, nonostante non avessi informazioni preliminari sulla connessione peer-to-peer (P2P).
Nonostante la semplicità di integrazione, lo streaming P2P è davvero un campo molto complesso nell'informatica. Deve affrontare la complessità di architetture di rete consolidate, come IP e NAT, per creare una sessione peer-to-peer. Durante il lavoro su questo progetto, ho accumulato molte conoscenze preziose sulla rete e sull'ottimizzazione delle prestazioni, quindi raccomando a tutti di provare a costruire prodotti P2P utilizzando WebRTC.
CloudRetro copre tutti gli scenari d'uso che mi aspettavo, dal mio punto di vista, come appassionato di retro gaming. Tuttavia, penso che ci siano molte aree nel progetto che posso migliorare, come rendere la rete più affidabile e performante, garantire una qualità grafica superiore nei giochi o la possibilità di condividere giochi tra utenti. Sto lavorando duramente su questo. Per favore, rimanete sintonizzati su e supportatelo se vi piace.
Fonte: habr.com







