Серьёзные ошибки в коде CryEngine V
В мае 2016 года немецкая компания Crytek решила опубликовать на GitHub исходный код игрового движка CryEngine V. Проект находится в стадии активной разработки, что влечёт за собой появление множества ошибок в коде. Мы уже проверяли проект с помощью PVS-Studio для Windows, а теперь смогли проверить проект с помощью PVS-Studio для Linux. Материала снова набралось на большую статью с описанием только очень серьёзных ошибок.
Введение
CryEngine — игровой движок, созданный немецкой компанией Crytek в 2002 году, и первоначально используемый в шутере от первого лица Far Cry. На CryEngine разных версий сделано много отличных игр от нескольких игровых студий, которые лицензировали движок: Far Cry, Crysis, Entropia Universe, Blue Mars, Warface, Homefront: The Revolution, Sniper: Ghost Warrior, Armored Warfare, Evolve и многие другие. В марте 2016 года компания Crytek анонсировала выход своего нового движка CryEngine V и вскоре опубликовала исходный код на GitHub.
Для проверки открытого исходного кода использовался статический анализатор PVS-Studio версии 6.14 для Linux. Теперь разработчикам кроссплатформенных проектов стало ещё удобнее следить за качеством кода с помощью одного инструмента анализа кода. Скачать версию для Linux можно в виде архива или пакета для пакетного менеджера. Для большинства дистрибутивов можно настроить установку и обновление, используя наш репозиторий.
В статью вошли предупреждения общего назначения и только уровня достоверности «High» (есть ещё Medium и Low). Честно говоря, я не осилил досмотреть внимательно и все «High» предупреждения, т.к. почти сразу насобирал ошибок для статьи при быстром просмотре. За проект я брался несколько раз и долго не мог найти время написать обзорную статью, поэтому про приведённые баги могу сказать, что они живут в коде уже не один месяц. Также некоторые ошибки не исправили из предыдущей статьи о проверке проекта.
Скачать и проверить исходный код в Linux было очень просто. Вот список всех необходимых команд:
Файл отчёта cryengine_ga.tasks можно открыть и просмотреть в QtCreator. Что же удалось найти в исходном коде CryEngine V?
Несчастная функция Active()
V501 There are identical sub-expressions to the left and to the right of the ‘==’ operator: bActive == bActive LightEntity.h 124
Из-за опечатки функция ничего не делает. Мне кажется, если бы был конкурс «Мисс Опечатка», то этот фрагмент кода точно бы занял первое место. Думаю, эта ошибка имеет все шансы попасть в раздел «C/C++ bugs of the month».
Но это ещё не всё, вот функция из другого класса:
V501 There are identical sub-expressions ‘m_staticObjects’ to the left and to the right of the ‘||’ operator. FeatureCollision.h 66
Тут в функции IsActive() два раза используется переменная m_staticObjects, хотя рядом есть неиспользуемая переменная m_dynamicObjects. Возможно, в коде хотели использовать именно её.
Тема: CryEngine Error
Опции темы
Поиск по теме
CryEngine Error
P.S. Заранее приношу извинения если вопрос по этой ошибке уже задавали (я не нашел)
CryRenderD3D9.dll скорее всего файл поврежден! пробовал переустановить игру?
это видюха проверено в аа работает норм! сам тестил! Дело не в видюхе скорее всего!
странно! Тогда последний вариант переустановка винды! Может что то там косячит! А другие игры на пример фар край 3 или варфейс норм пашет?
если они открываются («на cryeng 3 работают») норм. то проблема именно в аа!
Все игры идут без проблем в том числе и ММОРПГ и оффлайн игры
значит так варфейс работает, а АА нет. это странно как то!
значит проблема по софтовой части компа! МV С++ от 2005 до 2012 установи может поможет!
Эта ошибка означает,что нету этого файла.
Она может выскочить если вы не корректно скопировал у кого нибудь игру и скинули себе на комп.
После перестановки винды такая фигня может быть.
Но чаще всего это бывает при установке игры просто не устанавливается данный файл.
Скачайте файл CryRenderD3D9.dll
Скопируйте файл CryRenderD3D9.dll в папку с программой или игрой, которая требует этот файл и попробуйте запустить заново игру или программу
Если ошибка все еще не исчезла, скопируйте файл CryRenderD3D9.dll в папку C:\WINDOWS\SYSTEM32
P.s может и не помоч
А еще скорее всего когда запихнешь этот файл куда надо,попросит уже другой dll
А еще бывает ошибка похожая вылетает когда с игро не устанавливаешь DitrctX и microsoft visual,но в данном случае вряд ли.
Тоже dll требует.
Серьёзные ошибки в коде CryEngine V
В мае 2016 года немецкая компания Crytek решила опубликовать на GitHub исходный код игрового движка CryEngine V. Проект находится в стадии активной разработки, что влечёт за собой появление множества ошибок в коде. Мы уже проверяли проект с помощью PVS-Studio для Windows, а теперь смогли проверить проект с помощью PVS-Studio для Linux. Материала снова набралось на большую статью с описанием только очень серьёзных ошибок.
Введение
CryEngine — игровой движок, созданный немецкой компанией Crytek в 2002 году, и первоначально используемый в шутере от первого лица Far Cry. На CryEngine разных версий сделано много отличных игр от нескольких игровых студий, которые лицензировали движок: Far Cry, Crysis, Entropia Universe, Blue Mars, Warface, Homefront: The Revolution, Sniper: Ghost Warrior, Armored Warfare, Evolve и многие другие. В марте 2016 года компания Crytek анонсировала выход своего нового движка CryEngine V и вскоре опубликовала исходный код на GitHub.
Для проверки открытого исходного кода использовался статический анализатор PVS-Studio версии 6.14 для Linux. Теперь разработчикам кроссплатформенных проектов стало ещё удобнее следить за качеством кода с помощью одного инструмента анализа кода. Скачать версию для Linux можно в виде архива или пакета для пакетного менеджера. Для большинства дистрибутивов можно настроить установку и обновление, используя наш репозиторий.
В статью вошли предупреждения общего назначения и только уровня достоверности «High» (есть ещё Medium и Low). Честно говоря, я не осилил досмотреть внимательно и все «High» предупреждения, т.к. почти сразу насобирал ошибок для статьи при быстром просмотре. За проект я брался несколько раз и долго не мог найти время написать обзорную статью, поэтому про приведённые баги могу сказать, что они живут в коде уже не один месяц. Также некоторые ошибки не исправили из предыдущей статьи о проверке проекта.
Скачать и проверить исходный код в Linux было очень просто. Вот список всех необходимых команд:
Файл отчёта cryengine_ga.tasks можно открыть и просмотреть в QtCreator. Что же удалось найти в исходном коде CryEngine V?
Несчастная функция Active()
V501 There are identical sub-expressions to the left and to the right of the ‘==’ operator: bActive == bActive LightEntity.h 124
Из-за опечатки функция ничего не делает. Мне кажется, если бы был конкурс «Мисс Опечатка», то этот фрагмент кода точно бы занял первое место. Думаю, эта ошибка имеет все шансы попасть в раздел «C/C++ bugs of the month».
Но это ещё не всё, вот функция из другого класса:
V501 There are identical sub-expressions ‘m_staticObjects’ to the left and to the right of the ‘||’ operator. FeatureCollision.h 66
Тут в функции IsActive() два раза используется переменная m_staticObjects, хотя рядом есть неиспользуемая переменная m_dynamicObjects. Возможно, в коде хотели использовать именно её.
Долгожданная проверка CryEngine V
Введение
CryEngine — игровой движок, созданный немецкой компанией Crytek в 2002 году и первоначально используемый в шутере от первого лица Far Cry. На CryEngine разных версий сделано много отличных игр от разных игровых студий, которые лицензировали движок: Far Cry, Crysis, Entropia Universe, Blue Mars, Warface, Homefront: The Revolution, Sniper: Ghost Warrior, Armored Warfare, Evolve и многие другие. В марте 2016 года компания Crytek анонсировала выход своего нового движка CryEngine V и вскоре опубликовала исходный код на Github.
Для проверки открытого исходного кода использовался статический анализатор PVS-Studio версии 6.05. Это инструмент для выявления ошибок в исходном коде программ, написанных на языках С, C++ и C#. Единственный верный способ использования методологии статического анализа — регулярная проверка кода на компьютерах разработчиков и build-сервере. Но для демонстрации возможностей анализатора PVS-Studio мы выполняем разовые проверки open-source проектов и пишем обзорные статьи с описанием выявленных ошибок. Впрочем, если проект показался нам интересным, по пришествию одного-двух лет мы можем вновь проверить его. По сути такие повторные проверки ничем не отличаются от разовых, так как в коде накапливается много изменений.
Особое внимание хочу уделить проверке игрового движка Unreal Engine 4. На примере этого проекта мы смогли в подробностях рассказать о правильном применении методологии статического анализа на реальном проекте: от внедрения анализатора в проект до сведения количества предупреждений до нуля с последующим контролем за появлением ошибок на новом коде. Наша работа с проектом Unreal Engine 4 вылилась в сотрудничество с компанией Epic Games, в рамках которого команда PVS-Studio исправила все предупреждения анализатора в исходном коде движка, и была написана совместная статья о проделанной работе, которая опубликована в Unreal Engine Blog (ссылка на русский перевод). Также компания Epic Games приобрела лицензию PVS-Studio для самостоятельного контроля качества кода. К аналогичному сотрудничеству мы готовы и с компанией Crytek.
Структура отчёта анализатора
В этой статье я хочу ответить на несколько часто задаваемых вопросов, связанных с количеством предупреждений и ложных срабатываний. Например, — «Сколько процентов составляют ложные срабатывания?» — или — «Почему в таком большом проекте так мало ошибок?».
Для начала, все предупреждения анализатора PVS-Studio делятся на три уровня важности: High, Medium и Low. Так High уровень содержит высоко критичные предупреждения, с наибольшей вероятностью являющиеся реальными ошибками, а Low уровень — предупреждения с низкой критичностью, либо предупреждения с очень высокой вероятностью ложно-позитивного срабатывания. Стоит помнить, что конкретный код ошибки не обязательно привязывает её к определённому уровню важности, а распределение сообщений по группам сильно зависит от контекста, в котором они были сгенерированы.
Рисунок 1 — Распределение предупреждений по уровням важности в процентном отношении
В статью невозможно уместить описание всех предупреждений и показать соответствующие фрагменты кода. Обычно статья содержит 10-40 примеров ошибок с описанием. Некоторые подозрительные места кода приводятся просто списком. Большинство предупреждений остаются нерассмотренными. В лучшем случае, после уведомления разработчиков, они спрашивают полный отчёт анализатора для детального изучения. Горькая правда заключается в том, что в большинстве проверяемых нами проектов, материала для статьи более чем достаточно после просмотра только предупреждений уровня High. И игровой движок CryEngine V — не исключение. На рисунке 2 представлена структура предупреждений уровня High.
Рисунок 2 — Описание предупреждений High уровня
Результаты проверки
Обидный copy-paste
V501 There are identical sub-expressions to the left and to the right of the ‘-‘ operator: q2.v.z — q2.v.z entitynode.cpp 93
Опечатка в одной цифре, пожалуй, одна из самых обидных опечаток, которую может допустить программист. В этой функции анализатор обнаружил подозрительное выражение (q2.v.z — q2.v.z), в котором с большой вероятностью перепутали переменные q1 и q2.
V501 There are identical sub-expressions ‘(m_eTFSrc == eTF_BC6UH)’ to the left and to the right of the ‘||’ operator. texturestreaming.cpp 919
Другой тип опечаток связан с копированием констант. В данном случае переменная m_eTFSrc два раза сравнивается с константой eTF_BC6UH, на месте которой должна быть любая другая, например, отличающая всего одной буквой — константа eTF_BC6SH.
Вот пример ленивого копирования каскада условных операторов, в одном из случаев которого забыли внести изменения в код.
V517 The use of ‘if (A) <. >else if (A) <. >‘ pattern was detected. There is a probability of logical error presence. Check lines: 970, 974. environmentalweapon.cpp 970
В предыдущем коде была хоть какая-то вероятность того, что лишнее условие — это результат слишком большого количества копий кода и одна проверка осталась просто лишней. В этом фрагменте кода из-за идентичных условных выражений переменная attackStateName никогда не примет значение «Charged Throw».
V519 The ‘BlendFactor[2]’ variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1265, 1266. ccrydxgldevicecontext.cpp 1266
В коде проекта обнаружилась такая вот функция, в которой из-за опечатки в индексе пропущено заполнение элемента с индексом три: BlendFactor[3]. Это место было бы просто одним из интересных мест с опечаткой, если бы анализатор не обнаружил ещё 2 фрагмента, куда скопировали код с допущенной опечаткой:
V519 The ‘m_auBlendFactor[2]’ variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 904, 905. ccrydxgldevicecontext.cpp 905
Вот то самое место, где продолжают пропускать заполнение элемента с индексом ‘3’. На мгновение я даже подумал, что в этом был какой-то смысл, но эта мысль меня быстро покинула, когда в конце функции я увидел обращение ко всем четырём элементам массива m_auBlendFactor. Похоже в файле ccrydxgldevicecontext.cpp просто сделали несколько копий кода с опечаткой.
V523 The ‘then’ statement is equivalent to the ‘else’ statement. d3dshadows.cpp 1410
В заключение раздела про copy-paste привожу описание ещё одной интересной ошибки. Независимо от результата условного выражения, значение m_cEF.m_TempVecs[2][Num] всегда вычисляется по одной формуле. Судя по со соседнему коду этого фрагмента, здесь нет опечатки в индексе, в этом условии хотят заполнить именно элемент с индексом ‘2’. А вот формулу расчёта, скорее всего, хотели использовать разную, но после копирования строки забыли изменить код.
Проблемы с инициализацией
V546 Member of a class is initialized by itself: ‘eConfigMax(eConfigMax)’. particleparams.h 1013
Анализатор обнаружил возможную опечатку, когда поле класса инициализируется с использованием собственного значения.
V603 The object was created but it is not being used. If you wish to call constructor, ‘this->SRenderingPassInfo::SRenderingPassInfo(. )’ should be used. i3dengine.h 2589
Здесь анализатор обнаружил неправильное использование конструктора. Программист, вероятно, подумал, что вызов конструктора таким образом без параметров внутри другого конструктора выполнит инициализацию полей класса, но это не так.
В этом коде произойдёт следующее: будет создан новый неименованный объект типа SRenderingPassInfo и тут же разрушен. В результате поля класса остаются неинициализированными. Одним из способов исправления ошибки будет написание отдельной функции инициализации и вызов её из разных конструкторов.
V688 The ‘m_cNewGeomMML’ local variable possesses the same name as one of the class members, which can result in a confusion. terrain_node.cpp 344
Анализатор обнаружил совпадения имени локальной переменной cNewGeomMML с полем класса. Часто такой код не является ошибкой, но здесь это выглядит очень подозрительно на фоне инициализации других полей класса.
V575 The ‘memset’ function processes ‘0’ elements. Inspect the third argument. crythreadutil_win32.h 294
С помощью анализатора была найдена очень интересная ошибка. При вызове функции memset() перепутали переданные аргументы, в результате чего функцию зовут для заполнения 0 байт памяти. Вот как выглядит прототип функции:
Третьим аргументом должен передаваться размер буфера, а вторым — значение, которым необходимо заполнить буфер.
V630 The ‘_alloca’ function is used to allocate memory for an array of objects which are classes containing constructors. command_buffer.cpp 62
В коде проекта встречаются места, где с помощью функции alloca() выделяют память для массива объектов. В приведённом коде, при таком способе выделения памяти у объектов класса QuatT не будет вызывать ни конструктор, ни деструктор. Подобный код может привести к работе с неинициализированными переменными и другим ошибкам.
В коде проекта встречаются случаи, когда тернарный оператор ?: возвращает одно и то же значение. Возможно, в предыдущем случае так написали для красоты кода, но зачем так сделали в этом фрагменте?
Подозрительное присваивание переменной самой себе. Разработчикам стоит проверить это место.
Приоритеты операций
V502 Perhaps the ‘?:’ operator works in a different way than it was expected. The ‘?:’ operator has a lower priority than the ‘+’ operator. gpuparticlefeaturespawn.cpp 79
Похоже, в этой функции неверно выполняется замер времени. Приоритет оператора сложения выше, чему тернарного оператора ?:, поэтому к spawn.delay сначала прибавляется значение 0 или 1, а в переменную endTime всегда записывается значение spawn.duration или fHUGE. Это довольно распространённая ошибка. Об интересных паттернах ошибок в приоритетах операций, найденных в базе ошибок PVS-Studio, я рассказал в статье: Логические выражения в C/C++. Как ошибаются профессионалы.
V634 The priority of the ‘*’ operation is higher than that of the ‘ Рисунок 3 — Плохое форматирование кода
С таким стилем программирования избежать ошибок на большом объёме практически невозможно. Так, в рассматриваемом случае планировали при определённом условии освобождать память из-под массива объектов и обнулять указатель. Но из-за неправильного форматирования указатель m_parts[i].pMatMapping обнуляется на каждой итерации цикла. Какие негативные последствия это может иметь, я предсказать не могу, но код однозначно настораживает.
Сравнение беззнаковых чисел с нулём
В проверяемых проектах часто встречаются сравнения беззнаковых чисел с нулём, результат которых всегда либо истина, либо ложь. Такой код не всегда содержит серьёзную ошибку. Часто это результат излишней осторожности, либо проверка остаётся после изменения типа переменной со знакового на беззнаковый. В любом случае такие сравнения стоит внимательно проверить.
V547 Expression ‘m_socket p3DEngine’ pointer was utilized before it was verified against nullptr. Check lines: 1477, 1480. gameserialize.cpp 1477
Разыменование и проверка указателя gEnv->p3DEngine.
Пример 3
V595 The ‘pSpline’ pointer was utilized before it was verified against nullptr. Check lines: 158, 161. facechannelkeycleanup.cpp 158
Разыменование и проверка указателя pSpline.
Разные предупреждения
V622 Consider inspecting the ‘switch’ statement. It’s possible that the first ‘case’ operator is missing. mergedmeshrendernode.cpp 999
Пожалуй, этот фрагмент кода самый странный из всех, что встречались в проекте CryEngine V. Выбор метки case не зависит от условного оператора if, даже если там if (false). В операторе switch выполняется безусловный переход к метке, если она удовлетворяет выражению switch. Без использования оператора break, с помощью такого кода можно «обходить» выполнение ненужных операторов, но опять же, сопровождать такой неочевидный код будет легко не всем разработчикам. И почему при переходе к меткам GEOM_CAPSULE и GEOM_CYLINDER выполняется один и тот же код?
V510 The ‘LogError’ function is not expected to receive class-type variable as second actual argument. behaviortreenodes_action.cpp 143
Если в описании функции невозможно указать число и типы всех допустимых параметров, то список формальных параметров завершается эллипсисом (. ), что означает: «и, возможно, еще несколько аргументов». В качестве фактического параметра для эллипсиса могут выступать только POD (Plain Old Data) типы. Если эллипсис функции в качестве параметра передается объект класса, то практически всегда свидетельствует о наличии ошибки в программе. Вместо указателя на строку в стек попадает содержимое объекта. Такой код приведет к формированию в буфере «абракадабры» или к аварийному завершению программы. В коде CryEngine V используется свой класс строки, и у него уже есть подходящий метод c_str().
Очень подозрительный фрагмент кода. После цикла for поставили точку с запятой, хотя форматирование кода подразумевает о наличии тела у цикла.
V535 The variable ‘j’ is being used for this loop and for the outer loop. Check lines: 3447, 3490. physicalworld.cpp 3490
Код проекта полон и другим опасным кодом, например, использованием одного счётчика во вложенном и внешнем циклах. Большие файлы с исходным кодом содержат запутанное форматирование и изменение одних переменных в разных местах — без статического анализа здесь не обойтись!
Анализатор посчитал, что в функцию, выполняющую действие с контейнером, передаётся итератор от другого контейнера. В этом фрагменте кода это не так и ошибки нет. Переменная rPeaks является ссылкой на m_peaks. Однако такой код может вводить в заблуждение не только анализатор кода, но и людей, которые будут сопровождать код. Не стоит так писать.
V713 The pointer pCollision was utilized in the logical expression before it was verified against nullptr in the same logical expression. actiongame.cpp 4235
Оператор if содержит довольно большое условное выражение, в котором везде выполняется обращение к указателю pCollision. Ошибка заключается в том, что на равенство нулю указатель pCollision проверяется самым последним, т.е. уже после многократного разыменовывания.
V758 The ‘commandList’ reference becomes invalid when smart pointer returned by a function is destroyed. renderitemdrawer.cpp 274
В переменную commandList сохраняется ссылка на значение, которое хранит умный указатель. При разрушении объекта умным указателем ссылка становится недействительной.
Заключение
Исправление ошибок во время написания кода почти ничего не стоит, в отличии от тех, которые находятся на этапе работы тестировщиков, а ошибки в выпущенном продукте несут уже колоссальные расходы. Независимо от используемого анализатора, сама методология статического анализа кода уже давно показала себя как очень эффективный способ контроля качества кода и программного продукта в целом.
Сотрудничество с Epic Games продемонстрировало пользу от внедрения статического анализатора в Unreal Engine 4. Мы помогли разработчикам во всех вопросах интеграции анализатора и даже исправили найденные ошибки, чтобы они могли регулярно проверять новый код проекта. К аналогичному сотрудничеству мы готовы и с компанией Crytek.
Предлагаю всем желающим попробовать PVS-Studio на своих C/C++/C# проектах.
Разработчики CryEngineV заранее были уведомлены о проверке проекта, поэтому некоторые ошибки могут быть уже исправлены.





