Игровая индустрия не стоит на месте и с каждым днём развивается всё быстрее и быстрее. Вместе с ростом индустрии растёт и сложность разработки: кода становится больше и багов в нём тоже становится больше. Поэтому современные игровые проекты нуждаются в особом внимании к качеству кода. Сегодня мы расскажем об одном из способов сделать ваш код лучше – статическом анализе, а также о том, как PVS-Studio на практике помогает в разработке крупных (и не только) игровых проектов.
"The most important thing I have done as a programmer in recent years is to aggressively pursue static code analysis. Even more valuable than the hundreds of serious bugs I have prevented with it is the change in mindset about the way I view software reliability and code quality." – Джон Кармак
Мы работаем с крупными игровыми разработчиками уже много лет и за это время успели сделать много интересного и полезного для игровой индустрии. Это совсем не удивительно, учитывая список наших клиентов из игровой индустрии. Мы ведём активную поддержку наших клиентов: помогаем им интегрировать PVS-Studio в их собственный процесс разработки, исправляем найденные анализатором ошибки и даже делаем специальные фичи на заказ.
Помимо этого, мы много занимаемся и самостоятельным развитием анализатора в GameDev-направлении, а также популяризируем PVS-Studio, рассказывая людям об интересных ошибках, которые он нашел в различных видеоиграх.
Естественно, не обошлось и без интересных историй. О нескольких таких историях и пойдёт речь в этой статье.
PVS-Studio и Unity
Одним из способов, с помощью которых мы продвигаем наш продукт, является написание статей о проверке открытых проектов. От этих статей получают пользу все: читатель может посмотреть на интересные ошибки в знакомом проекте и узнать для себя что-то новое, мы получаем возможность показать работу PVS-Studio на реальном коде, а разработчики проектов могут узнать об ошибках и заблаговременно их исправить.
Наше первое серьезное знакомство с Unity началось в 2016 году, когда разработчики этого игрового движка выложили в свой официальный репозиторий исходный код нескольких компонентов, библиотек и демок. Естественно, что мимо такого "вкусного" случая мы пройти не смогли и захотели написать статью о проверке выложенного кода.
Тогда мы обнаружили, что код Unity3D (в то время движок назывался именно так) имеет весьма высокое качество, однако и в нём нам удалось найти достаточно много серьёзных ошибок для написания статьи.
Спустя два года случилось еще одно событие – на этот раз разработчики Unity выложили в открытый доступ код самого движка и редактора. И так же, как и в предыдущий раз, мы не смогли пройти мимо и проверили исходный код движка. И не зря: интересных ошибок мы так же нашли целую горсть.
Однако написание статей – это далеко не всё. Мы продолжаем работать над PVS-Studio, и GameDev является для нас одним из важнейших направлений для развития. Поэтому мы хотим, чтобы разработчики игр на Unity могли получить как можно более качественный анализ своих проектов.
Одним из шагов по улучшению качества анализа Unity-проектов для нас стало написание аннотаций для методов, определённых в Unity Scripting API.
Аннотации методов – это особый механизм, использующийся в PVS-Studio. Он позволяет предоставить анализатору всю необходимую информацию о каком-нибудь конкретном методе. Она прописывается в специальном коде самими разработчиками анализатора (то есть, нами же).
Эта информация может быть совершенно различного рода. Например: как метод может повлиять на переданные ему параметры, может ли он выделить память и возвращает ли он значение, которое обязательно нужно обработать. Таким образом, аннотирование позволяет анализатору лучше понимать логику работы методов, тем самым позволяя обнаруживать новые и более сложные ошибки.
Нами уже написано огромное множество различных аннотаций (например, для методов из пространства имён System), и мы были рады пополнить их аннотациями методов из Unity Scripting API.
Мы начали пополнение списка аннотаций с оценки. Сколько всего существует методов? Какие именно стоит проаннотировать первыми? Всего методов оказалось очень много, и мы решили для начала проаннотировать наиболее часто используемые методы.
Поиск популярных методов производился следующим образом: сначала мы собрали пул проектов с GitHub, использующих возможности Unity, после чего с помощью самописной утилиты (основанной на Roslyn) подсчитали вызовы интересующих нас методов. В итоге мы получили список классов, методы которых используются наиболее часто:
- UnityEngine.Vector3
- UnityEngine.Mathf
- UnityEngine.Debug
- UnityEngine.GameObject
- UnityEngine.Material
- UnityEditor.EditorGUILayout
- UnityEngine.Component
- UnityEngine.Object
- UnityEngine.GUILayout
- UnityEngine.Quaternion
- ...
Далее осталось проаннотировать методы этих классов. Мы создали тестовый проект и "закопались" в документацию, чтобы получить как можно больше информации об этих методах. Например, мы пробовали передавать null в качестве различных аргументов, чтобы посмотреть, как поведёт себя программа.
Во время таких проверок периодически обнаруживалась интересная незадокументированная информация – и мы даже нашли парочку интересных багов в движке. Так, при запуске подобного кода:
MeshRenderer renderer = cube.GetComponent<MeshRenderer>();
Material m = renderer.material;
List<int> outNames = null;
m.GetTexturePropertyNameIDs(outNames);
крашится непосредственно сам редактор Unity (по крайней мере, в версии 2019.3.10f1). Вряд ли, конечно, кто-то станет писать подобный код, но интересен сам факт того, что редактор Unity можно "повалить" запуском подобного скрипта.
Итак, аннотации написаны. После запуска анализа мы сразу обнаружили новые срабатывания. Например, анализатор обнаружил странный вызов метода GetComponent:
void OnEnable()
{
GameObject uiManager = GameObject.Find("UIRoot");
if (uiManager)
{
uiManager.GetComponent<UIManager>();
}
}
Предупреждение анализатора: V3010 The return value of function 'GetComponent' is required to be utilized. — ADDITIONAL IN CURRENT UIEditorWindow.cs 22
Метод GetComponent даже своим названием подразумевает возвращение определённого значения. Логично предположить, что это самое значение должно быть как-то использовано. Теперь (благодаря новой аннотации) анализатор знает, что подобный "бесхозный" вызов этого метода может свидетельствовать о логической ошибке, и предупреждает вас об этом.
Это далеко не единственное срабатывание, появившееся в наборе наших тестовых проектов после добавления новых аннотаций – я не буду приводить остальные, чтобы не делать эту статью слишком большой. Главное, что теперь разработка Unity-проектов с использованием PVS-Studio позволяет писать гораздо более безопасный и чистый код без багов.
Если вам хочется подробнее прочитать про нашу работу с аннотациями для Unity-методов, вы можете сделать это в нашей статье: Как анализатор PVS-Studio стал находить ещё больше ошибок в проектах на Unity.
Unreal Engine 4
Когда в далёком 2014-м году разработчики Unreal Engine 4 выложили исходный код движка в открытый доступ, мы просто не смогли обойти этот проект стороной и также написали о нём статью. Разработчикам движка понравилась статья и они исправили найденные нами ошибки. Но нам было этого мало, и мы решили попробовать продать компании Epic Games лицензию на наш анализатор.
Компания Epic Games была заинтересована в улучшении своего движка с помощью PVS-Studio, поэтому мы сошлись на соглашении: мы своими силами исправляем код Unreal Engine так, чтобы анализатор не выдавал на него ни одного предупреждения, а ребята из Epic Games покупают нашу лицензию и дополнительно вознаграждают нас за проделанную работу.
Зачем было нужно исправлять все предупреждения? Дело в том, что максимальную пользу от статического анализа можно получить, исправляя ошибки в момент их появления. А когда вы впервые проверяете свой проект, то, как правило, вы получаете несколько сотен (а иногда и тысяч) предупреждений. Среди всех этих срабатываний анализатора легко могут затеряться предупреждения, выданные на свеженаписанный код.
На первый взгляд эта проблема решается достаточно легко: нужно всего лишь сесть и полноценно обойти весь отчёт, постепенно исправляя ошибки. Однако этот способ хоть и более интуитивен, он может потребовать времени. Куда более удобным и быстрым является метод использования suppress-файлов.
Suppress-файлы – это специальная функция PVS-Studio, позволяющая "спрятать" срабатывания анализатора в специальный файл. При этом спрятанные предупреждения не будут появляться в последующих логах: их можно будет посмотреть отдельно.
Получив большое количество срабатываний после первой проверки, вы можете в пару кликов добавить все обнаруженные срабатывания в suppress-файл, и тогда при следующей проверке анализатором вы получите чистый лог без единого срабатывания.
Теперь, когда старые предупреждения больше не попадают в лог, вы сможете легко обнаружить новое предупреждение сразу в момент его появления. Написали код –> проверили анализатором –> увидели новое предупреждение –> исправили ошибку. Именно так вы получите максимальную пользу от использования анализатора.
При этом не стоит забывать про срабатывания, которые находятся в suppress-файле: в них всё так же, как и раньше, могут находиться предупреждения о серьёзных ошибках и уязвимостях. Поэтому стоит периодически возвращаться к этим предупреждениям и уменьшать их количество.
Этот сценарий, конечно, удобен, но разработчики из Epic Games хотели, чтобы их код был исправлен сразу, и передали это дело нам.
И мы приступили к работе. Проверив код проекта, мы обнаружили 1821 одно предупреждение уровней Level_1 и Level_2. Разбор такого объёма предупреждений требует серьезной работы, и, чтобы облегчить весь этот процесс, мы настроили непрерывный анализ кода на нашем CI-сервере.
Выглядело это так: каждую ночь на нашем сервере собиралась актуальная версия Unreal Engine 4, и сразу после сборки автоматически запускался анализ. Таким образом, когда наши ребята приходили утром на работу, у них всегда был на руках свежий отчёт анализатора, позволяющий отследить прогресс по исправлению предупреждений. Плюс такая система позволяла в любой момент проверить стабильность сборки, запустив её на сервере вручную.
Весь процесс занял у нас 17 рабочих дней. График исправления предупреждений получился следующий:
На самом деле, этот график не полностью отражает нашу работу. После того, как мы исправили все предупреждения, мы еще в течение двух дней ждали, когда они примут наши последние пулл-реквесты. Все это время у нас продолжала работать автоматическая проверка последней версии Unreal Engine, который, в свою очередь, продолжал пополняться новым кодом. И что бы вы думали? За эти два дня PVS-Studio обнаружил в коде еще целых четыре ошибки! Одна из них была особенно серьезной и потенциально могла привести к неопределённому поведению.
Конечно, и эти ошибки мы тоже исправили. Теперь разработчикам Unreal Engine осталось только одно: настроить у себя автоматический анализ так же, как это сделали мы. С этого момента они стали каждый день видеть предупреждения, которые были выданы на только что написанный код. Это позволило им исправлять ошибки в коде еще в момент их появления – на самых ранних этапах разработки.
Подробнее о том, как мы работали над кодом Unreal Engine, вы можете прочитать в официальном блоге Unreal Engine или на нашем сайте.
Анализ различных игр
Я уже говорил, что мы проверяем различные открытые проекты и пишем о них статьи? Так вот, подобных статей про игровые проекты у нас накопилась просто целая уйма! Мы писали про такие игры, как VVVVVV, Space Engineers, Command & Conquer, osu! и даже (очень древняя статья) Doom 3. А еще мы составили топ 10 самых интересных программных ошибок из видеоигровой индустрии.
Мы также проверили, пожалуй, большинство известных движков с открытым исходным кодом. Помимо Unity и Unreal Engine 4, под наш прицел попали такие проекты, как Godot, Bullet, Amazon Lumberyard, Cry Engine V и многие другие.
Самое приятное из всего этого – это то, что многие из описанных нами багов впоследствии были исправлены самими разработчиками проектов. Приятно чувствовать, что инструмент, который ты разрабатываешь, приносит миру настоящую, видимую и ощутимую пользу.
Вы можете посмотреть список всех наших статей, так или иначе связанных с разработкой видеоигр на специальной странице нашего блога.
Заключение
На этом моя небольшая статья подходит к концу. Желаю вам чистого и корректно работающего кода без багов и ошибок!
Заинтересовала тема статического анализа? Хочется проверить свой проект на наличие ошибок? Попробуйте PVS-Studio.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: George Gribkov. How static code analysis helps in the GameDev industry.