Como fabricamos o motor e o jogo por um ano e meio. Parte Dois A infraestrutura

Primeiro, alguns comentários sobre os traços do artigo anterior. Realmente costumávamos trabalhar na Wargaming , onde desenvolvemos um mecanismo conhecido como dava.framework ou dava.engine . Portanto, muitos colegas antigos, com quem ainda mantemos boas relações, participam ativamente da discussão.

Várias pessoas têm dúvidas: é a mesma tecnologia ou outra? Resposta: esta é uma nova tecnologia escrita do zero.

Como conseguimos em apenas um ano? Nossa equipe tem uma vasta experiência. Muitos desenvolvem motores e jogos há mais de 15 anos.

Por que, a partir do zero, se você pudesse usar nosso mecanismo antigo, que também está no código aberto? Ele tem cerca de 10 anos e a maior parte do código está desatualizada. Até as melhores partes do motor, das quais nos orgulhamos, às vezes continham partes do código e alguns rudimentos de 5, 7 e às vezes até 10 anos atrás. Muitas soluções arquitetônicas foram projetadas para dispositivos da época - começando com um iPhone 3G. Agora, focamos pelo menos no iPad Air 1 e similares em dispositivos Android avançados. Consequentemente, as abordagens mudaram um pouco.

E a pergunta mais comum: por que possuir um motor? No último artigo, houve vários argumentos de diferentes graus de persuasão. Quero me concentrar no principal: apenas nossa própria tecnologia pode permitir que você aproveite ao máximo o ferro, faça o número máximo de otimizações especificamente para sua jogabilidade, estilo visual. Nos posicionamos, inclusive como empresa de tecnologia, não apenas como desenvolvedor de jogos. Acreditamos que, com nosso nível de engenheiros e nossa experiência, podemos competir seriamente no mercado de produtos móveis de alta tecnologia.

E agora ao ponto: que ferramentas e técnicas nos ajudaram a realizar essa tarefa bastante ambiciosa em pouco tempo?

A infraestrutura


Escolhemos o Atlassian Bitbucket Server + Jenkins. No Bitbucket está o repositório principal (mestre), ao qual o Jenkins está conectado. Cada desenvolvedor tem seu próprio garfo. Para cada tarefa, uma nova bifurcação é criada na bifurcação, que é integrada de volta através da solicitação de extração. Em geral, o esquema é bastante padrão. Cada solicitação é submetida a uma revisão obrigatória e a testes automáticos. E, se for bem-sucedido, ele se funde automaticamente no mestre.

Jenkins


Jenkins tem várias deficiências: ele é um focinho da web antigo, não muito rápido, guloso, parece um portal da Internet dos anos 90. No entanto, sua flexibilidade, um grande número de módulos e de forma gratuita a tornam uma boa escolha, mesmo em 2019. Tendo brincado com os módulos e configurações, você pode obter uma aparência digerível, uma descrição declarativa dos pipelines (no repositório). A propósito, existem cerca de 40 pipelines agora: testes, editores, um jogo para todas as plataformas; trabalhe com infraestrutura e metagame do servidor. Colete todos os 20 buildagentov.

No futuro, é claro, quero experimentar soluções modernas de hipster, por exemplo, GitLab ou TravisCI auto-hospedado. Não consideramos completamente soluções em nuvem (Nevercode, Bitrise, CircleCI, etc.) devido ao grande tamanho de nosso repositório, ativos e, consequentemente, ao tempo de construção e tamanho dos artefatos.

Sistema de compilação


O principal requisito para o sistema era o seguinte: geração de projetos para iOS, MacOS, Android, Windows, Linux em um script. Conseguimos experimentar Premake, SCons, Bazel e CMake. Por várias razões, paramos no CMake, testado pelo tempo.

Nos últimos anos, o CMake tornou-se quase o padrão para bibliotecas C ++. Quase tudo, desde o rapel ao SDL, pode ser conectado ao seu projeto CMake em apenas algumas linhas. É claro que há exceções, como o OpenSSL ou V8, com as quais tive que suar um pouco. No topo do Zmeik nu, desenvolvemos uma pequena estrutura (cerca de 3.000 linhas no total). Principais recursos:
Modularidade. As partes individuais do motor são projetadas como módulos. Por exemplo, som, interface do usuário, física, rede etc. Cada módulo pode ter seus próprios ativos (por exemplo, sombreadores) e pode ter dependências em outros módulos.

O aplicativo final no mecanismo (jogo, editor, utilitários) conecta apenas os módulos necessários. Um pouco distante é o módulo principal, que é uma dependência para a maioria dos outros módulos. O núcleo implementa o ponto de entrada, o ciclo principal do aplicativo, a interação com o sistema operacional e outras entidades básicas.
Módulos de terceiros. Nossa estrutura permite baixar um repositório git ou arquivar em várias linhas, descompactar, compilar, copiar bibliotecas e / ou fontes. Hoje, temos 66 desses módulos de terceiros: análises, formatos de arquivos de terceiros, middleware como física, uma biblioteca de sons etc.

Processo de desenvolvimento


Com base na experiência anterior, decidimos adicionar o mecanismo e o jogo em um repositório. Isso permite que você faça alterações na API do mecanismo sem problemas e adapte o jogo de forma síncrona. O resultado foi o chamado monorepositório, com suas vantagens e desvantagens. Mas, como planejamos imediatamente manter um ritmo muito alto de desenvolvimento, a possibilidade de refatoração síncrona do mecanismo e do jogo superou todas as outras desvantagens dessa solução.

Em média, adicionamos mais de 20 solicitações pull por dia. Isso significa que o mestre pode potencialmente ser quebrado 20 vezes por dia. Felizmente, em 1991, eles criaram a técnica de integração contínua. A que chegamos?

Integração contínua


Como mencionado acima, um brunch é criado para cada tarefa na bifurcação do desenvolvedor. Em seguida, uma solicitação pull é criada desse brunch para o repositório principal. Essa solicitação de recebimento passa por uma série de testes automatizados na Jenkins:

  1. Testes de unidade para todas as plataformas (windows, linux, macos, ios, android). O Googletest é usado como base e o OpenCppCoverage, cujo relatório é verificado por um script python adicional, é usado para verificar a porcentagem de cobertura. Se a porcentagem de cobertura para um arquivo específico for menor que 75%, o teste será considerado com falha. Assim, cobrimos a maioria das classes de mecanismo de baixo nível com testes.
  2. Codeformatter Para código C ++, usamos o formato clang. A formatação do código alterado ocorre primeiro automaticamente ao confirmar na máquina do desenvolvedor e, em seguida, é verificada no teste. Para javascript, usado como linguagem de script, é usado o npm linter.
  3. Testes de ativos. Um grupo bastante grande de testes: da validação de formatos de arquivo à verificação de dependências (por exemplo, verificação da textura usada no nível do jogo).
  4. Testes unitários e funcionais do editor. Uma parte integrante do mecanismo é o editor, onde os níveis de jogo e outros ativos são criados e editados. Além dos testes de unidade, o froglogic Squish for Qt é usado para testar o editor - um utilitário para teste automático da GUI. Tudo isso nos permite fazer o teste manual do editor. Além disso, de acordo com as críticas de artistas e designers de níveis, o nível de qualidade e estabilidade é superior ao da empresa anterior, quando tivemos uma equipe de cinco testadores. Ao mesmo tempo, os lançamentos ocorrem diariamente e, com testes manuais, os lançamentos ocorrem a cada 2 semanas.
  5. Testes funcionais do jogo. É claro que eu quero usar testes funcionais automáticos para o jogo. Portanto, começamos a desenvolver o seguinte sistema:

  • O aplicativo de teste (especificamente - um script python) inicia um servidor de jogos e um cliente com certos parâmetros
  • servidor e cliente em execução abrem a porta de rede,
  • O aplicativo de teste se conecta a eles e envia comandos: faça o download de um mapa, selecione um personagem e armas, vá para um ponto, mire, atire, etc.
  • a própria sintaxe de teste é python pytest. Este sistema está atualmente em desenvolvimento ativo.

A maioria dos projetos para testes é coletada com o sinalizador "tratar avisos como erros" ativado e na plataforma MacOS com o clang AddressSanitizer ativado adicionalmente, o que permite detectar ainda mais erros no estágio de preparação da solicitação de recebimento.

Além dos testes, cada solicitação de recebimento é revisada por pelo menos dois outros desenvolvedores e, se necessário, é enviada para revisão. Quando todos os testes estiverem concluídos e os revisores não tiverem comentários, o pedido de solicitação é congelado automaticamente.
Como alguns testes podem levar um tempo considerável (por exemplo, um teste completo do editor da GUI dura mais de uma hora), um script abreviado é usado em solicitações pull. O conjunto completo de testes é iniciado no assistente a cada 4 horas.

Até o momento, 6.600 pull-quests foram criadas e realizadas dessa maneira.



Entrega contínua


Usamos o conceito de lançamentos diários automáticos (ou melhor, diários). Como exatamente isso acontece:

  1. a tag git é criada,
  2. executa versões completas de todos os testes,
  3. se for bem-sucedido, os artefatos são coletados:

  • editores para MacOS e Windows. Assim, todas as manhãs, todos têm uma nova versão das ferramentas. E, graças aos testes automáticos, estamos confiantes em sua certa qualidade e estabilidade.
  • cliente e servidor do jogo para todas as plataformas. O cliente para iOS é carregado para o TestFlight, para Android - para o Google Play, outras plataformas - para o JFrog Artifactory, servidores de jogos e outros serviços - para a nuvem. Ou seja, todas as manhãs, temos uma nova versão do jogo, pronta para testes e playtests.

Obviamente, nem todo lançamento noturno termina com sucesso. Alguns testes podem falhar ou ocorrerá um erro crítico na inicialização do aplicativo. Nesse caso, os problemas encontrados são reparados pelo desenvolvedor de plantão durante o dia e o processo de liberação é reiniciado.
Existem vários em serviço todos os dias:

  1. Atendente de 1º nível. Monitora a estabilidade dos testes no repositório principal.
  2. Atendente de segundo nível no jogo. Reparando bugs do jogo.
  3. Atendente de segundo nível em editores. Corrige bugs editoriais, aconselha usuários (artistas, designers de níveis, designers de jogos).

Também no dia de serviço, você pode se envolver em dívidas técnicas: adicione os testes ausentes para a funcionalidade antiga, complemente a documentação ou faça refatoração, o que não leva tempo com o planejamento de liberação usual.



No próximo artigo, examinaremos mais de perto a arquitetura de software do próprio mecanismo, bem como os principais módulos e subsistemas.

Para continuar ...

A primeira parte: habr.com/en/post/461623

Source: https://habr.com/ru/post/pt465343/


All Articles