- Программы для создания модов для игр
- OpenIV
- Valve Hammer Editor
- S.T.A.L.K E.R.
- Source Modding — Часть 1 — Основы основ
- Вступление
- Немножко терминов
- Так почему же именно Source?!
- Что нам необходимо?
- Введение в сурс дела
- Репозиторий
- Выбор ветки
- Стиль кода
- Структура кода
- Генерация проектов
- Исключение HL2
- Другие скрипты
- Первичная сборка и запуск
- Сборка
- Запуск — Способ 1 — Steam
- Запуск — Способ 2 — Visual Studio
- Первая модификация в коде
- Функции семейства Msg()
- Заключение
- Чему мы научились?
- Что дальше?
- Как разрабатываются моды для Unity-игр: внедряемся в код игры
- О Beat Saber
- Общая информация о Unity
- BSIPA
- IPA.exe
- IPA.Loader.dll
- IPA.Injector.dll
- Unity Doorstop
- Шаг 1. Проксируем GetProcAddress
- Шаг 2. Переопределеяем mono_jit_init_version
- winhttp.dll
- Перечисление действий в хронологическом порядке
- Про часть 2
Программы для создания модов для игр
Желание привнести в игру новые механики, окружение или, говоря проще, изменить что-то на свой лад идет с незапамятных времен. Модифицировать игру куда проще, чем создавать ее с нуля. Уже есть заданные механики, окружение и анимации. Остается только добавлять новый или отрезать уже существующий контент, меняя таким образом изначальный билд. Нередко бывает и так, что сообщество создает специальный софт, который упрощает ряд задач, включая импорт исходников и экспорт новых или измененных файлов в игру. К примеру, еще в 1994 году, благодаря Брендону Уайберу и наличию открытого в DOOM кода, модостроение стало популярно в таких масштабах. Используя DEU (DOOM Editor Utility), написанную Уайбером, игроки получили возможность делать собственные уровни и модификации к игре. На программе “Менеджмент игровых проектов” в Высшей школе бизнес информатики НИУ ВШЭ многие наши слушатели тоже имеют опыт использования программ для создания модов для игр или же интересуются этой темой.
Та же Dota – изначально была модификацией для Warcraft III. Карта была создана с помощью редактора уровней World Editor для Warcraft III: Reign of Chaos, а с выходом расширения Warcraft III: The Frozen Throne была улучшена. Сейчас же, Абдул Измаил (IceFrog), один из её создателей, продолжает работать над Dota 2 в Valve. Точно так же было и Counter-Strike. Первая бета Counter-Strike вышла в 1999 году и изначально являлась модификацией Half-Life. Сменив пять версий, игра остается актуальной до сих пор, давно отделившись от Half-Life и перейдя на новый движок Source, а в последствии на Source 2. Огромная работа проделана над S.T.A.L.K.E.R.: Lost Alpha – по сути это огромный аддон, вносящий в игру все, что было вырезано разработчиками из GSC Game World ранее, чтобы воссоздать ранний облик игры из анонсов периода 2003-2004 года. Как и в случае с Dota или с CS, будучи модом к Half-Life 2, Stanley Parable смогла в итоге стать полноценной отдельной игрой и завоевать множество наград.
Под каждую игру, как и в случае с DEU, обычно пишется свой инструментарий, который специально под нее заточен и облегчает работу с дистрибутивом. Но по некоторым играм все же можно выделить самые популярные программы для моддинга.
OpenIV
Универсальный редактор и файл-менеджер для PC-версий Grand Theft Auto V, Grand Theft Auto IV/EFLC и Max Payne 3. OpenIV позволяет редактировать скрытые архивы игры, добавлять и изменять в них файлы нужные файлы, в два клика интегрировать в игру новую текстуру. В режиме редактирования программа автоматически сохраняет все внесенные изменения, но занимаясь созданием модов, лучше вообще всегда не забывать делать бекапы.
Valve Hammer Editor
Редактор уровней, разработанный компанией Valve Software для работы с игровыми движками GoldSrc и Source входит в комплект Source SDK — набора программ для создания модификаций к играм, построенным на технологии Source.
Редактор был разработан Беном Моррисом для создания уровней в Quake 1 и размещен в свободном доступе. Начиная с версии 4.0 программа стала работать с Source, на котором написана Half-Life 2, изначально Dota 2 и еще ряд игр от Valve. Редактор поставляется в комплекте Source SDK и доступен к свободной загрузке через сервис Steam для обладателя любой игры, созданной на движке Source. Все обновления программы загружаются с помощью Steam. В Hammer можно создавать уровни для всех современных игр от Valve.
S.T.A.L.K E.R.
По S.T.A.L.K E.R.: Зов Припяти и S.T.A.L.K E.R.: Тень Чернобыля вышло уже столько программ, что их объединяют в целые паки.
Сборка для модостроительства ТЧ и ЗП v1.2 объединяет в себе свыше 25 программ и такое количество контента, что игру можно изменить практически до неузнаваемости. В пак входят 165 учебников по модостроению, конвертер для X-ray sdk, набор текстур, 4.XRspawner, Генератор однотипных квестов, DDSPacker v0.1, CharEd v0.2b, статьи по моддингу и еще столько всего, что проще увидеть.
Помимо понимания устройства игры, модификации могут дать небывалый простор для экспериментов и нововведений. Garry’s mod, разработанный Гарри Ньюманом, является физической “песочницей”, где игрок может создавать окружение и проводить любые манипуляции с объектами из игр, созданных на движке Source (CS:S, CS:GO, TF2, HL2 и других игр от Valve). Спустя два года после появления первой версии, 29 ноября 2006 года Garry’s Mod стал платной игрой и до сих пор распространяется через Steam.
В 2012 году Valve сделала огромный шаг навстречу модостроению и запустила Steam Workshop. Сервис позволил всем желающим заниматься созданием собственных модификаций к уже существующим играм, которые его поддерживали. Это дало пользователям возможность не просто делать модификации, но еще и зарабатывать, а также получать фидбек от сообщества прямо в Steam.
Наличие модификаций в портфолио вполне может стать преимуществом при устройстве на работу в геймдев-студию. Умение создать мод может показать, насколько претендент на должность разбирается в левел-дизане, 3D-моделлинге, геймдизайне или каком-либо другом направлении в разработке. Особенно полезно это будет при входе в игровую индустрию, когда выпущенных проектов еще нет, но при этом есть моды, которые ставят себе игроки и пишут хорошие отзывы или которые соответствуют выбранному направлению специальности.
Если вам интересно больше узнать про игровую индустрию, создать и запустить свою игру, или пойти на работу в игровую компанию, то приглашаем на нашу образовательную программу “Менеджмент игровых проектов”. У нас часто проходят и однодневные открытые мероприятия, узнать о которых можно на странице анонсов.
Источник
Source Modding — Часть 1 — Основы основ
В мире существует множество игровых движков, но нет ни одного движка, похожего на Source своей историей и особенностями.
В этом (пилотном) уроке мы разберем простейшие действия с исходными кодами SDK, а также внесем наше первое изменение в код Half-Life 2.
Вступление
Немножко терминов
Сам по себе Source SDK — набор утилит и программ, помогающих в разработке собственных уровней и модификаций для игры, а также исходные коды Half-Life 2 и эпизодов.
Игра/Мод (Для сурса нет никакой разницы, игра это или мод :p) — скомпилированные исходные коды SDK.
Так почему же именно Source?!
Что нам необходимо?
Введение в сурс дела
Репозиторий
Исходный код SDK находится в репозитории на GitHub. Склонируйте его в любое удобное для вас место:
Выбор ветки
Если вы хотите написать свой первый мод для Half-Life 2/Episode 1/2, то используйте директорию sp/ и Source SDK Base 2013 Singleplayer.
Стиль кода
Поля класса именуются с префиксом m_, использование венгерской нотации рекомендуется (на то есть свои причины, которые здесь обсуждать нет смысла):
Структура кода
SDK, так же как и движок, разделён на несколько частей.
Вот пути до некоторых таких файлов:
ВАЖНО: При внесении ЛЮБЫХ изменений в VPC файл решение должно быть заново перегенерировано!
Генерация проектов
Исключение HL2
SDK имеет внутри себя также разделение на HL2 и Episodic. Использование второго позволит нам иметь некоторые фичи, например отдельную от стамины шкалу заряда фонарика.
И поэтому, чтобы не компилировать лишний код, мы можем просто исключить HL2 из скриптов:
Другие скрипты
Рядом с creategameprojects.bat также лежит его клон для bash а также два интересных файла — createallprojects.bat и его клон для bash.
Эти два скрипта заставляют VPC создавать проекты не только для чистых библиотек мода, но и для различных утилит, таких как vrad (Radiosity!) или height2normal.
Сейчас использовать я его вам настоятельно не рекомендую, так как свои собственные компиляторы карт нам пока не нужны.
Первичная сборка и запуск
Сборка
Чтобы удостовериться, что вы всё сделали правильно, необходимо собрать всё сгенерированное решение. Итак, собираем (вы же ведь открыли решение в IDE?):
После сборки в папке game/mod_hl2/bin/ или game/mod_episodic/bin/ должны появится наши клиентская и серверная библиотеки!
Запуск — Способ 1 — Steam
Запуск — Способ 2 — Visual Studio
Я рекомендую использовать именно этот способ — не копировать же бинарники мода каждый раз после сборки!
Если карта загрузилась и вы можете передвигаться и двигать камеру мышью — сборка успешна!
Первая модификация в коде
Функции семейства Msg()
Заключение
Чему мы научились?
[Я надеюсь, что] из данного урока мы выяснили:
Что дальше?
Во второй части мы разберем систему сущностей Source SDK.
Источник
Как разрабатываются моды для Unity-игр: внедряемся в код игры
У большинства игр нет официальной поддержки модов, но при этом постоянно появляются новости о том, как моддеры добавляют в игры новый контент. Часто речь идет о замене моделей, текстур или музыки, но бывают и полноценные новые игровые механики, т.е. моддеры внедряются в код игры и изменяют ее внутреннюю логику.
В этой статье мы рассмотрим, какие методы используется для того, чтобы игра, написанная на Unity, могла запускать сторонние моды: модификации библиотек игры и самого движка, подмена адресов в таблице импорта dll, вмешательство в загрузку Mono Runtime и другие.
О Beat Saber
В качестве примера я буду использовать Beat Saber — одну из самых популярных игр для VR-шлемов. Если у вас есть такой шлем, то, скорее всего, вы уже знаете, что такое Beat Saber. Если нет, то, возможно, вы видели хотя бы одно видео из игры в рекомендациях Youtube.
Единственное, что Beat Saber поддерживает из модов — это пользовательские уровни. В игре есть официальный редактор, но нет никакого сервиса, чтобы как-либо распространять свои творения, и, по моему мнению, вряд ли появится. В основе практически любого уровня лежит какая-то песня. Разрешить выкладывать такие файлы в своем официальном сервисе — значит привлечь к себе внимание правообладателей с их постоянными запросами что-то удалить или вообще с требованиями закрыть весь сервис.
Поэтому не удивительно, что вокруг Beat Saber сформировалось огромное сообщество: Beat Saber Modding Group (BSMG). Именно сообщество ответственно за большую часть того, что есть в игре.
Все это поддерживается сообществом. Большинство модов и даже сами сайты — это проекты с открытым исходным кодом, их можно найти на GitHub, чем я и воспользовался при подготовке к этой статье.
Общая информация о Unity
Mono — это одна из реализаций спецификации общеязыковой инфраструктуры (Common Language Infrastructure, CLI). Mono работает на Windows, Linux, macOS, мобильных устройствах и игровых консолях. Разрабатывается с 2001 года компанией Ximian, которую в 2003 купила компания Novell, которую в 2011 купила Attachmate. Attachmate сократила почти всех, но разработчики Mono хотели и дальше разрабатывать Mono, поэтому организовали в том же году отдельную компанию Xamarin, которую в 2016 купили Microsoft. Код Mono открыт и хранится на GitHub.
Это стандартная практика в разработке игр, когда высокопроизводительная часть движка написана на C или C++, а игровая логика пишется на скриптовом языке, например Lua. Преимущество C# в качестве скриптового языка в том, что он достаточно “высокоуровневый”, чтобы упростить и ускорить разработку игры, но при этом позволяет использовать JIT-компиляцию в нативный код.
Подробнее об этом можно прочитать в документации Mono.
BSIPA
BSIPA (Beat Saber Illusion Plugin Architecture) — это набор библиотек, которые модифицируют файлы Beat Saber так, чтобы игра могла загружать сторонние моды. Иногда такие библиотеки называют менеджерами плагинов.
BSIPA написана на C# (исходный код) и является форком IPA. Т.е. моддеры взяли уже существующий менеджер плагинов для Unity и улучшили его специально под нужды Beat Saber. В исходном коде BSIPA можно выделить три основных модуля: IPA, IPA.Loader и IPA.Injector.
IPA.exe
Обычно игроки используют ModAssistant и его аналоги, чтобы устанавливать моды, но мы рассмотрим, как это делается вручную. Для этого нужно скачать последний релиз BSIPA, распаковать все файлы в папку с игрой и запустить IPA.exe. Это исполняемый файл, который копирует файлы из только что распакованного архива в те папки, где они должны находиться. И это, в общем-то, все, что он делает: просто копирует файлы, и если какой-то из файлов уже существует в игре, то он делает его резервную копию. Вот список копируемых файлов:
Мы видим здесь еще два модуля BSIPA, которые я упомянул ранее: IPA.Loader.dll и IPA.Injector.dll. Остальные библиотеки нужны, чтобы работали эти две. Некоторые из них мы еще рассмотрим подробнее в этой статье и во второй части.
IPA.Loader.dll
Как и следует из названия этого модуля, он отвечает за загрузку и управление плагинами. Здесь определены классы PluginLoader, PluginManager и PluginComponent. PluginLoader — это класс, в котором определена логика загрузки плагинов из папки Plugins. В оригинальной IPA плагины загружаются в алфавитном порядке, что иногда приводит к проблемам. В BSIPA логика загрузки была переписана: Loader сначала загружает метаданные плагинов, анализирует зависимости, находит конфликты и строит порядок загрузки на основе этой информации.
Класс PluginManager отвечает за управление плагинами: поиск, активацию и деактивацию с учетом их зависимостей.
Класс PluginComponent — это компонент Unity, отвечающий за старт загрузки модов, их хранение и передачу им событий из игры. Например, если в основной игре сменилась активная сцена (мы запустили какой-то уровень или, наоборот, вернулись в главное меню), то PluginComponent итерирует по всем модам и сообщает им об этом. Подробнее об этом будет во второй части.
IPA.Injector.dll
Название Injector намекает, что этот модуль отвечает за внедрение в оригинальную игру. Начнем с забавного факта: BSIPA добавляет в игру антипиратскую защиту.
Защита примитивная, она просто проверяет, есть ли в папке с игрой файлы, которые явно указывают на взлом: SmartSteamEmu.ini, BSteam crack.dll, HUHUVR_steam_api64.dll и другие.
Скорее всего, BSIPA это нужно из практических интересов. Если игра пиратская, то это значит, что она была взломана и часть ее файлов изменены. BSIPA тоже изменяет файлы игры, а значит, совместимость с пиратскими библиотеками не гарантируется.
IPA.Injector использует Mono.Cecil, чтобы модифицировать библиотеку UnityEngine.CoreModule.dll. Она написана на C# (иначе бы мы не смогли ее редактировать) и содержит базовые сущности Unity. Например, там определены классы GameObject (все объекты в игре, которые располагаются на игровых сценах, являются объектами этого класса) и класс MonoBehaviour (базовый класс для компонентов с игровой логикой, в том числе PluginComponent). IPA.Injector находит в библиотеке UnityEngine.CoreModule.dll класс UnityEngine.Application и модифицирует его статический конструктор (или создает, если его нет), добавляя туда создание бутстраппера:
По крайней мере, в Unity 2019.3.0f3 у Application нет статического конструктора, а значит, эта модификация не удаляет существующий важный код и относительно безопасна. Если статический конструктор уже существует и Injector заменяет его, то это только в том случае, если мы повторно модифицируем библиотеку.
После модификации в классе Application появляется новый статический конструктор (код получен с помощью декомпиляции модифицированной UnityEngine.CoreModule.dll):
Метод CreateBootstrapper создает новый объект и добавляет в него компонент Bootstrapper:
Бутстраппер представляет собой интересное техническое решение:
Метод Start похож на метод Awake, но Awake вызывается в первый кадр, когда создается компонент, а Start вызывается в первый кадр после активации компонента (компоненты могут создаваться неактивными). Каждый из методов вызывается ровно один раз за жизненный цикл компонента и может использоваться для разных этапов инициализации компонента. Компонент Bootstrapper в своем методе Start вызывает уничтожение объекта, к которому он прикреплен. В методе OnDestroy вызывается колбэк, установленный в CreateBootstrapper:
Колбэк дожидается асинхронной загрузки плагинов и создает компонент PluginComponent. Если честно, я не знаю, зачем нужен этот механизм с самоуничтожением бутстраппера и созданием PluginComponent в OnDestroy. Мое предположение: это нужно для того, чтобы сделать загрузку отложенной — метод Start вызывается, когда компонент становится активным, а он не может стать активным, пока не загрузится игровая сцена. Буду рад, если более опытные Unity-разработчики поправят меня в комментариях.
Помимо этого IPA.Injector редактирует еще один файл — MainAssembly.dll. Эта библиотека содержит код Beat Saber. С помощью все того же Mono.Cecil IPA.Injector убирает у всех классов sealed и делает все методы публичными и виртуальными. Теперь любой класс в любом плагине может наследоваться от любого класса в оригинальной игре и переопределять поведение его методов. Эта модификация уже не относится к загрузке плагинов, но она очень упрощает разработку модов.
Если бы у нас использовалась не BSIPA, а оригинальная IPA, то на этом можно было бы остановиться. В оригинальной версии IPA.exe запускает Injector, модифицирует UnityEngine.CoreModule.dll, и в игре появляются моды. У такого подхода есть один минус — каждый раз, когда оригинальная игра обновляется, нужно заново запускать IPA.exe и патчить игру. BSIPA решает эту проблему, но, как я уже писал выше, IPA.exe в BSIPA просто копирует файлы и не делает больше ничего. За запуск Injector в этом случае отвечает другая библиотека.
Unity Doorstop
За внедрение в код Unity отвечает библиотека UnityDoorstop-BSIPA. Она лежит среди файлов BSIPA и написана на чистом C. UnityDoorstop-BSIPA (исходный код) — это тоже форк, оригинальный проект можно найти здесь. Далее для простоты буду вместо UnityDoorstop-BSIPA писать Doorstop. Лозунгом Doorstop является фраза “Run managed code before Unity does!”, что в примерном переводе звучит как “Запускай управляемый код до того, как Unity сможет это сделать”. Напомню, что “управляемый код” — это в нашем случае код C#. Выше мы уже выяснили, что ядро движка Unity написано на C++, а пользовательские скрипты для игровой логики и некоторые части самого Unity — на C#. Значит, Doorstop каким-то образом позволяет нам вмешаться в логику, когда ядро Unity уже загрузилось, а C#-скрипты — еще нет.
Когда мы запускаем игру на Unity (например, Beat Saber.exe), то в память одной из первых загружается библиотека UnityPlayer.dll. Она прилагается ко всем Unity-играм и отвечает за запуск и выполнение самой игры. У этой библиотеки есть таблица импорта, в которой говорится, что UnityPlayer использует функцию GetProcAddress из библиотеки kernel32.dll. GetProcAddress — это функция WinAPI, которая возвращает адрес функции из определенной библиотеки по ее названию. Я не видел исходного кода Unity, но судя по тому, что я видел в BSIPA и Doorstop, в UnityPlayer должно быть что-то вроде такого:
mono_jit_init_version — это функция, которая отвечает за инициализацию и запуск Mono. Подробнее можно прочитать здесь. Doorstop вмешивается в этот процесс. Делается это в два шага.
Шаг 1. Проксируем GetProcAddress
Этот код находит в памяти уже загруженную библиотеку UnityPlayer.dll, берет ее таблицу импорта (Import Address Table, IAT), находит в ней GetProcAddress из kernel32.dll и заменяет ее на нашу функцию hookGetProcAddress из Doorstop.dll. hookGetProcAddress в упрощенном виде выглядит так:
Подробнее про IAT Hooking можно прочитать здесь. Получается, что hookGetProcAddress проксирует все вызовы GetProcAddress. Прокси-функция смотрит название функции, которую у нее запрашивают. Если это НЕ mono_jit_init_version, то hookGetProcAddress просто вызывает настоящую GetProcAddress и возвращает то, что у нее попросили, тем самым не мешая нормальной работе. Если у нее запрашивают mono_jit_init_version, то тогда она возвращает переопределенную функцию ownMonoJitInitVersion. Пользуясь случаем, прокси-функция получает указатель на библиотеку, в котором ищется mono_jit_init_version, и берет из нее настоящие функции Mono с помощью GetProcAddress (внутри init(module); ):
Шаг 2. Переопределеяем mono_jit_init_version
ownMonoJitInitVersion сначала вызывает настоящую mono_jit_init_version, чтобы создать Mono. Затем она с помощью Mono загружает сборку IPA.Injector.dll и запускает из нее статический метод Main. В (очень) упрощенном виде код ownMonoJitInitVersion выглядит так:
Мы уже рассмотрели выше, что IPA.Injector содержит код, который внедряет плагины в Beat Saber. После того, как IPA.Injector завершает свою работу, ownMonoJitInitVersion отдает Mono в Unity. Unity даже не в состоянии понять, что что-то было не так. Если б он вызвал настоящую mono_jit_init_version, то он бы получил Mono и начал бы дальше с ним работать. Если Unity запускает переопределенную ownMonoJitInitVersion, то он тоже получает Mono — он просто не в курсе, что этим Mono успели воспользоваться для чего-то еще.
winhttp.dll
Остался один нерешенный вопрос. Во-первых, в описании Doorstop я писал про библиотеку Doorstop.dll. Давайте опять взглянем на файлы, которые IPA.exe устанавливает в игру:
Как вы можете видеть, Doorstop.dll здесь нет. Во-вторых, даже если бы Doorstop.dll здесь бы, то почему Beat Saber или Unity должны его загружать в память, если его нет в таблице импорта? Решение: выдать Doorstop.dll за другую библиотеку, которую загружает Unity, и сделать так, чтобы она загрузилась раньше настоящей. Такой библиотекой в нашем случае является winhttp.dll — это системная библиотека Windows для http-запросов (хранится в C:/Windows/System32). У Unity в одной из библиотек в таблице импорта указано, что ей нужна библиотека winhttp.dll, поэтому во время загрузки Unity Windows сначала загружает winhttp.dll в память, и только после этого Unity начинает работать.
Doorstop собирается в библиотеку под тем же названием: winhttp.dll. Там содержится код Doorstop, отвечающий за все манипуляции с GetProcAddress и mono_jit_init_version, а также туда добавляется таблица экспорта (Export Address Table) со всеми теми же функциями, что в оригинальной winhttp.dll. Загрузка библиотек в Windows устроена так, что Windows сначала проверяет, есть ли нужные библиотеки в папке с программой, и только потом, если ничего не нашел, идет в System32. Поэтому при запуске игры Windows в первую очередь находит наш файл. Поддельная библиотека загружает настоящую winhttp динамически (с помощью LoadLibrary) и просто перенаправляет все вызовы из своей таблицы экспорта на адреса настоящих функций (с помощью GetProcAddress). Можно сравнить размеры: поддельная библиотека весит 16кб, а настоящая — 960кб.
В репозиториях IPA и BSIPA есть скрипты для генерации файла proxy.c, который содержит таблицу экспорта, совпадающую с оригинальной библиотекой, и загружает все методы через GetProcAddress. В IPA такой скрипт написан на PowerShell, а в BSIPA — на Python. Сгенерированный файл выглядит так: proxy.c.
Подробнее про DLL Search Order Hijacking можно прочитать тут.
Перечисление действий в хронологическом порядке
Повторяем все шаги в хронологическом порядке.
Про часть 2
На этом первая часть завершена. Мы разобрались, как с помощью манипуляций с библиотеками Windows и Unity можно добавить моды в Beat Saber. Несмотря на то, что BSIPA — это форк, сделанный специально для этой игры, мы в этой статье нигде не делали предположений о внутренней структуре игры и не использовали ее код, а значит можно сказать, что эти методы применимы ко всем Unity-играм.
В следующей части мы напишем свой собственный мод для Beat Saber: посмотрим, как моды обмениваются информацией с игрой, как модифицировать поведение оригинальной игры, а также воспользуемся Harmony — библиотекой для модификации C#-кода, которая используется моддерами в RimWorld, BATTLETECH, Cities: Skylines, Kerbal Space Program, Oxygen Not Included, Stardew Valley, Subnautica и многих других.
Источник