/
DiverseArrow54
/
MBM.ModLoader
Обзор
Документация
Войти
/
DiverseArrow54
/
MBM.ModLoader
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
HowToCreateMod.txt
885 строк
42 KB
Tsygan
v0.5.0
02 апр 2026, 06:24
02 апр 2026, 06:24
d1529d0
Код
Авторство
О чём код?
================================================================================ MBM.ModLoader v0.5.0 ================================================================================ Лоадер автоматически загружает все .dll файлы из папки "Mods" рядом с игрой. Ниже пошаговая инструкция создания мода. ================================================================================ 1. СОЗДАНИЕ ПРОЕКТА ================================================================================ 1) Откройте Visual Studio и создайте проект "Class Library". 2) Target Framework: .NET Framework 4.8.1 3) Назовите проект как хотите, например "MyMod". 4) Укажите версию мода в свойствах проекта (Properties → Assembly Information) или в .csproj: <PropertyGroup> <Version>1.0.0</Version> </PropertyGroup> Эта версия отображается в меню MODS рядом с именем мода. ================================================================================ 2. ПОДКЛЮЧЕНИЕ БИБЛИОТЕК ИГРЫ ================================================================================ Добавьте ссылки (References) на DLL из папки игры: <путь к игре>\MonsterBlackMarket_Data\Managed\ Минимально необходимые: - UnityEngine.CoreModule.dll (GameObject, MonoBehaviour, Debug и т.д.) Дополнительно, в зависимости от целей мода: - UnityEngine.UI.dll (Image, Button, Toggle и т.д.) - UnityEngine.UIModule.dll (Canvas, CanvasRenderer) - UnityEngine.IMGUIModule.dll (OnGUI) - Unity.TextMeshPro.dll (TextMeshProUGUI, TMP_Text) - Assembly-CSharp.dll (классы игры: GameManager, PlayerController и т.д.) В .csproj ссылки выглядят так: <Reference Include="UnityEngine.CoreModule"> <HintPath>ПУТЬ\MonsterBlackMarket_Data\Managed\UnityEngine.CoreModule.dll</HintPath> <Private>false</Private> </Reference> ВАЖНО: Ставьте <Private>false</Private>, чтобы DLL игры не копировались в папку мода при сборке — они уже есть у игры. ================================================================================ 3. ТОЧКА ВХОДА МОДА ================================================================================ Лоадер ищет в каждой загруженной DLL публичный статический метод Load() без параметров. Это точка входа вашего мода. Создайте публичный класс с таким методом: ------------------------------------------------------- using UnityEngine; namespace MyMod { public class MyModEntry { public static void Load() { Debug.Log("[MyMod] Мод загружен!"); // Ваш код инициализации мода здесь. // Например, создание GameObject с MonoBehaviour: var go = new GameObject("[MyMod]"); Object.DontDestroyOnLoad(go); go.AddComponent<MyModBehaviour>(); } } } ------------------------------------------------------- Требования к методу Load(): - public static void Load() - Без параметров - Класс должен быть public - Может быть в любом namespace - Если в DLL несколько классов с Load(), все они будут вызваны ================================================================================ 4. ПРИМЕР: МОД С MONOBEHAVIOUR ================================================================================ ------------------------------------------------------- using UnityEngine; namespace MyMod { public class MyModBehaviour : MonoBehaviour { private void Update() { // Вызывается каждый кадр if (Input.GetKeyDown(KeyCode.F5)) { Debug.Log("[MyMod] Нажата F5!"); } } private void OnGUI() { // Простой текст на экране (для отладки) GUI.Label(new Rect(10, 10, 300, 20), "MyMod активен"); } } } ------------------------------------------------------- ================================================================================ 5. ПРИМЕР: ДОСТУП К КЛАССАМ ИГРЫ ================================================================================ Если добавить ссылку на Assembly-CSharp.dll, можно напрямую обращаться к классам игры: ------------------------------------------------------- using UnityEngine; using MBMScripts; // пространство имён классов игры namespace MyMod { public class MyModEntry { public static void Load() { Debug.Log("[MyMod] GameManager: " + GameManager.Instance); } } } ------------------------------------------------------- Либо через рефлексию (без ссылки на Assembly-CSharp.dll): ------------------------------------------------------- using System; using System.Reflection; using UnityEngine; namespace MyMod { public class MyModEntry { public static void Load() { var gmType = Type.GetType("MBMScripts.GameManager, Assembly-CSharp"); var instanceProp = gmType?.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static); var gm = instanceProp?.GetValue(null); Debug.Log("[MyMod] GameManager через рефлексию: " + gm); } } } ------------------------------------------------------- ================================================================================ 6. ПЕРЕХВАТ И ПОДМЕНА ФУНКЦИЙ ИГРЫ (HARMONY) ================================================================================ Лоадер имеет встроенную библиотеку Harmony. Она автоматически находит все классы с атрибутом [HarmonyPatch] в вашем моде и применяет патчи. Вам НЕ нужно вызывать Harmony вручную — лоадер сделает это сам. Для использования добавьте ссылку на 0Harmony.dll из папки игры: <путь к игре>\MonsterBlackMarket_Data\Managed\0Harmony.dll --- Prefix: перехват ДО вызова оригинала --- Вызывается перед оригинальным методом. Вернуть false = пропустить оригинал, true = выполнить оригинал. ------------------------------------------------------- using HarmonyLib; using UnityEngine; namespace MyMod { // Перехватываем метод SomeMethod у GameManager [HarmonyPatch(typeof(MBMScripts.GameManager), "SomeMethod")] class PatchSomeMethod { static bool Prefix() { Debug.Log("[MyMod] SomeMethod перехвачен!"); return true; // true = вызвать оригинал после, false = пропустить } } } ------------------------------------------------------- --- Postfix: выполнение ПОСЛЕ оригинала --- Вызывается после оригинального метода. Можно изменить возвращаемое значение через ref __result. ------------------------------------------------------- using HarmonyLib; using UnityEngine; namespace MyMod { [HarmonyPatch(typeof(MBMScripts.GameManager), "GetSomeValue")] class PatchGetSomeValue { static void Postfix(ref int __result) { Debug.Log("[MyMod] Оригинал вернул: " + __result); __result = 999; // подменяем возвращаемое значение } } } ------------------------------------------------------- --- Полная подмена метода --- Prefix возвращает false — оригинал не вызывается вообще. ------------------------------------------------------- using HarmonyLib; using UnityEngine; namespace MyMod { [HarmonyPatch(typeof(MBMScripts.PlayerController), "TitleExitGame")] class PatchDisableExit { static bool Prefix() { Debug.Log("[MyMod] Выход из игры заблокирован!"); return false; // оригинальный метод НЕ вызовется } } } ------------------------------------------------------- --- Доступ к параметрам и экземпляру --- Параметры Prefix/Postfix совпадают с параметрами оригинала. __instance — ссылка на объект (для нестатических методов). ------------------------------------------------------- using HarmonyLib; using UnityEngine; namespace MyMod { [HarmonyPatch(typeof(MBMScripts.GameManager), "AddGold")] class PatchDoubleGold { // int amount — параметр оригинального метода AddGold(int amount) static void Prefix(ref int amount) { amount *= 2; // удваиваем золото Debug.Log("[MyMod] Золото удвоено: " + amount); } } } ------------------------------------------------------- ВАЖНО: - Имя класса патча может быть любым - Метод Load() НЕ обязателен если мод состоит только из патчей - Лоадер вызывает harmony.PatchAll(assembly) автоматически - Документация Harmony: https://harmony.pardeike.net/ ================================================================================ 7. НАСТРОЙКИ МОДА (ModSettings API) ================================================================================ Лоадер предоставляет систему настроек. Мод регистрирует свои параметры в Load(), а лоадер автоматически: - Сохраняет/загружает их в Mods\modsettings.cfg - Показывает в меню MODS кнопку "..." рядом с модом - При нажатии открывает панель с тогглами, полями ввода и выпадающими списками --- Регистрация настроек --- ------------------------------------------------------- using MBM.ModLoader.Settings; using UnityEngine; namespace MyMod { public class MyModEntry { public static void Load() { // Bool — отображается как ON/OFF переключатель ModSettings.RegisterBool("MyMod", "DoubleGold", true, "Удвоение золота"); // Int — поле ввода числа ModSettings.RegisterInt("MyMod", "BonusGold", 100, "Бонус к золоту"); // Float — поле ввода дробного числа ModSettings.RegisterFloat("MyMod", "SpeedMultiplier", 1.5f, "Множитель скорости"); // String — текстовое поле ModSettings.RegisterString("MyMod", "PlayerTitle", "Modded", "Титул игрока"); // Dropdown — выпадающий список ModSettings.RegisterDropdown("MyMod", "Difficulty", new[] { "Легко", "Нормально", "Сложно" }, defaultIndex: 1, description: "Уровень сложности"); // CheckboxGroup — группа чекбоксов (всплывающая сетка 2 столбца) ModSettings.RegisterCheckboxGroup("MyMod", "Features", new[] { "Авто-лечение", "Авто-лут", "Мини-карта", "Быстрый бег" }, defaults: new[] { true, false, true, false }, description: "Включённые функции"); Debug.Log("[MyMod] Loaded!"); } } } ------------------------------------------------------- --- Чтение настроек в коде мода --- Значение можно читать в любой момент (Update, Prefix, Postfix и т.д.): ------------------------------------------------------- // Не забудьте: using MBM.ModLoader.Settings; // В Harmony-патче или MonoBehaviour: if (ModSettings.GetBool("MyMod", "DoubleGold")) { gold *= 2; } int bonus = ModSettings.GetInt("MyMod", "BonusGold"); float speed = ModSettings.GetFloat("MyMod", "SpeedMultiplier"); string title = ModSettings.GetString("MyMod", "PlayerTitle"); // Dropdown — получить выбранный индекс или текст: int diffIdx = ModSettings.GetDropdown("MyMod", "Difficulty"); // 0, 1, 2... string diffText = ModSettings.GetDropdownValue("MyMod", "Difficulty"); // "Нормально" // CheckboxGroup — массив bool или массив выбранных строк: bool[] features = ModSettings.GetCheckboxGroup("MyMod", "Features"); // features[0]=true, features[1]=false, ... string[] active = ModSettings.GetCheckedOptions("MyMod", "Features"); // { "Авто-лечение", "Мини-карта" } ------------------------------------------------------- --- Изменение из кода --- ------------------------------------------------------- ModSettings.Set("MyMod", "DoubleGold", false); // программно выключить ------------------------------------------------------- --- Мгновенная реакция на изменение настроек --- Настройки применяются сразу при изменении в UI. Есть два способа получать уведомления: 1) Подписка на конкретную настройку через OnChanged: ------------------------------------------------------- public static void Load() { ModSettings.RegisterBool("MyMod", "DoubleGold", true, "Удвоение золота"); // Колбэк вызывается: // - при старте игры (с загруженным/дефолтным значением) // - при изменении значения в меню ModSettings.OnChanged("MyMod", "DoubleGold", (newValue) => { bool enabled = (bool)newValue; Debug.Log("[MyMod] Удвоение золота: " + (enabled ? "ВКЛ" : "ВЫКЛ")); }); } ------------------------------------------------------- Колбэки OnChanged автоматически вызываются при загрузке настроек из файла, поэтому мод может использовать их для инициализации — не нужно дублировать код чтения настроек вручную. 2) Подписка на глобальное событие (любая настройка любого мода): ------------------------------------------------------- ModSettings.OnSettingChanged += (modName, key, newValue) => { Debug.Log($"[MyMod] Настройка {modName}/{key} = {newValue}"); }; ------------------------------------------------------- --- Группировка настроек (group) --- Все методы Register*() принимают необязательный параметр group. Настройки с одинаковым group объединяются визуально в один блок с общим фоном: ------------------------------------------------------- ModSettings.RegisterBool("MyMod", "AutoHeal", true, "Авто-лечение", group: "Автоматизация"); ModSettings.RegisterBool("MyMod", "AutoLoot", false, "Авто-лут", group: "Автоматизация"); ModSettings.RegisterInt("MyMod", "HealThreshold", 50, "Порог здоровья (%)", group: "Автоматизация"); ------------------------------------------------------- Настройки без group отображаются как раньше — каждая в своём блоке. Группировка влияет только на визуальное оформление, API чтения/записи не меняется. --- Условная видимость (visibleKey / visibleWhen) --- Настройки можно показывать/скрывать в зависимости от значения другой настройки в той же панели мода. 1) При регистрации дочерней настройки укажите visibleKey — произвольный строковый идентификатор (не совпадает с key настройки): ------------------------------------------------------- ModSettings.RegisterBool("MyMod", "EnableAdvanced", false, "Включить расширенные настройки"); ModSettings.RegisterInt("MyMod", "Detail", 5, "Уровень детализации", visibleKey: "advanced"); ModSettings.RegisterFloat("MyMod", "Speed", 1.0f, "Множитель скорости", visibleKey: "advanced"); ------------------------------------------------------- 2) Затем вызовите SetVisibleWhen для настройки-родителя. Словарь: строковое значение родителя → массив visibleKey для показа. ------------------------------------------------------- ModSettings.SetVisibleWhen("MyMod", "EnableAdvanced", new Dictionary<string, string[]> { { "True", new[] { "advanced" } } }); ------------------------------------------------------- Пример с Dropdown: ------------------------------------------------------- ModSettings.RegisterDropdown("MyMod", "Mode", new[] { "Простой", "Обычный", "Эксперт" }, 0, "Режим"); ModSettings.RegisterBool("MyMod", "Hints", true, "Подсказки", visibleKey: "simple"); ModSettings.RegisterInt("MyMod", "ExpertParam", 10, "Параметр эксперта", visibleKey: "expert"); ModSettings.SetVisibleWhen("MyMod", "Mode", new Dictionary<string, string[]> { { "0", new[] { "simple" } }, { "2", new[] { "expert" } } }); ------------------------------------------------------- Правила: - Работает только в рамках одной панели мода (одного modName). - Скрытые настройки: Get-методы возвращают значение по умолчанию. - Значения скрытых настроек сохраняются в файл как обычно. - Если все настройки в группе (group) скрыты — блок группы тоже скрывается. - UI обновляется мгновенно при изменении настройки-родителя. - visibleKey можно комбинировать с group. ВАЖНО: - Первый аргумент (modName) должен совпадать с именем DLL без расширения - Настройки сохраняются автоматически при изменении через UI или Set() - Файл настроек: Mods\modsettings.cfg (ini-формат с секциями) - Если настройка не зарегистрирована, GetBool вернёт false, GetInt — 0 и т.д. - Настройки сохраняются между сеансами даже для отключённых модов. Кнопка "..." доступна и когда мод выключен — пользователь может настроить мод заранее, до его включения. - При повторном включении мода Register*() обновит тип и описание, но сохранит значение, установленное пользователем. - Dropdown сохраняется как int (индекс выбранного варианта). В UI отображается кнопка с текущим значением и стрелкой ▼. При нажатии открывается выпадающий список вариантов. Варианты задаются массивом строк при регистрации. - CheckboxGroup сохраняется как строка выбранных индексов через запятую (например "0,2,5"). В UI отображается кнопка «N / всего ▼», при нажатии — всплывающая сетка чекбоксов в 2 столбца. - Параметр group объединяет настройки визуально в один блок с общим фоном. Группировка не влияет на сохранение. - Параметр visibleKey + SetVisibleWhen позволяют скрывать/показывать настройки в зависимости от значения другой настройки в той же панели. Скрытые настройки возвращают значение по умолчанию через Get-методы. ================================================================================ 8. ЛОКАЛИЗАЦИЯ МОДА (автоматическая передача языка игры) ================================================================================ Лоадер определяет текущий язык игры и передаёт его модам через API. Это позволяет моду поддерживать несколько языков БЕЗ отдельной настройки выбора языка — язык берётся прямо из игры. Доступные свойства (MBM.ModLoader.Core.Localization): Localization.CurrentLanguageCode — код языка: "ru", "en", "ja", "ko", "zh-CN", "zh-TW", "de" Localization.CurrentCulture — CultureInfo текущего языка (для .resx и форматирования) --- Подписка на смену языка --- Когда игрок меняет язык в настройках игры, вызывается событие: Localization.OnLanguageChanged — event Action<string> параметр — новый код языка --- Пример: мод с .resx ресурсами --- 1) Создайте файлы ресурсов: - Properties\MyModStrings.resx (английский, по умолчанию) - Properties\MyModStrings.ru.resx (русский) - Properties\MyModStrings.ja.resx (японский) 2) В коде мода: ------------------------------------------------------- using System.Globalization; using MBM.ModLoader.Core; using UnityEngine; namespace MyMod { public class MyModEntry { public static void Load() { // При загрузке — применяем текущий язык игры ApplyLanguage(Localization.CurrentLanguageCode); // При смене языка в настройках — обновляем тексты Localization.OnLanguageChanged += ApplyLanguage; Debug.Log("[MyMod] Loaded, lang: " + Localization.CurrentLanguageCode); } private static void ApplyLanguage(string langCode) { // CultureInfo для .resx — автоматически подберёт нужный файл var culture = Localization.CurrentCulture; string greeting = Properties.MyModStrings.ResourceManager .GetString("Greeting", culture); Debug.Log("[MyMod] " + greeting); } } } ------------------------------------------------------- --- Пример: мод с простым словарём --- Если .resx не нужен, можно использовать простой словарь: ------------------------------------------------------- using System.Collections.Generic; using MBM.ModLoader.Core; using UnityEngine; namespace MyMod { public class MyModEntry { static readonly Dictionary<string, string> Greetings = new Dictionary<string, string> { { "en", "Hello!" }, { "ru", "Привет!" }, { "ja", "こんにちは!" }, }; public static void Load() { Localization.OnLanguageChanged += OnLangChanged; OnLangChanged(Localization.CurrentLanguageCode); } private static void OnLangChanged(string lang) { if (!Greetings.TryGetValue(lang, out string text)) text = Greetings["en"]; // fallback Debug.Log("[MyMod] " + text); } } } ------------------------------------------------------- ВАЖНО: - Язык определяется автоматически из настроек игры - Моду НЕ нужна собственная настройка выбора языка - Событие OnLanguageChanged вызывается при каждой смене языка в игре - Поддерживаемые коды: "ru", "en", "ja", "ko", "zh-CN", "zh-TW", "de" - Пространства имён лоадера: MBM.ModLoader.Core — Localization, Log, Loader MBM.ModLoader.Settings — ModSettings, ModSettingEntry, ModSettingType MBM.ModLoader.Mods — ModInfo, ModConfig MBM.ModLoader.UI — ModMenuUI - Для добавления ссылки на лоадер в .csproj мода: <Reference Include="MBM.ModLoader"> <HintPath>ПУТЬ\MBM.ModLoader\MBM.ModLoader.dll</HintPath> <Private>false</Private> </Reference> --- Локализация описаний настроек мода --- Если мод регистрирует настройки (ModSettings), описания можно обновлять при смене языка через ModSettings.SetDescription(): ------------------------------------------------------- using System.Collections.Generic; using MBM.ModLoader.Core; using MBM.ModLoader.Settings; using UnityEngine; namespace MyMod { public class MyModEntry { static readonly Dictionary<string, string> DescDoubleGold = new Dictionary<string, string> { { "en", "Double gold" }, { "ru", "Удвоение золота" }, { "ja", "ゴールド2倍" }, }; public static void Load() { // Регистрируем с описанием на текущем языке string lang = Localization.CurrentLanguageCode; ModSettings.RegisterBool("MyMod", "DoubleGold", true, GetDesc(DescDoubleGold, lang)); // При смене языка — обновляем описания Localization.OnLanguageChanged += OnLangChanged; } private static void OnLangChanged(string lang) { ModSettings.SetDescription("MyMod", "DoubleGold", GetDesc(DescDoubleGold, lang)); } private static string GetDesc(Dictionary<string, string> dict, string lang) { if (dict.TryGetValue(lang, out string text)) return text; return dict["en"]; // fallback } } } ------------------------------------------------------- Лоадер автоматически перерисовывает панель настроек при смене языка, поэтому новое описание появится сразу. ================================================================================ 9. СОХРАНЕНИЕ ДАННЫХ МОДА (ModSaveData API) ================================================================================ Лоадер предоставляет систему сохранения данных модов, привязанных к слоту сохранения игры. Когда игрок сохраняет/загружает игру, данные модов автоматически сохраняются/загружаются вместе с ней. Все моды пишут в один файл ModSaveData{slot}.dat в папке SaveData игры (рядом с PlayerData{slot}.sav). --- Запись данных --- Значения можно записывать в любой момент геймплея: ------------------------------------------------------- using MBM.ModLoader.Settings; // В Harmony-патче, MonoBehaviour или любом другом месте: ModSaveData.SetInt("MyMod", "Gold", 500); ModSaveData.SetBool("MyMod", "QuestDone", true); ModSaveData.SetFloat("MyMod", "PlayTime", 123.45f); ModSaveData.SetString("MyMod", "LastZone", "Dungeon"); ------------------------------------------------------- --- Чтение данных --- ------------------------------------------------------- int gold = ModSaveData.GetInt("MyMod", "Gold", 0); // 0 = значение по умолчанию bool done = ModSaveData.GetBool("MyMod", "QuestDone", false); float time = ModSaveData.GetFloat("MyMod", "PlayTime", 0f); string zone = ModSaveData.GetString("MyMod", "LastZone", ""); ------------------------------------------------------- --- Проверка и удаление --- ------------------------------------------------------- bool exists = ModSaveData.HasKey("MyMod", "Gold"); // true/false ModSaveData.DeleteKey("MyMod", "Gold"); // удалить ключ ModSaveData.ClearMod("MyMod"); // удалить все данные мода ------------------------------------------------------- --- События сохранения/загрузки --- Моды могут подписаться на события, чтобы подготовить данные перед сохранением или обработать их после загрузки: ------------------------------------------------------- using MBM.ModLoader.Settings; using UnityEngine; namespace MyMod { public class MyModEntry { private static int _gold = 0; public static void Load() { // Перед сохранением — записать текущие значения ModSaveData.OnBeforeSave += (slot) => { ModSaveData.SetInt("MyMod", "Gold", _gold); Debug.Log("[MyMod] Данные сохранены в слот " + slot); }; // После загрузки — прочитать сохранённые значения ModSaveData.OnAfterLoad += (slot) => { _gold = ModSaveData.GetInt("MyMod", "Gold", 0); Debug.Log("[MyMod] Данные загружены из слота " + slot); }; } } } ------------------------------------------------------- --- Текущий слот --- ------------------------------------------------------- int slot = ModSaveData.CurrentSlot; // -1 если ничего не загружено ------------------------------------------------------- ВАЖНО: - Первый аргумент (modName) должен совпадать с именем DLL без расширения - Данные сохраняются автоматически при сохранении игры (хук на PlayData.Save) - Данные загружаются автоматически при загрузке игры (хук на PlayData.Load) - Файл: <AppData>\LocalLow\<company>\<game>\SaveData\ModSaveData{slot}.dat - Формат файла: ini (секции [ModName], ключи key=value) - Если слот не загружен, Get-методы возвращают значения по умолчанию - OnBeforeSave вызывается ДО записи файла - OnAfterLoad вызывается ПОСЛЕ чтения файла (или если файла нет) - Отличие от ModSettings: настройки (ModSettings) — глобальные, а данные (ModSaveData) — привязаны к конкретному слоту сохранения ================================================================================ 10. СБОРКА И УСТАНОВКА ================================================================================ 1) Соберите проект (Build). 2) Скопируйте итоговый файл MyMod.dll из папки bin\Debug\ (или Release) в папку: <папка с игрой>\Mods\ Пример полного пути: C:\...\MonsterBlackMarket\Mods\MyMod.dll 3) Запустите игру — лоадер автоматически найдёт и загрузит ваш мод. 4) В главном меню нажмите кнопку MODS, чтобы увидеть список модов и включать/выключать их. Та же кнопка доступна в ESC-меню во время игры. ================================================================================ 11. ВКЛЮЧЕНИЕ / ВЫКЛЮЧЕНИЕ МОДОВ ================================================================================ - В главном меню есть кнопка "MODS" (между "ЗАГРУЗИТЬ" и "НАСТРОЙКИ"). - Та же кнопка доступна в ESC-меню во время игры (после LOAD). - Нажмите на неё — откроется панель со списком всех модов. - Рядом с именем каждого мода отображается его версия (из AssemblyVersion). Версия отображается даже для отключённых модов. - Переключайте ON/OFF для каждого мода. - Кнопка "..." (настройки мода) доступна даже для отключённых модов, если у них были сохранены настройки ранее. - Настройки сохраняются в файл: Mods\modconfig.cfg - Изменения вступают в силу после перезапуска игры. --- Раздел «Для разработчиков» --- Под списком модов расположена сворачиваемая секция «Для разработчиков». В ней отображаются инструменты из папки MBM.ModLoader\DevTools\. Каждая подпапка = один инструмент (например UnityExplorer). Инструменты включаются/выключаются независимо (требуется перезапуск). Для каждого инструмента отображается версия и описание. ================================================================================ 12. ОТЛАДКА ================================================================================ Используйте Debug.Log() / Debug.LogWarning() / Debug.LogError() для вывода сообщений. Логи можно просмотреть в файле: <папка с игрой>\MonsterBlackMarket_Data\output_log.txt Логи самого лоадера пишутся в отдельный файл: <папка с игрой>\ModLoader.log --- Встроенная консоль (F12) --- Лоадер имеет встроенное окно консоли для отладки в реальном времени. Консоль оформлена в стилистике игры (кастомные спрайты, шрифт NotoSansJP). Нажмите F12 в любой момент — откроется окно с логами: - Все сообщения Debug.Log / LogWarning / LogError отображаются в реальном времени с цветовой подсветкой - Фильтры Info / Warn / Error — показывайте только нужные типы - Счётчики сообщений по типам - Кнопка "Очистить" / "Clear" — сброс логов - Кнопка "Pin" — закрепляет окно консоли поверх всех окон - При открытии загружается история из ModLoader.log - Окно можно перетаскивать, ресайзить и разворачивать - Повторное нажатие F12 — скрыть/показать окно Это удобнее, чем искать в файле output_log.txt — видно сразу, без перезапуска игры. Рекомендуется добавлять префикс мода: Debug.Log("[MyMod] сообщение"); ================================================================================ 13. СТРУКТУРА ФАЙЛОВ ================================================================================ MonsterBlackMarket\ ├── MBM.ModLoader\ ← папка мод лоадера │ ├── MBM.ModLoader.dll ← сам лоадер │ ├── 0Harmony.dll ← библиотека перехвата функций │ ├── Accessibility.dll ← библиотека консоли │ ├── System.Windows.Forms.dll ← библиотека консоли │ ├── Mono.Posix.dll ← библиотека консоли │ ├── Localization\ ← локализация лоадера │ │ └── ru\ ← русские ресурсы │ │ └── MBM.ModLoader.resources.dll │ └── DevTools\ ← инструменты разработчика (опционально) │ └── UnityExplorer\ ← например UnityExplorer │ ├── UnityExplorer.dll │ └── ... ├── MonsterBlackMarket.exe ├── ModLoader.log ← лог работы лоадера ├── MonsterBlackMarket_Data\ │ └── Managed\ │ ├── Assembly-CSharp.dll ← пропатченная DLL (вызывает лоадер) │ ├── UnityEngine.CoreModule.dll │ └── ... └── Mods\ ├── modconfig.cfg ← конфиг включения/выключения модов ├── modsettings.cfg ← настройки модов ├── MyMod.dll ← ваш мод └── AnotherMod.dll ← ещё один мод ================================================================================ КРАТКАЯ СВОДКА ================================================================================ Версия лоадера: 0.5.0 Target Framework: .NET Framework 4.8.1 Точка входа: public static void Load() (без параметров) Перехват функций: [HarmonyPatch] атрибуты (применяются автоматически) Версия мода: AssemblyVersion из DLL (отображается в меню MODS) Папка модов: <игра>\Mods\ Расширение: .dll Конфиг: Mods\modconfig.cfg (создаётся автоматически) Настройки: Mods\modsettings.cfg (глобальные настройки модов) Данные сохранений: SaveData\ModSaveData{slot}.dat (данные привязанные к слоту) ================================================================================