Як розробляти моди для Minecraft за допомогою Temporal API?

Ця стаття призначена для тих, хто вже розробляв моди для Minecraft або лише планує спробувати себе в цьому напрямі. Йтиметься про спрощення розробки на NeoForge за допомогою бібліотеки Temporal API.

Що таке NeoForge?

Перш ніж переходити до Temporal API, варто коротко пояснити, що таке NeoForge.

NeoForge — це сучасний open-source API та модлоадер для Minecraft: Java Edition, створений як форк Minecraft Forge.

Простими словами, NeoForge — це один із найпопулярніших модлоадерів (прошарок між модами та грою) поряд із Fabric, Quilt та Forge.

NeoForge з’явився відносно нещодавно та розробляється частиною команди, яка раніше працювала над Forge. Наразі він активно підтримується й оновлюється, а більшість нових модів створюється саме на ньому. Тому для нових проєктів я рекомендую обирати саме NeoForge.

Одна з головних проблем NeoForge

Під час розробки модів на NeoForge виникає значна кількість шаблонного (boilerplate) коду. Це ускладнює підтримку проєкту та відволікає від створення унікального функціоналу.

Як приклад — створення звичайного предмета та додавання простої моделі у Minecraft 1.21.1 (без урахування налаштування проєкту, лише Java-частина):

@Mod(ExampleMod.MOD_ID)
public class ExampleMod { // 1
    public static final String MOD_ID = "example";

    public ExampleMod(IEventBus modEventBus, ModContainer modContainer) {
        ExampleItems.ITEMS.register(modEventBus); // 5
        NeoForge.EVENT_BUS.register(this); // 10
    }

    @SubscribeEvent
    public static void gatherData(GatherDataEvent event) { // 8
        DataGenerator generator = event.getGenerator();
        PackOutput output = generator.getPackOutput();
        ExistingFileHelper existingFileHelper = event.getExistingFileHelper();

        generator.addProvider(
            event.includeClient(),
            new ExampleItemModelProvider(output, existingFileHelper) // 9
        );
    }
}

public class ExampleItems {  // 2
    public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID);  // 3

    public static final DeferredItem<Item> EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties()); // 4
}

public class ExampleItemModelProvider extends ItemModelProvider { // 6
    public ExampleItemModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
        super(output, "examplemod", existingFileHelper);
    }

    @Override
    protected void registerModels() {
        basicItem(ExampleItems.EXAMPLE_ITEM.get()); // 7
    }
}

Що відбувається в цьому коді:

  1. Створюється головний клас моду. Його конструктор фактично виконує роль main.
  2. Створюється клас для зберігання предметів.
  3. Створюється DeferredRegister — контейнер, який дозволяє NeoForge коректно зареєструвати предмети в грі.
  4. Реєструється звичайний предмет.
  5. Контейнер предметів підключається до NeoForge.
  6. Створюється клас для генерації моделей.
  7. Описується базова модель.
  8. Підписка на GatherDataEvent для генерації даних.
  9. Підключається клас генератора моделей.
  10. Реєструються всі події в ExampleMod.

У невеликих прикладах це виглядає зрозуміло. Але зі зростанням кількості контенту код швидко розширюється, і його підтримка стає складнішою.

Temporal API

Що таке Temporal API?

Temporal API — це фреймворк, який автоматизує більшість рутинних задач під час розробки модів.

Він базується на рефлексії та анотаціях. Концептуально його можна порівняти з Spring Framework, але у світі моддингу.

Як Temporal API вирішує цю проблему

Перепишемо той самий приклад із використанням Temporal API:

@Mod(ExampleMod.MOD_ID)
public class ExampleMod { // 1
    public static final String MOD_ID = "example";

    public ExampleMod(IEventBus modEventBus, ModContainer modContainer) {
        TemporalEngine.run(ExampleMod.class, modEventBus, modContainer); // 2
    }
}

public final class ExampleItems { // 3
    private static final ItemFactory ITEM_FACTORY = InjectionPool.getFromInstance(ItemFactory.class); // 4

    @GenerateBasicItemModel // 6
    public static final DeferredItem<?> EXAMPLE_ITEM = ITEM_FACTORY.create("example_item"); // 5
}

Код став значно коротшим, але функціональність залишилася тією самою:

  1. Створюється клас моду.
  2. Ініціалізується Temporal API через TemporalEngine.run(...) — це точка входу фреймворку.
  3. Створюється клас із предметами.
  4. Отримується ItemFactory — обгортка над DeferredRegister.Items (всередині використовується TemporalRegister.Items).
  5. Створюється простий предмет.
  6. Модель генерується через анотацію @GenerateBasicItemModel.

І це лише один приклад. Temporal API також містить набір утиліт і власну систему анотацій, яку можна розширювати за потреби.

Недоліки Temporal API

Основний недолік — підтримка версій.

Остання версія Temporal API орієнтована на Minecraft 1.21.1. Перенесення функціональності на нові та старіші версії потребує додаткових ресурсів і контриб’юторів.

Де почитати більше

Temporal API — це open-source проєкт:

Висновок

Наприкінці кілька запитань до спільноти:

  • Чи доводилося вам розробляти моди для Minecraft?
  • Якщо так, який модлоадер і версію гри ви використовували?
  • Якщо ні, чи хотіли б спробувати?
  • Чи варто написати окрему статтю з детальним розбором того, як працює Temporal API «під капотом»?

Дякую за увагу.

Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини про геймдев

👍ПодобаєтьсяСподобалось2
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Було б цікаво спробувати. Плюс гадаю, що на ДОУ є багато користувачів, які грають і цікаво було б поєднати гру з розширенням знань.

Дякую, якщо буде інтерес, то підготую більш практичний приклад із покроковим розбором.

Підписатись на коментарі