Устали настраивать мод? Хотите действовать? Я тоже! Вот еще пара моментов и начнем 😀
Список уроков
Урок третий. Настраиваем файл mods.toml
Настройка мода. Последние штрихи
Нам мод уже есть в игре, но пользы от него мало. Давайте это исправим и выведем сообщение в лог (log — журнал).
Но сначала поправим имена папок и файлов. Ведь мод то теперь не example.
Нажимаем правой кнопкой мыши, находим пункт Refactor, а в выпавшем меню выбираем Rename…
В открывшемся окошке заменяем строку на путь, который мы указывали в файле Gradle.
И нажимаем Refactor. Если в папке осталась пустая папка com.example — просто удалите её.
То же самое делаем с файлом ExampleMod.java. Имя предлагаю записать в виде AwesomeMod, MyTestMod, GSMod и т.д.
Желанная практика
Если у вас все выглядит аналогично примеру — переходим к написанию кода!
В этой версии Forge заботливо создали несколько функций и регистров для нашего мода, но пока в них особой необходимости нет. Их можно скрыть комментариями или временно удалить. Комментарий, это фрагмент кода, который будет пропущен компилятором и никак не влияет на код. Удобно, если нужно написать описание функции там, или переменной. Подробнее читайте сами!
В Java это можно сделать такой командой //, что создаст однострочный комментарий, или же /*многострочный…
….
…комментарий*/
Тут удобнее использовать многострочный.
В конечном итоге получаем такую вот картину
Тут вы можете увидеть константу LOGGER. Обязательно должен быть импортирован так: org.apache.logging.log4j.Logger. Не подходят java.util или org.apache.logging.log4j.core.
Создадим конструктор для нашего класса. Конструктор в ООП — специальный блок кода, вызываемый при создании объекта. В коде изначально он был, но уже с кодом, который можно будет разобрать позже, а потому напишем свой.
Но сначала добавим еще одну константу типа String (строка). Назовем её MODID (Mod Id). Её значением и будет mod id мода. Она нам еще будет нужна и не раз.
Для создания конструктора сначала пишем модификатор доступа public, чтобы Forge имел к нему доступ. Далее идет имя класса, скобки () и фигурные скобки {}. Результат вот:
1 2 3 |
public TutorialGSMod () { } |
И записываем сюда команду для logger-а: LOGGER.debug(«Hello from » + MODID + «!»);. Теперь весь код должен выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package mod.astler.tutorial_mod_gs; import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @Mod(TutorialGSMod.MODID) public class TutorialGSMod { public static final Logger LOGGER = LogManager.getLogger(); public static final String MODID = "tutorial_mod_gs"; public TutorialGSMod () { LOGGER.debug("Hello from " + MODID + "!"); } } |
Отдельное внимание на строку 7:@Mod(TutorialGSMod.MODID). В прошлом уроке ID мода мы написали так, но это не особо удобно, да и у нас так не принято. Ведь если нужно изменить ID мода, или это вовсе другой мод, то любое использование id мода нужно будет ручками искать и менять. Константа другое дело. Везде записал константу, а если нужно — изменить только её значение! И вот это уже наша тема.
Ладно, написали, а теперь запускаем!
Игра запустилась, но вывелось ли наше сообщение? Проверяем код. Да, оно есть:
Может подобное сообщение всего набор символов в консоли пока, ведь это он и есть. Но в будущем именно подобный вывод поможет найти ошибки, проверить последовательность выполнения операций и многое-многое другое. Сложно и описать, как она важна.
Так что там с предметом?
Но хватит тянуть кота, добавляем наконец предмет!
Для этого добавим новый класс (и соотв. новый файл) в папку с файлом нашего мода, назовем его ModEventSubscriber. Как вы могли заметить, он должен быть в формате .java. Это важно!
Теперь добавим в самое начало @EventBusSubscriber (и импортируем, если это не произошло автоматически import net.minecraftforge.fml.common.Mod).
Что это такое? Аннотация @EventBusSubscriber сообщает Forge, что этот класс содержит методы, которые должны быть добавлены в обработчик событий. Она содержит параметры modid, bus и value.
В качестве modid указываем нашу константу MainModClass.MODID (замените на ваше имя класса), что сообщит Forge, что этот класс принадлежит именно нашему моду. Это важно, так как @EventBusSubscriber сканирование выполняется до полной загрузки модов и Forge не поймет сам к какому моду относится класс, который он сканирует.
Далее указываем параметр bus. Просто пишем: bus = EventBusSubscriber.Bus.MOD. Этот параметр сообщает Forge, что метод @SubscribeEvent в этом классе должен получать события от шины событий MOD.
Если вы все сделали правильно, то результат должен быть таким:
1 2 3 4 5 6 7 8 |
package mod.astler.tutorial_mod_gs; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(modid = TutorialGSMod.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModEventSubscriber { } |
Теперь, прям в тело класса вставляем три функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package mod.astler.tutorial_mod_gs; import net.minecraft.item.Item; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.IForgeRegistryEntry; @Mod.EventBusSubscriber(modid = TutorialGSMod.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModEventSubscriber { @SubscribeEvent public static void onRegisterItem(RegistryEvent.Register<Item> event) { } public static <T extends IForgeRegistryEntry<T>> T setup(final T entry, final String name) { return setup(entry, new ResourceLocation(TutorialGSMod.MODID, name)); } public static <T extends IForgeRegistryEntry<T>> T setup(final T entry, final ResourceLocation registryName) { entry.setRegistryName(registryName); return entry; } } |
Сейчас скорее всего это все довольно сложно, но на деле мы просто добавили функцию onRegisterItem. Ей единственный параметр RegistryEvent.Register<Item> нужен нам для регистрации наших предметов в игре. А предостовляет его Forge при помощи аннотации @SubscribeEvent, посредствой которой мы сообщаем, что хотим вызвать эту функцию когда наступит момент регистрации модом своих предметов. Подробнее можно прочитать тут.
Так как я взял за базу английские уроки, то местами следую советам автора. Как он и говорит, ожидать полного понимания тут не стоит, ведь это все работает вместо с продвинутыми концептами Java, такими как Generics и работает через сложную реализацию со стороны Forge. Плюс ко всему тут еще и перегрузка метода, такие вот дела.
В любом случае setup будет универсальным методом для многих других элементов игры (Предметы, Блоки, Измерения, TileEntity, EntityEntry). Да и функция хороша, ведь всегда можно неправильно настоить регистрацию ваших записей и полностью сломать мод (и не только ваш!).
Наконец заветный код нового предмета. Пишем внутри onRegisterItems такой вот текст:
1 2 3 4 5 6 |
@SubscribeEvent public static void onRegisterItem(RegistryEvent.Register<Item> event) { event.getRegistry().registerAll( setup(new Item(new Item.Properties()), "sausage") ); } |
Где мы регистрируем результат выполнения функции setup, параметрами которой мы указали новый Item, т.е. предмет и его имя: Сосиска.
Думаю можно еще добавить к Item.Properties() такой вот текст: .group(ItemGroup.FOOD). Эта команда разместит наш предмет на вкладке еды в творческом режиме! Так его будет проще найти в игре!
Хорошо, сверим код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package mod.astler.tutorial_mod_gs; import net.minecraft.item.Item; import net.minecraft.item.ItemGroup; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.IForgeRegistryEntry; @Mod.EventBusSubscriber(modid = TutorialGSMod.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModEventSubscriber { @SubscribeEvent public static void onRegisterItem(RegistryEvent.Register<Item> event) { event.getRegistry().registerAll( setup(new Item(new Item.Properties().group(ItemGroup.FOOD)), "sausage") ); } public static <T extends IForgeRegistryEntry<T>> T setup(final T entry, final String name) { return setup(entry, new ResourceLocation(TutorialGSMod.MODID, name)); } public static <T extends IForgeRegistryEntry<T>> T setup(final T entry, final ResourceLocation registryName) { entry.setRegistryName(registryName); return entry; } } |
И если все в норме — запускаем!
Начало большего
Ах, да, предмет ведь еще не имеет ни текстуры, ни модели, а значит будет выглядеть несколько пугающе. Но все же это он, ваш, родной 😀
Ура. Все работает и вкладка еды обзавелась новеньким. Выглядит страшненько, конечно, но скоро мы это исправим. Потом научимся делать настоящую еду, ведь сейчас с ним нельзя сделать вот вообще ничего.
На этом пока все. Можете пока составить план предметов, и набросать рецепты. Ведь дальше только интереснее!
Переводом и адаптацией занимался Astler. За базу брал статью тут. Спасибо за внимание!