Посібники

Kotlin і Java

Для великих модів. Те саме API, що й у CraftScript, зі статичними типами, повноцінним IDE та будь-якою бібліотекою Java, а також кілька частин, які поки що є лише тут. Цей посібник використовує Kotlin; Java працює так само й показана наприкінці.

Що можна зробити

Сфера В API У CraftScript
Предмети, блоки, рецепти ContentBuilder: item, block, shaped, shapeless, smelting Так
Істоти ContentBuilder.creature: тіло, текстура, характеристики, Behaviour, TargetRule, здобич, природна поява, приборкання, розведення, їзда, власні поведінки Так
Звуки й частинки ContentBuilder.sound, ContentBuilder.particle Так
Генерація світу ContentBuilder: ore, tree, patch, structure (пули, plan/build), biome, dimension, feature, terrain, biomes, carver, tag. Дивись Генерація світу і Генерація кодом Усе, крім feature, tag, salt і aboveBottom
Інтерфейс Form і player.showForm; server.bossBar; player.setSidebar Так
Події Crafting.events.on<E> { }, з пріоритетом і скасуванням Так
Ефекти, атрибути, спорядження addEffect, attribute, setAttribute, equipment, setEquipment, EquipmentSlot Yes
Клавіші ContentBuilder.key, PlayerKeyEvent Yes
Команди Crafting.commands.command(…): підкоманди, типізовані аргументи, дозволи, псевдоніми Так
Світ і гравці World, Block, Entity, Player, Inventory, Server Так
Завдання Crafting.scheduler: runLater, runRepeating, runAsync, runSync Без фонової роботи
Налаштування у файлах Типізований Config у TOML, може синхронізуватися з клієнтами Так (config)
Мережа Crafting.network.channel(…), з власними кодеками Так (listen, send)
Збережені дані player.data["rubies.visits"] = n, data.getInt(…); також у сутностей, блоків і світів Так
Бібліотеки Java Будь-які Ні

Того, чого в API ще немає, немає в жодній мові. Не обходь це викликами класів Minecraft: мод втратить гарантію, що працюватиме й далі, коли гра зміниться.

Створи проєкт

Термінал
crafting new rubies --kind kotlin
cd rubies && gradle build

API поки немає в публічному репозиторії Maven. Встанови його у свій локальний Maven з вихідного коду Crafting і оголоси як compileOnly: під час роботи Crafting сам надає його моду.

Термінал
cd crafting && ./gradlew :crafting-api:publishToMavenLocal
build.gradle.ktsKotlin
plugins { kotlin("jvm") version "2.4.21" }

repositories { mavenCentral(); mavenLocal() }

dependencies {
    compileOnly("dev.crafting:crafting-api:0.1.0")
}

kotlin { jvmToolchain(25) } // Minecraft 26.x працює на Java 25

crafting new генерує цей файл і проєкт, уже розділений за відповідальностями: <Name>Mod.kt, який лише з’єднує частини, content/ModContent.kt, events/Welcome.kt і commands/GemCommand.kt.

Структура

Пакети за темами, вхідний клас, який лише з’єднує частини, і один файл на частину. Посібник Як організувати мод пояснює чому; ось повний приклад мода:

СтруктураТекст
rubies/
├── build.gradle.kts
├── settings.gradle.kts
└── src/main/
    ├── kotlin/com/example/rubies/
    │   ├── RubiesMod.kt         entry point: only wires the parts
    │   ├── RubiesConfig.kt      player settings
    │   ├── content/
    │   │   ├── Items.kt
    │   │   ├── Blocks.kt
    │   │   ├── Creatures.kt
    │   │   ├── Sounds.kt
    │   │   ├── Particles.kt
    │   │   └── Recipes.kt
    │   ├── storage/
    │   │   └── Visits.kt        saved data: the keys, in one place
    │   ├── events/
    │   │   ├── MiningEvents.kt
    │   │   ├── CombatEvents.kt
    │   │   └── WelcomeEvents.kt
    │   ├── ui/
    │   │   └── ShopForm.kt       forms: what the player sees
    │   └── commands/
    │       ├── RubyCommand.kt
    │       └── ShopCommand.kt
    └── resources/
        ├── crafting.mod.json
        └── assets/rubies/
            ├── sounds/ruby/chime.ogg
            └── textures/
                ├── item/ruby.png
                ├── entity/ruby_golem.png
                └── particle/ruby_spark_0.png, ruby_spark_1.png

src/main/kotlin/com/example/rubies/

Вхідний клас сам нічого не робить: створює спільне й викликає кожну частину.

src/main/kotlin/com/example/rubies/RubiesMod.ktKotlin
package com.example.rubies

class RubiesMod : CraftingMod {
    override fun onLoad(ctx: ModContext) {
        val config = Crafting.configs.load(RubiesConfig())
        val content = ContentBuilder(ctx.metadata.id, Crafting.registries)

        Items.register(content)
        Blocks.register(content)
        Creatures.register(content)
        Sounds.register(content)
        Particles.register(content)
        Recipes.register(content)

        MiningEvents.register(config)
        CombatEvents.register(config)
        WelcomeEvents.register()
        RubyCommand.register()
        ShopCommand.register()
    }
}

Маніфест

src/main/resources/crafting.mod.jsonJSON
{
  "id": "rubies",
  "name": "Rubies",
  "version": "1.0.0",
  "kind": "mod",
  "side": "both",
  "entrypoints": ["com.example.rubies.RubiesMod"],
  "depends": { "crafting": ">=0.1.0" }
}
Поле Що це
id 2–64 символи: малі літери, цифри, _ або -, починається з літери. Це також простір назв для твоїх предметів і блоків.
kind mod або plugin (лише сервер, з перезавантаженням; дивись нижче).
side both, client або server. Клієнтський мод мовчки ігнорується на виділеному сервері.
entrypoints Класи, що реалізують CraftingMod.
depends, optional, incompatible Id → діапазон версій: >=1.2 <2, ^1.4, ~1.4.2, 1.20.x, [1.0,2.0). Якщо щось не так, Crafting повідомляє про всі проблеми одразу, з порадою.
loadAfter, loadBefore Порядок завантаження без жорсткої залежності.

Життєвий цикл

dev.crafting.api.mod.CraftingModKotlin
interface CraftingMod {
    fun onLoad(ctx: ModContext)
    fun onReady(ctx: ModContext) {}
    fun onShutdown(ctx: ModContext) {}
}
  1. onLoad: зареєструй вміст, події й команди.
  2. Crafting створює блоки й предмети в грі. Відтепер реєстрація вмісту викидає RegistryFrozenException.
  3. onReady: усі моди завантажено; Crafting.runtime.isModLoaded("other_mod") скаже, чи є інший мод.
  4. onShutdown: під час виходу.

Серверні плагіни

Плагін — це CraftingMod з "kind": "plugin" і "side": "server"; crafting new chatguard --kind plugin створює вхідний клас, events/ChatFilter.kt і commands/HelloCommand.kt. Він не додає блоків чи предметів, тож гравці зі звичайним Minecraft без модів можуть заходити на сервер, де є лише плагіни. Ти вмикаєш, вимикаєш і перезавантажуєш його без перезапуску: onLoad і onReady виконуються, коли його вмикають, onShutdown — коли вимикають або перезавантажують.

Термінал
crafting new chatguard --kind plugin
src/main/kotlin/com/example/chatguard/ChatGuardPlugin.ktKotlin
package com.example.chatguard

class ChatGuardPlugin : CraftingMod {
    override fun onLoad(ctx: ModContext) {
        ChatFilter.register()
        Crafting.commands.command("guard") {
            permission = "chatguard.admin"
            executes { it.reply("ChatGuard активний") }
        }
    }

    override fun onShutdown(ctx: ModContext) = ctx.logger.info("ChatGuard вимкнено")
}
src/main/kotlin/com/example/chatguard/events/ChatFilter.ktKotlin
package com.example.chatguard.events

object ChatFilter {
    private val blocked = listOf("spam", "scam")

    fun register() {
        Crafting.events.on<PlayerChatEvent> { e ->
            if (blocked.any { it in e.message.lowercase() }) {
                e.cancel()
                e.player.send("&cБудь ласка, спілкуйся дружньо")
            }
        }
    }
}

Усе, що реєструє плагін, прив’язане до нього. /crafting reload chatguard викликає onShutdown, прибирає його слухачів, команди й завдання, знову читає jar і викликає onLoad. У режимі розробки це відбувається автоматично, коли ти замінюєш jar.

Мод Плагін
Тека mods/ plugins/
Сторона Клієнт і/або сервер Лише сервер
Нові блоки й предмети Так Ні
Перезавантаження без перезапуску Ні Так
Можуть заходити клієнти без модів Ні, якщо він додає вміст Так

Java

Те саме API, та сама структура. З Java точкою входу є Crafting.INSTANCE, ти передаєш кожен параметр, що має значення за замовчуванням, а слухач повертає Unit.INSTANCE:

src/main/java/com/example/rubies/RubiesMod.javaJava
package com.example.rubies;

public final class RubiesMod implements CraftingMod {
    @Override
    public void onLoad(ModContext ctx) {
        ContentBuilder content = new ContentBuilder(ctx.getMetadata().getId(), Crafting.INSTANCE.getRegistries());
        Items.register(content);
        WelcomeEvents.register();
    }
}
src/main/java/com/example/rubies/events/WelcomeEvents.javaJava
package com.example.rubies.events;

public final class WelcomeEvents {
    public static void register() {
        Crafting.INSTANCE.getEvents().subscribe(PlayerJoinEvent.class, EventPriority.NORMAL, false, null, e -> {
            e.getPlayer().send("&aWelcome, " + e.getPlayer().getName() + "!");
            return Unit.INSTANCE;
        });
    }
}

Старі способи

Вони досі працюють, але позначені як застарілі й мають заміну:

Раніше Тепер
class X : CraftingPlugin() з onEnable/onDisable CraftingMod з "kind": "plugin"
Методи з @Subscribe і events.register(obj) events.on<E> { } (Java: events.subscribe(E.class, …))
@Mod("id") на класі Клас у entrypoints маніфесту
EntityDamageEvent.source damageType (id, наприклад minecraft:fall)
world.setBlockState(x, y, z, "…[open=true]") world.setBlock(x, y, z, type, mapOf("open" to true))

Правила

  • Не імпортуй нічого з net.minecraft. Саме це дає моду змогу працювати й далі, коли гра змінюється.
  • Реєструй вміст лише в onLoad.
  • Завдання runAsync не чіпає світ; повертайся в ігровий потік через runSync.