El software como servicio, infraestructura como servicio, plataforma como servicio, plataforma de comunicación como servicio, videoconferencias como servicio, ¿y qué tal los juegos en la nube como servicio? Ya se han realizado varios intentos de crear juegos en la nube, como Stadia, que fue lanzado recientemente por Google. Stadia , pero, ¿pueden otros usar WebRTC de la misma manera?
Thanh Nguyen decidió comprobar esta posibilidad en su proyecto de código abierto CloudRetro. CloudRetro se basa en Pion, WebRTC popular basada en Go (gracias a del equipo de desarrolladores de Pion por ayudar con la preparación de este artículo). En este artículo, Thanh revisa la arquitectura de su proyecto, así como lo que ha aprendido y los desafíos que ha enfrentado durante su trabajo.
Introducción
El año pasado, cuando Google anunció Stadia, simplemente me quedé asombrado. La idea es tan única e innovadora que me preguntaba constantemente cómo era posible con la tecnología existente. El deseo de comprender mejor este tema me llevó a crear mi propia versión de un juego en la nube de código abierto. El resultado fue simplemente fantástico. A continuación, me gustaría compartir el proceso de trabajo sobre mi proyecto anual .
TLDR: versión corta en diapositivas con los puntos principales
Por qué el futuro está en los juegos en la nube
Creo que el Cloud Gaming pronto se convertirá en la nueva generación no solo de juegos, sino de otros campos de la informática. Los juegos en la nube son la cúspide del modelo cliente/servidor. Este modelo maximiza el control del backend y minimiza el trabajo del frontend al colocar la lógica del juego en un servidor remoto y transmitir imágenes/audio al cliente. El servidor realiza el pesado procesamiento, por lo que el cliente ya no depende de las limitaciones de hardware.
Google Stadia, en esencia, permite jugar (es decir, juegos de alto nivel) en una interfaz como YouTube. La misma metodología puede aplicarse a otras aplicaciones pesadas fuera de línea, como sistemas operativos o diseño gráfico 2D/3D, etc., para que podamos ejecutarlas de manera estable en dispositivos con características técnicas bajas en diferentes plataformas.

El futuro de esta tecnología: imagina si Microsoft Windows 10 funcionara en el navegador Chrome?
Los juegos en la nube son técnicamente complejos
Los videojuegos son una de esas pocas áreas donde se requiere una rápida respuesta continua del usuario. Si de vez en cuando experimentamos un retraso de 2 segundos al hacer clic en una página, eso es aceptable. Las transmisiones de video en vivo suelen tener algunos segundos de retraso, pero siguen brindando suficiente comodidad de uso. Sin embargo, si un juego se retrasa frecuentemente 500 ms, simplemente es imposible jugar. Nuestro objetivo es alcanzar una latencia extremadamente baja para que la brecha entre la entrada y el medio sea lo más pequeña posible. Por lo tanto, el enfoque tradicional de transmisión de video no es aplicable aquí.

Plantilla general de juegos en la nube
Proyecto de código abierto CloudRetro
Decidí crear un prototipo de juego en la nube para verificar si esto era posible con tales restricciones de red. Para la prueba de concepto, elegí Golang, ya que es el lenguaje que mejor manejo y se adapta bien a esta implementación por muchas otras razones, como descubrí más tarde. Go es simple y evoluciona muy rápidamente; los canales en Go son ideales para manejar la concurrencia.
Proyecto es un servicio de juegos en la nube de código abierto para juegos retro. El objetivo del proyecto es aportar las experiencias de juego más agradables a los juegos retro tradicionales y añadir multiplayer.
Puedes conocer más sobre el proyecto aquí: .
Funcionalidad de CloudRetro
Para demostrar todo el potencial de los juegos en la nube, CloudRetro utiliza juegos retro. Lo que permite obtener una gran cantidad de experiencias de juego únicas.
- Portabilidad del juego
- Reproducción instantánea al abrir la página; no se requiere descarga ni instalación
- Funciona en el navegador móvil, por lo que no se necesita ningún software para comenzar
- Las sesiones de juego se pueden compartir en varios dispositivos y guardar en la nube para el próximo inicio de sesión
- El juego se puede transmitir, y varios usuarios pueden jugarlo simultáneamente:
- Crowdplay tipo TwitchPlayPokemon, solo que más multiplataforma y más en tiempo real
- Juegos offline en línea. Muchos usuarios pueden jugar sin necesidad de configurar la red. En Samurai Shodown ahora pueden jugar 2 jugadores a través de la nube CloudRetro

Versión demo de un juego multijugador en línea en diferentes dispositivosInfraestructura
Requisitos y stack tecnológico
A continuación se presenta una lista de requisitos que establecí antes de comenzar el proyecto.
1. Un jugador
Este requisito puede parecer poco importante y obvio aquí, pero es una de mis conclusiones clave, permite que los juegos en la nube se mantengan alejados de los servicios de streaming tradicionales. Si nos centramos en los videojuegos para un solo jugador, podemos prescindir de un servidor centralizado o un CDN, porque no necesitamos hacer streaming a las masas. En lugar de cargar flujos en un servidor absorbente o enviar paquetes a un servidor centralizado de WebSocket, los flujos de servicio se transmiten directamente al usuario a través de una conexión peer-to-peer de WebRTC.2. Flujo de media de baja latencia
Al leer sobre Stadia, a menudo encuentro en algunos artículos mención de WebRTC. Me he dado cuenta de que WebRTC es una tecnología sobresaliente y se adapta perfectamente para su uso en juegos en la nube. WebRTC es un proyecto que proporciona a los navegadores web y aplicaciones móviles comunicación en tiempo real a través de una API sencilla. Ofrece una conexión peer-to-peer, optimizada para medios y tiene códecs estándar incorporados, como VP8 y H264.He preferido asegurarme de que los usuarios tengan la experiencia más cómoda posible, en lugar de mantener una alta calidad gráfica. El algoritmo permite algunas pérdidas. En Google Stadia hay un paso adicional para reducir el tamaño de la imagen en el servidor, y los fotogramas se escalan a una calidad más alta antes de ser enviados a los nodos peer-to-peer.
3. Infraestructura distribuida con enrutamiento geográfico
Sin importar cuán optimizado esté el algoritmo de compresión y el código, la red sigue siendo el factor decisivo que más contribuye a la latencia. La arquitectura debe tener un mecanismo que conecte al servidor más cercano al usuario para reducir el tiempo de espera (RTT). La arquitectura debe tener 1 coordinador y varios servidores de streaming distribuidos por todo el mundo: Oeste de EE. UU., Este de EE. UU., Europa, Singapur, China. Todos los servidores de streaming deben estar completamente aislados. El sistema puede ajustar su distribución cuando un servidor se une o sale de la red. Así, ante un alto tráfico, la adición de servidores adicionales permite la escalabilidad horizontal.4. Compatibilidad del navegador
Los juegos en la nube brillan en su mejor forma cuando requieren un mínimo de esfuerzo por parte de los usuarios. Esto significa que es posible iniciar juegos en un navegador. Los navegadores ayudan a que la experiencia de juego sea lo más cómoda posible para los usuarios, liberándolos de la necesidad de instalar software y hardware. Además, los navegadores facilitan la compatibilidad entre plataformas para versiones móviles y de escritorio. Afortunadamente, WebRTC es ampliamente compatible con varios navegadores.5. División clara entre la interfaz de juego y el servicio
Considero el servicio de juegos en la nube como una plataforma. Todos deben tener la posibilidad de conectar cualquier cosa a la plataforma. Actualmente he integrado con el servicio de juegos en la nube, porque LibRetro ofrece una hermosa interfaz de emulador de juegos retro, como SNES, GBA, PS.6. Salas para multijugador, juego en grupo y enlace profundo (deep-link) con el juego
CloudRetro admite múltiples jugabilidades nuevas, como CrowdPlay y MultiJugador Online para juegos retro. Si varios usuarios abren el mismo deep-link en diferentes computadoras, verán el mismo juego en ejecución y podrán unirse a él.Además, los estados del juego se almacenan en la nube. Esto permite a los usuarios continuar jugando en cualquier momento desde cualquier otro dispositivo.
7. Escalabilidad horizontal
Al igual que cualquier SAAS en la actualidad, los juegos en la nube deben estar diseñados para ser escalables horizontalmente. La arquitectura 'coordinador-trabajador' permite agregar más trabajadores para manejar mayor tráfico.8. Sin dependencia de una sola nube
La infraestructura de CloudRetro se aloja en diferentes proveedores de la nube (Digital Ocean, Alibaba, proveedor personalizado) para diferentes regiones. Activo la ejecución en contenedores Docker para la infraestructura y configuro los parámetros de red mediante un script de bash, evitando así la dependencia de un solo proveedor de la nube. Combinando esto con NAT Traversal en WebRTC, podemos lograr flexibilidad para desplegar CloudRetro en cualquier plataforma en la nube e incluso en máquinas de cualquier usuario.Diseño arquitectónico
Trabajador: (o servidor de flujo mencionado anteriormente) multiplica los juegos, ejecuta el pipeline de codificación y transmite los medios codificados a los usuarios. Las instancias de trabajo se distribuyen por todo el mundo, y cada trabajador puede manejar múltiples sesiones de usuario simultáneamente.
Coordinador: se encarga de asignar un nuevo usuario al trabajador más adecuado para la transmisión. El coordinador interactúa con los trabajadores a través de WebSocket.
Almacenamiento de estados del juego: un almacenamiento remoto central para todos los estados del juego. Este almacenamiento proporciona funciones críticas como la carga/salida remota.

Arquitectura de CloudRetro de alto nivelEscenario del usuario
Cuando un nuevo usuario abre CloudRetro en los pasos 1 y 2, mostrados en la figura a continuación, se solicita al coordinador, junto con la lista de trabajadores disponibles, que muestre la primera página. Luego, en el paso 3, el cliente calcula las latencias para todos los candidatos mediante una solicitud HTTP ping. Esta lista de latencias se envía de vuelta al coordinador, para que pueda determinar el trabajador más adecuado para atender al usuario. En el paso 4 a continuación, se crea el juego. Se establece una conexión de streaming WebRTC entre el usuario y el trabajador asignado.

Escenario del usuario después de obtener accesoQué hay dentro del trabajador
Los pipelines de juegos y streaming se almacenan dentro del trabajador de manera aislada y se intercambian información a través de una interfaz. En este momento, esta conexión se realiza mediante la transmisión de datos en memoria a través de en el mismo proceso. El siguiente objetivo es la segregación, es decir, ejecutar el juego de manera independiente en otro proceso.

Interacción de componentes del trabajadorComponentes principales:
- WebRTC: componente del cliente que recibe la entrada del usuario y proporciona los medios codificados del servidor.
- Emulador de juegos: componente del juego. Gracias a la biblioteca Libretro, el sistema puede ejecutar un juego dentro del mismo proceso y capturar internamente los medios y el flujo de entrada.
- Los fotogramas dentro del juego son capturados y enviados al codificador.
- Codificador de imagen/audio: pipeline de codificación que recibe los fotogramas de media, los codifica en segundo plano y produce imágenes/audio codificados.
Implementación
CloudRetro se basa en WebRTC como su tecnología principal, por lo que antes de profundizar en los detalles de la implementación en Golang, decidí hablar sobre WebRTC. Es una tecnología increíble que me ha ayudado a lograr una latencia de transmisión de datos de solo una fracción de segundo.
WebRTC
WebRTC está diseñado para proporcionar conexiones P2P de alta calidad en aplicaciones móviles nativas y en navegadores a través de API simples.
NAT Traversal
WebRTC es conocido por su funcionalidad de NAT Traversal. WebRTC está destinado a la comunicación entre pares. Su objetivo es encontrar la ruta directa más adecuada, evitando gateways NAT y cortafuegos para la comunicación P2P a través de un proceso llamado . En este proceso, las API de WebRTC encuentran tu dirección IP pública a través de servidores STUN y la reenvían a un servidor de retransmisión (), cuando no se puede establecer una conexión directa.
Sin embargo, CloudRetro no utiliza completamente esta capacidad. Sus conexiones P2P no existen entre usuarios, sino entre usuarios y servidores en la nube. La parte del servidor del modelo tiene menos restricciones sobre la comunicación directa que un dispositivo de usuario típico. Esto permite abrir puertos entrantes de manera anticipada o utilizar direcciones IP públicas directamente, ya que el servidor no está detrás de NAT.
Antes quería convertir el proyecto en una plataforma de distribución de juegos para Cloud Gaming. La idea era permitir a los creadores de juegos proporcionar juegos y recursos de transmisión. Los usuarios interactuarían directamente con los proveedores. De esta manera descentralizada, CloudRetro es solo un medio para conectar recursos de transmisión de terceros con los usuarios, lo que lo hace más escalable, ya que ya no depende de un hosting. El papel de NAT Traversal de WebRTC es muy importante para facilitar la inicialización de conexiones P2P en recursos de transmisión de terceros, simplificando la conexión de los creadores a la red.
Compresión de video
La compresión de video es una parte indispensable del pipeline, que contribuye significativamente a la fluidez del streaming. Aunque no es necesario conocer todos los detalles de la codificación de video en VP8/H264, entender el concepto ayuda a comprender los parámetros de velocidad del video en streaming, a depurar comportamientos inesperados y a ajustar la latencia.
La compresión de video para servicios de streaming es una tarea compleja, porque el algoritmo debe garantizar que el tiempo total de codificación + el tiempo de transmisión por la red + el tiempo de decodificación sea lo más bajo posible. Además, el proceso de codificación debe ser consistente y continuo. Algunos compromisos durante la codificación no son aplicables; por ejemplo, no podemos preferir un largo tiempo de codificación a un tamaño de archivo más pequeño y a un tiempo de decodificación, o utilizar una compresión inconsistente.
La idea de la compresión de video es eliminar bits de información innecesarios, manteniendo al mismo tiempo un nivel aceptable de precisión para los usuarios. Además de codificar fotogramas estáticos individuales, el algoritmo deduce el fotograma actual de los anteriores y siguientes, enviando solo su diferencia. Como se puede ver en el ejemplo con Pacman, solo se envían los puntos diferenciales.

Comparación de fotogramas de video usando PacmanCompresión de audio
De manera similar, el algoritmo de compresión de audio omite datos que no pueden ser percibidos por el ser humano. Opus es, hasta el momento, el códec de audio con mejor rendimiento. Está diseñado para transmitir ondas de audio a través de un protocolo de datagramas ordenados, como RTP (Protocolo de Transporte en Tiempo Real). Su latencia es menor que la de mp3 y aac, y su calidad es superior. La latencia suele ser de alrededor de 5 a 66,5 ms.
Pion, WebRTC en Golang
es un proyecto de código abierto que trae WebRTC a Golang. En lugar de envolver las bibliotecas nativas de C++ de WebRTC, Pion es una implementación nativa de WebRTC en Golang con mejor rendimiento, integración con Go, y control de versiones sobre los protocolos de WebRTC.
La biblioteca también proporciona transmisión de datos con muchos excelentes módulos integrados y una latencia de menos de un segundo. Tiene su propia implementación de STUN, DTLS, SCTP, etc. y algunos experimentos con QUIC y WebAssembly. En sí misma, esta biblioteca de código abierto es realmente una buena fuente de aprendizaje con excelente documentación, implementación de protocolos de red y grandes ejemplos.
La comunidad Pion, liderada por un creador muy apasionado, es bastante activa, se llevan a cabo muchas discusiones de calidad sobre WebRTC. Si te interesa esta tecnología, únete a – aprenderás mucho nuevo.
Escribiendo CloudRetro en Golang

Implementación de un worker en GoCanales Go en acción
Gracias al hermoso diseño de los canales Go, los problemas de transmisión de eventos y paralelismo se simplifican significativamente. Como en el diagrama, varios componentes operan en paralelo en diferentes GoRoutines. Cada componente gestiona su propio estado y se comunica a través de canales. La instrucción selectiva de Golang obliga a procesar un solo evento atómico a la vez en el juego (game tick). Esto significa que para este diseño no se requiere bloqueo. Por ejemplo, cuando un usuario guarda, se necesita una instantánea completa del estado del juego. Este estado debe mantenerse continuo, ejecutando entradas hasta que el proceso de guardado se complete. Durante cada game tick, el backend puede manejar solo la operación de guardado o entrada, lo que hace que el proceso sea seguro para subprocesos.
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
Este patrón de Golang se adapta perfectamente a mi caso de uso de CrowdPlay y Múltiples Jugadores. Siguiendo este patrón, todas las entradas de usuario en una habitación se integran en un canal de entrada central. Los medios del juego luego se distribuyen a todos los usuarios en la misma habitación. De este modo, logramos la separación del estado del juego entre varias sesiones de juego de diferentes usuarios.

Sincronización entre diversas sesionesDesventajas de Golang
Golang no es perfecto. El canal es lento. En comparación con el bloqueo, el canal Go es simplemente una forma más sencilla de manejar eventos paralelos y de flujo, pero el canal no ofrece el mejor rendimiento. Detrás del canal hay una lógica de bloqueo compleja. Por lo tanto, hice algunos ajustes en la implementación, reaplicando bloqueos y valores atómicos al sustituir canales para optimizar el rendimiento.
Además, el recolector de basura en Golang es no gestionado, lo que a veces provoca pausas sospechosamente largas. Esto dificulta mucho el rendimiento de aplicaciones en tiempo real.
CGO
El proyecto utiliza una biblioteca existente VP8/H264 de Golang de código abierto para la compresión de medios y Libretro para emuladores de videojuegos. Todas estas bibliotecas son simplemente envoltorios de la biblioteca C en Go usando . Algunos de los inconvenientes se enumeran en . Los problemas que encontré:
- imposibilidad de capturar un fallo en CGO, incluso con Golang RecoveryCrash;
- imposibilidad de identificar el cuello de botella en el rendimiento, cuando no podemos detectar problemas detallados en CGO.
Conclusión
Logré mi objetivo: entendí los servicios de juegos en la nube y creé una plataforma que ayuda a jugar juegos retro nostálgicos con mis amigos en línea. La creación de este proyecto no habría sido posible sin la biblioteca Pion y el apoyo de la comunidad Pion. Estoy extremadamente agradecido por su desarrollo intensivo. Las API simples proporcionadas por WebRTC y Pion permitieron una integración fluida. Mi primera prueba de concepto se lanzó en la misma semana, a pesar de que no sabía con antelación sobre la conexión punto a punto (P2P).
A pesar de la simplicidad de la integración, la transmisión P2P es realmente un área muy compleja en la informática. Se enfrenta a la complejidad de arquitecturas de red de muchos años, como IP y NAT, para crear una sesión peer-to-peer. A lo largo de mi trabajo en este proyecto, acumulé mucho conocimiento valioso sobre redes y optimización del rendimiento, así que recomiendo a todos intentar construir productos P2P utilizando WebRTC.
CloudRetro atiende todos los escenarios de uso que esperaba, desde mi perspectiva como jugador retro. Sin embargo, creo que hay muchas áreas en el proyecto que puedo mejorar, como hacer la red más confiable y eficiente, proporcionar una mejor calidad gráfica en los juegos o permitir el intercambio de juegos entre usuarios. Estoy trabajando arduamente en ello. Por favor, estén atentos a y apoyen el proyecto si les gusta.
Fuente: habr.com







