Простейший C# моддинг Spaceflight Simulator

ВАЖНО! Эта статья не является универсальным пособием по созданию модов любой сложности, но она поможет вам сделать первые шаги в увлекательный и сложный мир моддинга. Каждый мод требует индивидуального подхода и занимает время и усилия.

Что нас ждёт?

Практически все Unity-игры написаны на языке C#, что позволяет относительно просто изменять код или добавлять функционал к коду игры. Основной код компилируется в библиотеки классов - DLL-файлы. Большая часть логики игры обычно находится в Assembly-CSharp.dll.

Здесь вы познакомитесь с основами реверс-инжиниринга и основными инструментами работы с кодом на C#.

Это будет нелегко

Почему не Android?

Все действия из этой статьи можно проделать только на ПК-версии Spaceflight Simulator (и аналогично для других игр, которые есть на нескольких платформах). На Android и других мобильных устройствах код игры компилируется в такой формат, который нельзя просто открыть и изменить, поэтому модификация DLL невозможна.

Reverse-Engineering

Чтобы приступить к моддингу игры, необходимо «заглянуть под её капот» и понять, как она работает. Это и есть реверс-инжиниринг.

Для этого можно использовать программы ILSpy и dnSpy — инструменты для просмотра и редактирования C# DLL. Я буду использовать dnSpy v6.1.8.

Нас встречает такой интерфейс.

Простейший C# моддинг Spaceflight Simulator, image #1

Слева находится окно «Обозреватель сборок» — наше главное окошко для изучения игры. С его помощью можно заглянуть в сборки и узнать, какие классы, методы и переменные в них находятся.

Сборка - скомпилированный файл программы на C#

Теперь нужно загрузить все необходимые сборки в dnSpy. Для этого:

  1. В левом верхнем углу выберите «Файл» → «Открыть».
  2. Откроется проводник. Найдите папку с установленной игрой. В случае лицензионной версии обычно она находится по пути: C:\Program Files (x86)\Steam\steamapps\common\Spaceflight Simulator
  3. В этой папке откройте Spaceflight Simulator_Data\Managed и выделите все файлы внутри.
  4. Нажмите «Открыть», чтобы загрузить сборки в dnSpy.

Открыв все сборки, в Обозревателе сборок мы можем выбрать Assembly-CSharp.dll. Именно в этой сборке содержится основная логика игры. Остальные сборки — это системные и Unity-библиотеки. Однако это не значит, что они не важны!

Простейший C# моддинг Spaceflight Simulator, image #2

Теоретически можно работать только с Assembly-CSharp.dll, но остальные сборки помогают получить более полное понимание кода и дают возможность компилировать изменения прямо в dnSpy.

Следует понимать, что dnSpy показывает нам не исходный код, а лишь его представление - псевдокод.

Псевдокод — это приближённая версия исходного C# кода, которую dnSpy восстанавливает из скомпилированной сборки. Он очень похож на оригинальный код, но некоторые детали могут отличаться, а имена переменных иногда упрощаются.

Это создаёт некоторые трудности с чтением кода, а также с компиляцией отдельных методов и классов.

Идея, анализ и изменение кода

Прежде чем углубляться в код сборки, важно задать себе вопрос: что именно мы хотим изменить или добавить в игре? Только после того, как цель ясна, имеет смысл приступать к анализу кода.

Я поставил себе цель: изменить удельный импульс двигателей в игре. Значит, надо найти класс или метод, отвечающий за двигатели. Наибольшее внимание привлекают пространства SFS.Parts и SFS.Parts.Modules. Жмём на них и смотрим содержимое

Простейший C# моддинг Spaceflight Simulator, image #3

Пространство SFS.Parts не содержало ничего подходящего, а вот SFS.Parts.Modules содержит EngineModule. Скорее всего, то что нужно мне - там.

Простейший C# моддинг Spaceflight Simulator, image #4

Открыв этот класс, я начинаю искать нужную часть кода — способ задания удельного импульса двигателя.

Метод Draw уже содержит почти то, что мне нужно. В нём можно увидеть, как создаются и отображаются характеристики двигателя, которые появляются при нажатии на него в игре.

Простейший C# моддинг Spaceflight Simulator, image #5

drawer.DrawStat(40, (this.ISP.Value * (float)Base.worldBase.settings.difficulty.IspMultiplier).ToEfficiencyString(), null);

Как видно, удельный импульс (ISP) вычисляется как произведение базового удельного импульса двигателя на множитель сложности difficulty.IspMultiplier.

Нажимаю на ISP, и программа переносит меня в то место, где эта переменная объявлена.

Простейший C# моддинг Spaceflight Simulator, image #6

Однако там видно только объявление поля, а само значение явно не задаётся — значит продолжаю копать дальше. Скорее всего, игра получает значение удельного импульса для каждого двигателя из ассетов.

Нажимаю на IspMultiplier в методе Draw и попадаю на свойство IspMultiplier.

Простейший C# моддинг Spaceflight Simulator, image #7

Это свойство берёт текущий уровень сложности и находит подходящий множитель ISP в таблице значений ispMultipliers

Жму на последний difficulty и меня перекидывает к переменной, которая определяет текущий уровень сложности мира.

Простейший C# моддинг Spaceflight Simulator, image #8

Дальше идут массивы значений, которые содержат множители для трёх уровней сложностей. Значит, существует и массив ispMultipliers.

Простейший C# моддинг Spaceflight Simulator, image #9

Уже на данном этапе мы можем внести изменения в код и скомпилировать его.

Для этого жмём ПКМ по пустому пространству около нужных значений и выбираем «Изменить метод (C#)»

Простейший C# моддинг Spaceflight Simulator, image #10

Открывается окно изменения кода, где мы вправе поменять значения. Увеличиваю множитель в два раза и нажимаю компилировать. Готово!

Простейший C# моддинг Spaceflight Simulator, image #11

Теперь осталось в верхнем левом углу, во вкладке «Файл», выбрать «Сохранить модуль» — после этого все внесённые изменения будут применены в игре (значение удельных импульсов всех двигателей увеличится в два раза).

Однако у такого способа есть ряд недостатков:

  • Изменения становятся неотделимы от самой игры — они будут действовать постоянно, пока вы вручную не вернёте исходные значения.
  • Некоторые методы или классы могут не скомпилироваться корректно из-за ошибок при декомпиляции псевдокода.

Создание мода

В отличие от предыдущего способа, создание полноценной модификации лишено перечисленных недостатков, однако занимает больше времени и требует знаний C#.

Для создания мода нам понадобится среда разработки.
Я буду использовать Microsoft Visual Studio Community 2022 — это бесплатная и удобная IDE для работы с C#.

При установке Visual Studio важно выбрать нужную рабочую нагрузку -отметьте пункт «Разработка классических приложений .NET». Она включает всё необходимое для написания и сборки C#-проектов.

Если Visual Studio уже установлена, но в ней нет поддержки C#, откройте Visual Studio Installer и добавьте эту рабочую нагрузку вручную.

Простейший C# моддинг Spaceflight Simulator, image #12

Открываем Visual Studio и создаём новый проект. Далее выбираем «Библиотека классов (.NET Framework)»

Простейший C# моддинг Spaceflight Simulator, image #13
Простейший C# моддинг Spaceflight Simulator, image #14

Для удобства можно переименовать файл с кодом — кликните по Class1.cs левой кнопкой мыши и выберите «Переименовать».
Аналогично можно переименовать и сам проект, и решение.

Теперь нам нужно добавить ссылку на игру — файл Assembly-CSharp.dll.
Это позволит обращаться к её классам и методам напрямую.

  1. В Обозревателе решений найдите раздел «Ссылки» и выберите «Добавить ссылку…».
  2. В открывшемся окне перейдите в знакомую папку:
    ...\Spaceflight Simulator_Data\Managed\
  3. Выберите файл Assembly-CSharp.dll и нажмите «Добавить».
  4. После этого убедитесь, что в менеджере ссылок стоит галочка напротив этого файла.

Теперь нужно определить сам мод в игре.

  1. Сначала подключим пространство имён Modloader, чтобы иметь доступ к необходимым базовым классам:
  1. Затем создаём свой класс мода, который наследует базовый класс Mod:
  1. Перезапишем публичные свойства, которые характеризуют наш мод:
  • уникальный идентификатор (ModNameID),
  • название (ModName),
  • описание (ModDescription) и т.д.

Это позволяет модлоадеру распознать мод, отобразить его в списке и применить его функциональность.

Простейший C# моддинг Spaceflight Simulator, image #15

Вернёмся к массиву с множителями. Мы не можем просто изменить его напрямую или использовать что-то вроде SetValue, потому что он закрытый (private) и только для чтения (readonly). Обычные способы присвоения нового значения здесь не сработают, потому придётся использовать Harmony для безопасного перехвата и изменения поведения методов игры на лету.

Простейший C# моддинг Spaceflight Simulator, image #16

Теперь нам нужно добавить ссылку на Harmony.
Делаем это точно так же, как мы делали для Assembly-CSharp.dll:

  1. В Обозревателе решений выбираем «Ссылки» → «Добавить ссылку…».
  2. Находим библиотеку 0Harmony.dll и добавляем её в проект.
  3. Убедитесь, что в списке ссылок стоит галочка напротив файла.
Простейший C# моддинг Spaceflight Simulator, image #17

В начале кода мы подключаем необходимые пространства имён:

using Harmonylib; // для работы с Harmony

using SFS.WorldBase; // так как массив с множителями хранится именно здесь

  • Harmonylib позволяет использовать все возможности Harmony: создавать патчи, перехватывать методы и свойства.
  • SFS.WorldBase нужен, чтобы обращаться к массиву ispMultipliers и другим классам игры, которые находятся в этом пространстве имён.

С помощью Harmony мы создаём патч для свойства IspMultiplier класса Difficulty

Простейший C# моддинг Spaceflight Simulator, image #18

Здесь мы перехватываем вызов геттера IspMultiplier и с помощью метода Postfix изменяем результат, возвращаемый этим свойством (__result).

Внутри метода мы смотрим на текущую сложность игры (__instance.difficulty) и подставляем новые значения множителя ISP:

  • Normal → 2.0
  • Hard → 2.5
  • Realistic → 3.0

public override void Early_Load()
— это метод, который переопределяет базовый метод Early_Load из класса Mod.
Он вызывается очень рано при загрузке игры, ещё до того, как начнёт работать основной игровой код.

var harmony = new Harmony(ModNameID);
— создаём объект Harmony для нашего мода.
ModNameID используется как уникальный идентификатор патчей, чтобы они не конфликтовали с другими модами.

harmony.PatchAll();
— Harmony автоматически находит все классы с атрибутом [HarmonyPatch] в проекте и применяет их.

Сборка

Выбираем добавленные ссылки (например, Assembly-CSharp.dll и 0Harmony.dll) и внизу, в свойствах ссылки, находим параметр «Копировать локально».
Ставим его в False.

Это важно, чтобы при сборке нашей библиотеки (.dll) не появлялось множество лишних файлов, которые не нужны для работы мода

Простейший C# моддинг Spaceflight Simulator, image #19

Теперь откроем свойства нашего проекта, чтобы настроить сборку.

  1. В свойствах можно убедиться в названии сборки.
  2. Слева выбираем вкладку «Сборка».
  3. В поле «Выходной путь» указываем папку, куда будет собираться наш мод (обязательно в папке Mods в корневой папке игры).
ВАЖНО! Название сборки и название папки мода должны совпадать, иначе игра не сможет задетектить мод.
Простейший C# моддинг Spaceflight Simulator, image #20
Простейший C# моддинг Spaceflight Simulator, image #21

Сверху выбираем вкладку «Сборка» и нажимаем «Собрать решение»

Простейший C# моддинг Spaceflight Simulator, image #22

Снизу увидим окошко с тем, что наша сборка успешно собралась и где собралась.

Простейший C# моддинг Spaceflight Simulator, image #23

Готовый мод

Заходим в игру в меню Modloader. Мод корректно отображается.

Простейший C# моддинг Spaceflight Simulator, image #24

Удельный импульс успешно увеличился в два раза.

Простейший C# моддинг Spaceflight Simulator, image #25

Пример более сложного мода

Также я создал мод, который каждую секунду случайным образом меняет текстуру (ColorTex) бака

Я не буду объяснять, как он работает, так как это потребует целого ряда дополнительных статей и хороших знаний C# от вас. Однако это может послужить вам примером и помочь лучше разобраться в моддинге.

Простейший C# моддинг Spaceflight Simulator, image #26

Итог

В этой статье на моём примере вы познакомились с основами реверс-инжиниринга, разобрались с некоторыми важными определениями и на практическом примере увидели, как осуществляется моддинг игры на Unity.

Вы узнали, какие инструменты используются — dnSpy для изучения и анализа кода, Visual Studio для создания собственного мода, и Harmony для безопасного изменения поведения игры.

По этой статье вы шаг за шагом можете создать тот же мод, что создал я, а затем попробовать другие идеи, например: увеличить максимальное физическое ускорение времени. Это поможет вам набить руку и начать понимать написанное.

Однако многое зависит от вас: насколько усердно вы будете изучать C# и разбираться в коде, насколько глубоко захотите погрузиться в мир моддинга. Эта статья — лишь первый шаг на долгом и увлекательном пути, который потребует практики, времени, терпения и желания учиться.

267 views·7 shares