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

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

КПП ИЗНУТРИ: 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-2016 by Homeworld :: The Russian Project.
Любое коммерческое использование материалов сайта запрещено.
Некоммерческое использование возможно с разрешения редакции сайта.
При перепечатке любого количества информации с этого сайта прямая ссылка на Homeworld3.RU ОБЯЗАТЕЛЬНА!

The Sierra logo, Homeworld, Homeworld: Cataclysm, Homeworld 2 and the Homeworld logo are trademarks of Sierra Entertainment, Inc.
Relic Entertainment and the Relic Entertainment logo are trademarks of Relic Entertainment, Inc.


Rambler's Top100 Рейтинг@Mail.ru

Генерация страницы: 0.036 сек. и 9 запросов к базе данных за 0.008 сек.
Web site engine code is Copyright © 2006 by SLAED CMS. All rights reserved.