Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero

Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
Software como serviço, infraestrutura como serviço, plataforma como serviço, plataforma de comunicação como serviço, videoconferência como serviço, e os jogos em nuvem como serviço? Já houve diversas tentativas de criação de jogos em nuvem (Cloud Gaming), como o Stadia, lançado recentemente pelo Google. Estádios não é novidade no WebRTC, mas outras pessoas podem usar o WebRTC da mesma maneira?

Thanh Nguyen decidiu testar esta oportunidade em seu projeto de código aberto CloudRetro. CloudRetro é baseado em Pion, popular Biblioteca WebRTC baseada em Go (obrigado Shownu da equipe de desenvolvimento do Pion pela assistência na preparação deste artigo). Neste artigo, Thanh fornece uma visão geral da arquitetura de seu projeto e também fala sobre as coisas úteis que aprendeu e quais desafios encontrou durante seu trabalho.

Entrada

No ano passado, quando o Google anunciou o Stadia, fiquei impressionado. A ideia é tão única e inovadora que sempre me perguntei como isso era possível com a tecnologia existente. O desejo de entender melhor este tópico me levou a criar minha própria versão de um jogo em nuvem de código aberto. O resultado foi simplesmente fantástico. Abaixo gostaria de compartilhar o processo de trabalho no meu ano projeto.

TLDR: versão de slide curto com destaques

Por que os jogos na nuvem são o futuro

Acredito que os Cloud Gaming em breve se tornarão a próxima geração não apenas de jogos, mas também de outras áreas da ciência da computação. Os jogos em nuvem são o auge do modelo cliente/servidor. Este modelo maximiza o gerenciamento de backend e minimiza o trabalho de frontend hospedando a lógica do jogo em um servidor remoto e transmitindo imagens/áudio para o cliente. O servidor faz o processamento pesado para que o cliente não fique mais à mercê das limitações de hardware.

O Google Stadia essencialmente permite que você jogue Jogos AAA (ou seja, jogos de grande sucesso) em uma interface como o YouTube. A mesma metodologia pode ser aplicada a outras aplicações off-line pesadas, como sistema operacional ou design gráfico 2D/3D, etc. para que possamos executá-los de forma consistente em dispositivos de baixa especificação em múltiplas plataformas.

Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
O futuro desta tecnologia: imagine se o Microsoft Windows 10 rodasse no navegador Chrome?

Jogos em nuvem são tecnicamente desafiadores

Os jogos são uma daquelas raras áreas onde é necessária uma resposta rápida e constante do usuário. Se ocasionalmente encontrarmos um atraso de 2 segundos ao clicar em uma página, isso é aceitável. As transmissões de vídeo ao vivo tendem a atrasar alguns segundos, mas ainda oferecem uma usabilidade razoável. No entanto, se o jogo atrasar frequentemente 500 ms, é simplesmente impossível de jogar. Nosso objetivo é alcançar uma latência extremamente baixa para que a distância entre a entrada e a mídia seja a menor possível. Portanto, a abordagem tradicional de streaming de vídeo não é aplicável aqui.

Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
Modelo geral de jogo na nuvem

Projeto de código aberto CloudRetro

Decidi criar um exemplo de teste de um jogo na nuvem para ver se tudo isso era possível com restrições de rede tão rígidas. Escolhi Golang para a prova de conceito porque era a linguagem com a qual eu estava mais familiarizado e adequada para esta implementação por muitos outros motivos, como descobri mais tarde. Go é simples e se desenvolve muito rapidamente; Os canais no Go são ótimos para gerenciar multithreading.

Projeto CloudRetro.io é um serviço de jogos em nuvem de código aberto para jogos retrô. O objetivo do projeto é trazer a experiência de jogo mais confortável aos jogos retrô tradicionais e agregar multiplayer.
Você pode saber mais sobre o projeto aqui: https://github.com/giongto35/cloud-game.

Funcionalidade CloudRetro

CloudRetro usa jogos retrô para demonstrar o poder dos jogos em nuvem. O que permite que você obtenha muitas experiências de jogo únicas.

  • Portabilidade do jogo
    • Reprodução instantânea ao abrir a página; não é necessário download ou instalação
    • Funciona em um navegador móvel, portanto, nenhum software é necessário para executá-lo

  • As sessões de jogo podem ser compartilhadas entre vários dispositivos e armazenadas na nuvem para a próxima vez que você fizer login
  • O jogo pode ser transmitido ou jogado por vários usuários ao mesmo tempo:
    • Crowdplay como TwitchPlayPokemon, só que mais multiplataforma e mais em tempo real
    • Jogos off-line on-line. Muitos usuários podem jogar sem configurar uma rede. Samurai Shodown agora pode ser jogado por 2 jogadores na rede CloudRetro

    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Versão demo do jogo multijogador online em diferentes dispositivos

    Infra-estrutura

    Requisitos e pilha de tecnologia

    Abaixo está uma lista de requisitos que defini antes de iniciar o projeto.

    1. Um jogador
    Este requisito pode não parecer muito importante ou óbvio aqui, mas é uma das minhas principais conclusões: permite que os jogos em nuvem fiquem o mais longe possível dos serviços tradicionais de streaming. Se nos concentrarmos em um jogo para um jogador, podemos nos livrar de um servidor centralizado ou CDN porque não precisamos transmitir para as massas. Em vez de enviar fluxos para um servidor coletor ou passar pacotes para um servidor WebSocket centralizado, os fluxos de serviço são entregues diretamente ao usuário por meio de uma conexão WebRTC ponto a ponto.

    2. Fluxo de mídia de baixa latência
    Lendo sobre o Stadia, vejo frequentemente o WebRTC mencionado em alguns artigos. Percebi que WebRTC é uma tecnologia excelente e perfeita para uso em jogos em nuvem. WebRTC é um projeto que fornece aos navegadores da web e aplicativos móveis comunicação em tempo real por meio de uma API simples. Ele fornece conectividade ponto a ponto, é otimizado para mídia e possui codecs padrão integrados, como VP8 e H264.

    Priorizei garantir a melhor experiência de usuário possível em vez de manter gráficos de alta qualidade. Algumas perdas são aceitáveis ​​no algoritmo. O Google Stadia tem uma etapa adicional para reduzir o tamanho da imagem no servidor, e os frames são aumentados para uma qualidade mais alta antes de serem transmitidos aos pares.

    3. Infraestrutura distribuída com roteamento geográfico
    Não importa quão otimizados sejam o algoritmo e o código de compactação, a rede ainda é o fator decisivo que mais contribui para a latência. A arquitetura deve ter um mecanismo para emparelhar o servidor mais próximo do usuário para reduzir o tempo de ida e volta (RTT). A arquitetura deve ter 1 coordenador e vários servidores de streaming distribuídos pelo mundo: Oeste dos EUA, Leste dos EUA, Europa, Singapura, China. Todos os servidores de streaming devem estar completamente isolados. O sistema pode ajustar sua distribuição quando um servidor entra ou sai da rede. Assim, com grande tráfego, a adição de servidores adicionais permite o dimensionamento horizontal.

    4. Compatibilidade do navegador
    Os jogos na nuvem atingem o seu melhor quando exigem o mínimo dos usuários. Isso significa que é possível rodar em um navegador. Os navegadores ajudam a tornar a experiência de jogo o mais confortável possível para os usuários, evitando que eles instalem software e hardware. Os navegadores também ajudam a fornecer funcionalidade de plataforma cruzada entre versões móveis e desktop. Felizmente, o WebRTC é bem suportado em vários navegadores.

    5. Separação clara entre interface e serviço do jogo
    Vejo o serviço de jogos em nuvem como uma plataforma. Todos deveriam ser capazes de conectar qualquer coisa à plataforma. Agora eu integrei LibRetro com serviço de jogos em nuvem porque LibRetro oferece uma bela interface de emulador de jogos para jogos retrô como SNES, GBA, PS.

    6. Salas para multiplayer, crowd play e linkagem externa (deep-link) com o jogo
    CloudRetro oferece suporte a muitos novos jogos, como CrowdPlay e Online MultiPlayer para jogos retrô. Se vários usuários abrirem o mesmo link direto em computadores diferentes, eles verão o mesmo jogo em execução e poderão até entrar nele.

    Além disso, os estados do jogo são armazenados em armazenamento em nuvem. Isso permite que os usuários continuem jogando a qualquer momento em qualquer outro dispositivo.

    7. Escala horizontal
    Como qualquer SAAS hoje em dia, os jogos em nuvem devem ser projetados para serem escalonáveis ​​horizontalmente. O design coordenador-trabalhador permite adicionar mais trabalhadores para atender mais tráfego.

    8. Sem conexão com uma nuvem
    A infraestrutura da CloudRetro está hospedada em diferentes provedores de nuvem (Digital Ocean, Alibaba, provedor personalizado) para diferentes regiões. Habilito a execução em um contêiner Docker para a infraestrutura e defino as configurações de rede usando um script bash para evitar ficar preso a um único provedor de nuvem. Ao combinar isso com NAT Traversal no WebRTC, podemos ter flexibilidade para implantar CloudRetro em qualquer plataforma de nuvem e até mesmo nas máquinas de qualquer usuário.

    Projeto arquitetônico

    Trabalhador: (ou o servidor de streaming mencionado acima) multiplica os jogos, executa o pipeline de codificação e transmite a mídia codificada para os usuários. As instâncias de trabalho são distribuídas em todo o mundo e cada trabalhador pode lidar com múltiplas sessões de usuário simultaneamente.

    Coordenador: é responsável por emparelhar o novo usuário com o trabalhador mais adequado para streaming. O coordenador interage com os trabalhadores via WebSocket.

    Armazenamento do estado do jogo: armazenamento remoto central para todos os estados do jogo. Este armazenamento fornece funções importantes, como salvar/carregar remotamente.

    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Arquitetura de nível superior do CloudRetro

    Script Personalizado

    Quando um novo usuário abre o CloudRetro nas etapas 1 e 2 mostradas na figura abaixo, o coordenador junto com a lista de trabalhadores disponíveis é solicitado para a primeira página. Depois disso, na etapa 3 o cliente calcula os atrasos para todos os candidatos usando uma solicitação HTTP ping. Essa lista de atrasos é então devolvida ao coordenador para que ele determine o trabalhador mais adequado para atender o usuário. A etapa 4 abaixo cria o jogo. Uma conexão de streaming WebRTC é estabelecida entre o usuário e o trabalhador atribuído.
    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Script do usuário após obter acesso

    O que há dentro do trabalhador

    Os pipelines de jogos e streaming são armazenados isoladamente dentro do trabalhador e trocam informações por meio da interface. Atualmente esta comunicação é realizada através da transferência de dados na memória via Canais Golang no mesmo processo. O próximo objetivo é a segregação, ou seja, lançamento independente do jogo em outro processo.

    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Interação de componentes de trabalho

    Componentes principais:

    • WebRTC: um componente cliente que aceita entrada do usuário e gera mídia codificada do servidor.
    • Emulador de jogo: componente do jogo. Graças à biblioteca Libretro, o sistema é capaz de rodar o jogo dentro do mesmo processo e interceptar internamente mídia e fluxo de entrada.
    • Os frames do jogo são capturados e enviados para o codificador.
    • Codificador de imagem/áudio: um pipeline de codificação que pega quadros de mídia, os codifica em segundo plano e gera imagens/áudio codificados.

    Implementação

    CloudRetro depende do WebRTC como tecnologia de backbone, portanto, antes de mergulhar nos detalhes da implementação do Golang, decidi falar sobre o próprio WebRTC. Esta é uma tecnologia incrível que me ajudou muito a alcançar latência inferior a um segundo para streaming de dados.

    WebRTC

    O WebRTC foi projetado para fornecer conexões ponto a ponto de alta qualidade em aplicativos móveis e navegadores nativos usando APIs simples.

    Percurso NAT

    WebRTC é conhecido por sua funcionalidade NAT Traversal. WebRTC foi projetado para comunicação ponto a ponto. Seu objetivo é encontrar a rota direta mais adequada, evitando gateways NAT e firewalls para comunicação peer-to-peer através de um processo denominado ICE. Como parte desse processo, as APIs WebRTC encontram seu endereço IP público usando servidores STUN e o encaminham para o servidor de retransmissão (VIRAR) quando uma conexão direta não pode ser estabelecida.

    No entanto, CloudRetro não explora totalmente esse recurso. Suas conexões ponto a ponto não existem entre usuários, mas entre usuários e servidores em nuvem. O lado servidor do modelo tem menos restrições de comunicação direta do que um dispositivo de usuário típico. Isso permite pré-abrir portas de entrada ou usar endereços IP públicos diretamente, já que o servidor não está atrás de NAT.

    Anteriormente, eu queria transformar o projeto em uma plataforma de distribuição de jogos para Cloud Gaming. A ideia era permitir que criadores de jogos fornecessem jogos e recursos de streaming. E os usuários interagiriam diretamente com os provedores. Dessa forma descentralizada, CloudRetro é apenas uma estrutura para conectar recursos de streaming de terceiros aos usuários, tornando-o mais escalável quando não estiver mais hospedado. A função do WebRTC NAT Traversal aqui é muito importante para facilitar a inicialização da conexão ponto a ponto em recursos de streaming de terceiros, facilitando a conexão do criador à rede.

    Compressão de vídeo

    A compressão de vídeo é uma parte indispensável do pipeline e contribui muito para um fluxo suave. Embora não seja necessário conhecer todos os detalhes da codificação de vídeo VP8/H264, compreender os conceitos pode ajudá-lo a entender as opções de velocidade de streaming de vídeo, depurar comportamentos inesperados e ajustar a latência.

    A compactação de vídeo para um serviço de streaming é um desafio porque o algoritmo deve garantir que o tempo total de codificação + tempo de transmissão da rede + tempo de decodificação seja o mais baixo possível. Além disso, o processo de codificação deve ser consistente e contínuo. Algumas compensações de codificação não se aplicam – por exemplo, não podemos favorecer tempos de codificação longos em vez de tamanhos de arquivo e tempos de decodificação menores, ou usar compactação inconsistente.

    A ideia por trás da compactação de vídeo é eliminar informações desnecessárias e, ao mesmo tempo, manter um nível aceitável de precisão para os usuários. Além de codificar quadros de imagem estáticos individuais, o algoritmo infere o quadro atual dos quadros anteriores e seguintes, de forma que apenas a diferença entre eles é enviada. Como pode ser visto no exemplo do Pacman, apenas pontos diferenciais são transmitidos.

    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Comparação de frames de vídeo usando Pacman como exemplo

    Compressão de áudio

    Da mesma forma, o algoritmo de compressão de áudio omite dados que não podem ser percebidos por humanos. Opus é atualmente o codec de áudio com melhor desempenho. Ele foi projetado para transmitir uma onda de áudio por meio de um protocolo de datagrama ordenado, como RTP (Real Time Transport Protocol). Sua latência é menor que mp3 e aac, e a qualidade é maior. A latência geralmente é em torno de 5 ~ 66,5 ms.

    Pion, WebRTC em Golang

    Penhor é um projeto de código aberto que traz WebRTC para Golang. Em vez do empacotamento usual de bibliotecas C++ WebRTC nativas, Pion é uma implementação Golang nativa de WebRTC com melhor desempenho, integração Go e controle de versão em protocolos WebRTC.

    A biblioteca também permite streaming com vários recursos integrados excelentes com latência inferior a um segundo. Possui implementação própria de STUN, DTLS, SCTP, etc. e alguns experimentos com QUIC e WebAssembly. Esta biblioteca de código aberto em si é um recurso de aprendizagem realmente bom, com excelente documentação, implementações de protocolo de rede e exemplos interessantes.

    A comunidade Pion, liderada por um criador muito apaixonado, é bastante animada, com muitas discussões de qualidade sobre WebRTC. Se você está interessado nesta tecnologia, junte-se http://pion.ly/slack – você aprenderá muitas coisas novas.

    Escrevendo CloudRetro em Golang

    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Implementação de um trabalhador em Go

    Vá para os canais em ação

    Graças ao belo design do canal do Go, os problemas de streaming e simultaneidade de eventos são bastante simplificados. Como no diagrama, diferentes GoRoutines possuem vários componentes rodando em paralelo. Cada componente gerencia seu estado e se comunica por meio de canais. A afirmação seletiva de Golang força um evento atômico a ser processado toda vez no jogo (game tick). Isto significa que nenhum bloqueio é necessário para este projeto. Por exemplo, quando um usuário salva, é necessário um instantâneo completo do estado do jogo. Este estado deve permanecer contínuo, efetuando login até que o salvamento seja concluído. Durante cada tick do jogo, o back-end só pode lidar com uma operação de salvamento ou entrada, tornando o thread do processo 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
    	}
        }
    }

    Fan-in/Fan-out

    Este modelo Golang se encaixa perfeitamente no meu caso de uso CrowdPlay e Multiple Player. Seguindo esse padrão, todas as entradas do usuário em uma sala são incorporadas ao canal de entrada central. A mídia do jogo é então distribuída para todos os usuários na mesma sala. Desta forma, conseguimos a divisão do estado do jogo entre várias sessões de jogo de diferentes utilizadores.

    Jogos em nuvem de código aberto no WebRTC: p2p, multijogador, latência zero
    Sincronização entre diferentes sessões

    Desvantagens do Golang

    Golang não é perfeito. O canal é lento. Comparado ao bloqueio, o canal Go é simplesmente uma maneira mais fácil de lidar com eventos simultâneos e encadeados, mas o canal não oferece o melhor desempenho. Existe uma lógica de bloqueio complexa abaixo do canal. Por isso fiz alguns ajustes na implementação, reaplicando bloqueios e valores atômicos na substituição de canais para otimizar o desempenho.

    Além disso, o coletor de lixo em Golang não é gerenciado, o que às vezes causa pausas suspeitamente longas. Isso interfere muito no aplicativo de streaming em tempo real.

    CGO

    O projeto usa a biblioteca Golang VP8/H264 de código aberto existente para compactação de mídia e Libretro para emuladores de jogos. Todas essas bibliotecas são simplesmente wrappers da biblioteca C em Go usando CGO. Algumas das desvantagens estão listadas em esta postagem de Dave Cheney. Problemas que encontrei:

    • incapacidade de travar no CGO, mesmo com Golang RecoveryCrash;
    • falha em identificar gargalos de desempenho quando não conseguimos detectar problemas detalhados no CGO.

    Conclusão

    Atingi meu objetivo de compreender os serviços de jogos em nuvem e criar uma plataforma que me ajudasse a jogar jogos retrô nostálgicos com meus amigos online. Este projeto não teria sido possível sem a biblioteca Pion e o apoio da comunidade Pion. Estou extremamente grato por seu intenso desenvolvimento. As APIs simples fornecidas pelo WebRTC e Pion garantiram uma integração perfeita. Minha primeira prova de conceito foi lançada na mesma semana, embora eu não tivesse nenhum conhecimento prévio de comunicação ponto a ponto (P2P).

    Apesar da facilidade de integração, o streaming P2P é de fato uma área muito complexa na ciência da computação. Ela tem que lidar com a complexidade de arquiteturas de rede de longa data, como IP e NAT, para criar uma sessão ponto a ponto. Enquanto trabalhava neste projeto, adquiri muito conhecimento valioso sobre rede e otimização de desempenho, por isso incentivo todos a tentarem construir produtos P2P usando WebRTC.

    CloudRetro atende a todos os casos de uso que eu esperava da minha perspectiva como jogador retrô. No entanto, acho que há muitas áreas no projeto que posso melhorar, como tornar a rede mais confiável e com melhor desempenho, fornecer gráficos de jogo de maior qualidade ou a capacidade de compartilhar jogos entre usuários. Estou trabalhando duro nisso. Por favor segue projeto e apoie-o se você gostar.

Fonte: habr.com

Adicionar um comentário