КПП ИЗНУТРИ: HOMEWORLD – КРАТКАЯ ИСТОРИЯ КОДА
Автор: Patrick Deupree
Перевод: Ten
![](pict/hwr/inside_the_box_homeworld_02.jpg)
Рубрика «КПП изнутри» (Inside the Box) служит своеобразной площадкой для лиц, занимающихся созданием продуктов компании, где они могут рассказать о своих личных мотивах, методах, работе и результатах. Проекты Gearbox Software создаются самыми разными людьми, из разных регионов, со своим мировоззрением, интересами и желаниями. Взгляды и мнения, выраженные в данной статье, принадлежат автору и не обязательно отражают официальную политику и позиции компании, или мнение других её сотрудников.
Я работаю «программистом на все руки» в Gearbox вот уж более 14 лет. Мне приходилось заниматься созданием Opposing Force , серии Brothers in Arms, Borderlands и многим другим. Вообще говоря, Gearbox больше известна созданием пострелушек от первого лица, но мне приходилось работать и над симуляторами скейтбординга, а также заниматься переносом Samba De Amigo, игры про погремушки-маракасы, на Wii. Несмотря на это, должен признать, что не ожидал, что когда-либо вновь буду привлечён к работе над стратегиями реального времени.
Ранее в этом году я уже был немного удивлен, когда один из руководителей компании известил меня, что мы собираемся заполучить серию Homeworld. Он сказал мне, что мы должны будем получить у THQ список файлов, связанных с франшизой. Также он отметил, что Брайан Мартел (креативный директор Gearbox), скачал исходный код Homeworld, выложенный в общий доступ, и спросил, не мог бы я глянуть на него, чтобы понять, что мы могли получить в случае успешного приобретения серии у THQ.
Homeworld вышел как раз, когда мы закончили Opposing Force, и я, помнится, немного поиграл в него тогда. Дома у меня до сих пор лежала коробка с игрой в шкафу – я ведь немного «плюшкин». Месяц назад или чуть больше я сдул с неё пыль, и теперь она неизменно дежурит на моём рабочем столе. Также я достаточно долго работал над созданием стратегии реального времени Dominion: Storm Over Gift 3 [было дело], в компании, в которой я работал перед тем, как устроиться в Gearbox. Работа над Homeworld, в некоторой степени, позволила мне сделать шаг в прошлое – к тому, чем я был занят, прежде чем взялся за стрелялки.
![](pict/hw1/AK_home12_small.jpg)
Этот рисунок (как и все остальные далее в статье) – одна из тех удивительных вещей, что мы нашли, просматривая исходные материалы игр серии.
Исходный код Homeworld, который загрузил Брайан, был опубликован его разработчиками – компанией Relic Entertainment – в конце сентября 2003-го, незадолго до выхода Homeworld 2 . Первый Homeworld был выпущен в конце 1999-го, что означает, что игре на этот момент было более 13-ти лет. Сборка этого кода имеющимися у меня инструментами стала настоящим испытанием .
Вышедший Homeworld, судя по всему, использовал Microsoft Visual Studio 97 в качестве основного C/C++ компилятора . Я решил идти в ногу со временем и попробовал собрать игру самой последней версией компилятора от Microsoft, чтобы посмотреть, что из этого выйдет. В результате я получил многочисленные предупреждения и ошибки, показывающие, насколько изменились компиляторы за эти 13 лет.
В общем, не очень-то я далеко продвинулся в сборке Homeworld, когда мы получили список файлов для обеих игр, которые THQ должна была предоставить победителю аукциона.
![](pict/hw2/RC_fleet_scale_small.jpg)
Вообще, интересно оценивать, что делает тот или иной код, на основе одних лишь имен каталогов и файлов. Не так уж и мало было понятно из того, что я увидел в этом списке, но и почвы для предположений тоже хватало. Главная мысль, которую я вынес на том этапе, что исходный код, судя по всему, был полным, и если здесь используются любые библиотеки сторонних производителей, мы будем в курсе. Главным образом я сосредоточился файлах, связанных с Homeworld 2, т.к. в исходники первой части уже заглянул.
Визуализатор второй части построен на базе OpenGL, и на этот раз я не увидел никаких признаков враппера для Direct3D, шедшего вместе с первым Homeworld. Звуковое сопровождение большей частью основывалось на вызовах DirectSound, хотя на глаза мне попался каталог "mss6", в котором, очевидно, находилась библиотека Miles Sound System. Был там и проект "NetworkDP8", который предположительно использовал DirectPlay 8 (сетевой интерфейс в DirectX, который так и не изменился с выходом DirectX 9). Кажется, DirectShow использовался для проигрывания видео, но также там присутствовали DLL-библиотеки DivX, указывающие на использование видеокодека. Сам движок явно был написан на C++, с ним в комплекте даже шёл файл проекта Visual Studio, так что его загрузка и сборка должна быть определённо легче, чем в случае с первым Homeworld. Казалось, что работа с ним в современной среде разработки не составит труда...
Я вкратце задокументировал результаты своих изысканий, построил список всех программ и библиотек сторонних производителей, которые увидел, и отдельно отметил те, которые имели GPL-лицензию, а не коммерческую. Эти оценки и документация были переданы моим коллегам, и мы стали ждать начала аукциона. Брайан Берлсон, ведущий продюсер Gearbox, написал обширную статью «КПП изнутри: Homeworld», в которой раскрыл события на аукционе и ему предшествующие. Я был брошен в помощь другим проектам Gearbox, хотя время от времени спрашивал своих коллег по электронной почте, как там обстоят дела с аукционом. Почти три недели прошло с тех пор, как я отослал обновление моим коллегам, и наконец Рэнди Питчфорд (президент Gearbox), спустился к нам и сказал, что мы приобрели серию. Пока были утрясены все юридические тонкости, прошёл ещё месяц, и наконец-то мы получили файлы от THQ.
![](pict/hw2/AK_china_gate_mid08_small.jpg)
Мне не терпелось увидеть их содержимое – ведь до этого у меня был всего лишь список в документе Excel. Учитывая, что оригинальная игра была написана на чистом C, я полагал, что с переходом на C++ многое изменится. Помня о проблемах, с которыми я столкнулся при сборке 13-летнего кода в современном компиляторе, мне было интересно, сколько кода Homeworld 2 я смогу скомпилировать прямо из коробки. Я запустил Visual Studio 2012 и был готов открыть один из проектов в папке исходников.
Здесь следует отметить, что сборка Homeworld 2 включает в себя сборку многочисленных статических и динамических библиотек. Одно из преимуществ этого заключается в том, что множество инструментов для игры используют эти же библиотеки. Это дало мне возможность начать с малого, и я стал прикидывать, что нужно сделать, чтобы собрать какую-либо часть игры. Начать было решено с редактора фонов Homeworld 2 . И мои первые попытки компиляции показали, насколько компилятор Microsoft эволюционировал за эти 10 лет.
Сначала я получил целый ворох предупреждений, связанных с тем, что Microsoft изменила определения некоторых стандартных функций, чтобы соответствовать стандартам ISO. Хотя эти предупреждения не препятствовали компиляции, огромное их количество привело к появлению полосы прокрутки в окне выдачи компилятора, затрудняя его просмотр. В одних случаях я просто отключал предупреждения, в других пришлось переделать код, их выдающий, чтобы он соответствовал новым стандартам.
Потом мне пришлось иметь дело с некоторыми потенциально ошибочными языковыми конструкциями, на которые старые компиляторы просто закрывали глаза, но современные обращают внимание. Вообще, написание кода примечательно тем, что вы имеете дело с «языком», который не прощает даже малейших ошибок. Местами ошибки были очевидны – сродни попыткам заказать что-то на иностранном языке, что в переводе будет звучать как «я хотел бы тарелку тостеров». В других случаях вы могли перепутать слова для курицы с костным мозгом, и результат вроде бы и правильный, но всё равно не тот, что вы ожидали.
При написании кода программист в поиске ошибок полагается на компилятор точно так же, как писатель опирается на средства проверки правописания и грамматики в текстовых редакторах (или отдаёт своё произведение для проверки редактору-человеку). Если код компилируется, мы идём дальше и можем не замечать ошибку до тех пор, пока запуск не выявит очевидно некорректные результаты. Имея дело с огромными объёмами кода и допуская иногда опечатки, всегда есть шанс, что какая-то из них останется незамеченной.
Для тех, кому приходилось иметь дело с кодом, приведу несколько простых примеров того, что скомпилировать бы раньше, но не компилируется сейчас:
for (int i=0; i<3; i++);
{
<математические операции с матрицами, использующие i в качестве индекса в двухмерном массиве>
}
if (fabs(thrustDiff <= k_thrustEpsilon))
{
}
if (fabs(thrustDiff) <= k_thrustEpsilon)
{
}
bool depthWrite = gl.getDepthWrite;
<getDepthWrite() на самом деле функция>
bool depthWrite = gl.getDepthWrite();
for (uint i = 0; i < numTextures; ++i)
{
<что-то делаем>
std::vector<Texture *>::iterator i = std::find( temp.begin(), temp.end(), tex );
<что-то делаем>
}
static s_playerIndexToCountBy;
По мере устранения более-менее очевидных ошибок им на смену приходили более сложные и трудоёмкие в исправлении. Они были связаны с изменениями, которые претерпел C++ за эти годы. На этапе становления он распространялся всё шире и шире, и стандартизация имеющегося кода и библиотек шла с некоторым отставанием. Ранняя реализация некоторых стандартных библиотек и соглашений от Microsoft несколько отличалась от используемой другими компиляторами. Поиск в Интернете стал моим лучшим другом, когда пришлось выяснять, какие изменения были внесены в некоторые стандартные библиотеки, из-за которых код нельзя было собрать. Большая их часть была связана с тем, как C++ обрабатывал списки. В большинстве игр приходится иметь дело с многочисленными списками различных сущностей, поэтому мне пришлось разбирать немало нюансов, связанных с использованием списков.
Наконец, пришло время, когда вышеуказанные проблемы были решены, компиляция проходила успешно, и настал черёд попытаться запустить игру. Я был слегка удивлен, что после первого же запуска Homeworld 2 на экране появилось работающее меню с мышью и звуком. Я переходил из меню в меню, и все работало отлично. Было приятно, что все эти изменения наконец принесли результат, который можно было увидеть и услышать – с учётом их количества я ожидал, что игра в конце концов вылетит. Что, впрочем, и произошло, когда я попытался начать кампанию или запустить сражение.
Это вылет, да и немногие другие, в основном были обусловлены двумя причинами. Одна из них связана с уже упоминавшимися изменениями в работе со списками в C++. Некоторые из этих изменений привели к тому, что Homeworld 2 в разных местах стал по-разному работать со списками. Другая причина заключалось в том, что в скомпилированный код было добавлено больше проверок, чтобы выявить потенциальные проблемы во время запуска игры . Впрочем, проблемных мест осталось не так уж и много, так что я достаточно быстро их поправил и смог запустить первую миссию.
![](pict/hw1/AK_inter19_small.jpg)
Должен признать, что хотя игре уже 10 лет, он по-прежнему достойно смотрится. Закончив первую миссию, я решил продолжить и прошёл ещё несколько. Все, казалось, работало, и мне был весело играть с плодами реликовской работы в прошлом, и моей работы в настоящем. На тот момент у нас не было системы сборки Homeworld, и теперь, когда у меня было что-то работающее, я решил вспомнить былые времена и занялся тем, что в сети мы привыкли называть "frankenbuild" . Я знал, что у нас есть любители сыграть в Homeworld 2, кроме того, за ним были закреплены несколько художников и дизайнер, поэтому я решил сделать им копии для простоты.
Спустя всего лишь нескольких минут после отправки электронного письма, оповестившего всю компанию, что копия игры выложена во внутренней сети, я получил несколько ответов от коллег, которым не удалось запустить игру. Проблема оказалась пустяковой – мы ещё не начали использовать Visual Studio 2012 при создании других наших продуктов, так что у большинства моих сослуживцев не было её библиотек времени выполнения. Тем не менее, я был поражен, что получил обратную связь так быстро.
Окрылённый успехом после всех этих достижений, я решил вернуться к сборке оригинального Homeworld. Теперь у меня был некоторый опыт решения проблем, с которыми я мог столкнуться при сборке старого кода последней версией компилятора, и я уже предвкушал, что ошибки, с которыми я столкнулся во время предыдущих попыток, будет легче исправить.
Я начал с создания проекта для Visual Studio 2012 – сборка Homeworld осуществлялась из командной строки, так что файл проекта нужно было создавать с нуля. К счастью, игра собиралась в единственный exe-файл, так что мне оставалось перетащить все исходные файлы в проект и скопировать опции компиляции из скрипта сборки. После этого я приступил к сборке средствами студии, решая всплывающие по ходу дела проблемы. Большая их часть была связана с предупреждениями, которые были вызваны сделанными Microsoft изменениями в компиляторе для соответствия современным стандартам (о чём я уже говорил выше).
За решение одной из самых сложных проблем, с которыми я столкнулся, хочу горячо поблагодарить модостроительское сообщество, которое работало с этим кодом после его публикации. Речь идёт о миссиях кампании. Они представляют собой специализированные скрипты, подготовка которых начинается с обработки препроцессором (препроцессинг – один из этапов компиляции, на котором предварительно объявленные определения заменяются на их фактические значения – например, идентификаторы или ссылки на общие для всех миссий файлы, которые заменяются содержимым этих файлов). Созданный в результате подобной обработки файл подается на вход утилите kas2c.exe, которая преобразует скрипт в обычный код на языке C, и после этого он компилируется в качестве составной части файла Homeworld.exe. Мне было немного непонятно, как должен проходить этот этап, но после поисков в Сети я нашёл несколько сообщений на форуме, в которых была нужная мне информация.
В итоге первый запуск Homeworld был аналогичен запуску продолжения: появились мышь и экранное меню, я мог перемещаться между ними, и звук работал. Я запустил кампанию и тут же получил вылет. Но причины вылетов, с которыми я столкнулся при запуске Homeworld, были другими, чем причины вылетов Homeworld 2.
В результате исследований первого вылета было обнаружено, что в некоторых переменных оказались запредельные значения, далёкие о того, что я мог бы ожидать. Такие ситуации всегда интересно исследовать, так как иногда довольно трудно отследить, где именно были испорчены начальные данные. К счастью, размышляя о «делах минувших», я вспомнил одну вещь.
![](pict/hw2/AKminer_small.jpg)
На этапе компиляции кода используется понятие «выравнивание структуры данных» . В современных версиях компилятора Microsoft по умолчанию используется выравнивание в 8 байт. В прошлом, когда памяти было куда меньше , выравнивание по умолчанию составляло 4 байта. Это можно наглядно проиллюстрировать на примере яиц и бумажной коробки для них. Вы отправляете двух человек в два курятника с коробками, «выровненными» на 12 яиц. Первый нашёл 10 яиц – ему хватит одной коробки и ещё 2 места останется. Второй смотрел внимательнее и нашёл 16 яиц, соответственно, ему понадобятся 2 коробки, но при этом во второй будет пустовать 8 мест. Если вы выдадите им коробки, «выровненными» на 6 яиц, второму парню понадобится уже три коробки, но свободных мест будет всего 2. Таким образом, чем меньше выравнивание, тем меньше памяти расходуется впустую, но его снижение может привести к потере производительности, поэтому в наши дни данные выравниваются на границу 8 байт.
Следующий вылет произошёл, когда я уничтожил мишень в первой миссии. У Homeworld довольно интересная система вызова кода для создания эффектов. В ней применены несколько действительно неординарных решений для того, чтобы эта система сносно работала на старых версиях Windows. Современные средства обнаружения ошибок времени выполнения, о которых я говорил ранее, местами так или иначе затрагивают эти решения. Для исправления ситуации я просто отключил некоторые проверки, так как код Homeworld работал, хотя и делал что-то, что с точки зрения системы обнаружения ошибок могло привести к проблемам.
После решения этих проблем Homeworld засиял как ёлочная игрушка. Я прошёл несколько миссий в обеих играх и поймал всего один случайный вылет (причину которого, судя по всему, удалось устранить). В довесок к остальному я также был удивлён тем, что мультиплеер всё ещё исправно работал, несмотря на то, что DirectPlay был объявлен устаревшим корпорацией Майкрософт.
Объёмы потраченного времени и количество проделанных изменений в попытках запустить Homeworld были существенно меньше, чем в случае с Homeworld 2. В основном это свидетельствует о том, что C к 1999 году очень хорошо устоялся и соответствовал стандарту. C++, с другой стороны, продолжал стремительно развиваться. Это, а также значительно возросший объём исходного кода, сделало процесс портирования Homeworld 2 немного сложнее.
![](pict/hw2/RC_vibe_small.jpg)
Вообще, конечно, было интересно сделать шаг назад во времени, получив эти две игры и заставив их работать. Я увидел часть истории в этом коде и немного предался воспоминаниям о некоторых этапах этой эволюции. Моя первая работа после окончания колледжа заключалась в объектно-ориентированной разработке и программировании на C под Windows 2.0. После этого большую часть работы я делал на C++ под Windows и Unix, и иногда на Objective C, с которым столкнулся в одном или двух проектах.
Когда я начал на свою первую работу в качестве разработчика игр, я вернулся к программированию на чистом C, поскольку именно на нём был написан движок, с которым мы работали. Эта тенденция сохранилась и на моей 2-ой работе по разработке игр, так как движки Quake и Quake 2 были написаны на C. В Gearbox я работал с движком Half-Life, в котором код Quake был дополнен кодом на C++, и с движками Unreal 2 и 3, где, как правило, были свои версии стандартных функций и конструкций.
Код Homeworld 2, по сути, оказался первым «промышленным» кодом, с которым я когда-либо работал, где контейнеры стандартной библиотеки C++ использовались настолько широко. Также это моё первое знакомство с Lua, так как движок использует Lua-скрипты для поддержки очень гибкой и настраиваемой архитектуры. Вообще, в коде Homeworld 2 присутствует изрядное количество вещей, с которыми за последние 18 лет мне не приходилось сталкиваться в тех промышленных движках, с которыми я работал. Было очень занимательно разбираться с этим всем и как бы заново пройти последние 14 лет развития C, C++ и методов разработки программ под Windows .
Также было интересно взглянуть на другой подход к Data Driven Development. В Gearbox мы всегда двигались в этом направлении, но с тех пор, как много лет назад небольшая группа из нас начала работу над Borderlands , эта методология значительно шагнула вперед. И вот я вижу подход в использовании Lua-скриптов для создании правил геймплея, инфраструктуры и пользовательского интерфейса Homeworld 2 – это действительно здорово видеть.
Теперь, однако, пришло время оставить изучение прошлого, вернуться в настоящее и приступить к будущему. Многое изменилось со времён выхода обеих игр серии, и теперь мы взяли курс на обновление некоторых элементов их инфраструктуры. И когда мы вновь выпустим обе игры, то хотим поддержать то, что уже доступно в настоящее время . Мне не терпится начать разговор на эту тему... но всему своё время.
Если вам есть что сказать по этой статье или о том, что бы вы хотели увидеть в рубрике «КПП изнутри», пожалуйста, дайте нам знать: insidethebox@gearboxsoftware.com.