Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero

Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
Software como servicio, infraestructura como servicio, plataforma como servicio, plataforma de comunicación como servicio, videoconferencia como servicio, ¿qué pasa con los juegos en la nube como servicio? Ya ha habido varios intentos de crear juegos en la nube (Cloud Gaming), como Stadia, lanzado recientemente por Google. Estadios no es nuevo en WebRTC, pero ¿pueden otros usar WebRTC de la misma manera?

Thanh Nguyen decidió probar esta oportunidad en su proyecto de código abierto CloudRetro. CloudRetro está basado en Pion, popular Biblioteca WebRTC basada en Go (gracias Shownu (del equipo de desarrollo de Pion por su ayuda en la preparación de este artículo). En este artículo, Thanh ofrece una descripción general de la arquitectura de su proyecto y también habla sobre las cosas útiles que aprendió y los desafíos que encontró durante su trabajo.

Entrada

El año pasado, cuando Google anunció Stadia, me dejó boquiabierto. La idea es tan única e innovadora que constantemente me preguntaba cómo era posible esto con la tecnología existente. El deseo de comprender mejor este tema me impulsó a crear mi propia versión de un juego en la nube de código abierto. El resultado fue sencillamente fantástico. A continuación me gustaría compartir el proceso de trabajo en mi año. proyecto.

TLDR: versión de diapositiva corta con aspectos destacados

Por qué los juegos en la nube son el futuro

Creo que los juegos en la nube pronto se convertirán en la próxima generación no solo de los juegos, sino también de otras áreas de la informática. Los juegos en la nube son el pináculo del modelo cliente/servidor. Este modelo maximiza la gestión de backend y minimiza el trabajo de frontend al alojar la lógica del juego en un servidor remoto y transmitir imágenes/audio al cliente. El servidor realiza el procesamiento pesado para que el cliente ya no esté a merced de las limitaciones del hardware.

Google Stadia esencialmente te permite jugar juegos AAA (es decir, juegos de gran éxito de alta gama) en una interfaz como YouTube. La misma metodología se puede aplicar a otras aplicaciones pesadas fuera de línea, como sistemas operativos o diseño gráfico 2D/3D, etc. para que podamos ejecutarlos de manera consistente en dispositivos de baja especificación en múltiples plataformas.

Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
El futuro de esta tecnología: ¿Imagínese si Microsoft Windows 10 se ejecutara en el navegador Chrome?

Los juegos en la nube son un desafío técnico

Los juegos son una de esas raras áreas donde se requiere una respuesta rápida y constante del usuario. Si ocasionalmente nos encontramos con un retraso de 2 segundos al hacer clic en una página, esto es aceptable. Las transmisiones de video en vivo tienden a demorarse unos segundos, pero aun así ofrecen una usabilidad razonable. Sin embargo, si el juego se retrasa frecuentemente 500 ms, simplemente no se puede jugar. Nuestro objetivo es lograr una latencia extremadamente baja para que la brecha entre la entrada y los medios sea lo más pequeña posible. Por lo tanto, el enfoque tradicional de transmisión de vídeo no es aplicable aquí.

Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
Plantilla general de juego en la nube

Proyecto de código abierto CloudRetro

Decidí crear una muestra de prueba de un juego en la nube para ver si todo esto era posible con restricciones de red tan estrictas. Elegí Golang para la prueba de concepto porque era el lenguaje con el que estaba más familiarizado y era muy adecuado para esta implementación por muchas otras razones, como descubrí más tarde. Go es simple y se desarrolla muy rápidamente; Los canales en Go son excelentes para administrar subprocesos múltiples.

proyecto NubeRetro.io es un servicio de juegos en la nube de código abierto para juegos retro. El objetivo del proyecto es llevar la experiencia de juego más cómoda a los juegos retro tradicionales y agregar el modo multijugador.
Puedes conocer más sobre el proyecto aquí: https://github.com/giongto35/cloud-game.

Funcionalidad CloudRetro

CloudRetro utiliza juegos retro para demostrar el poder de los juegos en la nube. Lo que te permite obtener muchas experiencias de juego únicas.

  • Portabilidad del juego
    • Reproducción instantánea al abrir una página; no se necesita descarga ni instalación
    • Funciona en un navegador móvil, por lo que no se necesita software para ejecutarlo

  • Las sesiones de juego se pueden compartir entre múltiples dispositivos y almacenar en la nube para la próxima vez que inicies sesión.
  • El juego se puede transmitir o varios usuarios pueden jugar a la vez:
    • Crowdplay como TwitchPlayPokemon, sólo que más multiplataforma y más en tiempo real
    • Juegos sin conexión en línea. Muchos usuarios pueden jugar sin configurar una red. Samurai Shodown ahora puede ser jugado por 2 jugadores a través de la red CloudRetro

    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Versión demo del juego multijugador online en diferentes dispositivos

    Infraestructura

    Pila de requisitos y tecnología

    A continuación se muestra una lista de requisitos que establecí antes de comenzar el proyecto.

    1. Un jugador
    Este requisito puede no parecer demasiado importante u obvio aquí, pero es una de mis conclusiones clave: permite que los juegos en la nube se mantengan lo más alejados posible de los servicios de transmisión tradicionales. Si nos centramos en un juego para un solo jugador, podemos deshacernos de un servidor centralizado o CDN porque no tenemos que transmitir a las masas. En lugar de cargar transmisiones en un servidor receptor o pasar paquetes a un servidor WebSocket centralizado, las transmisiones de servicios se entregan directamente al usuario a través de una conexión WebRTC de igual a igual.

    2. Transmisión de medios de baja latencia
    Al leer sobre Stadia, a menudo veo que WebRTC se menciona en algunos artículos. Me di cuenta de que WebRTC es una tecnología excepcional y perfecta para usar 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 simple. Proporciona conectividad de igual a igual, está optimizado para medios y tiene códecs estándar integrados como VP8 y H264.

    Prioricé garantizar la mejor experiencia de usuario posible sobre mantener gráficos de alta calidad. Algunas pérdidas son aceptables en el algoritmo. Google Stadia tiene un paso adicional para reducir el tamaño de la imagen en el servidor y los marcos se actualizan a una calidad superior antes de transmitirse a sus pares.

    3. Infraestructura distribuida con enrutamiento geográfico
    No importa cuán optimizados estén el código y el algoritmo de compresión, la red sigue siendo el factor decisivo que más contribuye a la latencia. La arquitectura debe tener un mecanismo para emparejar el servidor más cercano al usuario para reducir el tiempo de ida y vuelta (RTT). La arquitectura debe tener 1 coordinador y varios servidores de streaming distribuidos por todo el mundo: EE.UU. Oeste, EE.UU. Este, 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. Por lo tanto, con un gran tráfico, agregar servidores adicionales permite el escalamiento horizontal.

    4. Compatibilidad del navegador
    Los juegos en la nube son mejores cuando requieren menos de los usuarios. Esto significa que es posible ejecutarlo en un navegador. Los navegadores ayudan a que la experiencia de juego sea lo más cómoda posible para los usuarios, evitando que tengan que instalar software y hardware. Los navegadores también ayudan a proporcionar funcionalidad multiplataforma entre las versiones móvil y de escritorio. Afortunadamente, WebRTC es compatible con una variedad de navegadores.

    5. Clara separación entre la interfaz y el servicio del juego.
    Veo el servicio de juegos en la nube como una plataforma. Todos deberían poder conectar cualquier cosa a la plataforma. ahora me he integrado LibRetro con servicio de juegos en la nube porque LibRetro ofrece una hermosa interfaz de emulador de juegos para juegos retro como SNES, GBA, PS.

    6. Salas para multijugador, juego multitudinario y vinculación externa (enlace profundo) con el juego.
    CloudRetro admite muchos juegos nuevos, como CrowdPlay y Online MultiPlayer para juegos retro. Si varios usuarios abren el mismo enlace profundo en diferentes computadoras, verán el mismo juego ejecutándose e incluso podrán unirse a él.

    Además, los estados del juego se almacenan en la nube. Esto permite a los usuarios seguir jugando en cualquier momento en cualquier otro dispositivo.

    7. Escalado horizontal
    Como cualquier SAAS hoy en día, los juegos en la nube deben diseñarse para que sean escalables horizontalmente. El diseño de coordinador-trabajador le permite agregar más trabajadores para atender más tráfico.

    8. Sin conexión a una nube
    La infraestructura de CloudRetro está alojada en diferentes proveedores de nube (Digital Ocean, Alibaba, proveedor personalizado) para diferentes regiones. Habilito la ejecución en un contenedor Docker para la infraestructura y configuro los ajustes de red usando un script bash para evitar quedar atrapado en un único proveedor de nube. Al combinar esto con NAT Traversal en WebRTC, podemos tener la flexibilidad de implementar CloudRetro en cualquier plataforma en la nube e incluso en las máquinas de cualquier usuario.

    Diseño arquitectonico

    Obrero: (o el servidor de transmisión mencionado anteriormente) multiplica los juegos, ejecuta el proceso de codificación y transmite los medios codificados a los usuarios. Las instancias de trabajador están distribuidas por todo el mundo y cada trabajador puede manejar múltiples sesiones de usuario simultáneamente.

    Coordinador: se encarga de emparejar al nuevo usuario con el trabajador más adecuado para el streaming. El coordinador interactúa con los trabajadores a través de WebSocket.

    Almacenamiento del estado del juego: Almacenamiento remoto central para todos los estados del juego. Este almacenamiento proporciona funciones importantes como guardar/cargar de forma remota.

    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Arquitectura de alto nivel de CloudRetro

    Guión personalizado

    Cuando un nuevo usuario abre CloudRetro en los pasos 1 y 2 que se muestran en la figura siguiente, se solicita al coordinador junto con la lista de trabajadores disponibles en la primera página. Después de esto, en el paso 3, el cliente calcula los retrasos para todos los candidatos mediante una solicitud de ping HTTP. Esta lista de retrasos se envía luego al coordinador para que pueda determinar el trabajador más adecuado para atender al usuario. El paso 4 a continuación crea el juego. Se establece una conexión de transmisión WebRTC entre el usuario y el trabajador asignado.
    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Script de usuario después de obtener acceso

    ¿Qué hay dentro del trabajador?

    Los canales de juegos y streaming se almacenan dentro del trabajador de forma aislada e intercambian información allí a través de la interfaz. Actualmente, esta comunicación se realiza mediante la transferencia de datos en la memoria a través de canales de golang en el mismo proceso. El próximo objetivo es la segregación, es decir. lanzamiento independiente del juego en otro proceso.

    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Interacción de los componentes del trabajador.

    Componentes principales:

    • WebRTC: un componente de cliente que acepta entradas del usuario y genera medios codificados desde el servidor.
    • Emulador de juegos: componente del juego. Gracias a la biblioteca Libretro, el sistema puede ejecutar el juego dentro del mismo proceso e interceptar internamente medios y flujos de entrada.
    • Los fotogramas del juego se capturan y envían al codificador.
    • Codificador de imagen/audio: un canal de codificación que toma fotogramas multimedia, los codifica en segundo plano y genera imágenes/audio codificados.

    implementación

    CloudRetro se basa en WebRTC como tecnología principal, por lo que antes de profundizar en los detalles de la implementación de Golang, decidí hablar sobre WebRTC en sí. Esta es una tecnología asombrosa que me ha ayudado enormemente a lograr una latencia inferior a un segundo para la transmisión de datos.

    WebRTC

    WebRTC está diseñado para proporcionar conexiones punto a punto de alta calidad en navegadores y aplicaciones móviles nativas utilizando API simples.

    NAT transversal

    WebRTC es conocido por su funcionalidad NAT Traversal. WebRTC está diseñado para la comunicación entre pares. Su objetivo es encontrar la ruta directa más adecuada, evitando gateways NAT y firewalls para la comunicación peer-to-peer mediante un proceso llamado HIELO. Como parte de este proceso, las API de WebRTC encuentran su dirección IP pública utilizando servidores STUN y la reenvían al servidor de retransmisión (GIRO) cuando no se puede establecer una conexión directa.

    Sin embargo, CloudRetro no aprovecha plenamente esta característica. Sus conexiones peer-to-peer no existen entre usuarios, sino entre usuarios y servidores en la nube. El lado del servidor del modelo tiene menos restricciones de comunicación directa que un dispositivo de usuario típico. Esto le permite abrir previamente los puertos entrantes o utilizar direcciones IP públicas directamente, ya que el servidor no está detrás de NAT.

    Anteriormente 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. Y los usuarios interactuarían directamente con los proveedores. De esta manera descentralizada, CloudRetro es solo un marco para conectar recursos de transmisión de terceros a los usuarios, haciéndolo más escalable cuando ya no está alojado. La función de WebRTC NAT Traversal aquí es muy importante para facilitar la inicialización de la conexión de igual a igual en recursos de transmisión de terceros, lo que facilita que el creador se conecte a la red.

    Compresión de video

    La compresión de vídeo es una parte indispensable del proceso y contribuye en gran medida a un flujo fluido. Si bien no es necesario conocer todos los detalles de la codificación de video VP8/H264, comprender los conceptos puede ayudarlo a comprender las opciones de velocidad de transmisión de video, depurar comportamientos inesperados y ajustar la latencia.

    Comprimir video para un servicio de transmisión es un desafío porque el algoritmo debe garantizar que el tiempo total de codificación + tiempo de transmisión de red + tiempo de decodificación sea lo más bajo posible. Además, el proceso de codificación debe ser consistente y continuo. Algunas compensaciones de codificación no se aplican; por ejemplo, no podemos favorecer tiempos de codificación prolongados en lugar de tamaños de archivo y tiempos de decodificación más pequeños, o utilizar una compresión inconsistente.

    La idea detrás de la compresión de video es eliminar bits de información innecesarios manteniendo un nivel aceptable de precisión para los usuarios. Además de codificar fotogramas de imágenes estáticas individuales, el algoritmo infiere el fotograma actual a partir del anterior y del siguiente, por lo que sólo se envía su diferencia. Como puede verse en el ejemplo de Pacman, sólo se transmiten puntos diferenciales.

    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Comparación de cuadros de video usando Pacman como ejemplo

    Compresión de audio

    Asimismo, el algoritmo de compresión de audio omite datos que los humanos no pueden percibir. Opus es actualmente el códec de audio con mejor rendimiento. Está diseñado para transmitir una onda de audio a través de un protocolo de datagrama ordenado como RTP (Protocolo de transporte en tiempo real). Su latencia es menor que la de mp3 y aac, y la calidad es mayor. La latencia suele rondar entre 5 y 66,5 ms.

    Pion, WebRTC en Golang

    Peón es un proyecto de código abierto que lleva WebRTC a Golang. En lugar del empaquetado habitual de las bibliotecas WebRTC nativas de C++, Pion es una implementación nativa de Golang de WebRTC con mejor rendimiento, integración de Go y control de versiones en protocolos WebRTC.

    La biblioteca también permite la transmisión con muchas funciones integradas excelentes con una latencia inferior a un segundo. Tiene su propia implementación de STUN, DTLS, SCTP, etc. y algunos experimentos con QUIC y WebAssembly. Esta biblioteca de código abierto en sí misma es un recurso de aprendizaje realmente bueno con excelente documentación, implementaciones de protocolos de red y ejemplos interesantes.

    La comunidad Pion, dirigida por un creador muy apasionado, es bastante animada y hay muchos debates de calidad sobre WebRTC. Si estás interesado en esta tecnología, únete http://pion.ly/slack – aprenderás muchas cosas nuevas.

    Escribiendo CloudRetro en Golang

    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Implementación de un trabajador en Go

    Ir a los canales en acción

    Gracias al hermoso diseño del canal de Go, los problemas de transmisión de eventos y concurrencia se simplifican enormemente. Como en el diagrama, diferentes GoRoutines tienen múltiples componentes ejecutándose en paralelo. Cada componente gestiona su estado y se comunica a través de canales. La afirmación selectiva de Golang obliga a procesar un evento atómico cada vez en el juego (tick del juego). Esto significa que no se necesita bloqueo para este diseño. Por ejemplo, cuando un usuario guarda, se requiere una instantánea completa del estado del juego. Este estado debe permanecer continuo, iniciando sesión hasta que se complete el guardado. Durante cada tic del juego, el backend solo puede manejar una operación de guardado o entrada, lo que hace que el proceso sea seguro.

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

    Entrada/salida en abanico

    Esta plantilla de Golang se adapta perfectamente a mi caso de uso de CrowdPlay y Múltiples jugadores. Siguiendo este patrón, todas las entradas del usuario en una habitación están integradas en el canal de entrada central. Luego, los medios del juego se implementan para todos los usuarios en la misma sala. De esta forma conseguimos la división del estado del juego entre varias sesiones de juego de diferentes usuarios.

    Juegos en la nube de código abierto en WebRTC: p2p, multijugador, latencia cero
    Sincronización entre diferentes sesiones.

    Desventajas 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 fácil de manejar eventos concurrentes y encadenados, pero el canal no proporciona el mejor rendimiento. Hay una lógica de bloqueo compleja debajo del canal. Así que hice algunos ajustes en la implementación, volviendo a aplicar bloqueos y valores atómicos al reemplazar canales para optimizar el rendimiento.

    Además, el recolector de basura en Golang no está administrado, lo que a veces provoca pausas sospechosamente largas. Esto interfiere enormemente con la aplicación de transmisión en tiempo real.

    CGO

    El proyecto utiliza la biblioteca Golang VP8/H264 de código abierto existente para la compresión de medios y Libretro para emuladores de juegos. Todas estas bibliotecas son simplemente contenedores de la biblioteca C en Go usando CGO. Algunas de las desventajas se enumeran en esta publicación de Dave Cheney. Problemas que encontré:

    • incapacidad para detectar un fallo en CGO, incluso con Golang RecoveryCrash;
    • No identificar cuellos de botella en el rendimiento cuando no podemos detectar problemas detallados en CGO.

    Conclusión

    Logré mi objetivo de comprender los servicios de juegos en la nube y crear una plataforma que me ayude a jugar juegos retro nostálgicos con mis amigos en línea. Este proyecto no hubiera sido posible sin la biblioteca Pion y el apoyo de la comunidad Pion. Estoy sumamente agradecido por su intenso desarrollo. Las API simples proporcionadas por WebRTC y Pion garantizaron una integración perfecta. Mi primera prueba de concepto se publicó esa misma semana, aunque no tenía conocimientos previos de comunicación entre pares (P2P).

    A pesar de la facilidad de integración, la transmisión P2P es de hecho un área muy compleja en informática. Tiene que lidiar con la complejidad de arquitecturas de red de larga data, como IP y NAT, para crear una sesión de igual a igual. Mientras trabajaba en este proyecto, adquirí muchos conocimientos valiosos sobre redes y optimización del rendimiento, por lo que animo a todos a que intenten crear productos P2P utilizando WebRTC.

    CloudRetro cubre todos los casos de uso que esperaba desde mi perspectiva como jugador retro. Sin embargo, creo que hay muchas áreas del proyecto que puedo mejorar, como hacer que la red sea más confiable y eficaz, proporcionar gráficos de juegos de mayor calidad o la capacidad de compartir juegos entre usuarios. Estoy trabajando duro en esto. Por favor, siga proyecto y apóyalo si te gusta.

Fuente: habr.com

Añadir un comentario