Что такое mixin в майнкрафт
Миксины в основном предназначены для объемных проектов, меняющих много исходного кода. Но они существенно облегчают жизнь, если грамотно настроить рабочее пространство и знать как их использовать. С их помощью можно с легкостью изменить логику работы ванильных механик и добавить свои. Также миксины могут стать интересной альтернативой IEEP и Capability от Forge.
Миксины лучше писать только на Java. На других языках (scala, kotlin) писать не рекомендуется по причине того, что будет сложно реализовывать некоторые хуки, да и с java байткодом понятнее всего взаимодействовать, используя java.
- Изменять, дополнять методы классов (частично и полностью) множеством способов, вставлять свой код практически в любое место метода
- Перехватывать и менять возвращаемое из функций значение (в необходимом методе)
- Менять значения аргументов метода при его вызове, а также значения локальных переменных внутри метода
- Дополнять классы своими полями и методами
- Практически не волноваться об обфускации и маппингах
- Практически не волноваться с совместимостью модов, также использующих миксины
- Почти не волноваться обо всех гадких вещах, связанных с трансформерами Forge и модификацией байткода
- Среда разработки (желательно IntelliJ IDEA)
- Настоятельно рекомендую установить плагин Minecraft Dev Minecraft Dev for IntelliJ
Скопируйте проект-пример для вашей версии из репозитория
Основные изменения в build.gradle
Предпочтительно за основу взять build.gradle нужной вам версии из репозитория.
В примере на 1.7 используется форк ForgeGradle 1.2, позволяющий использовать последние версии сборщика Gradle (изначально он работает только с Gradle 2.8, который даже уже не поддерживается IntelliJ IDEA). Это позволяет нам не проводить танцы с бубном для смены IDEA JRE и без проблем пользоваться новыми возможностями.
В примере на 1.12 и 1.15 используется ForgeGradle 4+ (изначально для работы с 1.12 требовался ForgeGradle 2.3). Он позволяет нам использовать систему Миксинов и плагин MixinGradle последних версий.
Добавились репозиторий Sponge, плагин MixinGradle (не для 1.7), зависимость от Mixins необходимой версии (для 1.7 самой последней является 0.7.11, версии выше работают крайне нестабильно), процессор аннотаций Mixins (проверяет правильность написанных миксинов, генерирует карты имен).Для версии 1.7 нет плагина MixinGradle, так что некоторые действия придется дописать.
Не забудьте изменить имена mixinSrg и mixinRefMapName под ваш мод.
Для того, чтобы мод определялся как миксин, добавляем информацию о миксине в MANIFEST.MF.
Магический файл mixins.json
Создадим новый файл в корне папки resources с названием mixins.[название мода].json.
Именно его мы указывали в MANIFEST.MF . Он сообщает системе о версии миксинов, которую нужно использовать, о том, какие миксины нужно применить, на какой стороне, где они находятся и тд.
Его наполнение будет примерно таким:
Из этих параметров нас интересуют:
minVersion - Минимальная версия Mixins, необходимая для работы наших миксинов.
package - Папка с вашими миксинами. В ней должны находится ТОЛЬКО миксины, так как эта папка убирается из classpath при запуске. Интерфейсы и вспомогательные классы следует располагать в папках выше этой.
Я обычно использую папку "[путь и название мода].mixin" для интерфейсов и всп. классов, и "[путь и название мода].mixin.impl" для самих миксинов.
В этом случае "package" будет именно "[путь и название мода].mixin.impl"
refmap - Пусть к автоматически сгенерированному refmap (из build.gradle)
mixins - Перечисление всех ваших миксинов внутри package. Эти миксины будут применены и на сервере, и на клиенте. Если нужно применить миксин только на одной стороне, используй client и server .
Сборка и запуск мода
Чтобы собрать наш мод, нужно нажать кнопочку build. Если все прошло хорошо, мы увидим наш джарник, а в нем файлы mixins.[название мода].json и mixins.[название мода].refmap.json , а также в магические строки в файле META-INF/MANIFEST.MF .
Запускать и дебажить мод следует через сгенерированные скрипты запуска.
На версиях 1.12+ в раздел "runs" следует добавить дополнительные аргументы запуска:
в параметры запуска (arguments) необходимо добавить строку
--tweakClass org.spongepowered.asm.launch.MixinTweaker --mixin mixins.[название мода].json
Запуск мода после сборки
Версии 1.7 и 1.12.
Для того, чтобы система миксинов начала работать, необходимо добавить и развернуть саму либу миксинов, которой изначально нет на клиенте/сервере. В среде разработки это происходило за счет зависимости от Mixins и аргументов запуска. Вариантов решения два: либо засовывать систему миксинов в каждый мод, использующий ее, либо поставить мой вспомогательный мод JustMixins
Правильнее всего будет поставить нормальный мод для инициализации миксинов (Grimoire для 1.7.10 и MixinBootstrap для 1.12+).
Версии 1.15+.
Версии 1.15 и 1.16 уже включают в себя систему миксинов, так что сторонних действий не требуется.
Создаем первый миксин
На этом этапе у вас должна быть настроена рабочая среда, создан Forge проект и добавлены основные файлы. Здесь мы рассмотрим, как создать свой первый миксин, как добавить новое поле или метод, как к ним обратиться и как быстро и просто дополнить существующие методы.
Добавление новых полей и методов в существующие классы
Рассматривать будем заезженную тему в новом свете - добавление некого нового абстрактного ресурса - Эссенции. Предположим эссенцию будут иметь возможность содержать любые вещи, но мы рассмотрим игрока. Максимум эссенции игрока будет равен его уровню.
Создадим интерфейс EssenceContainer , который будут реализовывать классы, имеющие эссенцию, а также новый пакет с названием mixin и класс MixinEntityPlayer с аннотацией @Mixin . Класс следует сделать абстрактным, это позволит не реализовывать методы, взятые из целевого класса.
В аннотации @Mixin укажем целевой класс - тот, к которому будет применен данный миксин. Так как пока что мы добавляем эссенцию только игроку, целевой класс - EntityPlayer .
(. ) Не забудьте добавить MixinEntityPlayer в mixins.[название мода].json
Простым движением руки - объявлением нового поля и методов, мы говорим системе миксинов изменить байткод класса EntityPlayer, добавив совершенно новые поля и методы. Теперь у каждого игрока потенциально будет эссенция =).
Аннотация Shadow
Эта аннотация указывает на то, что данное поле или метод ссылается на существующее поле или метод в целевом классе. В данном случае в классе EntityPlayer есть публичное поле experienceLevel, и мы хотим использовать его значение в нашем миксине в методе getMaxEssence() . Аналогично можно вызывать методы или изменять значения полей целевого класса.
Параметр remap в Mixin, Shadow, Inject и всех других местах
Параметр remap (true по умолчанию) отвечает за то, подвергается ли поле ремаппингу после компиляции. Чаще всего работает такое правило: если это поле/метод из ванильного кода - ремаппинг необходим. Если пытаемся получить доступ, изменить или сделать еще что-то с кодом Forge или других модов - обязательно ставим remap = false .
Информация про обфускацию и ремаппинг, для чего нужен тот самый магический refmap, и как же все это работает, вот (базовая инфа, рус) и вот (более подробно)
Простейшее дополнение существующих методов (хуки)
Ну вот мы и добрались до самого интересного. Система миксинов в сочетании со средой разработки и плагином Minecraft Dev позволяет полностью избавить мододела от бесполезной работы по поиску опкодов, линий для вставки кода, определения параметров и прочей ереси, с которой сталкивались те, кто работал с хуклибой, а не то и с кормодами.
Разберем же наиболее используемые приемы! Продолжим наш пример с эссенцией. Да, она теперь есть у игрока, но… Она не сохраняется после выгрузки игрока из памяти, да и вообще не изменяется! Исправим же это =)
Для начала разберемся, как сохранить количество нашей эссенции на века. Для хранения параметров сущностей используется NBT, следовательно наиболее логично будет влезть в методы writeEntityToNBT и readEntityFromNBT целевого класса EntityPlayer .
Аннотация Inject
Аннотация @Inject указывает, что система миксинов должна вставить вызов нашего метода-обработчика в целевой метод в определенном месте. Параметр method указывает на метод в целевом классе, в который необходимо внедриться. Параметр at указывает, в какое место/места это нужно сделать. Чаще всего используются @At("HEAD") - самое начало метода, и @At("TAIL") - перед самым последним return из метода. Также можно внедриться после вызова определенного метода, доступа к полю, с определенным сдвигом и тд, для полного списка возможностей читай далее раздел магии.
Создадим метод onWriteEntityToNBT и onReadFromNBT , настроим аннотации и добавим необходимый нам код (в данном случае - сохранение и загрузка поля essence из NBT).
Готово! Теперь, когда Minecraft будет вызывать эти методы при сохранении и загрузке игрока, он также будет сохранять и загружать значения нашей эссенции.
Использование новых методов класса
Для красоты примера добавим немного логики нашему моду-примеру. Пусть эссенция будет необходима для того, чтобы ломать блоки, а зарабатываться она будет при нанесении урона живым существам (приветик моей шизе).
Чтобы в использовать новые поля и методы, достаточно просто сделать каст объекта EntityPlayer к нашему интерфейсу (как ни странно, компилятор Java позволяет это сделать, даже если изначально класс этот интерфейс не реализует).
Создадим слушатель событий на эти два ивента:
Если не получается сделать прямой каст final класса (например ItemStack ) к нашему интерфейсу, можно прибегнуть к хитрости - сначала сделать каст к Object , а затем к интерфейсу.
Также для доступа к полям и методам можно использовать рефлексию, но делать этого я вам крайне не советую…
Попрошу отметить, что пример создан только чтобы показать самую малость возможностей миксинов, а не для реализации полной новой механики!
Подробнее про магию
В этом разделе подробнее разберем основные возможности и подводные камни системы миксинов.
Мистический Inject - тут добавим, тут отрежем
Указывает, что система миксинов должна вставить обратный вызов к нашему обработчику в целевом методе. Используем его, когда хотим добавить какую-либо логику к существующему методу, или же прервать его выполнение раньше, чем задумано изначально.
Методы, аннотированные @Inject всегда должны быть VOID и иметь CallbackInfo/CallbackInfoReturnable в параметрах (этот объект генерируется обратным вызовом и служит дескриптором, который можно использовать при отменяемых инъекциях, подробнее далее).
Простейшее использование
Вот простейший пример использования при инъекции в void метод без параметров:
Получаем аргументы целевого метода
При внедрении в метод с аргументами, их можно передать в метод-обработчик, указав эти аргументы перед CallbackInfo . Пример:
Завершающие инъекции
До этого момента наши инъекции не меняли структуру целевого метода, они просто вызывали нашу функцию-обработчик. Завершающие инъекции позволяют нам преждевременно завершить (вставить return) целевой метод.
Для этого необходимо в аннотации @Inject выставить параметр cancellable = true и в нужном нам месте вызвать cancel() . Пример:
В приведенном выше примере обработчик проверяет, установлена ли позиция в (0, 0), и если условие выполнено, вместо стандартного поведения целевого метода он выполняет некоторую логику, помечая обратный вызов как завершенный, чтобы заставить целевой метод (setPos) завершиться сразу после завершения обработчика.
Целевые методы, возвращающие тип
До сих пор мы внедрялись только в void методы. При внедрении в метод с возвращаемым типом, в обработчике вместо CallbackInfo следует указать CallbackInfoReturnable<ВозвращаемыйТип> . Он отличается от своего собрата тем, что при завершающей инъекции следует использовать не cancel() , а setReturnValue() . Пример:
Больше информации про Inject тут.
SpongePowered/Mixin
- В большинстве случаев, инжектор вставит код ПЕРЕД опкодом, найденным точкой внедрения. Вот пара примеров:
- RETURN определяет коды операций RETURN в методе, внедрение происходит непосредственно перед возвратом метода
- HEAD идентифицирует первый код операции в методе, внедрение происходит в самом начале метода
- INVOKE (см. ниже) идентифицирует вызов метода, внедрение происходит непосредственно перед вызовом найденного метода
- Поскольку точки внедрения фактически являются запросами, они могут не возвращать результатов (не найти опкод, удовлетворяющий параметрам поиска).
- Хотя точки внедрения очень гибки, не стоит забывать, что код, в который мы внедряемся, в будущем может измениться (выйти новая версия мода, например).
- Определение более сложных точек внедрения является одним из немногих мест в системе миксинов, где вам придется испачкать руки и взглянуть на байт-код целевого метода. Это часто необходимо, чтобы выбрать наиболее подходящее место для внедрения кода (хотя плагин Minecraft Dev прекрасно справляется с большинством таких ситуаций).
- INVOKE - Находит вызов указанного в target метода.
- FIELD - Находит обращение к указанному в target полю (чтение/запить)
- NEW - Находит опкод new
- JUMP - Находит код операции перехода (IF любого типа) и вставляет перед ним
- INVOKE_ASSIGN - Находит вызов метода, который возвращает значение и внедряет его сразу же после присвоения значения локальной переменной. Обратите внимание, что это единственная точка, которая внедряет обработчик после своей цели
- RETURN соответствует всем кодам операций RETURN в целевом методе
Больше информации о точках внедрения тут, а также явадоках к ним.
SpongePowered/Mixin.
Таинственный Redirect - перенаправляем код в другое русло
Указывает, что система миксинов должна заменить указанный вызов метода, доступ к полю или создание объекта (через ключевое слово new) на вызов нашего обработчика. К перенаправлению стоит прибегать, когда нужно заменить, или же вовсе отменить вызов какого-либо метода, изменить значение получаемого поля или создание нового объекта в целевом методе.
MixinBootstrap [1.17.1] [1.16.5] [1.15.2] [1.14.4] [1.12.2]
Мод MixinBootstrap - это неофициальный метод для загрузки Mixin при загрузке Modlauncher в майнкрафте, для обычного игрока является самой классической библиотекой без которой не работают другие моды.
Моды требующие данную библиотеку:
Immersive Portals с загрузчиком Minecraft Forge, а последнее время таких модов стало в разы больше.
Mixin 0.7-0.8 Compatibility [1.12.2]
Мод Mixin - это очередной технический мод созданный NotStirred, он используется для корректного запуска некоторых модов при запуске игры, как правильно если для таких модов не установить Mixin, игра даже не попытается запуститься.
Есть похожий мод MixinBootstrap и даже MixinBooter, увы, они не могут заменить друг друга, потому обращайте внимание какие дополнительные моды требуются для установки желаемого мода.
Mixin Bootstrap 1.15.2
MixinBootstrap – это библиотека, которая содержит неофициальный и временный способ загрузки Mixin в среду MinecraftForge. Не ожидайте поддержки от разработчика. Она требуется для работы:
-
– запуск Optifine на Forge 1.15.2.
- Snow Real Magic – новая физика для снега на блоках.
- Discord Integration – интеграция Дискорда в игровой чат.
- Lithium – оптимизация производительности сервера.
– улучшения для светового движка. - Water Erosion – вода разрушает ландшафт, создает ручьи и водопады.
Модификацию разработал LX_Gaming. Исходный код проекта лежит на Github.
Как установить мод:
- Установи Forge
- Установи все дополнительные моды если указано.
- Скачай мод и скопируй в .minecraft/mods
- В лаунчере запускай версию игры с форджем.
1.14.4\1.15.1\1.15.2: mixinbootstrap-1.0.0.jar [1018,31 Kb]
1.14.4\1.15.2 (v0.1) : mixinbootstrap-1.0.1.jar [1 Mb]
1.14.4\1.15.2 (v0.2) : mixinbootstrap-1.0.2.jar [1,01 Mb]
1.12.2\1.14.4\1.15.2\1.16.1 (v0.3) : mixinbootstrap-1.0.3.jar [1,01 Mb]
1.12.2\1.14.4\1.15.2\1.16.x (new v0.5) : mixinbootstrap-1.0.5.jar [1,02 Mb]
1.12.2\1.14.4\1.15.2\1.16.5\1.17.1 (new v1.1) : _mixinbootstrap-1.1.0.jar [1,07 Mb]
Читайте также: