Homeworld3.RU  

  Регистрация или вход Регистрация или вход Главная | Обратная связь | В избранное | Сделать стартовой  

 
:: Главная ::
 
 
:: Homeworld 3 ::
 
 
:: Deserts of Kharak ::
 
 
:: HW Remastered ::
 
 
:: Homeworld ::
 
 
:: Cataclysm ::
 
 
:: Homeworld 2 ::
 
 
Дата публикации: 28.12.2013 13:54
Прочитано: 4698 раз
 
 

КПП ИЗНУТРИ: HOMEWORLD – КРАТКАЯ ИСТОРИЯ КОДА

Автор: Patrick Deupree
Перевод: Ten



Рубрика «КПП изнутри» (Inside the Box) служит своеобразной площадкой для лиц, занимающихся созданием продуктов компании, где они могут рассказать о своих личных мотивах, методах, работе и результатах. Проекты Gearbox Software создаются самыми разными людьми, из разных регионов, со своим мировоззрением, интересами и желаниями. Взгляды и мнения, выраженные в данной статье, принадлежат автору и не обязательно отражают официальную политику и позиции компании, или мнение других её сотрудников.

Я работаю «программистом на все руки» в Gearbox вот уж более 14 лет. Мне приходилось заниматься созданием Opposing Force [дополнение для первого Half-Life], серии Brothers in Arms, Borderlands и многим другим. Вообще говоря, Gearbox больше известна созданием пострелушек от первого лица, но мне приходилось работать и над симуляторами скейтбординга, а также заниматься переносом Samba De Amigo, игры про погремушки-маракасы, на Wii. Несмотря на это, должен признать, что не ожидал, что когда-либо вновь буду привлечён к работе над стратегиями реального времени.

Ранее в этом году я уже был немного удивлен, когда один из руководителей компании известил меня, что мы собираемся заполучить серию Homeworld. Он сказал мне, что мы должны будем получить у THQ список файлов, связанных с франшизой. Также он отметил, что Брайан Мартел (креативный директор Gearbox), скачал исходный код Homeworld, выложенный в общий доступ, и спросил, не мог бы я глянуть на него, чтобы понять, что мы могли получить в случае успешного приобретения серии у THQ.

Homeworld вышел как раз, когда мы закончили Opposing Force, и я, помнится, немного поиграл в него тогда. Дома у меня до сих пор лежала коробка с игрой в шкафу – я ведь немного «плюшкин». Месяц назад или чуть больше я сдул с неё пыль, и теперь она неизменно дежурит на моём рабочем столе. Также я достаточно долго работал над созданием стратегии реального времени Dominion: Storm Over Gift 3 [было дело], в компании, в которой я работал перед тем, как устроиться в Gearbox. Работа над Homeworld, в некоторой степени, позволила мне сделать шаг в прошлое – к тому, чем я был занят, прежде чем взялся за стрелялки.


Неизвестные истребители и корвет, компоновкой подозрительно напоминающие туранские – прим.перев. Этот рисунок (как и все остальные далее в статье) – одна из тех удивительных вещей, что мы нашли, просматривая исходные материалы игр серии.

Исходный код Homeworld, который загрузил Брайан, был опубликован его разработчиками – компанией Relic Entertainment – в конце сентября 2003-го, незадолго до выхода Homeworld 2 [если быть точнее – чуть позже того как]. Первый Homeworld был выпущен в конце [сентября] 1999-го, что означает, что игре на этот момент было более 13-ти лет. Сборка этого кода имеющимися у меня инструментами стала настоящим испытанием [ещё бы – если интерфейс к видеокодеку ещё как-то можно набросать из подручных средств, то статически собранный с тогдашней STL «Titan» (сетевая библиотека игры) надёжно охраняет один из путей успеха – прим.перев.].

Вышедший Homeworld, судя по всему, использовал Microsoft Visual Studio 97 в качестве основного C/C++ компилятора [если быть точнее, 97-я студия, она же предшественник легендарной 6-ки, до сих пор горячо любимой и ценимой, использовалась больше для разработки и отладки – итоговая версия игры процентов на 90 собрана с помощью интеловского компилятора, производящего несравненно лучшую оптимизацию кода в плане скорости выполнения – прим.перев.]. Я решил идти в ногу со временем и попробовал собрать игру самой последней версией компилятора от Microsoft, чтобы посмотреть, что из этого выйдет. В результате я получил многочисленные предупреждения и ошибки, показывающие, насколько изменились компиляторы за эти 13 лет.

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


Масштабы различных классов кораблей на фоне варяжского флагмана – прим.перев.

Вообще, интересно оценивать, что делает тот или иной код, на основе одних лишь имен каталогов и файлов. Не так уж и мало было понятно из того, что я увидел в этом списке, но и почвы для предположений тоже хватало. Главная мысль, которую я вынес на том этапе, что исходный код, судя по всему, был полным, и если здесь используются любые библиотеки сторонних производителей, мы будем в курсе. Главным образом я сосредоточился файлах, связанных с 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.


«Китайские ворота» мегастанции. По окончании игры именно туда отбыл древний линкор – прим.перев.

Мне не терпелось увидеть их содержимое – ведь до этого у меня был всего лишь список в документе Excel. Учитывая, что оригинальная игра была написана на чистом C, я полагал, что с переходом на C++ многое изменится. Помня о проблемах, с которыми я столкнулся при сборке 13-летнего кода в современном компиляторе, мне было интересно, сколько кода Homeworld 2 я смогу скомпилировать прямо из коробки. Я запустил Visual Studio 2012 и был готов открыть один из проектов в папке исходников.

Здесь следует отметить, что сборка Homeworld 2 включает в себя сборку многочисленных статических и динамических библиотек. Одно из преимуществ этого заключается в том, что множество инструментов для игры используют эти же библиотеки. Это дало мне возможность начать с малого, и я стал прикидывать, что нужно сделать, чтобы собрать какую-либо часть игры. Начать было решено с редактора фонов Homeworld 2 [не включенного в состав RDN из-за лицензионных ограничений – прим.перев.]. И мои первые попытки компиляции показали, насколько компилятор Microsoft эволюционировал за эти 10 лет.

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

Потом мне пришлось иметь дело с некоторыми потенциально ошибочными языковыми конструкциями, на которые старые компиляторы просто закрывали глаза, но современные обращают внимание. Вообще, написание кода примечательно тем, что вы имеете дело с «языком», который не прощает даже малейших ошибок. Местами ошибки были очевидны – сродни попыткам заказать что-то на иностранном языке, что в переводе будет звучать как «я хотел бы тарелку тостеров». В других случаях вы могли перепутать слова для курицы с костным мозгом, и результат вроде бы и правильный, но всё равно не тот, что вы ожидали.

При написании кода программист в поиске ошибок полагается на компилятор точно так же, как писатель опирается на средства проверки правописания и грамматики в текстовых редакторах (или отдаёт своё произведение для проверки редактору-человеку). Если код компилируется, мы идём дальше и можем не замечать ошибку до тех пор, пока запуск не выявит очевидно некорректные результаты. Имея дело с огромными объёмами кода и допуская иногда опечатки, всегда есть шанс, что какая-то из них останется незамеченной.

Для тех, кому приходилось иметь дело с кодом, приведу несколько простых примеров того, что скомпилировать бы раньше, но не компилируется сейчас:


for (int i=0; i<3; i++);
{
	<математические операции с матрицами, использующие i в качестве индекса в двухмерном массиве>
}

Здесь, согласно последней версии стандарта, переменная i считается объявленной внутри цикла, и потому не должна быть видна за его пределами. Кроме того, имеет место логическая ошибка, когда, вместо четырёхкратного выполнения блока в фигурных скобках с всёвозрастающим значением i, прогоняется пустой цикл, после чего блок в фигурных скобках выполняется один раз, и при этом i имеет значение 3, выходящее за предполагаемый диапазон. Для устранения ошибки достаточно убрать точку с запятой.


if (fabs(thrustDiff <= k_thrustEpsilon))
{
}

Здесь аргументом функции fabs() становится результат логического выражения, имеющий булевый тип, который теперь автоматически к вещественному не приводится. В итоге получаем несовпадение типов, что, впрочем, и к лучшему, т.к. это указывает на ещё одну логическую ошибку, вызванную – кто бы мог подумать – всего лишь несвоевременным закрытием скобки:

if (fabs(thrustDiff) <= k_thrustEpsilon)
{
}

bool depthWrite = gl.getDepthWrite;

<getDepthWrite() на самом деле функция>

Здесь в переменную depthWrite передаётся не результат выполнения функции, а её адрес. Для её вызова необходимо дописать скобки:

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;

Здесь переменная определена как статическая (сохраняющая своё значение между вызовами функции – в противовес локальным, таким свойством не обладающим), но не указан её тип. Раньше по умолчанию принимался int, теперь это запрещено.


По мере устранения более-менее очевидных ошибок им на смену приходили более сложные и трудоёмкие в исправлении. Они были связаны с изменениями, которые претерпел C++ за эти годы. На этапе становления он распространялся всё шире и шире, и стандартизация имеющегося кода и библиотек шла с некоторым отставанием. Ранняя реализация некоторых стандартных библиотек и соглашений от Microsoft несколько отличалась от используемой другими компиляторами. Поиск в Интернете стал моим лучшим другом, когда пришлось выяснять, какие изменения были внесены в некоторые стандартные библиотеки, из-за которых код нельзя было собрать. Большая их часть была связана с тем, как C++ обрабатывал списки. В большинстве игр приходится иметь дело с многочисленными списками различных сущностей, поэтому мне пришлось разбирать немало нюансов, связанных с использованием списков.

Наконец, пришло время, когда вышеуказанные проблемы были решены, компиляция проходила успешно, и настал черёд попытаться запустить игру. Я был слегка удивлен, что после первого же запуска Homeworld 2 на экране появилось работающее меню с мышью и звуком. Я переходил из меню в меню, и все работало отлично. Было приятно, что все эти изменения наконец принесли результат, который можно было увидеть и услышать – с учётом их количества я ожидал, что игра в конце концов вылетит. Что, впрочем, и произошло, когда я попытался начать кампанию или запустить сражение.

Это вылет, да и немногие другие, в основном были обусловлены двумя причинами. Одна из них связана с уже упоминавшимися изменениями в работе со списками в C++. Некоторые из этих изменений привели к тому, что Homeworld 2 в разных местах стал по-разному работать со списками. Другая причина заключалось в том, что в скомпилированный код было добавлено больше проверок, чтобы выявить потенциальные проблемы во время запуска игры [такие проверки обычно включаются в отладочную версию и изымаются из финальной для снижения накладных расходов]. Впрочем, проблемных мест осталось не так уж и много, так что я достаточно быстро их поправил и смог запустить первую миссию.


Тайданский лёгкий перехватчик на фоне харакианской тропинки. Даже отсутствие положенных в таком случае крыльев ничуть не скрадывает изящество машины – прим.перев.

Должен признать, что хотя игре уже 10 лет, он по-прежнему достойно смотрится. Закончив первую миссию, я решил продолжить и прошёл ещё несколько. Все, казалось, работало, и мне был весело играть с плодами реликовской работы в прошлом, и моей работы в настоящем. На тот момент у нас не было системы сборки Homeworld, и теперь, когда у меня было что-то работающее, я решил вспомнить былые времена и занялся тем, что в сети мы привыкли называть "frankenbuild" [таким термином обычно обозначается сборка чего-либо из подручных частей]. Я знал, что у нас есть любители сыграть в Homeworld 2, кроме того, за ним были закреплены несколько художников и дизайнер, поэтому я решил сделать им копии для простоты.

Спустя всего лишь нескольких минут после отправки электронного письма, оповестившего всю компанию, что копия игры выложена во внутренней сети, я получил несколько ответов от коллег, которым не удалось запустить игру. Проблема оказалась пустяковой – мы ещё не начали использовать Visual Studio 2012 при создании других наших продуктов, так что у большинства моих сослуживцев не было её библиотек времени выполнения. Тем не менее, я был поражен, что получил обратную связь так быстро.

Окрылённый успехом после всех этих достижений, я решил вернуться к сборке оригинального Homeworld. Теперь у меня был некоторый опыт решения проблем, с которыми я мог столкнуться при сборке старого кода последней версией компилятора, и я уже предвкушал, что ошибки, с которыми я столкнулся во время предыдущих попыток, будет легче исправить.

Я начал с создания проекта для Visual Studio 2012 – сборка Homeworld осуществлялась из командной строки, так что файл проекта нужно было создавать с нуля. К счастью, игра собиралась в единственный exe-файл, так что мне оставалось перетащить все исходные файлы в проект и скопировать опции компиляции из скрипта сборки. После этого я приступил к сборке средствами студии, решая всплывающие по ходу дела проблемы. Большая их часть была связана с предупреждениями, которые были вызваны сделанными Microsoft изменениями в компиляторе для соответствия современным стандартам (о чём я уже говорил выше).

За решение одной из самых сложных проблем, с которыми я столкнулся, хочу горячо поблагодарить модостроительское сообщество, которое работало с этим кодом после его публикации. Речь идёт о миссиях кампании. Они представляют собой специализированные скрипты, подготовка которых начинается с обработки препроцессором (препроцессинг – один из этапов компиляции, на котором предварительно объявленные определения заменяются на их фактические значения – например, идентификаторы или ссылки на общие для всех миссий файлы, которые заменяются содержимым этих файлов). Созданный в результате подобной обработки файл подается на вход утилите kas2c.exe, которая преобразует скрипт в обычный код на языке C, и после этого он компилируется в качестве составной части файла Homeworld.exe. Мне было немного непонятно, как должен проходить этот этап, но после поисков в Сети я нашёл несколько сообщений на форуме, в которых была нужная мне информация.

В итоге первый запуск Homeworld был аналогичен запуску продолжения: появились мышь и экранное меню, я мог перемещаться между ними, и звук работал. Я запустил кампанию и тут же получил вылет. Но причины вылетов, с которыми я столкнулся при запуске Homeworld, были другими, чем причины вылетов Homeworld 2.

В результате исследований первого вылета было обнаружено, что в некоторых переменных оказались запредельные значения, далёкие о того, что я мог бы ожидать. Такие ситуации всегда интересно исследовать, так как иногда довольно трудно отследить, где именно были испорчены начальные данные. К счастью, размышляя о «делах минувших», я вспомнил одну вещь.


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

На этапе компиляции кода используется понятие «выравнивание структуры данных» [расположение различных данных в несмежных ячейках памяти для повышения производительности]. В современных версиях компилятора Microsoft по умолчанию используется выравнивание в 8 байт. В прошлом, когда памяти было куда меньше [и достаточно редко использовались восьмибайтные целые и дробные числа], выравнивание по умолчанию составляло 4 байта. Это можно наглядно проиллюстрировать на примере яиц и бумажной коробки для них. Вы отправляете двух человек в два курятника с коробками, «выровненными» на 12 яиц. Первый нашёл 10 яиц – ему хватит одной коробки и ещё 2 места останется. Второй смотрел внимательнее и нашёл 16 яиц, соответственно, ему понадобятся 2 коробки, но при этом во второй будет пустовать 8 мест. Если вы выдадите им коробки, «выровненными» на 6 яиц, второму парню понадобится уже три коробки, но свободных мест будет всего 2. Таким образом, чем меньше выравнивание, тем меньше памяти расходуется впустую, но его снижение может привести к потере производительности, поэтому в наши дни данные выравниваются на границу 8 байт.

Падение производительности происходит потому, что память можно логически рассматривать как разделённую на блоки по 8 байт (или 64 бита, что в точности соответствует ширине её шины данных). И без выравнивания может случиться так, что переменная фактически будет располагаться в двух смежных блоках – например, если последовательно расположить целое (4 байта), логическое (1 байт) и дробное малой точности (4 байта), то последняя переменная займёт 3 байта текущего блока и ещё 1 байт следующего – значит, при работе с ней придётся использовать уже две операции чтения/записи вместо одной. Поэтому выгоднее дополнять переменные меньшего размера до 4(8) байт, что расходует часть памяти впустую, но позволяет получать/обновлять значения переменных гарантированно за одну операцию. По сути, перед нами – сферический в вакууме пример обмена памяти на производительность, и подобный подход в IT-сфере применяется очень широко.

Следующий вылет произошёл, когда я уничтожил мишень в первой миссии. У Homeworld довольно интересная система вызова кода для создания эффектов. В ней применены несколько действительно неординарных решений для того, чтобы эта система сносно работала на старых версиях Windows. Современные средства обнаружения ошибок времени выполнения, о которых я говорил ранее, местами так или иначе затрагивают эти решения. Для исправления ситуации я просто отключил некоторые проверки, так как код Homeworld работал, хотя и делал что-то, что с точки зрения системы обнаружения ошибок могло привести к проблемам.

После решения этих проблем Homeworld засиял как ёлочная игрушка. Я прошёл несколько миссий в обеих играх и поймал всего один случайный вылет (причину которого, судя по всему, удалось устранить). В довесок к остальному я также был удивлён тем, что мультиплеер всё ещё исправно работал, несмотря на то, что DirectPlay был объявлен устаревшим корпорацией Майкрософт.

Объёмы потраченного времени и количество проделанных изменений в попытках запустить Homeworld были существенно меньше, чем в случае с Homeworld 2. В основном это свидетельствует о том, что C к 1999 году очень хорошо устоялся и соответствовал стандарту. C++, с другой стороны, продолжал стремительно развиваться. Это, а также значительно возросший объём исходного кода, сделало процесс портирования Homeworld 2 немного сложнее.


Крейсер (а может быть и нет ) в атмосфере. Несмотря на туман, выглядит величественно – прим.перев.

Вообще, конечно, было интересно сделать шаг назад во времени, получив эти две игры и заставив их работать. Я увидел часть истории в этом коде и немного предался воспоминаниям о некоторых этапах этой эволюции. Моя первая работа после окончания колледжа заключалась в объектно-ориентированной разработке и программировании на 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 [возможно, здесь опечатка – не 18, а 8, иначе получается противоречие].

Также было интересно взглянуть на другой подход к Data Driven Development. В Gearbox мы всегда двигались в этом направлении, но с тех пор, как много лет назад небольшая группа из нас начала работу над Borderlands [навряд ли это было раньше 2005-го], эта методология значительно шагнула вперед. И вот я вижу подход в использовании Lua-скриптов для создании правил геймплея, инфраструктуры и пользовательского интерфейса Homeworld 2 – это действительно здорово видеть.

Теперь, однако, пришло время оставить изучение прошлого, вернуться в настоящее и приступить к будущему. Многое изменилось со времён выхода обеих игр серии, и теперь мы взяли курс на обновление некоторых элементов их инфраструктуры. И когда мы вновь выпустим обе игры, то хотим поддержать то, что уже доступно в настоящее время [видимо, имеются ввиду внушительные наработки модостроительного сообщества]. Мне не терпится начать разговор на эту тему... но всему своё время.

Если вам есть что сказать по этой статье или о том, что бы вы хотели увидеть в рубрике «КПП изнутри», пожалуйста, дайте нам знать: insidethebox@gearboxsoftware.com.

 
  Другие материалы в этом разделе
Обзор Homeworld: RemasteredОбзор Homeworld: Remastered
Воссоздание саундтрека Homeworld для нынешней аудиторииВоссоздание саундтрека Homeworld для нынешней аудитории
Сводка по обновлениям Homeworld RemasteredСводка по обновлениям Homeworld Remastered
КПП изнутри: HomeworldКПП изнутри: Homeworld
Зачем Gearbox купила Homeworld и передала его в руки создателейЗачем Gearbox купила Homeworld и передала его в руки создателей
Gearbox Software на PAX Australia 2013Gearbox Software на PAX Australia 2013
Gearbox Software на PAX Prime 2013Gearbox Software на PAX Prime 2013
День сообщества Gearbox 2013 – В поисках Родного мираДень сообщества Gearbox 2013 – В поисках Родного мира
День сообщества Gearbox 2013 – Интервью с Робом КаннингемомДень сообщества Gearbox 2013 – Интервью с Робом Каннингемом
 
 
[ Назад | Начало | Наверх ]
 

Главная | Новости | Форум | Темы | Галерея | Статьи | Обратная связь


Copyright (c) 2002-2024 by Homeworld3.RU.
Любое коммерческое использование материалов сайта запрещено.
Некоммерческое использование возможно с разрешения редакции сайта.
При перепечатке любого количества информации с этого сайта прямая ссылка на Homeworld3.RU ОБЯЗАТЕЛЬНА!
Сайт рассчитан на разрешение экрана от 1280x720 и выше.

Homeworld, Gearbox & the Gearbox Publishing logos are registered trademarks, and the Homeworld logo is a trademark of Gearbox Enterprises, LLC.
BBI, Blackbird Interactive, and the associated logo are registered trademarks.
Sierra and the Sierra logo are trademarks of Sierra Entertainment, Inc.
Relic Entertainment and the Relic Entertainment logo are trademarks of Relic Entertainment, Inc.