Расширение перечислений 26.1.2
Узнайте, как добавлять элементы в перечисления (enum) с помощью Mixin и Class Tweakers.
Расширение перечисления (Enum) — это функция миксинов (Mixin), которая позволяет надежно добавлять новые записи в перечисление.
При работе с перечислениями Minecraft можно использовать миксины вместе с твикенгом классов, чтобы отображать новые записи перечислений в декомпилированном исходном коде. Если для него установлено значение транзитивный, то моды, зависящие от вашего, также увидят добавленные вами записи.
WARNING
Для поддержки миксинов в расширении Enum требуется как минимум Loader 0.19.0, а для поддержки твикинга классов — как минимум Loom 1.16.
Кроме того, в заголовках файлов твикеров класса должно быть указано v2 как версию для использования расширений перечислений.
Создание миксинов
Перед созданием класса миксина убедитесь, что версия Loader 0.19.0 или выше является явной зависимостью в вашем файле fabric.mod.json:
json
...
"depends": {
...
"fabricloader": ">=0.19.0"
...
}
...Даже если вы используете правильную версию Loader в качестве зависимости Gradle, вам необходимо явно указать зависимость как минимум от версии 0.19.0, чтобы включить эту функцию Mixin.
Чтобы создать расширение перечислений, создайте enum в вашем пакете миксинов, аннотируйте его @Mixin и добавьте в него ваши константы, как если бы они были частью целевого класса перечислений. Например, добавим новую запись в RecipeBookType:
java
@Mixin(RecipeBookType.class)
enum RecipeBookTypeMixin {
EXAMPLE_MOD_RECIPE_BOOK_TYPE
}1
2
3
4
2
3
4
ВАЖНО
Для обеспечения уникальности всегда следует добавлять перед добавляемыми константами перечисления ID вашего мода. Для документации мы будем использовать EXAMPLE_MOD_.
Передача аргументов конструктора
Если у целевого перечисления нет конструктора по умолчанию, необходимо переопределить конструктор целевого класса и передать необходимые аргументы в объявление добавленной записи.
Например, давайте добавим новую запись в категорию RecipeCategory. Создайте в целевом классе конструктор, соответствующий требуемому, и аннотируйте его с помощью @Shadow.
java
@Mixin(RecipeCategory.class)
enum RecipeCategoryMixin {
EXAMPLE_MOD_RECIPE_CATEGORY("exampleModRecipeFolderName");
@Shadow
RecipeCategoryMixin(String recipeFolderName) {
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Реализация абстрактных методов
Чтобы реализовать абстрактные методы целевого перечисления, переопределите и реализуйте их в добавляемой записи. Например, давайте добавим новую запись ConversionType:
java
@Mixin(ConversionType.class)
enum ConversionTypeMixin {
EXAMPLE_MOD_CONVERSION_TYPE(false) {
@Override
void convert(Mob from, Mob to, ConversionParams params) {
// ...
}
};
@Shadow
abstract void convert(Mob from, Mob to, ConversionParams params);
@Shadow
ConversionTypeMixin(final boolean discardAfterConversion) {
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Создание Class Tweaker
Если вы работаете с перечислением Minecraft, вы можете использовать запись в твикере классов, чтобы визуально изменить целевое перечисление в декомпилированном исходном коде.
Чтобы включить эту функцию, не забудьте использовать Loom версии 1.16 или выше и установить версию заголовка файла на v2.
Синтаксис для расширения перечисления следующий:
classtweaker
extend-enum <targetClassName> <ENUM_CONSTANT_NAME>Для твикинга классов используются их внутренние имена.
Например, запись в твикере класса для константы RecipeBookType, которую мы добавили в раздел mixin, будет выглядеть так:
classtweaker
extend-enum net/minecraft/world/inventory/RecipeBookType EXAMPLE_MOD_RECIPE_BOOK_TYPEПрименение изменений
Вам потребуется обновить проект Gradle и выполнить перегенерацию исходных файлов, прежде чем вы сможете увидеть добавленные вами записи перечисления в декомпилированном исходном коде. Если изменения не отображаются, вы можете попробовать проверить файл и проверить, не появились ли какие-либо ошибки.
INFO
В декомпилированном исходном коде вы не увидите переданные аргументы конструктора, реализации методов или другие элементы. Это происходит потому, что эти параметры обрабатываются миксином и применяются только во время выполнения.
Теперь вы можете использовать константу перечисления в своем коде:
java
void exampleRecipeBookTypeMethod(RecipeBookType recipeBookType) {
if (recipeBookType == RecipeBookType.EXAMPLE_MOD_RECIPE_BOOK_TYPE) {
// ...
}
}1
2
3
4
5
2
3
4
5
Если вы добавляете его только с помощью миксинов, и он отсутствует в декомпилированном исходном коде, вы можете проверить это, сравнив имя:
java
void exampleRecipeBookTypeMethodNoCT(RecipeBookType recipeBookType) {
if (recipeBookType.name().equals("EXAMPLE_MOD_RECIPE_BOOK_TYPE")) {
// ...
}
}1
2
3
4
5
2
3
4
5
Если вам необходимо использовать константу в нескольких областях, получите её, вызвав метод valueOf, и сохраните результат в поле:
java
public static final RecipeBookType ADDED_RECIPE_BOOK_TYPE = RecipeBookType.valueOf("EXAMPLE_MOD_RECIPE_BOOK_TYPE");1
Подводные камни
Расширение перечисления не может гарантировать, что добавленные вами записи не приведут к сбоям.
Ваша задача — изучить использование целевого перечисления и постараться предотвратить ошибки, где это возможно. Если вам не удаётся решить некоторые проблемы, и возникают сбои, возможно, лучше вообще не использовать расширение перечисления.
В этом разделе рассматриваются некоторые шаблоны, на которые следует обращать внимание и которых следует избегать при расширении перечислений, но он не является исчерпывающим.
Выражения switch
Оператор switch часто используется для работы с константами перечислений. Из-за этого может произойти сбой, если выражение switch не обрабатывает записи, добавленные другими модами. Например, предположим, у нас есть следующее выражение switch:
java
void exampleProblematicSwitch(RecipeBookType recipeBookType) {
String s = switch (recipeBookType) {
case SMOKER -> "smoker";
case CRAFTING -> "crafting";
case FURNACE -> "furnace";
case BLAST_FURNACE -> "blast_furnace";
case EXAMPLE_MOD_RECIPE_BOOK_TYPE -> "example_mod_recipe_book_type";
};
// ...
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Обратите внимание, что здесь отсутствует пункт default. Несмотря на то, что мы обработали все значения в ванильном перечислении, включая наши собственные, это вызовет ошибку, если другой мод добавит другую запись.
Как можно избежать этого? Единого универсального способа избежать сбоев не существует - ваш подход должен адаптироваться к каждому конкретному случаю. В целом, однако:
- Если выражение
switchнаходится в стандартном методе, вы можете использовать миксины для его редактирования - Если выражение
switchвзято из модификации, вам следует связаться с разработчиками, чтобы совместно разработать совместимый подход. В противном случае вам, возможно, придётся создать примись для другого мода.
Сериализованные перечисления
Некоторые записи в перечислениях сериализуются автоматически. Примером может служить перечисление Variants в классе Axolotl.
Расширение этих перечислений приведёт к сериализации вашей пользовательской записи в пространстве имён Minecraft, а в некоторых версиях это может происходить на основе числового ID. Это не очень хорошо, потому что может повлиять на индексы всех остальных записей.
Лучше вообще избегать расширения перечислений, если их элементы сериализуются так. Вместо этого, если хотите, вы можете поискать в API.






