Руководства

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.