Инди
Blitz Team
2199

Как мы сделали движок и игру на нем за полтора года. Часть вторая. Инфраструктура

Для начала несколько комментариев по следам предыдущей статьи. Мы действительно раньше работали в компании Wargaming, где разрабатывали движок, известный как dava.framework или dava.engine. Поэтому многие старые коллеги, с которыми мы по-прежнему в хороших отношениях, активно участвуют в обсуждении.

В закладки
Аудио

У ряда людей возникли сомнения: это та же технология или другая? Ответ: это новая технология, написанная с нуля.

Как же мы справились всего за год? Наша команда имеет огромный опыт. Многие занимаются разработкой движков и игр более 15-и лет.

Почему с нуля, если можно было взять наш старый движок, который к тому же лежит в open-source? Ему около 10 лет, и большая часть кода устарела. Даже самые лучшие части движка, которыми мы гордимся, местами содержали куски кода и какие-то рудименты 5-и, 7-и и иногда даже 10-ти летней давности. Многие архитектурные решения были рассчитаны на устройства того времени — начиная с айфона 3G. Сейчас же мы ориентируемся минимум на iPad Air 1 и аналогичные ему по мощности Android-устройства. Соответственно и подходы несколько поменялись.

И самый часто встречающийся вопрос: почему собственный движок? В прошлой статье было несколько доводов разной степени убедительности. Хочу сконцентрироваться на главном: только собственная технология может позволить вам получить максимум из железа, сделать максимальное количество оптимизаций именно для вашего геймплея, визуального стиля. Мы позиционируем себя в том числе как технологическую компанию, не только разработчика игр. Мы считаем, что с нашим уровнем инженеров и нашим опытом можем составить серьезную конкуренцию на рынке высокотехнологичных мобильных продуктов.

А теперь к делу: какие инструменты и техники помогли осуществить нам эту довольно амбициозную задачу в сжатые сроки?

Инфраструктура

Мы выбрали Atlassian Bitbucket Server+Jenkins. В Битбакете лежит главный репозиторий (мастер), к которому подключен Дженкинс. У каждого разработчика есть свой форк. Под каждую задачу создается новый бранч в форке, который назад интегрируется через пулл-реквест. В общем, схема довольно стандартная. Каждый пуллреквест проходит обязательное ревью и автоматические тесты. И, в случае успеха, автоматически вливается в мастер.

Дженкинс

Дженкинс обладает рядом недостатков: он древний, не очень быстрый, прожорливый, веб-морда выглядит как интернет портал из 90-х. Однако его гибкость, огромное количество модулей и бесплатность делают его неплохим выбором даже в 2019м. Пошаманив с модулями и настройкой можно добиться удобоваримого внешнего вида, декларативного описания пайплайнов (лежащих в репозитарии). К слову, пайплайнов сейчас около 40: тесты, редакторы, игра под все платформы; работа с серверной инфраструктурой и метагеймом. Собирают все это 20 билдагентов.В перспективе, конечно, хочется попробовать и современные хипстерские решения, к примеру GitLab или self-hosted TravisCI. Полностью облачные решения (Nevercode, Bitrise, CircleCI и т.п.) мы не рассматриваем ввиду большого размера нашего репозитария, ассетов, и, соответственно времени сборки и размера артефактов.

Система сборки

Основное требование к системе было следующим: генерация проекта для iOS, MacOS, Android, Windows, Linux одним скриптом. Мы успели попробовать Premake, SCons, Bazel и CMake. По разным причинам остановились на проверенном временем CMake.В последние годы CMake стал практически стандартом для C++ библиотек. Практически все, начиная от abseil и заканчивая SDL, можно подключить к своему CMake проекту буквально в несколько строк. Есть конечно и исключения, как OpenSSL или V8, с которыми пришлось немного попотеть. Поверх голого Цмейка мы разработали небольшой фреймворк (всего порядка 3000 строк). Основные возможности:Модульность. Отдельные части движка оформлены в виде модулей. Например, звук, UI, физика, сеть и т.п. Каждый модуль может иметь собственные ассеты (например, шейдеры) и может иметь зависимости от других модулей.Конечное приложение на движке (игра, редактор, утилиты) подключает только те модули, которые ей необходимы. Немного особняком стоит модуль core, который является зависимостью для большинства других модулей. Core имплементирует точку входа, главный цикл приложения, взаимодействие с операционной системой и другие базовые сущности.Thirdparty модули. Наш фреймворк позволяет в несколько строк скачать git репозитарий или архив, распаковать, собрать, скопировать библиотеки и/или исходники. На сегодняшний день у нас 66 таких thirdparty модулей: аналитика, сторонние файловые форматы, middleware вроде физики, звуковой библиотеки и т.п.

Процесс разработки

Учитывая предыдущий опыт, мы решили сложить и движок, и игру в один репозитарий. Это позволяет безболезненно вносить изменения в API движка и синхронно адаптировать под него игру. Получился так называемый монорепозитарий с его достоинствами и недостатками. Но, поскольку мы сразу планировали поддержать очень высокий темп разработки, возможность синхронного рефакторинга движка и игры перевесила все остальные недостатки данного решения.В среднем у нас добавляется более 20 пулл-реквестов в день. Это означает, что мастер может быть потенциально сломан 20 раз в день. К счастью, еще в 1991-м году придумали технику Непрерывной интеграции (Continuous Integration). К чему же мы пришли?

Continuous Integration

Как сказано выше, для каждой задачи в форке разработчика создается бранч. Далее создается пулл-реквест из этого бранча в в основной репозитарий. Этот пулл-реквест проходит ряд автоматических тестов на Дженкинсе:

  • Юнит-тесты под все платформы (windows, linux, macos, ios, android). В качестве основы используется googletest, а для проверки процента покрытия — OpenCppCoverage, отчет которого проверяется дополнительным питон-скриптом. Если процент покрытия конкретного файла меньше 75%, тест считается проваленным. Таким образом у нас покрыта тестами большая часть низкоуровневых классов движка.
  • Codeformatter. Для C++ кода используем clang-format. Форматирование измененного кода сначала автоматически происходит при коммите на машине разработчика, а потом проверяется в тесте. Для джаваскрипта, который используется у нас в качестве скриптового языка, используется npm linter.
  • Тесты ассетов. Довольно большая группа тестов: от валидации форматов файлов до проверки зависимостей (например, проверка, что текстура, используемая в игровом уровне, действительно существует).
  • Юнит- и функциональные тесты редактора. Неотъемлемой частью движка является редактор, где создаются и редактируются игровые уровни и прочие ассеты. Кроме юнит-тестов, для тестирования редактора используется froglogic Squish for Qt — утилита для автоматического GUI тестирования. Все это позволяет нам обходиться вообще без ручного тестирования редактора. При этом, по отзывам художников и левелдизайнеров, уровень его качества и стабильности выше, чем в прошлой компании, когда у нас была команда из пяти тестеров. При этом релизы происходят ежедневно, а при ручном тестировании релизы происходили раз в 2 недели.
  • Функциональные тесты игры. Понятно, что автоматическое функциональные тесты хочется использовать и для игры. Поэтому мы начали разработку следующей системы:
  • тестовое приложение (конкретно — скрипт на питоне) запускает игровые сервер и клиент с определенными параметрами
  • запущенные сервер и клиент открывают сетевой порт,
  • тестовое приложение подключается к ним и посылает команды: загрузить карту, выбрать персонажа и оружие, переместиться в точку, прицелиться, выстрелить и т.п.
  • сам синтаксис тестов — питоновский pytest. Эта система сейчас находится в активной разработке.

Большинство проектов для тестов собираются с включенным флагом «treat warnings as errors», а на платформе MacOS с дополнительно включенным clang AddressSanitizer, что позволяет отлавливать еще больше ошибок на этапе подготовки пулл-реквеста.Кроме тестов, каждый пулл-реквест проходит ревью как минимум двумя другими разработчиками и, в случае необходимости, отправляется на доработку. Когда все тесты пройдены и у ревьюверов нет замечаний, пуллреквест автоматически вмерживается.Поскольку некоторые тесты могут занимать значительное время (например, полный GUI тест редактора длится более часа), в пулл-реквестах используется сокращенный сценарий. Полный же набор тестов запускается в мастере каждые 4 часа.На сегодняшний день так создано и вмержено уже 6600 пуллреквестов.

Continuous Delivery

Мы используем концепцию автоматических ежедневных (а точнее еженочных) релизов. Как именно это происходит:

  • создается git tag,
  • в нем запускаются полные версии всех тестов,
  • в случае успеха собираются артефакты:
  • редакторы для MacOS и Windows. Таким образом, каждое утро у всех есть свежая версия инструментов. И, благодаря автоматическим тестам, мы уверены в их определенном качестве и стабильности.
  • игровые клиент и сервер для всех платформ. Клиент для iOS заливается в TestFlight, для Android — в Google Play, остальные платформы — в JFrog Artifactory, игровые сервера и прочие сервисы — в облако. То есть каждое утро у нас есть свежая версия игры, готовая для тестирования и плейтестов.

Конечно, не каждый ночной релиз заканчивается успешно. Какие-то тесты могут не пройти, или на запуске приложения возникнет критическая ошибка. В этом случае найденные проблемы чинятся дежурным разработчиком в течение дня и процесс релиза запускается заново. Дежурных каждый день несколько:

  • Дежурный 1го уровня. Следит за стабильностью тестов в главном репозитарии.
  • Дежурный 2го уровня по игре. Чинит игровые баги.
  • Дежурный 2го уровня по редакторам. Чинит редакторные баги, консультирует пользователей (художников, левелдизайнеров, геймдизайнеров).

Также в день дежурства можно заниматься техническим долгом: дописать недостающие тесты на старую функциональность, дополнить документацию, или сделать рефакторинг, на который не находится времени при обычном планировании релизов.

В следующей статье мы подробнее рассмотрим программную архитектуру самого движка, а также основные модули и подсистемы.

Продолжение следует…

Первая часть:

Материал опубликован пользователем.
Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Blitz Team", "author_type": "self", "tags": [], "comments": 34, "likes": 30, "favorites": 83, "is_advertisement": false, "subsite_label": "indie", "id": 66867, "is_wide": false, "is_ugc": true, "date": "Fri, 30 Aug 2019 11:24:08 +0300", "is_special": false }
0
{ "id": 66867, "author_id": 145455, "diff_limit": 1000, "urls": {"diff":"\/comments\/66867\/get","add":"\/comments\/66867\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/66867"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64960, "last_count_and_date": null }
34 комментария
Популярные
По порядку
Написать комментарий...
8

Кажется, это должно быть в "Gamedev", не?

Ответить
1

*шутка, не стреляйте*
Статья распубликована, blitz team забанен, редактор DTF выкладывает новую статью про создание движка Blitz

А надо было ссылки добавлять

Ответить
–1

А почему все белые и девушки всего две? Непорядок.

Ответить
3

Там 4 девушки минимум, а скорее всего 5. 5ая скорее всего это рыжая справа сверху, там просто очень маленький кусок не позволяющий произвести идентификацию, но парни патлачи обычно не красят волосы и так челку не зачесывают

Ответить
0

Бля. Как этот комментарий оказался раньше моего?! Я же так старался, считал...

Ответить
0

Даже двух девушек достаточно, чтобы обвинить кого-то в домогательствах

Ответить
–1

это вам не пендосия

Ответить
1

Мы считаем, что с нашим уровнем инженеров и нашим опытом можем составить серьезную конкуренцию на рынке высокотехнологичных мобильных продуктов.

Движок только для мобильных дрочилен?

Ответить
1

Конкуренты Юнити)

Ответить
0

Любая платформа, поддерживающая их стэк.

Ответить
1

Это только наши так любят в метапрограммирование? Стопиццот кастомных инструментов, сервисов и фреймворков, а игры то где?! Вы там 40 человек для друг друга программируете?

Ответить
2

Это обычная игфраструктура для CD/CI пайплайна.
2 вида сборки - релиз и дебаг.
N платформ.
Патчи, юнит тесты, автотесты и прочее.
Без этого никак. У них миллион строк кода и они должны гарантировать доставку фич и фиксов до клиента, а не до ближайшего репозитория. У всех крупных так.

Ответить
1

Давайте без этих вот "миллион сток кода". Зачем? Зачем столько всего? Для чего все это, Билли?

Ответить
–1

LOC - 1382000 - 100%
====================================
TOOLS - 225000 - 16%
SRC - 1150000 - 84%
====================================
AI - 20000 - 1%
GAME BACKEND - 35000 - 2%
GRAPHICS BACKEND - 260000 - 18%
GUI - 80000 - 5%
LIB WRAPPERS - 230000 - 16%
INPUT - 20000 - 1%
MATH - 50000 - 3%
PHYSICS - 90000 - 6%
DATA LOADERS - 80000 - 5%
SCENE - 200000 - 14%
SOUND - 25000 - 1%
SYSTEM WRAPPERS - 45000 - 3%
====================================
GAME FRONTEND - ~500000

Вот UE4 тебе расписал. Да, я знаю, что 90% этого кода отрабатывает 1 раз при запуске игры. Но это не меняет сути.

Ответить
1

3 года делаю игры на UE4, и мелкие и крупные, и в команде и в соло... Кто все эти люди?! Что это за список Шиндлера? Вот поэтому у нас на собеседованиях спрашивают про три отличительных фичи ООП, а не по делу.

Слов для вас не хватает.

Ответить
1

Так, стоп, это не варгеи? Название, конечно, выбрали до степени смешения ВГшное, отлично делаете.

Ответить
1

А какая разница варгейм или нет? Если движок будет нормальным, то какая разница!?

Ответить
0

С менеджментом у ВГ большие проблемы

Ответить
1

Слегка риторический вопрос: нафига кросспостить свои статьи на хабр (https://habr.com/ru/post/461623/) и не отвечать на вопросы в комментах? Сомнительная самореклама.

Ответить
0

Как дать всем понять, что вы бохатая студия? Использовать коробки от MacbookPro вместо ноги у монитора.

Ответить
0

Наоборот. Им не хватило денег на кронштейны.

Ответить
0

Что за мониторы у вас на первом фото ?)

Ответить
0

DELL U27 и тд.

Ответить
0

Однако.

Ответить
0

Учитывая предыдущий опыт, мы решили сложить и движок, и игру в один репозитарий

F

Ответить
0

Ничоси процесс разработки! о_О

Ответить
0

че такой офис тухлый?

Ответить
0

Я сперва прочитал «за полтора часа» и такой думаю - чего??? :)
А вообще круто, сам занимался созданием собственного движка, в качестве фана. Но в какой момент работа снесла все планы лавиной:)

Ответить
0

Может стоит ссылку на первую часть в начало статьи перенести?

Ответить
0

Серьезный вопрос - на сколько продуктивнее идет процесс разработки при использовании слова "репозитАрий"?

Ответить
–1

Четыре девушки на 37 белых мужчин (если не сбился со счета). Просто патриархальный плевок в лицо! Считаете, женщины не способны в C++ и форки контроля версий?

Ответить
0

Способны, просто не хотят

Ответить
0

5 вроде.

Ответить

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovz", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } } ]