Программист
0,0
рейтинг
14 октября 2015 в 11:52

Разработка → Как создавался кроссплатформенный Half-Life или «Хедкрабы внутри ваших часов» из песочницы



Парочку месяцев назад на Гиктаймсе проскакивала новость о запущенной Half-Life на Android Wear. В статье ни слова не сказано было о разработчиках и тогда один из хабровчан спросил в комментариях, почему никто из них не напишет здесь?

И вот уже годовщина с того момента, как я начал порт свободного движка Xash3D на Linux, а также, поскольку несколько моих знакомых всё же интересовались подробностями, я решил написать этот пост. Кому интересна история проекта, обходы разных проблем и мои личные советы, прошу под кат.

Начну с истории проекта. Движок изначально был написан программистом с ником Дядя Миша или g-cont. Именно ему мы обязаны тому, что сейчас есть. Лишь в 2011 году он выложил исходники движка под GPLv3. Но у него был фатальный недостаток, Дядя Миша не хотел портировать движок на что-то кроме Windows. За Android порт он хотел некоторую сумму, и, естественно, идея провалилась из-за нашего коммьюнити.

Я лично начал следить за движком с 2012 года, год спустя после открытия исходников, но лишь осенью 2014, с небольшим опытом программирования на С++ я скачал исходный код движка. Загоревшись мыслью о том, что свободный игровой движок и кроссплатформенность — понятия ещё с кармаковских времён тесно связанные, я начал портировать его на Linux.

Поначалу порт планировался с использованием winelib, но со временем, вникнув в архитектуру движка, я остановился на SDL2, как самой вездесущей мультимедийной библиотеке. Да и Valve для своих портов использовали его.

И вот сразу мой первый совет тем, кто хочет переносить любое неизвестное ему приложение на любую ОС:

1) Создавайте проектные файлы сразу и, не делая ни единого изменения в проекте, компилируйте его.

Да, от моего совета ваша IDE может даже подвиснуть, так как компилятор выдаст не одну тысячу ошибок и предупреждений. У меня компилятор выдавал около 4000 ошибок.

Переходя от одной ошибки к другой, я постепенно избавлялся от них. Не оставалось и виндового кода, -Wl,--no-undefined очень сильно спасает при портировании.

И вот свершилось — загрузился движок. Показывает собственную консоль, а за окном, тем временем, уже было примерно начало декабря прошлого года. Должен подчеркнуть, что до меня попытки предпринимались и примерно на консоли и заканчивались, если не раньше. Весь декабрь ушёл на загрузку библиотек в рантайме, SDL ввод, звук — и вот я выкладываю на Linux.org.ru скриншот работы.

Скриншоты, сделанные за декабрь


То, что я скидывал когда-то EXL.



Первый публичный скриншот


Несколько человек заинтересовываются проделанной работой. Я же оглядываюсь на исходники и понимаю, что с этим уже работать нельзя: во многих местах код из-за макросов становится нечитабелен, появляются странные баги, сборка за пределами моего локалхоста очень сложна. Да и версия движка сильно старая. В комментариях к треду указывают, что новые версии распространяются на каком-то другом форуме. В итоге я начинаю всё с нуля, а мой знакомый помогает приводить всё в серьёзный вид — переводит сборку на CMake, гонит на меня плохо пахнущими тряпками за слишком большие коммиты и говнокод. Также на GitHub создаётся организация SDLash3D, названная в честь старого порта. На момент написания статьи в ней аж пять человек, но активны только двое.

Внезапносовет номер 2, который я для себя уяснил в этом время:

2) Сделайте заголовочный файл, содержащий одни макросы, которые будут вставлять нужный для целевой платформы код.

Также тут совет для тех, кто, читая эту статью, пишет код в своей студии.

3) На счёт всякого подозрительного и странного кода обращайтесь к Google. Он вам расскажет, что ваш код компилируется только под Visual Studio, у которого много нестандартных расширений. И поменьше завязывайтесь на WinAPI.

Тем временем, nicknekit/Unc0nnected молча начинает портировать старый порт на Android.

Скриншоты и видео, любезно предоставленные Никитой






С февраля мы с Никитой начинаем работать вместе над портом. Он занимается старым портом, а я допиливаю новый и переношу в него функционал, связанный с Android.

В этот момент устанавливается базовая схема работы порта на Android. Сейчас оно выглядит так:



В марте мы создаём тред на 4PDA, о котором сообщаем об успехах переноса, новые видео и скрины. Первого же апреля выходит версия 0.1, но первое апреля не может быть без шутки, поэтому в движке сделано специально условие, при котором он закрывается, если не найден pakandroid.pak, в котором был «зашит» очень весёлый и достаточно качественный мод G-Man Invasion.

С тех пор, среди значимых событий для порта оказались:

  • Поддержка сенсорного управления, сделанная совместно с Beloko Games;
  • Релиз версии 0.14, который действительно принёс успех. Около 20 тыс. посетителей за два дня, судя по статистике на GitHub;
  • Интерфейс Android-версии для модификаций, сделанный нашим mittorn. Мододелы могут самостоятельно выпустить свою игру ещё и на Android.

На этом можно и закончить, ниже бонус в виде отдельных коммитов и ссылки на нас на GitHub и ModDB.

Бонус №1. Несколько коммитов, связанных с разным пониманием кода в Visual Studio и GCC. После исправления их всех я точно уверен, что поддержка Си в студии — это худшее, что случалось в сфере программирования:


Бонус №2. Также некоторые коммиты, связанные с ARM, Android и OpenGL ES:

  • Баг, со странным умножением и делением на 1.0 на ARM;
  • Ещё проблемы с float. Вызывало рандомные краши с SIGBUS;
  • А здесь ссылки нет. Просто внезапно для себя открыл, что все char на ARM — беззнаковые. Оттого навигация NPC сходила с ума, а в движке не работали спрайты. Можно или ключ -fsigned-char вставить, или явно указывать, что char должен быть знаковым для этой переменной;
  • Баг рендера. Не совсем проблемы ARM, зато проблема OpenGL ES. До сих пор не пофикшена полностью;
  • Своя собственная реализация dlsym() на Android, а всё потому что в Android до Lollipop просуществовал баг, с которым не резолвились некоторые символы из библиотек;

Ссылки:

Организация на GitHub.
Страница на ModDB.

Спасибо за внимание, ваш a1batross.
Alibek @libpony
карма
15,0
рейтинг 0,0
Программист
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (17)

  • +2
    Часа два вчера думал о том, что же я забыл. Добавил абзац про дорогого Дядю Мишу.
  • +3
    Motorola Droid 4. Эх, скучаю. Шикарнейшая клавиатура.
    • 0
      Это вообще Photon Q. EXL больше расскажет об этом. Руки-то его
  • +3
    >>> pchan->words[i] = pwords[i++];
    Тут undefined behaviour, оно может в будущем сломаться и от другой версии Студии, например.

    >>>Просто внезапно для себя открыл, что все char на ARM — беззнаковые.
    А стандарт языка не специфицирует знаковость char. Компилятор вправе реализовать этот момент как ему удобнее. Странно, что где-то он знаковый, в большинстве компиляторов он как раз unsigned по умолчанию.

    • 0
      Согласен. Но ты не представляешь как много в этом коде UB.

      Таков алгоритм из SDK. И в его реализации ошибка, поскольку он полагается на знаковость char.
  • 0
    А расскажите про баг с умножением на 1
    • 0
      pandoralive.info/?p=5043

      Вот тут точно описано. Баг исправил ptitSeb, который сильно помог с багами на ARM. Он занимается движоком на Pandora.
    • 0
      Это не бага ARM, это вполне себе бага разработчиков (оригинальной версии). У них там плавающая арифметика считается с точностью single float. Но на x86 на некоторых компиляторах плавающие числа всё равно считались с точностью 80 бит (внутреннее представление FPU), а потом конвертировались (округлялись) к нужно точности.
      А на ARM всё считается с указанной точностью (более того, никто не обещает даже double float на ARM) и повылазили ошибки округления. Ровно с тем же успехом они бы повылазили и на x86 с другим компилятором или с другими ключами сборки, т.к. компилятор по факту отклонялся от стандарта в данном случае.
      Более того, кажется, что там алгоритмическая/архитектурная ошибка и патч не исправляет её полностью — пропадают лишь самые явные проявления, но, возможно, что это не критично.
  • +2
    >char на ARM — беззнаковые
    Используйте int8_t (или аналоги из SDL) и ваши волосы будут мягкие и шелковистые
    • +1
      Это существующий код. Я все же стараюсь делать так, чтобы работало оно везде.
      • 0
        Не очень понятно, почему вдруг fixed-width типы должны где-то не работать.
        • 0
          Вы не совсем меня поняли. Код уже был написан и использование какого-то char программистом из Valve(ошибка была в Half-Life SDK) стоило недели поиска бага. А я всё же стараюсь писать так, чтобы работало везде. В возможностях собственого опыта, конечно.
          • +1
            Ах, тогда приношу извинения.
  • 0
    Пока не посмотришь скриншоты, из текста совершенно невозможно понять при чем здесь собственно Half-Life :)
    • 0
      А заголовок? О_о
  • 0
    Про баг с одинаковыми именами:

    max = max( max( r, g ), b );
    


    Насколько я помню, в Visual Studio при правильном подборе заголовочных файлов и дефайнов max(a, b) является макросом с содержанием в духе (((a) > (b))? (a): (b)). Это объясняет, почему данная строка компилируется (в правой части скобки есть, и поэтому max развернулся в тело соответствующего макроса, в левой остался, как был, переменной max).

    Особо приятной плюшкой такого макроса является черт знает какое поведение, когда выражения a и b имеют сайд-эффекты при вычислении, или, что еще хуже, когда макрос используется в рекурсии (легкой подстановкой макроса время исполнения функции из логарифмического становится экспоненциальным!)
    • 0
      Так и есть. Но тем не менее, что GCC, что шланг на этом месте фейлелись. И там тоже max() — макрос.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.